backend/wayland: implement output layers

This commit is contained in:
Simon Ser 2022-08-18 12:04:39 +02:00
parent 3e0ce761ad
commit cd17b18495
3 changed files with 182 additions and 12 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;