From 1e3662ce5769bb82e5893733c48423e10ae47b56 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 9 Jan 2022 23:48:24 +0100 Subject: [PATCH] scene: Add layer_shell_v1 helper This helper behaves similar to the xdg_shell helper, and additionally provides a little assistance for positioning and exclusive_zone management. --- include/wlr/types/wlr_scene.h | 38 +++++++ types/meson.build | 1 + types/scene/layer_shell_v1.c | 183 ++++++++++++++++++++++++++++++++++ 3 files changed, 222 insertions(+) create mode 100644 types/scene/layer_shell_v1.c diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index a77eb6c2f..867753996 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -26,6 +26,7 @@ struct wlr_output; struct wlr_output_layout; struct wlr_xdg_surface; +struct wlr_layer_surface_v1; enum wlr_scene_node_type { WLR_SCENE_NODE_ROOT, @@ -136,6 +137,19 @@ struct wlr_scene_output { bool prev_scanout; }; +/** A layer shell scene helper */ +struct wlr_scene_layer_surface_v1 { + struct wlr_scene_node *node; + struct wlr_layer_surface_v1 *layer_surface; + + // private state + + struct wl_listener tree_destroy; + struct wl_listener layer_surface_destroy; + struct wl_listener layer_surface_map; + struct wl_listener layer_surface_unmap; +}; + typedef void (*wlr_scene_node_iterator_func_t)(struct wlr_scene_node *node, int sx, int sy, void *data); @@ -354,4 +368,28 @@ struct wlr_scene_node *wlr_scene_subsurface_tree_create( struct wlr_scene_node *wlr_scene_xdg_surface_create( struct wlr_scene_node *parent, struct wlr_xdg_surface *xdg_surface); +/** + * Add a node displaying a layer_surface_v1 and all of its sub-surfaces to the + * scene-graph. + * + * The origin of the returned scene-graph node will match the top-left corner + * of the layer surface. + */ +struct wlr_scene_layer_surface_v1 *wlr_scene_layer_surface_v1_create( + struct wlr_scene_node *parent, struct wlr_layer_surface_v1 *layer_surface); + +/** + * Configure a layer_surface_v1, position its scene node in accordance to its + * current state, and update the remaining usable area. + * + * full_area represents the entire area that may be used by the layer surface + * if its exclusive_zone is -1, and is usually the output dimensions. + * usable_area represents what remains of full_area that can be used if + * exclusive_zone is >= 0. usable_area is updated if the surface has a positive + * exclusive_zone, so that it can be used for the next layer surface. + */ +void wlr_scene_layer_surface_v1_configure( + struct wlr_scene_layer_surface_v1 *scene_layer_surface, + const struct wlr_box *full_area, struct wlr_box *usable_area); + #endif diff --git a/types/meson.build b/types/meson.build index 476bffe82..43e5875c0 100644 --- a/types/meson.build +++ b/types/meson.build @@ -11,6 +11,7 @@ wlr_files += files( 'scene/wlr_scene.c', 'scene/output_layout.c', 'scene/xdg_shell.c', + 'scene/layer_shell_v1.c', 'seat/wlr_seat_keyboard.c', 'seat/wlr_seat_pointer.c', 'seat/wlr_seat_touch.c', diff --git a/types/scene/layer_shell_v1.c b/types/scene/layer_shell_v1.c new file mode 100644 index 000000000..1c2b6e4ff --- /dev/null +++ b/types/scene/layer_shell_v1.c @@ -0,0 +1,183 @@ +#include +#include +#include + +static void scene_layer_surface_handle_tree_destroy( + struct wl_listener *listener, void *data) { + struct wlr_scene_layer_surface_v1 *scene_layer_surface = + wl_container_of(listener, scene_layer_surface, tree_destroy); + // tree and surface_node will be cleaned up by scene_node_finish + wl_list_remove(&scene_layer_surface->tree_destroy.link); + wl_list_remove(&scene_layer_surface->layer_surface_destroy.link); + wl_list_remove(&scene_layer_surface->layer_surface_map.link); + wl_list_remove(&scene_layer_surface->layer_surface_unmap.link); + free(scene_layer_surface); +} + +static void scene_layer_surface_handle_layer_surface_destroy( + struct wl_listener *listener, void *data) { + struct wlr_scene_layer_surface_v1 *scene_layer_surface = + wl_container_of(listener, scene_layer_surface, layer_surface_destroy); + wlr_scene_node_destroy(scene_layer_surface->node); +} + +static void scene_layer_surface_handle_layer_surface_map( + struct wl_listener *listener, void *data) { + struct wlr_scene_layer_surface_v1 *scene_layer_surface = + wl_container_of(listener, scene_layer_surface, layer_surface_map); + wlr_scene_node_set_enabled(scene_layer_surface->node, true); +} + +static void scene_layer_surface_handle_layer_surface_unmap( + struct wl_listener *listener, void *data) { + struct wlr_scene_layer_surface_v1 *scene_layer_surface = + wl_container_of(listener, scene_layer_surface, layer_surface_unmap); + wlr_scene_node_set_enabled(scene_layer_surface->node, false); +} + +static void layer_surface_exclusive_zone( + struct wlr_layer_surface_v1_state *state, + struct wlr_box *usable_area) { + switch (state->anchor) { + case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): + // Anchor top + usable_area->y += state->exclusive_zone + state->margin.top; + usable_area->height -= state->exclusive_zone + state->margin.top; + break; + case (ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): + // Anchor bottom + usable_area->height -= state->exclusive_zone + state->margin.bottom; + break; + case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT): + // Anchor left + usable_area->x += state->exclusive_zone + state->margin.left; + usable_area->width -= state->exclusive_zone + state->margin.left; + break; + case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): // Anchor right + // Anchor right + usable_area->width -= state->exclusive_zone + state->margin.right; + break; + } +} + +void wlr_scene_layer_surface_v1_configure( + struct wlr_scene_layer_surface_v1 *scene_layer_surface, + const struct wlr_box *full_area, struct wlr_box *usable_area) { + struct wlr_layer_surface_v1 *layer_surface = + scene_layer_surface->layer_surface; + struct wlr_layer_surface_v1_state *state = &layer_surface->current; + + // If the exclusive zone is set to -1, the layer surface will use the + // full area of the output, otherwise it is constrained to the + // remaining usable area. + struct wlr_box bounds; + if (state->exclusive_zone == -1) { + bounds = *full_area; + } else { + bounds = *usable_area; + } + + struct wlr_box box = { + .width = state->desired_width, + .height = state->desired_height, + }; + + // Horizontal positioning + if (box.width == 0) { + box.x = bounds.x + state->margin.left; + box.width = bounds.width - + state->margin.left + state->margin.right; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT && + state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { + box.x = bounds.x + bounds.width/2 -box.width/2; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) { + box.x = bounds.x + state->margin.left; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { + box.x = bounds.x + bounds.width - box.width - state->margin.right; + } else { + box.x = bounds.x + bounds.width/2 - box.width/2; + } + + // Vertical positioning + if (box.height == 0) { + box.y = bounds.y + state->margin.top; + box.height = bounds.height - + state->margin.top + state->margin.bottom; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP && + state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { + box.y = bounds.y + bounds.height/2 - box.height/2; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { + box.y = bounds.y + state->margin.top; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { + box.y = bounds.y + bounds.height - box.height - state->margin.bottom; + } else { + box.y = bounds.y + bounds.height/2 - box.height/2; + } + + wlr_scene_node_set_position(scene_layer_surface->node, box.x, box.y); + wlr_layer_surface_v1_configure(layer_surface, box.width, box.height); + + if (state->exclusive_zone > 0) { + layer_surface_exclusive_zone(state, usable_area); + } +} + +struct wlr_scene_layer_surface_v1 *wlr_scene_layer_surface_v1_create( + struct wlr_scene_node *parent, + struct wlr_layer_surface_v1 *layer_surface) { + struct wlr_scene_layer_surface_v1 *scene_layer_surface = + calloc(1, sizeof(*scene_layer_surface)); + if (scene_layer_surface == NULL) { + return NULL; + } + + scene_layer_surface->layer_surface = layer_surface; + + struct wlr_scene_tree *tree = wlr_scene_tree_create(parent); + if (tree == NULL) { + free(scene_layer_surface); + return NULL; + } + scene_layer_surface->node = &tree->node; + + struct wlr_scene_node *surface_node = wlr_scene_subsurface_tree_create( + scene_layer_surface->node, layer_surface->surface); + if (surface_node == NULL) { + wlr_scene_node_destroy(scene_layer_surface->node); + free(scene_layer_surface); + return NULL; + } + + scene_layer_surface->tree_destroy.notify = + scene_layer_surface_handle_tree_destroy; + wl_signal_add(&scene_layer_surface->node->events.destroy, + &scene_layer_surface->tree_destroy); + + scene_layer_surface->layer_surface_destroy.notify = + scene_layer_surface_handle_layer_surface_destroy; + wl_signal_add(&layer_surface->events.destroy, + &scene_layer_surface->layer_surface_destroy); + + scene_layer_surface->layer_surface_map.notify = + scene_layer_surface_handle_layer_surface_map; + wl_signal_add(&layer_surface->events.map, + &scene_layer_surface->layer_surface_map); + + scene_layer_surface->layer_surface_unmap.notify = + scene_layer_surface_handle_layer_surface_unmap; + wl_signal_add(&layer_surface->events.unmap, + &scene_layer_surface->layer_surface_unmap); + + wlr_scene_node_set_enabled(scene_layer_surface->node, + layer_surface->mapped); + + return scene_layer_surface; +}