aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKenny Levinsen <kl@kl.wtf>2022-01-09 23:48:24 +0100
committerKenny Levinsen <kl@kl.wtf>2022-02-01 13:31:26 +0100
commit1e3662ce5769bb82e5893733c48423e10ae47b56 (patch)
treec00b4f399acb66cd3e5f1eb9d9ff4a6b07771aa6
parent3db1bcbe641b407b9f5c9e5d0a012b45aa2c6cb7 (diff)
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.
-rw-r--r--include/wlr/types/wlr_scene.h38
-rw-r--r--types/meson.build1
-rw-r--r--types/scene/layer_shell_v1.c183
3 files changed, 222 insertions, 0 deletions
diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h
index a77eb6c2..86775399 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 476bffe8..43e5875c 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 00000000..1c2b6e4f
--- /dev/null
+++ b/types/scene/layer_shell_v1.c
@@ -0,0 +1,183 @@
+#include <stdlib.h>
+#include <wlr/types/wlr_scene.h>
+#include <wlr/types/wlr_layer_shell_v1.h>
+
+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;
+}