diff --git a/include/types/wlr_output_layer.h b/include/types/wlr_output_layer.h new file mode 100644 index 000000000..64412494a --- /dev/null +++ b/include/types/wlr_output_layer.h @@ -0,0 +1,11 @@ +#ifndef TYPES_WLR_OUTPUT_LAYER_H +#define TYPES_WLR_OUTPUT_LAYER_H + +#include + +void output_layer_destroy(struct wlr_output_layer *layer); +void output_layer_state_move(struct wlr_output_layer_state *dst, + struct wlr_output_layer_state *src); +void output_layer_state_clear(struct wlr_output_layer_state *state); + +#endif diff --git a/include/wlr/interfaces/wlr_output.h b/include/wlr/interfaces/wlr_output.h index 34b3e675e..6324592a9 100644 --- a/include/wlr/interfaces/wlr_output.h +++ b/include/wlr/interfaces/wlr_output.h @@ -29,6 +29,8 @@ struct wlr_output_impl { size_t (*get_gamma_size)(struct wlr_output *output); bool (*export_dmabuf)(struct wlr_output *output, struct wlr_dmabuf_attributes *attribs); + struct wlr_output_layer *(*create_layer)(struct wlr_output *output); + void (*destroy_layer)(struct wlr_output_layer *layer); }; void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, @@ -44,4 +46,7 @@ void wlr_output_send_frame(struct wlr_output *output); void wlr_output_send_present(struct wlr_output *output, struct wlr_output_event_present *event); +void wlr_output_layer_init(struct wlr_output_layer *layer, + struct wlr_output *output); + #endif diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 89ad509c9..ed8a90985 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -60,6 +60,7 @@ enum wlr_output_state_field { WLR_OUTPUT_STATE_SCALE = 1 << 4, WLR_OUTPUT_STATE_TRANSFORM = 1 << 5, WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED = 1 << 6, + WLR_OUTPUT_STATE_LAYERS = 1 << 7, }; enum wlr_output_state_buffer_type { @@ -94,6 +95,8 @@ struct wlr_output_state { int32_t width, height; int32_t refresh; // mHz, may be zero } custom_mode; + + struct wl_list layers; // wlr_output_layer.pending.link }; struct wlr_output_impl; @@ -141,6 +144,8 @@ struct wlr_output { bool frame_pending; float transform_matrix[9]; + struct wl_list layers; // wlr_output_layer.current.link + struct wlr_output_state pending; // Commit sequence number. Incremented on each commit, may overflow. diff --git a/include/wlr/types/wlr_output_layer.h b/include/wlr/types/wlr_output_layer.h new file mode 100644 index 000000000..abb13aad1 --- /dev/null +++ b/include/wlr/types/wlr_output_layer.h @@ -0,0 +1,83 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_OUTPUT_LAYER_H +#define WLR_TYPES_WLR_OUTPUT_LAYER_H + +/** + * Output layers provide an API to display buffers without rendering them. It + * relies on backend features which are not always available: the backend can + * refuse to display a layer. However when the backend accepts to display a + * layer, performance and battery usage will be improved. + * + * Operations on output layers are double-buffered. An output commit is needed + * to apply the pending state. + */ + +#include + +enum wlr_output_layer_state_field { + WLR_OUTPUT_LAYER_STATE_LINK = 1 << 0, + WLR_OUTPUT_LAYER_STATE_BUFFER = 1 << 1, + WLR_OUTPUT_LAYER_STATE_POSITION = 1 << 2, +}; + +struct wlr_output_layer_state { + uint32_t committed; // enum wlr_output_layer_state_field + struct wl_list link; + struct wlr_buffer *buffer; // only valid if WLR_OUTPUT_LAYER_STATE_BUFFER + int x, y; // only valid if WLR_OUTPUT_LAYER_STATE_POSITION +}; + +struct wlr_output_layer { + struct wlr_output *output; + /** If true, the backend has accepted to display the layer. If false, the + * compositor needs to manually render the layer. After each output commit, + * the backend will update this field. */ + bool accepted; + + struct wlr_output_layer_state current, pending; +}; + +/** + * Create a new output layer. + */ +struct wlr_output_layer *wlr_output_layer_create(struct wlr_output *output); +/** + * Remove the output layer. This operation is double-buffered, see + * wlr_output_commit. + * + * Callers must not access the wlr_output_layer after calling this function. + */ +void wlr_output_layer_remove(struct wlr_output_layer *layer); +/** + * Attach a buffer to the layer. This operation is double-buffered, see + * wlr_output_commit. + */ +void wlr_output_layer_attach_buffer(struct wlr_output_layer *layer, + struct wlr_buffer *buffer); +/** + * Set the position of the layer relative to the output. The coordinates are + * given in output-buffer-local coordinates. This operation is double-buffered, + * see wlr_output_commit. + */ +void wlr_output_layer_move(struct wlr_output_layer *layer, int x, int y); +/** + * Move the layer right above the specified sibling. This operation is + * double-buffered, see wlr_output_commit. + */ +void wlr_output_layer_place_above(struct wlr_output_layer *layer, + struct wlr_output_layer *sibling); +/** + * Move the layer right below the specified sibling. This operation is + * double-buffered, see wlr_output_commit. + */ +void wlr_output_layer_place_below(struct wlr_output_layer *layer, + struct wlr_output_layer *sibling); + +#endif diff --git a/types/meson.build b/types/meson.build index 757cc4c48..36e9dc2d9 100644 --- a/types/meson.build +++ b/types/meson.build @@ -45,6 +45,7 @@ wlr_files += files( 'wlr_matrix.c', 'wlr_output_damage.c', 'wlr_output_layout.c', + 'wlr_output_layer.c', 'wlr_output_management_v1.c', 'wlr_output_power_management_v1.c', 'wlr_output.c', diff --git a/types/wlr_output.c b/types/wlr_output.c index 3316653a2..adebf16cf 100644 --- a/types/wlr_output.c +++ b/types/wlr_output.c @@ -17,6 +17,7 @@ #include #include "util/global.h" #include "util/signal.h" +#include "types/wlr_output_layer.h" #define OUTPUT_VERSION 3 @@ -340,6 +341,8 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, wl_signal_init(&output->events.description); wl_signal_init(&output->events.destroy); pixman_region32_init(&output->pending.damage); + wl_list_init(&output->layers); + wl_list_init(&output->pending.layers); const char *no_hardware_cursors = getenv("WLR_NO_HARDWARE_CURSORS"); if (no_hardware_cursors != NULL && strcmp(no_hardware_cursors, "1") == 0) { @@ -493,6 +496,42 @@ static void output_pending_resolution(struct wlr_output *output, int *width, } } +static void output_commit_layers(struct wlr_output *output) { + if (output->pending.committed & WLR_OUTPUT_STATE_LAYERS) { + // Update the layer ordering in the current output state + struct wlr_output_layer *layer; + wl_list_for_each(layer, &output->pending.layers, pending.link) { + wl_list_remove(&layer->current.link); + wl_list_insert(output->layers.prev, &layer->current.link); + } + } + + struct wlr_output_layer *layer, *tmp; + wl_list_for_each_safe(layer, tmp, &output->layers, current.link) { + if (wl_list_empty(&layer->pending.link)) { + output_layer_destroy(layer); + } else { + output_layer_state_move(&layer->current, &layer->pending); + } + } +} + +static void output_rollback_layers(struct wlr_output *output) { + if (output->pending.committed & WLR_OUTPUT_STATE_LAYERS) { + // Rollback the layer ordering in the pending output state + struct wlr_output_layer *layer; + wl_list_for_each(layer, &output->layers, current.link) { + wl_list_remove(&layer->pending.link); + wl_list_insert(output->pending.layers.prev, &layer->pending.link); + } + } + + struct wlr_output_layer *layer, *tmp; + wl_list_for_each_safe(layer, tmp, &output->pending.layers, pending.link) { + output_layer_state_clear(&layer->pending); + } +} + static bool output_basic_test(struct wlr_output *output) { if (output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { if (output->frame_pending) { @@ -576,7 +615,7 @@ bool wlr_output_commit(struct wlr_output *output) { wlr_signal_emit_safe(&output->events.precommit, &event); if (!output->impl->commit(output)) { - output_state_clear(&output->pending); + wlr_output_rollback(output); return false; } @@ -626,11 +665,13 @@ bool wlr_output_commit(struct wlr_output *output) { output->needs_frame = false; } + output_commit_layers(output); output_state_clear(&output->pending); return true; } void wlr_output_rollback(struct wlr_output *output) { + output_rollback_layers(output); output_state_clear(&output->pending); if (output->impl->rollback) { diff --git a/types/wlr_output_layer.c b/types/wlr_output_layer.c new file mode 100644 index 000000000..154974ae1 --- /dev/null +++ b/types/wlr_output_layer.c @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include +#include "types/wlr_output_layer.h" + +struct wlr_output_layer *wlr_output_layer_create(struct wlr_output *output) { + if (output->impl->create_layer) { + return output->impl->create_layer(output); + } + + struct wlr_output_layer *layer = calloc(1, sizeof(*layer)); + if (layer == NULL) { + return NULL; + } + wlr_output_layer_init(layer, output); + return layer; +} + +void wlr_output_layer_init(struct wlr_output_layer *layer, + struct wlr_output *output) { + layer->output = output; + wl_list_insert(output->layers.prev, &layer->current.link); + wl_list_insert(output->pending.layers.prev, &layer->pending.link); + layer->pending.committed |= WLR_OUTPUT_LAYER_STATE_LINK; + output->pending.committed |= WLR_OUTPUT_STATE_LAYERS; +} + +void output_layer_destroy(struct wlr_output_layer *layer) { + output_layer_state_clear(&layer->current); + output_layer_state_clear(&layer->pending); + + wl_list_remove(&layer->current.link); + wl_list_remove(&layer->pending.link); + + if (layer->output->impl->destroy_layer) { + layer->output->impl->destroy_layer(layer); + } else { + free(layer); + } +} + +void wlr_output_layer_remove(struct wlr_output_layer *layer) { + wlr_output_layer_attach_buffer(layer, NULL); + + wl_list_remove(&layer->pending.link); + wl_list_init(&layer->pending.link); + layer->pending.committed |= WLR_OUTPUT_LAYER_STATE_LINK; + layer->output->pending.committed |= WLR_OUTPUT_STATE_LAYERS; +} + +void wlr_output_layer_attach_buffer(struct wlr_output_layer *layer, + struct wlr_buffer *buffer) { + if (buffer == layer->current.buffer) { + layer->pending.committed &= ~WLR_OUTPUT_LAYER_STATE_BUFFER; + return; + } + + wlr_buffer_unlock(layer->pending.buffer); + layer->pending.buffer = NULL; + + if (buffer == NULL) { + return; + } + + layer->pending.buffer = wlr_buffer_lock(buffer); + layer->pending.committed |= WLR_OUTPUT_LAYER_STATE_BUFFER; +} + +void wlr_output_layer_move(struct wlr_output_layer *layer, int x, int y) { + if (x == layer->current.x && y == layer->current.y) { + layer->pending.committed &= ~WLR_OUTPUT_LAYER_STATE_POSITION; + return; + } + + layer->pending.x = x; + layer->pending.y = y; + layer->pending.committed |= WLR_OUTPUT_LAYER_STATE_POSITION; +} + +void wlr_output_layer_place_above(struct wlr_output_layer *layer, + struct wlr_output_layer *sibling) { + assert(layer->output == sibling->output); + + wl_list_remove(&layer->pending.link); + wl_list_insert(&sibling->pending.link, &layer->pending.link); + layer->pending.committed |= WLR_OUTPUT_LAYER_STATE_LINK; + layer->output->pending.committed |= WLR_OUTPUT_STATE_LAYERS; +} + +void wlr_output_layer_place_below(struct wlr_output_layer *layer, + struct wlr_output_layer *sibling) { + assert(layer->output == sibling->output); + + wl_list_remove(&layer->pending.link); + wl_list_insert(sibling->pending.link.prev, &layer->pending.link); + layer->pending.committed |= WLR_OUTPUT_LAYER_STATE_LINK; + layer->output->pending.committed |= WLR_OUTPUT_STATE_LAYERS; +} + +static void output_layer_state_copy(struct wlr_output_layer_state *dst, + struct wlr_output_layer_state *src) { + // link has already been taken care of + if (src->committed & WLR_OUTPUT_LAYER_STATE_BUFFER) { + wlr_buffer_unlock(dst->buffer); + if (src->buffer != NULL) { + dst->buffer = wlr_buffer_lock(src->buffer); + } else { + dst->buffer = NULL; + } + } + if (src->committed & WLR_OUTPUT_LAYER_STATE_POSITION) { + dst->x = src->x; + dst->y = src->y; + } + dst->committed |= src->committed; +} + +void output_layer_state_clear(struct wlr_output_layer_state *state) { + wlr_buffer_unlock(state->buffer); + state->buffer = NULL; + state->committed = 0; +} + +void output_layer_state_move(struct wlr_output_layer_state *dst, + struct wlr_output_layer_state *src) { + output_layer_state_copy(dst, src); + output_layer_state_clear(src); +}