summaryrefslogtreecommitdiff
path: root/subsurface_tree.c
diff options
context:
space:
mode:
authorSimon Ser <[email protected]>2021-08-20 12:41:23 +0200
committerIsaac Freund <[email protected]>2021-09-21 16:48:31 +0200
commitda2fd7b1ab124c36e9caca17218d33f6e9df852e (patch)
tree770be5542d02f21549409d6cfcffed6b3c3e39fd /subsurface_tree.c
parentc171032baa8574b3629772a82ec5ea2ac98d6f11 (diff)
scene: add wlr_scene_subsurface_tree_create
Diffstat (limited to 'subsurface_tree.c')
-rw-r--r--subsurface_tree.c218
1 files changed, 218 insertions, 0 deletions
diff --git a/subsurface_tree.c b/subsurface_tree.c
new file mode 100644
index 0000000..7cfa882
--- /dev/null
+++ b/subsurface_tree.c
@@ -0,0 +1,218 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <wlr/types/wlr_scene.h>
+#include <wlr/util/addon.h>
+
+/**
+ * A tree for a surface and all of its child sub-surfaces.
+ *
+ * `tree` contains `scene_surface` and one node per sub-surface.
+ */
+struct wlr_scene_subsurface_tree {
+ struct wlr_scene_tree *tree;
+ struct wlr_surface *surface;
+ struct wlr_scene_surface *scene_surface;
+
+ struct wlr_scene_subsurface_tree *parent; // NULL for the top-level surface
+ struct wlr_addon surface_addon; // only set if there's a parent
+
+ struct wl_listener tree_destroy;
+ struct wl_listener surface_destroy;
+ struct wl_listener surface_commit;
+ struct wl_listener surface_new_subsurface;
+};
+
+static void subsurface_tree_destroy(struct wlr_scene_subsurface_tree *subsurface_tree) {
+ // tree and scene_surface will be cleaned up by scene_node_finish
+ if (subsurface_tree->parent) {
+ wlr_addon_finish(&subsurface_tree->surface_addon);
+ }
+ wl_list_remove(&subsurface_tree->tree_destroy.link);
+ wl_list_remove(&subsurface_tree->surface_destroy.link);
+ wl_list_remove(&subsurface_tree->surface_commit.link);
+ wl_list_remove(&subsurface_tree->surface_new_subsurface.link);
+ free(subsurface_tree);
+}
+
+static void subsurface_tree_handle_tree_destroy(struct wl_listener *listener,
+ void *data) {
+ struct wlr_scene_subsurface_tree *subsurface_tree =
+ wl_container_of(listener, subsurface_tree, tree_destroy);
+ subsurface_tree_destroy(subsurface_tree);
+}
+
+static void subsurface_tree_handle_surface_destroy(struct wl_listener *listener,
+ void *data) {
+ struct wlr_scene_subsurface_tree *subsurface_tree =
+ wl_container_of(listener, subsurface_tree, surface_destroy);
+ wlr_scene_node_destroy(&subsurface_tree->tree->node);
+}
+
+static const struct wlr_addon_interface subsurface_tree_addon_impl;
+
+static struct wlr_scene_subsurface_tree *subsurface_tree_from_subsurface(
+ struct wlr_scene_subsurface_tree *parent,
+ struct wlr_subsurface *subsurface) {
+ struct wlr_addon *addon = wlr_addon_find(&subsurface->surface->addons,
+ parent, &subsurface_tree_addon_impl);
+ assert(addon != NULL);
+ struct wlr_scene_subsurface_tree *subsurface_tree =
+ wl_container_of(addon, subsurface_tree, surface_addon);
+ return subsurface_tree;
+}
+
+static void subsurface_tree_reconfigure(
+ struct wlr_scene_subsurface_tree *subsurface_tree) {
+ struct wlr_surface *surface = subsurface_tree->surface;
+
+ struct wlr_scene_node *prev = NULL;
+ struct wlr_subsurface *subsurface;
+ wl_list_for_each(subsurface, &surface->current.subsurfaces_below,
+ current.link) {
+ struct wlr_scene_subsurface_tree *child =
+ subsurface_tree_from_subsurface(subsurface_tree, subsurface);
+ if (prev != NULL) {
+ wlr_scene_node_place_above(&child->tree->node, prev);
+ }
+ prev = &child->tree->node;
+
+ wlr_scene_node_set_position(&child->tree->node,
+ subsurface->current.x, subsurface->current.y);
+ }
+
+ if (prev != NULL) {
+ wlr_scene_node_place_above(&subsurface_tree->scene_surface->node, prev);
+ }
+ prev = &subsurface_tree->scene_surface->node;
+
+ wl_list_for_each(subsurface, &surface->current.subsurfaces_above,
+ current.link) {
+ struct wlr_scene_subsurface_tree *child =
+ subsurface_tree_from_subsurface(subsurface_tree, subsurface);
+ wlr_scene_node_place_above(&child->tree->node, prev);
+ prev = &child->tree->node;
+
+ wlr_scene_node_set_position(&child->tree->node,
+ subsurface->current.x, subsurface->current.y);
+ }
+}
+
+static void subsurface_tree_handle_surface_commit(struct wl_listener *listener,
+ void *data) {
+ struct wlr_scene_subsurface_tree *subsurface_tree =
+ wl_container_of(listener, subsurface_tree, surface_commit);
+
+ // TODO: only do this on subsurface order or position change
+ subsurface_tree_reconfigure(subsurface_tree);
+}
+
+static void subsurface_tree_addon_destroy(struct wlr_addon *addon) {
+ struct wlr_scene_subsurface_tree *subsurface_tree =
+ wl_container_of(addon, subsurface_tree, surface_addon);
+ wlr_scene_node_destroy(&subsurface_tree->tree->node);
+}
+
+static const struct wlr_addon_interface subsurface_tree_addon_impl = {
+ .name = "wlr_scene_subsurface_tree",
+ .destroy = subsurface_tree_addon_destroy,
+};
+
+static struct wlr_scene_subsurface_tree *scene_surface_tree_create(
+ struct wlr_scene_node *parent, struct wlr_surface *surface);
+
+static bool subsurface_tree_create_subsurface(
+ struct wlr_scene_subsurface_tree *parent,
+ struct wlr_subsurface *subsurface) {
+ struct wlr_scene_subsurface_tree *child = scene_surface_tree_create(
+ &parent->tree->node, subsurface->surface);
+ if (child == NULL) {
+ return false;
+ }
+
+ child->parent = parent;
+ wlr_addon_init(&child->surface_addon, &subsurface->surface->addons,
+ parent, &subsurface_tree_addon_impl);
+
+ return true;
+}
+
+static void subsurface_tree_handle_surface_new_subsurface(
+ struct wl_listener *listener, void *data) {
+ struct wlr_scene_subsurface_tree *subsurface_tree =
+ wl_container_of(listener, subsurface_tree, surface_new_subsurface);
+ struct wlr_subsurface *subsurface = data;
+ if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) {
+ wl_resource_post_no_memory(subsurface->resource);
+ }
+}
+
+static struct wlr_scene_subsurface_tree *scene_surface_tree_create(
+ struct wlr_scene_node *parent, struct wlr_surface *surface) {
+ struct wlr_scene_subsurface_tree *subsurface_tree =
+ calloc(1, sizeof(*subsurface_tree));
+ if (subsurface_tree == NULL) {
+ return NULL;
+ }
+
+ subsurface_tree->tree = wlr_scene_tree_create(parent);
+ if (subsurface_tree->tree == NULL) {
+ goto error_surface_tree;
+ }
+
+ subsurface_tree->scene_surface =
+ wlr_scene_surface_create(&subsurface_tree->tree->node, surface);
+ if (subsurface_tree->scene_surface == NULL) {
+ goto error_scene_surface;
+ }
+
+ subsurface_tree->surface = surface;
+
+ struct wlr_subsurface *subsurface;
+ wl_list_for_each(subsurface, &surface->current.subsurfaces_below,
+ current.link) {
+ if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) {
+ goto error_scene_surface;
+ }
+ }
+ wl_list_for_each(subsurface, &surface->current.subsurfaces_above,
+ current.link) {
+ if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) {
+ goto error_scene_surface;
+ }
+ }
+
+ subsurface_tree_reconfigure(subsurface_tree);
+
+ subsurface_tree->tree_destroy.notify = subsurface_tree_handle_tree_destroy;
+ wl_signal_add(&subsurface_tree->tree->node.events.destroy,
+ &subsurface_tree->tree_destroy);
+
+ subsurface_tree->surface_destroy.notify = subsurface_tree_handle_surface_destroy;
+ wl_signal_add(&surface->events.destroy, &subsurface_tree->surface_destroy);
+
+ subsurface_tree->surface_commit.notify = subsurface_tree_handle_surface_commit;
+ wl_signal_add(&surface->events.commit, &subsurface_tree->surface_commit);
+
+ subsurface_tree->surface_new_subsurface.notify =
+ subsurface_tree_handle_surface_new_subsurface;
+ wl_signal_add(&surface->events.new_subsurface,
+ &subsurface_tree->surface_new_subsurface);
+
+ return subsurface_tree;
+
+error_scene_surface:
+ wlr_scene_node_destroy(&subsurface_tree->tree->node);
+error_surface_tree:
+ free(subsurface_tree);
+ return NULL;
+}
+
+struct wlr_scene_node *wlr_scene_subsurface_tree_create(
+ struct wlr_scene_node *parent, struct wlr_surface *surface) {
+ struct wlr_scene_subsurface_tree *subsurface_tree =
+ scene_surface_tree_create(parent, surface);
+ if (subsurface_tree == NULL) {
+ return NULL;
+ }
+ return &subsurface_tree->tree->node;
+}