aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Ser <contact@emersion.fr>2022-08-18 12:04:39 +0200
committerSimon Ser <contact@emersion.fr>2023-02-20 18:38:57 +0100
commitcd17b18495953b88222fe0e770fbcdf5bde7bb5b (patch)
tree9e3c7850284007d6da13606513672810fd22d079
parent3e0ce761ad9b081690ee64e37807781b33a4289c (diff)
backend/wayland: implement output layers
-rw-r--r--backend/wayland/backend.c6
-rw-r--r--backend/wayland/output.c180
-rw-r--r--include/backend/wayland.h8
3 files changed, 182 insertions, 12 deletions
diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c
index 91364234..cd1c6a4d 100644
--- a/backend/wayland/backend.c
+++ b/backend/wayland/backend.c
@@ -393,6 +393,9 @@ static void registry_global(void *data, struct wl_registry *registry,
} else if (strcmp(iface, xdg_activation_v1_interface.name) == 0) {
wl->activation_v1 = wl_registry_bind(registry, name,
&xdg_activation_v1_interface, 1);
+ } else if (strcmp(iface, wl_subcompositor_interface.name) == 0) {
+ wl->subcompositor = wl_registry_bind(registry, name,
+ &wl_subcompositor_interface, 1);
}
}
@@ -500,6 +503,9 @@ static void backend_destroy(struct wlr_backend *backend) {
if (wl->zwp_relative_pointer_manager_v1) {
zwp_relative_pointer_manager_v1_destroy(wl->zwp_relative_pointer_manager_v1);
}
+ if (wl->subcompositor) {
+ wl_subcompositor_destroy(wl->subcompositor);
+ }
free(wl->drm_render_name);
free(wl->activation_token);
xdg_wm_base_destroy(wl->xdg_wm_base);
diff --git a/backend/wayland/output.c b/backend/wayland/output.c
index 152c879f..fbbfc205 100644
--- a/backend/wayland/output.c
+++ b/backend/wayland/output.c
@@ -13,6 +13,7 @@
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_matrix.h>
+#include <wlr/types/wlr_output_layer.h>
#include <wlr/util/log.h>
#include "backend/wayland.h"
@@ -275,6 +276,154 @@ static bool output_test(struct wlr_output *wlr_output,
return false;
}
+ if (state->committed & WLR_OUTPUT_STATE_LAYERS) {
+ // If we can't use a sub-surface for a layer, then we can't use a
+ // sub-surface for any layer underneath
+ bool supported = output->backend->subcompositor != NULL;
+ for (ssize_t i = state->layers_len - 1; i >= 0; i--) {
+ struct wlr_output_layer_state *layer_state = &state->layers[i];
+ if (layer_state->buffer != NULL) {
+ if (layer_state->x < 0 || layer_state->y < 0 ||
+ layer_state->x + layer_state->buffer->width > wlr_output->width ||
+ layer_state->y + layer_state->buffer->height > wlr_output->height) {
+ supported = false;
+ }
+ supported = supported &&
+ test_buffer(output->backend, layer_state->buffer);
+ }
+ layer_state->accepted = supported;
+ }
+ }
+
+ return true;
+}
+
+static void output_layer_handle_addon_destroy(struct wlr_addon *addon) {
+ struct wlr_wl_output_layer *layer = wl_container_of(addon, layer, addon);
+
+ wlr_addon_finish(&layer->addon);
+ wl_subsurface_destroy(layer->subsurface);
+ wl_surface_destroy(layer->surface);
+ free(layer);
+}
+
+static const struct wlr_addon_interface output_layer_addon_impl = {
+ .name = "wlr_wl_output_layer",
+ .destroy = output_layer_handle_addon_destroy,
+};
+
+static struct wlr_wl_output_layer *get_or_create_output_layer(
+ struct wlr_wl_output *output, struct wlr_output_layer *wlr_layer) {
+ assert(output->backend->subcompositor != NULL);
+
+ struct wlr_wl_output_layer *layer;
+ struct wlr_addon *addon = wlr_addon_find(&wlr_layer->addons, output,
+ &output_layer_addon_impl);
+ if (addon != NULL) {
+ layer = wl_container_of(addon, layer, addon);
+ return layer;
+ }
+
+ layer = calloc(1, sizeof(*layer));
+ if (layer == NULL) {
+ return NULL;
+ }
+
+ wlr_addon_init(&layer->addon, &wlr_layer->addons, output,
+ &output_layer_addon_impl);
+
+ layer->surface = wl_compositor_create_surface(output->backend->compositor);
+ layer->subsurface = wl_subcompositor_get_subsurface(
+ output->backend->subcompositor, layer->surface, output->surface);
+
+ // Set an empty input region so that input events are handled by the main
+ // surface
+ struct wl_region *region = wl_compositor_create_region(output->backend->compositor);
+ wl_surface_set_input_region(layer->surface, region);
+ wl_region_destroy(region);
+
+ return layer;
+}
+
+static bool output_layer_commit(struct wlr_wl_output *output,
+ struct wlr_wl_output_layer *layer,
+ const struct wlr_output_layer_state *state) {
+ // TODO: only do this if the layer moved
+ wl_subsurface_set_position(layer->subsurface, state->x, state->y);
+
+ struct wlr_wl_buffer *buffer = NULL;
+ if (state->buffer != NULL) {
+ buffer = get_or_create_wl_buffer(output->backend, state->buffer);
+ if (buffer == NULL) {
+ return false;
+ }
+ }
+
+ wl_surface_attach(layer->surface, buffer ? buffer->wl_buffer : NULL, 0, 0);
+ wl_surface_damage_buffer(layer->surface, 0, 0, INT32_MAX, INT32_MAX);
+
+ wl_surface_commit(layer->surface);
+ return true;
+}
+
+static bool commit_layers(struct wlr_wl_output *output,
+ struct wlr_output_layer_state *layers, size_t layers_len) {
+ if (output->backend->subcompositor == NULL) {
+ return true;
+ }
+
+ struct wlr_wl_output_layer *prev_layer = NULL;
+ for (size_t i = 0; i < layers_len; i++) {
+ struct wlr_wl_output_layer *layer =
+ get_or_create_output_layer(output, layers[i].layer);
+ if (layer == NULL) {
+ return false;
+ }
+
+ if (!layers[i].accepted) {
+ // Unmap the sub-surface
+ // TODO: only do this once
+ wl_surface_attach(layer->surface, NULL, 0, 0);
+ wl_surface_commit(layer->surface);
+ continue;
+ }
+
+ // TODO: only do this if layers were re-ordered
+ if (prev_layer != NULL) {
+ wl_subsurface_place_above(layer->subsurface,
+ prev_layer->surface);
+ }
+
+ if (!output_layer_commit(output, layer, &layers[i])) {
+ return false;
+ }
+ }
+
+ // Unmap any layer we haven't seen
+ struct wlr_output_layer *wlr_layer;
+ wl_list_for_each(wlr_layer, &output->wlr_output.layers, link) {
+ bool found = false;
+ for (size_t i = 0; i < layers_len; i++) {
+ if (layers[i].layer == wlr_layer) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ continue;
+ }
+
+ struct wlr_wl_output_layer *layer =
+ get_or_create_output_layer(output, wlr_layer);
+ if (layer == NULL) {
+ continue;
+ }
+
+ // TODO: only do this once
+ wl_surface_attach(layer->surface, NULL, 0, 0);
+ wl_surface_commit(layer->surface);
+ }
+
return true;
}
@@ -288,23 +437,11 @@ static bool output_commit(struct wlr_output *wlr_output,
}
if (state->committed & WLR_OUTPUT_STATE_BUFFER) {
- struct wp_presentation_feedback *wp_feedback = NULL;
- if (output->backend->presentation != NULL) {
- wp_feedback = wp_presentation_feedback(output->backend->presentation,
- output->surface);
- }
-
const pixman_region32_t *damage = NULL;
if (state->committed & WLR_OUTPUT_STATE_DAMAGE) {
damage = &state->damage;
}
- if (output->frame_callback != NULL) {
- wl_callback_destroy(output->frame_callback);
- }
- output->frame_callback = wl_surface_frame(output->surface);
- wl_callback_add_listener(output->frame_callback, &frame_listener, output);
-
struct wlr_buffer *wlr_buffer = state->buffer;
struct wlr_wl_buffer *buffer =
get_or_create_wl_buffer(output->backend, wlr_buffer);
@@ -327,6 +464,25 @@ static bool output_commit(struct wlr_output *wlr_output,
r->x2 - r->x1, r->y2 - r->y1);
}
}
+ }
+
+ if ((state->committed & WLR_OUTPUT_STATE_LAYERS) &&
+ !commit_layers(output, state->layers, state->layers_len)) {
+ return false;
+ }
+
+ if (state->committed & (WLR_OUTPUT_STATE_BUFFER | WLR_OUTPUT_STATE_LAYERS)) {
+ if (output->frame_callback != NULL) {
+ wl_callback_destroy(output->frame_callback);
+ }
+ output->frame_callback = wl_surface_frame(output->surface);
+ wl_callback_add_listener(output->frame_callback, &frame_listener, output);
+
+ struct wp_presentation_feedback *wp_feedback = NULL;
+ if (output->backend->presentation != NULL) {
+ wp_feedback = wp_presentation_feedback(output->backend->presentation,
+ output->surface);
+ }
wl_surface_commit(output->surface);
diff --git a/include/backend/wayland.h b/include/backend/wayland.h
index 5af8ed53..a1ed4414 100644
--- a/include/backend/wayland.h
+++ b/include/backend/wayland.h
@@ -47,6 +47,7 @@ struct wlr_wl_backend {
struct wlr_drm_format_set linux_dmabuf_v1_formats;
struct wl_drm *legacy_drm;
struct xdg_activation_v1 *activation_v1;
+ struct wl_subcompositor *subcompositor;
char *drm_render_name;
};
@@ -65,6 +66,13 @@ struct wlr_wl_presentation_feedback {
uint32_t commit_seq;
};
+struct wlr_wl_output_layer {
+ struct wlr_addon addon;
+
+ struct wl_surface *surface;
+ struct wl_subsurface *subsurface;
+};
+
struct wlr_wl_output {
struct wlr_output wlr_output;