diff --git a/backend/backend.c b/backend/backend.c index de2acfe3..e4e8c8d8 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 7bc06f90..09be3551 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 e7d230b9..6e226958 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 da57cae9..938ca73c 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 da89bb41..818f4549 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -663,26 +663,13 @@ bool wlr_output_test_state(struct wlr_output *output, return success; } -bool wlr_output_commit_state(struct wlr_output *output, - const struct wlr_output_state *state) { - uint32_t unchanged = output_compare_state(output, state); - - // Create a shallow copy of the state with only the fields which have been - // changed and potentially a new buffer. - struct wlr_output_state pending = *state; - pending.committed &= ~unchanged; - - if (!output_basic_test(output, &pending)) { +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; } - bool new_back_buffer = false; - if (!output_ensure_buffer(output, &pending, &new_back_buffer)) { - return false; - } - - if ((pending.committed & WLR_OUTPUT_STATE_BUFFER) && + if ((state->committed & WLR_OUTPUT_STATE_BUFFER) && output->idle_frame != NULL) { wl_event_source_remove(output->idle_frame); output->idle_frame = NULL; @@ -694,17 +681,14 @@ bool wlr_output_commit_state(struct wlr_output *output, struct wlr_output_event_precommit pre_event = { .output = output, .when = &now, - .state = &pending, + .state = state, }; wl_signal_emit_mutable(&output->events.precommit, &pre_event); - if (!output->impl->commit(output, &pending)) { - if (new_back_buffer) { - wlr_buffer_unlock(pending.buffer); - } - return false; - } + 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)) { @@ -712,14 +696,49 @@ bool wlr_output_commit_state(struct wlr_output *output, output->needs_frame = false; } - output_apply_state(output, &pending); + output_apply_state(output, state); + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); struct wlr_output_event_commit event = { .output = output, .when = &now, - .state = &pending, + .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); + + // Create a shallow copy of the state with only the fields which have been + // changed and potentially a new buffer. + struct wlr_output_state pending = *state; + pending.committed &= ~unchanged; + + if (!output_basic_test(output, &pending)) { + wlr_log(WLR_ERROR, "Basic output test failed for %s", output->name); + return false; + } + + bool new_back_buffer = false; + if (!output_ensure_buffer(output, &pending, &new_back_buffer)) { + return false; + } + + if (!output_prepare_commit(output, &pending)) { + return false; + } + + if (!output->impl->commit(output, &pending)) { + if (new_back_buffer) { + wlr_buffer_unlock(pending.buffer); + } + return false; + } + + output_apply_commit(output, &pending); if (new_back_buffer) { wlr_buffer_unlock(pending.buffer);