diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index 913642343..cd1c6a4d1 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 152c879f0..fbbfc2051 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #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 5af8ed53d..a1ed4414f 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;