diff --git a/backend/backend.c b/backend/backend.c index de2acfe38..e4e8c8d8e 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -15,6 +15,7 @@ #include "backend/backend.h" #include "backend/multi.h" #include "render/allocator/allocator.h" +#include "types/wlr_output.h" #include "util/env.h" #include "util/time.h" @@ -445,3 +446,52 @@ error: #endif return NULL; } + +bool wlr_backend_test(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len) { + if (backend->impl->test) { + return backend->impl->test(backend, states, states_len); + } + + for (size_t i = 0; i < states_len; i++) { + const struct wlr_backend_output_state *state = &states[i]; + assert(state->output->backend == backend); + if (!wlr_output_test_state(states[i].output, &state->base)) { + return false; + } + } + + return true; +} + +bool wlr_backend_commit(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len) { + if (!backend->impl->commit) { + for (size_t i = 0; i < states_len; i++) { + const struct wlr_backend_output_state *state = &states[i]; + if (!wlr_output_commit_state(state->output, &state->base)) { + return false; + } + } + + return true; + } + + for (size_t i = 0; i < states_len; i++) { + const struct wlr_backend_output_state *state = &states[i]; + if (!output_prepare_commit(state->output, &state->base)) { + return false; + } + } + + if (!backend->impl->commit(backend, states, states_len)) { + return false; + } + + for (size_t i = 0; i < states_len; i++) { + const struct wlr_backend_output_state *state = &states[i]; + output_apply_commit(state->output, &state->base); + } + + return true; +} diff --git a/include/types/wlr_output.h b/include/types/wlr_output.h index 7bc06f90e..09be35510 100644 --- a/include/types/wlr_output.h +++ b/include/types/wlr_output.h @@ -22,4 +22,7 @@ bool output_cursor_set_texture(struct wlr_output_cursor *cursor, void output_defer_present(struct wlr_output *output, struct wlr_output_event_present event); +bool output_prepare_commit(struct wlr_output *output, const struct wlr_output_state *state); +void output_apply_commit(struct wlr_output *output, const struct wlr_output_state *state); + #endif diff --git a/include/wlr/backend.h b/include/wlr/backend.h index e7d230b97..6e2269586 100644 --- a/include/wlr/backend.h +++ b/include/wlr/backend.h @@ -10,10 +10,19 @@ #define WLR_BACKEND_H #include +#include struct wlr_session; struct wlr_backend_impl; +/** + * Per-output state for wlr_backend_test() and wlr_backend_commit(). + */ +struct wlr_backend_output_state { + struct wlr_output *output; + struct wlr_output_state base; +}; + /** * A backend provides a set of input and output devices. */ @@ -62,4 +71,23 @@ void wlr_backend_destroy(struct wlr_backend *backend); */ int wlr_backend_get_drm_fd(struct wlr_backend *backend); +/** + * Atomically test a new configuration for multiple outputs. + * + * Some backends (e.g. DRM) have global backend-wide limitations. This function + * can be used to check whether changes across multiple outputs are supported by + * the backend. + */ +bool wlr_backend_test(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len); +/** + * Atomically apply a new configuration for multiple outputs. + * + * There is no guarantee that the changes will be applied atomically. Users + * should call wlr_backend_test() first to check that the new state is supported + * by the backend. + */ +bool wlr_backend_commit(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len); + #endif diff --git a/include/wlr/backend/interface.h b/include/wlr/backend/interface.h index da57cae90..938ca73c9 100644 --- a/include/wlr/backend/interface.h +++ b/include/wlr/backend/interface.h @@ -12,11 +12,17 @@ #include #include +struct wlr_output_state; + struct wlr_backend_impl { bool (*start)(struct wlr_backend *backend); void (*destroy)(struct wlr_backend *backend); int (*get_drm_fd)(struct wlr_backend *backend); uint32_t (*get_buffer_caps)(struct wlr_backend *backend); + bool (*test)(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len); + bool (*commit)(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len); }; /** diff --git a/types/output/output.c b/types/output/output.c index da89bb413..818f4549e 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -663,6 +663,51 @@ bool wlr_output_test_state(struct wlr_output *output, return success; } +bool output_prepare_commit(struct wlr_output *output, const struct wlr_output_state *state) { + if (!output_basic_test(output, state)) { + wlr_log(WLR_ERROR, "Basic output test failed for %s", output->name); + return false; + } + + if ((state->committed & WLR_OUTPUT_STATE_BUFFER) && + output->idle_frame != NULL) { + wl_event_source_remove(output->idle_frame); + output->idle_frame = NULL; + } + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + struct wlr_output_event_precommit pre_event = { + .output = output, + .when = &now, + .state = state, + }; + wl_signal_emit_mutable(&output->events.precommit, &pre_event); + + return true; +} + +void output_apply_commit(struct wlr_output *output, const struct wlr_output_state *state) { + output->commit_seq++; + + if (output_pending_enabled(output, state)) { + output->frame_pending = true; + output->needs_frame = false; + } + + output_apply_state(output, state); + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + struct wlr_output_event_commit event = { + .output = output, + .when = &now, + .state = state, + }; + wl_signal_emit_mutable(&output->events.commit, &event); +} + bool wlr_output_commit_state(struct wlr_output *output, const struct wlr_output_state *state) { uint32_t unchanged = output_compare_state(output, state); @@ -682,22 +727,10 @@ bool wlr_output_commit_state(struct wlr_output *output, return false; } - if ((pending.committed & WLR_OUTPUT_STATE_BUFFER) && - output->idle_frame != NULL) { - wl_event_source_remove(output->idle_frame); - output->idle_frame = NULL; + if (!output_prepare_commit(output, &pending)) { + return false; } - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - struct wlr_output_event_precommit pre_event = { - .output = output, - .when = &now, - .state = &pending, - }; - wl_signal_emit_mutable(&output->events.precommit, &pre_event); - if (!output->impl->commit(output, &pending)) { if (new_back_buffer) { wlr_buffer_unlock(pending.buffer); @@ -705,21 +738,7 @@ bool wlr_output_commit_state(struct wlr_output *output, return false; } - output->commit_seq++; - - if (output_pending_enabled(output, state)) { - output->frame_pending = true; - output->needs_frame = false; - } - - output_apply_state(output, &pending); - - struct wlr_output_event_commit event = { - .output = output, - .when = &now, - .state = &pending, - }; - wl_signal_emit_mutable(&output->events.commit, &event); + output_apply_commit(output, &pending); if (new_back_buffer) { wlr_buffer_unlock(pending.buffer);