summaryrefslogtreecommitdiff
path: root/types
diff options
context:
space:
mode:
Diffstat (limited to 'types')
-rw-r--r--types/meson.build8
-rw-r--r--types/scene/layer_shell_v1.c186
-rw-r--r--types/scene/output_layout.c157
-rw-r--r--types/scene/subsurface_tree.c259
-rw-r--r--types/scene/surface.c211
-rw-r--r--types/scene/wlr_scene.c1728
-rw-r--r--types/scene/xdg_shell.c124
7 files changed, 2673 insertions, 0 deletions
diff --git a/types/meson.build b/types/meson.build
new file mode 100644
index 0000000..0654669
--- /dev/null
+++ b/types/meson.build
@@ -0,0 +1,8 @@
+wlr_files += files(
+ 'scene/subsurface_tree.c',
+ 'scene/surface.c',
+ 'scene/wlr_scene.c',
+ 'scene/output_layout.c',
+ 'scene/xdg_shell.c',
+ 'scene/layer_shell_v1.c',
+)
diff --git a/types/scene/layer_shell_v1.c b/types/scene/layer_shell_v1.c
new file mode 100644
index 0000000..3ed616a
--- /dev/null
+++ b/types/scene/layer_shell_v1.c
@@ -0,0 +1,186 @@
+#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->tree->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->tree->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->tree->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:
+ 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:
+ 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_LEFT:
+ 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_RIGHT:
+ case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
+ ZWLR_LAYER_SURFACE_V1_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->tree->node, box.x, box.y);
+ wlr_layer_surface_v1_configure(layer_surface, box.width, box.height);
+
+ if (layer_surface->mapped && 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_tree *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;
+
+ scene_layer_surface->tree = wlr_scene_tree_create(parent);
+ if (scene_layer_surface->tree == NULL) {
+ free(scene_layer_surface);
+ return NULL;
+ }
+
+ struct wlr_scene_tree *surface_tree = wlr_scene_subsurface_tree_create(
+ scene_layer_surface->tree, layer_surface->surface);
+ if (surface_tree == NULL) {
+ wlr_scene_node_destroy(&scene_layer_surface->tree->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->tree->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->tree->node,
+ layer_surface->mapped);
+
+ return scene_layer_surface;
+}
diff --git a/types/scene/output_layout.c b/types/scene/output_layout.c
new file mode 100644
index 0000000..1d1484a
--- /dev/null
+++ b/types/scene/output_layout.c
@@ -0,0 +1,157 @@
+#include <stdlib.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_scene.h>
+
+struct wlr_scene_output_layout {
+ struct wlr_output_layout *layout;
+ struct wlr_scene *scene;
+
+ struct wl_list outputs; // wlr_scene_output_layout_output.link
+
+ struct wl_listener layout_add;
+ struct wl_listener layout_change;
+ struct wl_listener layout_destroy;
+ struct wl_listener scene_destroy;
+};
+
+struct wlr_scene_output_layout_output {
+ struct wlr_output_layout_output *layout_output;
+ struct wlr_scene_output *scene_output;
+
+ struct wl_list link; // wlr_scene_output_layout.outputs
+
+ struct wl_listener layout_output_destroy;
+ struct wl_listener scene_output_destroy;
+};
+
+static void scene_output_layout_output_destroy(
+ struct wlr_scene_output_layout_output *solo) {
+ wl_list_remove(&solo->layout_output_destroy.link);
+ wl_list_remove(&solo->scene_output_destroy.link);
+ wl_list_remove(&solo->link);
+ wlr_scene_output_destroy(solo->scene_output);
+ free(solo);
+}
+
+static void scene_output_layout_output_handle_layout_output_destroy(
+ struct wl_listener *listener, void *data) {
+ struct wlr_scene_output_layout_output *solo =
+ wl_container_of(listener, solo, layout_output_destroy);
+ scene_output_layout_output_destroy(solo);
+}
+
+static void scene_output_layout_output_handle_scene_output_destroy(
+ struct wl_listener *listener, void *data) {
+ struct wlr_scene_output_layout_output *solo =
+ wl_container_of(listener, solo, scene_output_destroy);
+ solo->scene_output = NULL;
+ scene_output_layout_output_destroy(solo);
+}
+
+static void scene_output_layout_destroy(struct wlr_scene_output_layout *sol) {
+ struct wlr_scene_output_layout_output *solo, *tmp;
+ wl_list_for_each_safe(solo, tmp, &sol->outputs, link) {
+ scene_output_layout_output_destroy(solo);
+ }
+ wl_list_remove(&sol->layout_add.link);
+ wl_list_remove(&sol->layout_change.link);
+ wl_list_remove(&sol->layout_destroy.link);
+ wl_list_remove(&sol->scene_destroy.link);
+ free(sol);
+}
+
+static void scene_output_layout_handle_layout_change(
+ struct wl_listener *listener, void *data) {
+ struct wlr_scene_output_layout *sol =
+ wl_container_of(listener, sol, layout_change);
+
+ struct wlr_scene_output_layout_output *solo;
+ wl_list_for_each(solo, &sol->outputs, link) {
+ wlr_scene_output_set_position(solo->scene_output,
+ solo->layout_output->x, solo->layout_output->y);
+ }
+}
+
+static void scene_output_layout_add(struct wlr_scene_output_layout *sol,
+ struct wlr_output_layout_output *lo) {
+ struct wlr_scene_output_layout_output *solo = calloc(1, sizeof(*solo));
+ if (solo == NULL) {
+ return;
+ }
+
+ solo->scene_output = wlr_scene_output_create(sol->scene, lo->output);
+ if (solo->scene_output == NULL) {
+ free(solo);
+ return;
+ }
+
+ solo->layout_output = lo;
+
+ solo->layout_output_destroy.notify =
+ scene_output_layout_output_handle_layout_output_destroy;
+ wl_signal_add(&lo->events.destroy, &solo->layout_output_destroy);
+
+ solo->scene_output_destroy.notify =
+ scene_output_layout_output_handle_scene_output_destroy;
+ wl_signal_add(&solo->scene_output->events.destroy,
+ &solo->scene_output_destroy);
+
+ wl_list_insert(&sol->outputs, &solo->link);
+
+ wlr_scene_output_set_position(solo->scene_output, lo->x, lo->y);
+}
+
+static void scene_output_layout_handle_layout_add(
+ struct wl_listener *listener, void *data) {
+ struct wlr_scene_output_layout *sol =
+ wl_container_of(listener, sol, layout_add);
+ struct wlr_output_layout_output *lo = data;
+
+ scene_output_layout_add(sol, lo);
+}
+
+static void scene_output_layout_handle_layout_destroy(
+ struct wl_listener *listener, void *data) {
+ struct wlr_scene_output_layout *sol =
+ wl_container_of(listener, sol, layout_destroy);
+ scene_output_layout_destroy(sol);
+}
+
+static void scene_output_layout_handle_scene_destroy(
+ struct wl_listener *listener, void *data) {
+ struct wlr_scene_output_layout *sol =
+ wl_container_of(listener, sol, scene_destroy);
+ scene_output_layout_destroy(sol);
+}
+
+bool wlr_scene_attach_output_layout(struct wlr_scene *scene,
+ struct wlr_output_layout *output_layout) {
+ struct wlr_scene_output_layout *sol = calloc(1, sizeof(*sol));
+ if (sol == NULL) {
+ return false;
+ }
+
+ sol->scene = scene;
+ sol->layout = output_layout;
+
+ wl_list_init(&sol->outputs);
+
+ sol->layout_destroy.notify = scene_output_layout_handle_layout_destroy;
+ wl_signal_add(&output_layout->events.destroy, &sol->layout_destroy);
+
+ sol->layout_change.notify = scene_output_layout_handle_layout_change;
+ wl_signal_add(&output_layout->events.change, &sol->layout_change);
+
+ sol->layout_add.notify = scene_output_layout_handle_layout_add;
+ wl_signal_add(&output_layout->events.add, &sol->layout_add);
+
+ sol->scene_destroy.notify = scene_output_layout_handle_scene_destroy;
+ wl_signal_add(&scene->tree.node.events.destroy, &sol->scene_destroy);
+
+ struct wlr_output_layout_output *lo;
+ wl_list_for_each(lo, &output_layout->outputs, link) {
+ scene_output_layout_add(sol, lo);
+ }
+
+ return true;
+}
diff --git a/types/scene/subsurface_tree.c b/types/scene/subsurface_tree.c
new file mode 100644
index 0000000..35420ab
--- /dev/null
+++ b/types/scene/subsurface_tree.c
@@ -0,0 +1,259 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <wlr/types/wlr_scene.h>
+#include <wlr/types/wlr_subcompositor.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 wl_listener tree_destroy;
+ struct wl_listener surface_destroy;
+ struct wl_listener surface_commit;
+ struct wl_listener surface_new_subsurface;
+
+ struct wlr_scene_subsurface_tree *parent; // NULL for the top-level surface
+
+ // Only valid if the surface is a sub-surface
+
+ struct wlr_addon surface_addon;
+
+ struct wl_listener subsurface_destroy;
+ struct wl_listener subsurface_map;
+ struct wl_listener subsurface_unmap;
+};
+
+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);
+ // 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->subsurface_destroy.link);
+ wl_list_remove(&subsurface_tree->subsurface_map.link);
+ wl_list_remove(&subsurface_tree->subsurface_unmap.link);
+ }
+ 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 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->buffer->node, prev);
+ }
+ prev = &subsurface_tree->scene_surface->buffer->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_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 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_handle_subsurface_destroy(struct wl_listener *listener,
+ void *data) {
+ struct wlr_scene_subsurface_tree *subsurface_tree =
+ wl_container_of(listener, subsurface_tree, subsurface_destroy);
+ wlr_scene_node_destroy(&subsurface_tree->tree->node);
+}
+
+static void subsurface_tree_handle_subsurface_map(struct wl_listener *listener,
+ void *data) {
+ struct wlr_scene_subsurface_tree *subsurface_tree =
+ wl_container_of(listener, subsurface_tree, subsurface_map);
+
+ wlr_scene_node_set_enabled(&subsurface_tree->tree->node, true);
+}
+
+static void subsurface_tree_handle_subsurface_unmap(struct wl_listener *listener,
+ void *data) {
+ struct wlr_scene_subsurface_tree *subsurface_tree =
+ wl_container_of(listener, subsurface_tree, subsurface_unmap);
+
+ wlr_scene_node_set_enabled(&subsurface_tree->tree->node, false);
+}
+
+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_tree *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, subsurface->surface);
+ if (child == NULL) {
+ return false;
+ }
+
+ child->parent = parent;
+ wlr_scene_node_set_enabled(&child->tree->node, subsurface->mapped);
+
+ wlr_addon_init(&child->surface_addon, &subsurface->surface->addons,
+ parent, &subsurface_tree_addon_impl);
+
+ child->subsurface_destroy.notify = subsurface_tree_handle_subsurface_destroy;
+ wl_signal_add(&subsurface->events.destroy, &child->subsurface_destroy);
+
+ child->subsurface_map.notify = subsurface_tree_handle_subsurface_map;
+ wl_signal_add(&subsurface->events.map, &child->subsurface_map);
+
+ child->subsurface_unmap.notify = subsurface_tree_handle_subsurface_unmap;
+ wl_signal_add(&subsurface->events.unmap, &child->subsurface_unmap);
+
+ 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_tree *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, 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_tree *wlr_scene_subsurface_tree_create(
+ struct wlr_scene_tree *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;
+}
diff --git a/types/scene/surface.c b/types/scene/surface.c
new file mode 100644
index 0000000..553cc42
--- /dev/null
+++ b/types/scene/surface.c
@@ -0,0 +1,211 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <wlr/types/wlr_scene.h>
+#include <wlr/types/wlr_presentation_time.h>
+#include "types/wlr_scene.h"
+
+static void handle_scene_buffer_output_enter(
+ struct wl_listener *listener, void *data) {
+ struct wlr_scene_surface *surface =
+ wl_container_of(listener, surface, output_enter);
+ struct wlr_scene_output *output = data;
+
+ wlr_surface_send_enter(surface->surface, output->output);
+}
+
+static void handle_scene_buffer_output_leave(
+ struct wl_listener *listener, void *data) {
+ struct wlr_scene_surface *surface =
+ wl_container_of(listener, surface, output_leave);
+ struct wlr_scene_output *output = data;
+
+ wlr_surface_send_leave(surface->surface, output->output);
+}
+
+static void handle_scene_buffer_output_present(
+ struct wl_listener *listener, void *data) {
+ struct wlr_scene_surface *surface =
+ wl_container_of(listener, surface, output_present);
+ struct wlr_scene_output *scene_output = data;
+
+ if (surface->buffer->primary_output == scene_output) {
+ struct wlr_scene *root = scene_node_get_root(&surface->buffer->node);
+ struct wlr_presentation *presentation = root->presentation;
+
+ if (presentation) {
+ wlr_presentation_surface_sampled_on_output(
+ presentation, surface->surface, scene_output->output);
+ }
+ }
+}
+
+static void handle_scene_buffer_frame_done(
+ struct wl_listener *listener, void *data) {
+ struct wlr_scene_surface *surface =
+ wl_container_of(listener, surface, frame_done);
+ struct timespec *now = data;
+
+ wlr_surface_send_frame_done(surface->surface, now);
+}
+
+static void scene_surface_handle_surface_destroy(
+ struct wl_listener *listener, void *data) {
+ struct wlr_scene_surface *surface =
+ wl_container_of(listener, surface, surface_destroy);
+
+ wlr_scene_node_destroy(&surface->buffer->node);
+}
+
+// This is used for wlr_scene where it unconditionally locks buffers preventing
+// reuse of the existing texture for shm clients. With the usage pattern of
+// wlr_scene surface handling, we can mark its locked buffer as safe
+// for mutation.
+static void client_buffer_mark_next_can_damage(struct wlr_client_buffer *buffer) {
+ buffer->n_ignore_locks++;
+}
+
+static void scene_buffer_unmark_client_buffer(struct wlr_scene_buffer *scene_buffer) {
+ if (!scene_buffer->buffer) {
+ return;
+ }
+
+ struct wlr_client_buffer *buffer = wlr_client_buffer_get(scene_buffer->buffer);
+ if (!buffer) {
+ return;
+ }
+
+ assert(buffer->n_ignore_locks > 0);
+ buffer->n_ignore_locks--;
+}
+
+static void set_buffer_with_surface_state(struct wlr_scene_buffer *scene_buffer,
+ struct wlr_surface *surface) {
+ struct wlr_surface_state *state = &surface->current;
+
+ wlr_scene_buffer_set_opaque_region(scene_buffer, &surface->opaque_region);
+
+ struct wlr_fbox src_box;
+ wlr_surface_get_buffer_source_box(surface, &src_box);
+ wlr_scene_buffer_set_source_box(scene_buffer, &src_box);
+
+ wlr_scene_buffer_set_dest_size(scene_buffer, state->width, state->height);
+ wlr_scene_buffer_set_transform(scene_buffer, state->transform);
+
+ scene_buffer_unmark_client_buffer(scene_buffer);
+
+ if (surface->buffer) {
+ client_buffer_mark_next_can_damage(surface->buffer);
+
+ wlr_scene_buffer_set_buffer_with_damage(scene_buffer,
+ &surface->buffer->base, &surface->buffer_damage);
+ } else {
+ wlr_scene_buffer_set_buffer(scene_buffer, NULL);
+ }
+}
+
+static void handle_scene_surface_surface_commit(
+ struct wl_listener *listener, void *data) {
+ struct wlr_scene_surface *surface =
+ wl_container_of(listener, surface, surface_commit);
+ struct wlr_scene_buffer *scene_buffer = surface->buffer;
+
+ set_buffer_with_surface_state(scene_buffer, surface->surface);
+
+ // If the surface has requested a frame done event, honour that. The
+ // frame_callback_list will be populated in this case. We should only
+ // schedule the frame however if the node is enabled and there is an
+ // output intersecting, otherwise the frame done events would never reach
+ // the surface anyway.
+ int lx, ly;
+ bool enabled = wlr_scene_node_coords(&scene_buffer->node, &lx, &ly);
+
+ if (!wl_list_empty(&surface->surface->current.frame_callback_list) &&
+ surface->buffer->primary_output != NULL && enabled) {
+ wlr_output_schedule_frame(surface->buffer->primary_output->output);
+ }
+}
+
+static bool scene_buffer_point_accepts_input(struct wlr_scene_buffer *scene_buffer,
+ int sx, int sy) {
+ struct wlr_scene_surface *scene_surface =
+ wlr_scene_surface_from_buffer(scene_buffer);
+
+ return wlr_surface_point_accepts_input(scene_surface->surface, sx, sy);
+}
+
+static void surface_addon_destroy(struct wlr_addon *addon) {
+ struct wlr_scene_surface *surface = wl_container_of(addon, surface, addon);
+
+ scene_buffer_unmark_client_buffer(surface->buffer);
+
+ wlr_addon_finish(&surface->addon);
+
+ wl_list_remove(&surface->output_enter.link);
+ wl_list_remove(&surface->output_leave.link);
+ wl_list_remove(&surface->output_present.link);
+ wl_list_remove(&surface->frame_done.link);
+ wl_list_remove(&surface->surface_destroy.link);
+ wl_list_remove(&surface->surface_commit.link);
+
+ free(surface);
+}
+
+static const struct wlr_addon_interface surface_addon_impl = {
+ .name = "wlr_scene_surface",
+ .destroy = surface_addon_destroy,
+};
+
+struct wlr_scene_surface *wlr_scene_surface_from_buffer(
+ struct wlr_scene_buffer *scene_buffer) {
+ struct wlr_addon *addon = wlr_addon_find(&scene_buffer->node.addons,
+ scene_buffer, &surface_addon_impl);
+ if (!addon) {
+ return NULL;
+ }
+
+ struct wlr_scene_surface *surface = wl_container_of(addon, surface, addon);
+ return surface;
+}
+
+struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_tree *parent,
+ struct wlr_surface *wlr_surface) {
+ struct wlr_scene_surface *surface = calloc(1, sizeof(*surface));
+ if (surface == NULL) {
+ return NULL;
+ }
+
+ struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create(parent, NULL);
+ if (!scene_buffer) {
+ free(surface);
+ return NULL;
+ }
+
+ surface->buffer = scene_buffer;
+ surface->surface = wlr_surface;
+ scene_buffer->point_accepts_input = scene_buffer_point_accepts_input;
+
+ surface->output_enter.notify = handle_scene_buffer_output_enter;
+ wl_signal_add(&scene_buffer->events.output_enter, &surface->output_enter);
+
+ surface->output_leave.notify = handle_scene_buffer_output_leave;
+ wl_signal_add(&scene_buffer->events.output_leave, &surface->output_leave);
+
+ surface->output_present.notify = handle_scene_buffer_output_present;
+ wl_signal_add(&scene_buffer->events.output_present, &surface->output_present);
+
+ surface->frame_done.notify = handle_scene_buffer_frame_done;
+ wl_signal_add(&scene_buffer->events.frame_done, &surface->frame_done);
+
+ surface->surface_destroy.notify = scene_surface_handle_surface_destroy;
+ wl_signal_add(&wlr_surface->events.destroy, &surface->surface_destroy);
+
+ surface->surface_commit.notify = handle_scene_surface_surface_commit;
+ wl_signal_add(&wlr_surface->events.commit, &surface->surface_commit);
+
+ wlr_addon_init(&surface->addon, &scene_buffer->node.addons,
+ scene_buffer, &surface_addon_impl);
+
+ set_buffer_with_surface_state(scene_buffer, wlr_surface);
+
+ return surface;
+}
diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c
new file mode 100644
index 0000000..eb1ce3e
--- /dev/null
+++ b/types/scene/wlr_scene.c
@@ -0,0 +1,1728 @@
+#define _POSIX_C_SOURCE 200809L
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wlr/backend.h>
+#include <wlr/render/wlr_renderer.h>
+#include <wlr/types/wlr_compositor.h>
+#include <wlr/types/wlr_damage_ring.h>
+#include <wlr/types/wlr_matrix.h>
+#include <wlr/types/wlr_presentation_time.h>
+#include <wlr/types/wlr_scene.h>
+#include <wlr/util/log.h>
+#include <wlr/util/region.h>
+#include "types/wlr_buffer.h"
+#include "types/wlr_scene.h"
+#include "util/array.h"
+#include "util/env.h"
+#include "util/time.h"
+
+#define HIGHLIGHT_DAMAGE_FADEOUT_TIME 250
+
+static struct wlr_scene_tree *scene_tree_from_node(struct wlr_scene_node *node) {
+ assert(node->type == WLR_SCENE_NODE_TREE);
+ struct wlr_scene_tree *tree = wl_container_of(node, tree, node);
+ return tree;
+}
+
+static struct wlr_scene_rect *scene_rect_from_node(
+ struct wlr_scene_node *node) {
+ assert(node->type == WLR_SCENE_NODE_RECT);
+ struct wlr_scene_rect *rect = wl_container_of(node, rect, node);
+ return rect;
+}
+
+struct wlr_scene_buffer *wlr_scene_buffer_from_node(
+ struct wlr_scene_node *node) {
+ assert(node->type == WLR_SCENE_NODE_BUFFER);
+ struct wlr_scene_buffer *buffer = wl_container_of(node, buffer, node);
+ return buffer;
+}
+
+struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) {
+ struct wlr_scene_tree *tree;
+ if (node->type == WLR_SCENE_NODE_TREE) {
+ tree = scene_tree_from_node(node);
+ } else {
+ tree = node->parent;
+ }
+
+ while (tree->node.parent != NULL) {
+ tree = tree->node.parent;
+ }
+ return (struct wlr_scene *)tree;
+}
+
+static void scene_node_init(struct wlr_scene_node *node,
+ enum wlr_scene_node_type type, struct wlr_scene_tree *parent) {
+ memset(node, 0, sizeof(*node));
+ node->type = type;
+ node->parent = parent;
+ node->enabled = true;
+
+ wl_list_init(&node->link);
+
+ wl_signal_init(&node->events.destroy);
+ pixman_region32_init(&node->visible);
+
+ if (parent != NULL) {
+ wl_list_insert(parent->children.prev, &node->link);
+ }
+
+ wlr_addon_set_init(&node->addons);
+}
+
+struct highlight_region {
+ pixman_region32_t region;
+ struct timespec when;
+ struct wl_list link;
+};
+
+void wlr_scene_node_destroy(struct wlr_scene_node *node) {
+ if (node == NULL) {
+ return;
+ }
+
+ // We want to call the destroy listeners before we do anything else
+ // in case the destroy signal would like to remove children before they
+ // are recursively destroyed.
+ wl_signal_emit_mutable(&node->events.destroy, NULL);
+ wlr_addon_set_finish(&node->addons);
+
+ wlr_scene_node_set_enabled(node, false);
+
+ struct wlr_scene *scene = scene_node_get_root(node);
+ if (node->type == WLR_SCENE_NODE_BUFFER) {
+ struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
+
+ uint64_t active = scene_buffer->active_outputs;
+ if (active) {
+ struct wlr_scene_output *scene_output;
+ wl_list_for_each(scene_output, &scene->outputs, link) {
+ if (active & (1ull << scene_output->index)) {
+ wl_signal_emit_mutable(&scene_buffer->events.output_leave,
+ scene_output);
+ }
+ }
+ }
+
+ wlr_texture_destroy(scene_buffer->texture);
+ wlr_buffer_unlock(scene_buffer->buffer);
+ pixman_region32_fini(&scene_buffer->opaque_region);
+ } else if (node->type == WLR_SCENE_NODE_TREE) {
+ struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+
+ if (scene_tree == &scene->tree) {
+ assert(!node->parent);
+ struct wlr_scene_output *scene_output, *scene_output_tmp;
+ wl_list_for_each_safe(scene_output, scene_output_tmp, &scene->outputs, link) {
+ wlr_scene_output_destroy(scene_output);
+ }
+
+ wl_list_remove(&scene->presentation_destroy.link);
+ } else {
+ assert(node->parent);
+ }
+
+ struct wlr_scene_node *child, *child_tmp;
+ wl_list_for_each_safe(child, child_tmp,
+ &scene_tree->children, link) {
+ wlr_scene_node_destroy(child);
+ }
+ }
+
+ wl_list_remove(&node->link);
+ pixman_region32_fini(&node->visible);
+ free(node);
+}
+
+static void scene_tree_init(struct wlr_scene_tree *tree,
+ struct wlr_scene_tree *parent) {
+ memset(tree, 0, sizeof(*tree));
+ scene_node_init(&tree->node, WLR_SCENE_NODE_TREE, parent);
+ wl_list_init(&tree->children);
+}
+
+struct wlr_scene *wlr_scene_create(void) {
+ struct wlr_scene *scene = calloc(1, sizeof(struct wlr_scene));
+ if (scene == NULL) {
+ return NULL;
+ }
+
+ scene_tree_init(&scene->tree, NULL);
+
+ wl_list_init(&scene->outputs);
+ wl_list_init(&scene->presentation_destroy.link);
+
+ const char *debug_damage_options[] = {
+ "none",
+ "rerender",
+ "highlight",
+ NULL
+ };
+
+ scene->debug_damage_option = env_parse_switch("WLR_SCENE_DEBUG_DAMAGE", debug_damage_options);
+ scene->direct_scanout = !env_parse_bool("WLR_SCENE_DISABLE_DIRECT_SCANOUT");
+ scene->calculate_visibility = !env_parse_bool("WLR_SCENE_DISABLE_VISIBILITY");
+
+ return scene;
+}
+
+struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent) {
+ assert(parent);
+
+ struct wlr_scene_tree *tree = calloc(1, sizeof(struct wlr_scene_tree));
+ if (tree == NULL) {
+ return NULL;
+ }
+
+ scene_tree_init(tree, parent);
+ return tree;
+}
+
+static void scene_node_get_size(struct wlr_scene_node *node, int *lx, int *ly);
+
+typedef bool (*scene_node_box_iterator_func_t)(struct wlr_scene_node *node,
+ int sx, int sy, void *data);
+
+static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box,
+ scene_node_box_iterator_func_t iterator, void *user_data, int lx, int ly) {
+ if (!node->enabled) {
+ return false;
+ }
+
+ switch (node->type) {
+ case WLR_SCENE_NODE_TREE:;
+ struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_node *child;
+ wl_list_for_each_reverse(child, &scene_tree->children, link) {
+ if (_scene_nodes_in_box(child, box, iterator, user_data, lx + child->x, ly + child->y)) {
+ return true;
+ }
+ }
+ break;
+ case WLR_SCENE_NODE_RECT:
+ case WLR_SCENE_NODE_BUFFER:;
+ struct wlr_box node_box = { .x = lx, .y = ly };
+ scene_node_get_size(node, &node_box.width, &node_box.height);
+
+ if (wlr_box_intersection(&node_box, &node_box, box) &&
+ iterator(node, lx, ly, user_data)) {
+ return true;
+ }
+ break;
+ }
+
+ return false;
+}
+
+static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box,
+ scene_node_box_iterator_func_t iterator, void *user_data) {
+ int x, y;
+ wlr_scene_node_coords(node, &x, &y);
+
+ return _scene_nodes_in_box(node, box, iterator, user_data, x, y);
+}
+
+static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y,
+ pixman_region32_t *opaque) {
+ if (node->type == WLR_SCENE_NODE_RECT) {
+ struct wlr_scene_rect *scene_rect = scene_rect_from_node(node);
+ if (scene_rect->color[3] != 1) {
+ return;
+ }
+ } else if (node->type == WLR_SCENE_NODE_BUFFER) {
+ struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
+
+ if (!scene_buffer->buffer) {
+ return;
+ }
+
+ if (!buffer_is_opaque(scene_buffer->buffer)) {
+ pixman_region32_copy(opaque, &scene_buffer->opaque_region);
+ pixman_region32_translate(opaque, x, y);
+ return;
+ }
+ }
+
+ int width, height;
+ scene_node_get_size(node, &width, &height);
+ pixman_region32_fini(opaque);
+ pixman_region32_init_rect(opaque, x, y, width, height);
+}
+
+struct scene_update_data {
+ pixman_region32_t *visible;
+ pixman_region32_t *update_region;
+ struct wl_list *outputs;
+ bool calculate_visibility;
+};
+
+static uint32_t region_area(pixman_region32_t *region) {
+ uint32_t area = 0;
+
+ int nrects;
+ pixman_box32_t *rects = pixman_region32_rectangles(region, &nrects);
+ for (int i = 0; i < nrects; ++i) {
+ area += (rects[i].x2 - rects[i].x1) * (rects[i].y2 - rects[i].y1);
+ }
+
+ return area;
+}
+
+static void scale_output_damage(pixman_region32_t *damage, float scale) {
+ wlr_region_scale(damage, damage, scale);
+
+ if (floor(scale) != scale) {
+ wlr_region_expand(damage, damage, 1);
+ }
+}
+
+static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *damage) {
+ if (!pixman_region32_not_empty(damage)) {
+ return;
+ }
+
+ struct wlr_scene_output *scene_output;
+ wl_list_for_each(scene_output, &scene->outputs, link) {
+ pixman_region32_t output_damage;
+ pixman_region32_init(&output_damage);
+ pixman_region32_copy(&output_damage, damage);
+ pixman_region32_translate(&output_damage,
+ -scene_output->x, -scene_output->y);
+ scale_output_damage(&output_damage, scene_output->output->scale);
+ if (wlr_damage_ring_add(&scene_output->damage_ring, &output_damage)) {
+ wlr_output_schedule_frame(scene_output->output);
+ }
+ pixman_region32_fini(&output_damage);
+ }
+}
+
+static void update_node_update_outputs(struct wlr_scene_node *node,
+ struct wl_list *outputs, struct wlr_scene_output *ignore) {
+ if (node->type != WLR_SCENE_NODE_BUFFER) {
+ return;
+ }
+
+ struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
+
+ uint32_t largest_overlap = 0;
+ scene_buffer->primary_output = NULL;
+
+ uint64_t active_outputs = 0;
+
+ // let's update the outputs in two steps:
+ // - the primary outputs
+ // - the enter/leave signals
+ // This ensures that the enter/leave signals can rely on the primary output
+ // to have a reasonable value. Otherwise, they may get a value that's in
+ // the middle of a calculation.
+ struct wlr_scene_output *scene_output;
+ wl_list_for_each(scene_output, outputs, link) {
+ if (scene_output == ignore) {
+ continue;
+ }
+
+ if (!scene_output->output->enabled) {
+ continue;
+ }
+
+ struct wlr_box output_box = {
+ .x = scene_output->x,
+ .y = scene_output->y,
+ };
+ wlr_output_effective_resolution(scene_output->output,
+ &output_box.width, &output_box.height);
+
+ pixman_region32_t intersection;
+ pixman_region32_init(&intersection);
+ pixman_region32_intersect_rect(&intersection, &node->visible,
+ output_box.x, output_box.y, output_box.width, output_box.height);
+
+ if (pixman_region32_not_empty(&intersection)) {
+ uint32_t overlap = region_area(&intersection);
+ if (overlap >= largest_overlap) {
+ largest_overlap = overlap;
+ scene_buffer->primary_output = scene_output;
+ }
+
+ active_outputs |= 1ull << scene_output->index;
+ }
+
+ pixman_region32_fini(&intersection);
+ }
+
+ uint64_t old_active = scene_buffer->active_outputs;
+ scene_buffer->active_outputs = active_outputs;
+
+ wl_list_for_each(scene_output, outputs, link) {
+ uint64_t mask = 1ull << scene_output->index;
+ bool intersects = active_outputs & mask;
+ bool intersects_before = old_active & mask;
+
+ if (intersects && !intersects_before) {
+ wl_signal_emit_mutable(&scene_buffer->events.output_enter, scene_output);
+ } else if (!intersects && intersects_before) {
+ wl_signal_emit_mutable(&scene_buffer->events.output_leave, scene_output);
+ }
+ }
+
+ // if there are active outputs on this node, we should always have a primary
+ // output
+ assert(!scene_buffer->active_outputs || scene_buffer->primary_output);
+}
+
+static bool scene_node_update_iterator(struct wlr_scene_node *node,
+ int lx, int ly, void *_data) {
+ struct scene_update_data *data = _data;
+
+ struct wlr_box box = { .x = lx, .y = ly };
+ scene_node_get_size(node, &box.width, &box.height);
+
+ pixman_region32_subtract(&node->visible, &node->visible, data->update_region);
+ pixman_region32_union(&node->visible, &node->visible, data->visible);
+ pixman_region32_intersect_rect(&node->visible, &node->visible,
+ lx, ly, box.width, box.height);
+
+ if (data->calculate_visibility) {
+ pixman_region32_t opaque;
+ pixman_region32_init(&opaque);
+ scene_node_opaque_region(node, lx, ly, &opaque);
+ pixman_region32_subtract(data->visible, data->visible, &opaque);
+ pixman_region32_fini(&opaque);
+ }
+
+ update_node_update_outputs(node, data->outputs, NULL);
+
+ return false;
+}
+
+static void scene_node_visibility(struct wlr_scene_node *node,
+ pixman_region32_t *visible) {
+ if (!node->enabled) {
+ return;
+ }
+
+ if (node->type == WLR_SCENE_NODE_TREE) {
+ struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_node *child;
+ wl_list_for_each(child, &scene_tree->children, link) {
+ scene_node_visibility(child, visible);
+ }
+ return;
+ }
+
+ pixman_region32_union(visible, visible, &node->visible);
+}
+
+static void scene_node_bounds(struct wlr_scene_node *node,
+ int x, int y, pixman_region32_t *visible) {
+ if (!node->enabled) {
+ return;
+ }
+
+ if (node->type == WLR_SCENE_NODE_TREE) {
+ struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_node *child;
+ wl_list_for_each(child, &scene_tree->children, link) {
+ scene_node_bounds(child, x + child->x, y + child->y, visible);
+ }
+ return;
+ }
+
+ int width, height;
+ scene_node_get_size(node, &width, &height);
+ pixman_region32_union_rect(visible, visible, x, y, width, height);
+}
+
+static void scene_update_region(struct wlr_scene *scene,
+ pixman_region32_t *update_region) {
+ pixman_region32_t visible;
+ pixman_region32_init(&visible);
+ pixman_region32_copy(&visible, update_region);
+
+ struct scene_update_data data = {
+ .visible = &visible,
+ .update_region = update_region,
+ .outputs = &scene->outputs,
+ .calculate_visibility = scene->calculate_visibility,
+ };
+
+ struct pixman_box32 *region_box = pixman_region32_extents(update_region);
+ struct wlr_box box = {
+ .x = region_box->x1,
+ .y = region_box->y1,
+ .width = region_box->x2 - region_box->x1,
+ .height = region_box->y2 - region_box->y1,
+ };
+
+ // update node visibility and output enter/leave events
+ scene_nodes_in_box(&scene->tree.node, &box, scene_node_update_iterator, &data);
+
+ pixman_region32_fini(&visible);
+}
+
+static void scene_node_update(struct wlr_scene_node *node,
+ pixman_region32_t *damage) {
+ struct wlr_scene *scene = scene_node_get_root(node);
+
+ int x, y;
+ if (!wlr_scene_node_coords(node, &x, &y)) {
+ if (damage) {
+ scene_update_region(scene, damage);
+ scene_damage_outputs(scene, damage);
+ pixman_region32_fini(damage);
+ }
+
+ return;
+ }
+
+ pixman_region32_t visible;
+ if (!damage) {
+ pixman_region32_init(&visible);
+ scene_node_visibility(node, &visible);
+ damage = &visible;
+ }
+
+ pixman_region32_t update_region;
+ pixman_region32_init(&update_region);
+ pixman_region32_copy(&update_region, damage);
+ scene_node_bounds(node, x, y, &update_region);
+
+ scene_update_region(scene, &update_region);
+ pixman_region32_fini(&update_region);
+
+ scene_node_visibility(node, damage);
+ scene_damage_outputs(scene, damage);
+ pixman_region32_fini(damage);
+}
+
+struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent,
+ int width, int height, const float color[static 4]) {
+ struct wlr_scene_rect *scene_rect =
+ calloc(1, sizeof(struct wlr_scene_rect));
+ if (scene_rect == NULL) {
+ return NULL;
+ }
+ assert(parent);
+ scene_node_init(&scene_rect->node, WLR_SCENE_NODE_RECT, parent);
+
+ scene_rect->width = width;
+ scene_rect->height = height;
+ memcpy(scene_rect->color, color, sizeof(scene_rect->color));
+
+ scene_node_update(&scene_rect->node, NULL);
+
+ return scene_rect;
+}
+
+void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height) {
+ if (rect->width == width && rect->height == height) {
+ return;
+ }
+
+ rect->width = width;
+ rect->height = height;
+ scene_node_update(&rect->node, NULL);
+}
+
+void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[static 4]) {
+ if (memcmp(rect->color, color, sizeof(rect->color)) == 0) {
+ return;
+ }
+
+ memcpy(rect->color, color, sizeof(rect->color));
+ scene_node_update(&rect->node, NULL);
+}
+
+struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent,
+ struct wlr_buffer *buffer) {
+ struct wlr_scene_buffer *scene_buffer = calloc(1, sizeof(*scene_buffer));
+ if (scene_buffer == NULL) {
+ return NULL;
+ }
+ assert(parent);
+ scene_node_init(&scene_buffer->node, WLR_SCENE_NODE_BUFFER, parent);
+
+ if (buffer) {
+ scene_buffer->buffer = wlr_buffer_lock(buffer);
+ }
+
+ wl_signal_init(&scene_buffer->events.output_enter);
+ wl_signal_init(&scene_buffer->events.output_leave);
+ wl_signal_init(&scene_buffer->events.output_present);
+ wl_signal_init(&scene_buffer->events.frame_done);
+ pixman_region32_init(&scene_buffer->opaque_region);
+
+ scene_node_update(&scene_buffer->node, NULL);
+
+ return scene_buffer;
+}
+
+void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer,
+ struct wlr_buffer *buffer, pixman_region32_t *damage) {
+ // specifying a region for a NULL buffer doesn't make sense. We need to know
+ // about the buffer to scale the buffer local coordinates down to scene
+ // coordinates.
+ assert(buffer || !damage);
+
+ bool update = false;
+ wlr_buffer_unlock(scene_buffer->buffer);
+
+ wlr_texture_destroy(scene_buffer->texture);
+ scene_buffer->texture = NULL;
+
+ if (buffer) {
+ // if this node used to not be mapped or its previous displayed
+ // buffer region will be different from what the new buffer would
+ // produce we need to update the node.
+ update = !scene_buffer->buffer ||
+ (scene_buffer->dst_width == 0 && scene_buffer->dst_height == 0 &&
+ (scene_buffer->buffer->width != buffer->width ||
+ scene_buffer->buffer->height != buffer->height));
+
+ scene_buffer->buffer = wlr_buffer_lock(buffer);
+ } else {
+ update = true;
+ scene_buffer->buffer = NULL;
+ }
+
+ if (update) {
+ scene_node_update(&scene_buffer->node, NULL);
+ // updating the node will already damage the whole node for us. Return
+ // early to not damage again
+ return;
+ }
+
+ int lx, ly;
+ if (!wlr_scene_node_coords(&scene_buffer->node, &lx, &ly)) {
+ return;
+ }
+
+ pixman_region32_t fallback_damage;
+ pixman_region32_init_rect(&fallback_damage, 0, 0, buffer->width, buffer->height);
+ if (!damage) {
+ damage = &fallback_damage;
+ }
+
+ struct wlr_fbox box = scene_buffer->src_box;
+ if (wlr_fbox_empty(&box)) {
+ box.x = 0;
+ box.y = 0;
+ box.width = buffer->width;
+ box.height = buffer->height;
+ }
+
+ wlr_fbox_transform(&box, &box, scene_buffer->transform,
+ buffer->width, buffer->height);
+
+ float scale_x, scale_y;
+ if (scene_buffer->dst_width || scene_buffer->dst_height) {
+ scale_x = scene_buffer->dst_width / box.width;
+ scale_y = scene_buffer->dst_height / box.height;
+ } else {
+ scale_x = buffer->width / box.width;
+ scale_y = buffer->height / box.height;
+ }
+
+ pixman_region32_t trans_damage;
+ pixman_region32_init(&trans_damage);
+ wlr_region_transform(&trans_damage, damage,
+ scene_buffer->transform, buffer->width, buffer->height);
+ pixman_region32_intersect_rect(&trans_damage, &trans_damage,
+ box.x, box.y, box.width, box.height);
+ pixman_region32_translate(&trans_damage, -box.x, -box.y);
+
+ struct wlr_scene *scene = scene_node_get_root(&scene_buffer->node);
+ struct wlr_scene_output *scene_output;
+ wl_list_for_each(scene_output, &scene->outputs, link) {
+ float output_scale = scene_output->output->scale;
+ float output_scale_x = output_scale * scale_x;
+ float output_scale_y = output_scale * scale_y;
+ pixman_region32_t output_damage;
+ pixman_region32_init(&output_damage);
+ wlr_region_scale_xy(&output_damage, &trans_damage,
+ output_scale_x, output_scale_y);
+
+ // One output pixel will match (buffer_scale_x)x(buffer_scale_y) buffer pixels.
+ // If the buffer is upscaled on the given axis (output_scale_* > 1.0,
+ // buffer_scale_* < 1.0), its contents will bleed into adjacent
+ // (ceil(output_scale_* / 2)) output pixels because of linear filtering.
+ // Additionally, if the buffer is downscaled (output_scale_* < 1.0,
+ // buffer_scale_* > 1.0), and one output pixel matches a non-integer number of
+ // buffer pixels, its contents will bleed into neighboring output pixels.
+ // Handle both cases by computing buffer_scale_{x,y} and checking if they are
+ // integer numbers; ceilf() is used to ensure that the distance is at least 1.
+ float buffer_scale_x = 1.0f / output_scale_x;
+ float buffer_scale_y = 1.0f / output_scale_y;
+ int dist_x = floor(buffer_scale_x) != buffer_scale_x ?
+ (int)ceilf(output_scale_x / 2.0f) : 0;
+ int dist_y = floor(buffer_scale_y) != buffer_scale_y ?
+ (int)ceilf(output_scale_y / 2.0f) : 0;
+ // TODO: expand with per-axis distances
+ wlr_region_expand(&output_damage, &output_damage,
+ dist_x >= dist_y ? dist_x : dist_y);
+
+ pixman_region32_t cull_region;
+ pixman_region32_init(&cull_region);
+ pixman_region32_copy(&cull_region, &scene_buffer->node.visible);
+ scale_output_damage(&cull_region, output_scale);
+ pixman_region32_translate(&cull_region, -lx * output_scale, -ly * output_scale);
+ pixman_region32_intersect(&output_damage, &output_damage, &cull_region);
+ pixman_region32_fini(&cull_region);
+
+ pixman_region32_translate(&output_damage,
+ (lx - scene_output->x) * output_scale,
+ (ly - scene_output->y) * output_scale);
+ if (wlr_damage_ring_add(&scene_output->damage_ring, &output_damage)) {
+ wlr_output_schedule_frame(scene_output->output);
+ }
+ pixman_region32_fini(&output_damage);
+ }
+
+ pixman_region32_fini(&trans_damage);
+ pixman_region32_fini(&fallback_damage);
+}
+
+void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer,
+ struct wlr_buffer *buffer) {
+ wlr_scene_buffer_set_buffer_with_damage(scene_buffer, buffer, NULL);
+}
+
+void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer,
+ pixman_region32_t *region) {
+ if (pixman_region32_equal(&scene_buffer->opaque_region, region)) {
+ return;
+ }
+
+ pixman_region32_copy(&scene_buffer->opaque_region, region);
+ scene_node_update(&scene_buffer->node, NULL);
+}
+
+void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer,
+ const struct wlr_fbox *box) {
+ struct wlr_fbox *cur = &scene_buffer->src_box;
+ if ((wlr_fbox_empty(box) && wlr_fbox_empty(cur)) ||
+ (box != NULL && wlr_fbox_equal(cur, box))) {
+ return;
+ }
+
+ if (box != NULL) {
+ memcpy(cur, box, sizeof(*box));
+ } else {
+ memset(cur, 0, sizeof(*cur));
+ }
+
+ scene_node_update(&scene_buffer->node, NULL);
+}
+
+void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer,
+ int width, int height) {
+ if (scene_buffer->dst_width == width && scene_buffer->dst_height == height) {
+ return;
+ }
+
+ scene_buffer->dst_width = width;
+ scene_buffer->dst_height = height;
+ scene_node_update(&scene_buffer->node, NULL);
+}
+
+void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer,
+ enum wl_output_transform transform) {
+ if (scene_buffer->transform == transform) {
+ return;
+ }
+
+ scene_buffer->transform = transform;
+ scene_node_update(&scene_buffer->node, NULL);
+}
+
+void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer,
+ struct timespec *now) {
+ if (pixman_region32_not_empty(&scene_buffer->node.visible)) {
+ wl_signal_emit_mutable(&scene_buffer->events.frame_done, now);
+ }
+}
+
+static struct wlr_texture *scene_buffer_get_texture(
+ struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) {
+ struct wlr_client_buffer *client_buffer =
+ wlr_client_buffer_get(scene_buffer->buffer);
+ if (client_buffer != NULL) {
+ return client_buffer->texture;
+ }
+
+ if (scene_buffer->texture != NULL) {
+ return scene_buffer->texture;
+ }
+
+ scene_buffer->texture =
+ wlr_texture_from_buffer(renderer, scene_buffer->buffer);
+ return scene_buffer->texture;
+}
+
+static void scene_node_get_size(struct wlr_scene_node *node,
+ int *width, int *height) {
+ *width = 0;
+ *height = 0;
+
+ switch (node->type) {
+ case WLR_SCENE_NODE_TREE:
+ return;
+ case WLR_SCENE_NODE_RECT:;
+ struct wlr_scene_rect *scene_rect = scene_rect_from_node(node);
+ *width = scene_rect->width;
+ *height = scene_rect->height;
+ break;
+ case WLR_SCENE_NODE_BUFFER:;
+ struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
+ if (scene_buffer->dst_width > 0 && scene_buffer->dst_height > 0) {
+ *width = scene_buffer->dst_width;
+ *height = scene_buffer->dst_height;
+ } else if (scene_buffer->buffer) {
+ if (scene_buffer->transform & WL_OUTPUT_TRANSFORM_90) {
+ *height = scene_buffer->buffer->width;
+ *width = scene_buffer->buffer->height;
+ } else {
+ *width = scene_buffer->buffer->width;
+ *height = scene_buffer->buffer->height;
+ }
+ }
+ break;
+ }
+}
+
+static int scale_length(int length, int offset, float scale) {
+ return round((offset + length) * scale) - round(offset * scale);
+}
+
+static void scale_box(struct wlr_box *box, float scale) {
+ box->width = scale_length(box->width, box->x, scale);
+ box->height = scale_length(box->height, box->y, scale);
+ box->x = round(box->x * scale);
+ box->y = round(box->y * scale);
+}
+
+void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled) {
+ if (node->enabled == enabled) {
+ return;
+ }
+
+ int x, y;
+ pixman_region32_t visible;
+ pixman_region32_init(&visible);
+ if (wlr_scene_node_coords(node, &x, &y)) {
+ scene_node_visibility(node, &visible);
+ }
+
+ node->enabled = enabled;
+
+ scene_node_update(node, &visible);
+}
+
+void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) {
+ if (node->x == x && node->y == y) {
+ return;
+ }
+
+ node->x = x;
+ node->y = y;
+ scene_node_update(node, NULL);
+}
+
+void wlr_scene_node_place_above(struct wlr_scene_node *node,
+ struct wlr_scene_node *sibling) {
+ assert(node != sibling);
+ assert(node->parent == sibling->parent);
+
+ if (node->link.prev == &sibling->link) {
+ return;
+ }
+
+ wl_list_remove(&node->link);
+ wl_list_insert(&sibling->link, &node->link);
+ scene_node_update(node, NULL);
+}
+
+void wlr_scene_node_place_below(struct wlr_scene_node *node,
+ struct wlr_scene_node *sibling) {
+ assert(node != sibling);
+ assert(node->parent == sibling->parent);
+
+ if (node->link.next == &sibling->link) {
+ return;
+ }
+
+ wl_list_remove(&node->link);
+ wl_list_insert(sibling->link.prev, &node->link);
+ scene_node_update(node, NULL);
+}
+
+void wlr_scene_node_raise_to_top(struct wlr_scene_node *node) {
+ struct wlr_scene_node *current_top = wl_container_of(
+ node->parent->children.prev, current_top, link);
+ if (node == current_top) {
+ return;
+ }
+ wlr_scene_node_place_above(node, current_top);
+}
+
+void wlr_scene_node_lower_to_bottom(struct wlr_scene_node *node) {
+ struct wlr_scene_node *current_bottom = wl_container_of(
+ node->parent->children.next, current_bottom, link);
+ if (node == current_bottom) {
+ return;
+ }
+ wlr_scene_node_place_below(node, current_bottom);
+}
+
+void wlr_scene_node_reparent(struct wlr_scene_node *node,
+ struct wlr_scene_tree *new_parent) {
+ assert(new_parent != NULL);
+
+ if (node->parent == new_parent) {
+ return;
+ }
+
+ /* Ensure that a node cannot become its own ancestor */
+ for (struct wlr_scene_tree *ancestor = new_parent; ancestor != NULL;
+ ancestor = ancestor->node.parent) {
+ assert(&ancestor->node != node);
+ }
+
+ int x, y;
+ pixman_region32_t visible;
+ pixman_region32_init(&visible);
+ if (wlr_scene_node_coords(node, &x, &y)) {
+ scene_node_visibility(node, &visible);
+ }
+
+ wl_list_remove(&node->link);
+ node->parent = new_parent;
+ wl_list_insert(new_parent->children.prev, &node->link);
+ scene_node_update(node, &visible);
+}
+
+bool wlr_scene_node_coords(struct wlr_scene_node *node,
+ int *lx_ptr, int *ly_ptr) {
+ assert(node);
+
+ int lx = 0, ly = 0;
+ bool enabled = true;
+ while (true) {
+ lx += node->x;
+ ly += node->y;
+ enabled = enabled && node->enabled;
+ if (node->parent == NULL) {
+ break;
+ }
+
+ node = &node->parent->node;
+ }
+
+ *lx_ptr = lx;
+ *ly_ptr = ly;
+ return enabled;
+}
+
+static void scene_node_for_each_scene_buffer(struct wlr_scene_node *node,
+ int lx, int ly, wlr_scene_buffer_iterator_func_t user_iterator,
+ void *user_data) {
+ if (!node->enabled) {
+ return;
+ }
+
+ lx += node->x;
+ ly += node->y;
+
+ if (node->type == WLR_SCENE_NODE_BUFFER) {
+ struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
+ user_iterator(scene_buffer, lx, ly, user_data);
+ } else if (node->type == WLR_SCENE_NODE_TREE) {
+ struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_node *child;
+ wl_list_for_each(child, &scene_tree->children, link) {
+ scene_node_for_each_scene_buffer(child, lx, ly, user_iterator, user_data);
+ }
+ }
+}
+
+void wlr_scene_node_for_each_buffer(struct wlr_scene_node *node,
+ wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) {
+ scene_node_for_each_scene_buffer(node, 0, 0, user_iterator, user_data);
+}
+
+struct node_at_data {
+ double lx, ly;
+ double rx, ry;
+ struct wlr_scene_node *node;
+};
+
+static bool scene_node_at_iterator(struct wlr_scene_node *node,
+ int lx, int ly, void *data) {
+ struct node_at_data *at_data = data;
+
+ double rx = at_data->lx - lx;
+ double ry = at_data->ly - ly;
+
+ if (node->type == WLR_SCENE_NODE_BUFFER) {
+ struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
+
+ if (scene_buffer->point_accepts_input &&
+ !scene_buffer->point_accepts_input(scene_buffer, rx, ry)) {
+ return false;
+ }
+ }
+
+ at_data->rx = rx;
+ at_data->ry = ry;
+ at_data->node = node;
+ return true;
+}
+
+struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node,
+ double lx, double ly, double *nx, double *ny) {
+ struct wlr_box box = {
+ .x = floor(lx),
+ .y = floor(ly),
+ .width = 1,
+ .height = 1
+ };
+
+ struct node_at_data data = {
+ .lx = lx,
+ .ly = ly
+ };
+
+ if (scene_nodes_in_box(node, &box, scene_node_at_iterator, &data)) {
+ if (nx) {
+ *nx = data.rx;
+ }
+ if (ny) {
+ *ny = data.ry;
+ }
+ return data.node;
+ }
+
+ return NULL;
+}
+
+static void scissor_output(struct wlr_output *output, pixman_box32_t *rect) {
+ struct wlr_renderer *renderer = output->renderer;
+ assert(renderer);
+
+ struct wlr_box box = {
+ .x = rect->x1,
+ .y = rect->y1,
+ .width = rect->x2 - rect->x1,
+ .height = rect->y2 - rect->y1,
+ };
+
+ int ow, oh;
+ wlr_output_transformed_resolution(output, &ow, &oh);
+
+ enum wl_output_transform transform =
+ wlr_output_transform_invert(output->transform);
+ wlr_box_transform(&box, &box, transform, ow, oh);
+
+ wlr_renderer_scissor(renderer, &box);
+}
+
+static void render_rect(struct wlr_output *output,
+ pixman_region32_t *damage, const float color[static 4],
+ const struct wlr_box *box, const float matrix[static 9]) {
+ struct wlr_renderer *renderer = output->renderer;
+ assert(renderer);
+
+ int nrects;
+ pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
+ for (int i = 0; i < nrects; ++i) {
+ scissor_output(output, &rects[i]);
+ wlr_render_rect(renderer, box, color, matrix);
+ }
+}
+
+static void render_texture(struct wlr_output *output,
+ pixman_region32_t *damage, struct wlr_texture *texture,
+ const struct wlr_fbox *src_box, const struct wlr_box *dst_box,
+ const float matrix[static 9]) {
+ struct wlr_renderer *renderer = output->renderer;
+ assert(renderer);
+
+ struct wlr_fbox default_src_box = {0};
+ if (wlr_fbox_empty(src_box)) {
+ default_src_box.width = texture->width;
+ default_src_box.height = texture->height;
+ src_box = &default_src_box;
+ }
+
+ int nrects;
+ pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
+ for (int i = 0; i < nrects; ++i) {
+ scissor_output(output, &rects[i]);
+ wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, 1.0);
+ }
+}
+
+static void scene_node_render(struct wlr_scene_node *node,
+ struct wlr_scene_output *scene_output, pixman_region32_t *damage) {
+ int x, y;
+ wlr_scene_node_coords(node, &x, &y);
+ x -= scene_output->x;
+ y -= scene_output->y;
+
+ struct wlr_output *output = scene_output->output;
+
+ pixman_region32_t render_region;
+ pixman_region32_init(&render_region);
+ pixman_region32_copy(&render_region, &node->visible);
+ pixman_region32_translate(&render_region, -scene_output->x, -scene_output->y);
+ scale_output_damage(&render_region, output->scale);
+ pixman_region32_intersect(&render_region, &render_region, damage);
+ if (!pixman_region32_not_empty(&render_region)) {
+ pixman_region32_fini(&render_region);
+ return;
+ }
+
+ struct wlr_box dst_box = {
+ .x = x,
+ .y = y,
+ };
+ scene_node_get_size(node, &dst_box.width, &dst_box.height);
+ scale_box(&dst_box, output->scale);
+
+ struct wlr_texture *texture;
+ float matrix[9];
+ enum wl_output_transform transform;
+ switch (node->type) {
+ case WLR_SCENE_NODE_TREE:
+ assert(false);
+ break;
+ case WLR_SCENE_NODE_RECT:;
+ struct wlr_scene_rect *scene_rect = scene_rect_from_node(node);
+
+ render_rect(output, &render_region, scene_rect->color, &dst_box,
+ output->transform_matrix);
+ break;
+ case WLR_SCENE_NODE_BUFFER:;
+ struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
+ assert(scene_buffer->buffer);
+
+ struct wlr_renderer *renderer = output->renderer;
+ texture = scene_buffer_get_texture(scene_buffer, renderer);
+ if (texture == NULL) {
+ break;
+ }
+
+ transform = wlr_output_transform_invert(scene_buffer->transform);
+ wlr_matrix_project_box(matrix, &dst_box, transform, 0.0,
+ output->transform_matrix);
+
+ render_texture(output, &render_region, texture, &scene_buffer->src_box,
+ &dst_box, matrix);
+
+ wl_signal_emit_mutable(&scene_buffer->events.output_present, scene_output);
+ break;
+ }
+
+ pixman_region32_fini(&render_region);
+}
+
+static void scene_handle_presentation_destroy(struct wl_listener *listener,
+ void *data) {
+ struct wlr_scene *scene =
+ wl_container_of(listener, scene, presentation_destroy);
+ wl_list_remove(&scene->presentation_destroy.link);
+ wl_list_init(&scene->presentation_destroy.link);
+ scene->presentation = NULL;
+}
+
+void wlr_scene_set_presentation(struct wlr_scene *scene,
+ struct wlr_presentation *presentation) {
+ assert(scene->presentation == NULL);
+ scene->presentation = presentation;
+ scene->presentation_destroy.notify = scene_handle_presentation_destroy;
+ wl_signal_add(&presentation->events.destroy, &scene->presentation_destroy);
+}
+
+static void scene_output_handle_destroy(struct wlr_addon *addon) {
+ struct wlr_scene_output *scene_output =
+ wl_container_of(addon, scene_output, addon);
+ wlr_scene_output_destroy(scene_output);
+}
+
+static const struct wlr_addon_interface output_addon_impl = {
+ .name = "wlr_scene_output",
+ .destroy = scene_output_handle_destroy,
+};
+
+static void scene_node_output_update(struct wlr_scene_node *node,
+ struct wl_list *outputs, struct wlr_scene_output *ignore) {
+ if (node->type == WLR_SCENE_NODE_TREE) {
+ struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_node *child;
+ wl_list_for_each(child, &scene_tree->children, link) {
+ scene_node_output_update(child, outputs, ignore);
+ }
+ return;
+ }
+
+ update_node_update_outputs(node, outputs, ignore);
+}
+
+static void scene_output_update_geometry(struct wlr_scene_output *scene_output) {
+ int width, height;
+ wlr_output_transformed_resolution(scene_output->output, &width, &height);
+ wlr_damage_ring_set_bounds(&scene_output->damage_ring, width, height);
+ wlr_output_schedule_frame(scene_output->output);
+
+ scene_node_output_update(&scene_output->scene->tree.node,
+ &scene_output->scene->outputs, NULL);
+}
+
+static void scene_output_handle_commit(struct wl_listener *listener, void *data) {
+ struct wlr_scene_output *scene_output = wl_container_of(listener,
+ scene_output, output_commit);
+ struct wlr_output_event_commit *event = data;
+
+ if (event->committed & (WLR_OUTPUT_STATE_MODE |
+ WLR_OUTPUT_STATE_TRANSFORM |
+ WLR_OUTPUT_STATE_SCALE |
+ WLR_OUTPUT_STATE_ENABLED)) {
+ scene_output_update_geometry(scene_output);
+ }
+}
+
+static void scene_output_handle_mode(struct wl_listener *listener, void *data) {
+ struct wlr_scene_output *scene_output = wl_container_of(listener,
+ scene_output, output_mode);
+ scene_output_update_geometry(scene_output);
+}
+
+static void scene_output_handle_damage(struct wl_listener *listener, void *data) {
+ struct wlr_scene_output *scene_output = wl_container_of(listener,
+ scene_output, output_damage);
+ struct wlr_output_event_damage *event = data;
+ if (wlr_damage_ring_add(&scene_output->damage_ring, event->damage)) {
+ wlr_output_schedule_frame(scene_output->output);
+ }
+}
+
+static void scene_output_handle_needs_frame(struct wl_listener *listener, void *data) {
+ struct wlr_scene_output *scene_output = wl_container_of(listener,
+ scene_output, output_needs_frame);
+ wlr_output_schedule_frame(scene_output->output);
+}
+
+struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene,
+ struct wlr_output *output) {
+ struct wlr_scene_output *scene_output = calloc(1, sizeof(*scene_output));
+ if (scene_output == NULL) {
+ return NULL;
+ }
+
+ scene_output->output = output;
+ scene_output->scene = scene;
+ wlr_addon_init(&scene_output->addon, &output->addons, scene, &output_addon_impl);
+
+ wlr_damage_ring_init(&scene_output->damage_ring);
+ wl_list_init(&scene_output->damage_highlight_regions);
+
+ int prev_output_index = -1;
+ struct wl_list *prev_output_link = &scene->outputs;
+
+ struct wlr_scene_output *current_output;
+ wl_list_for_each(current_output, &scene->outputs, link) {
+ if (prev_output_index + 1 != current_output->index) {
+ break;
+ }
+
+ prev_output_index = current_output->index;
+ prev_output_link = &current_output->link;
+ }
+
+ scene_output->index = prev_output_index + 1;
+ assert(scene_output->index < 64);
+ wl_list_insert(prev_output_link, &scene_output->link);
+
+ wl_signal_init(&scene_output->events.destroy);
+
+ scene_output->output_commit.notify = scene_output_handle_commit;
+ wl_signal_add(&output->events.commit, &scene_output->output_commit);
+
+ scene_output->output_mode.notify = scene_output_handle_mode;
+ wl_signal_add(&output->events.mode, &scene_output->output_mode);
+
+ scene_output->output_damage.notify = scene_output_handle_damage;
+ wl_signal_add(&output->events.damage, &scene_output->output_damage);
+
+ scene_output->output_needs_frame.notify = scene_output_handle_needs_frame;
+ wl_signal_add(&output->events.needs_frame, &scene_output->output_needs_frame);
+
+ scene_output_update_geometry(scene_output);
+
+ return scene_output;
+}
+
+static void highlight_region_destroy(struct highlight_region *damage) {
+ wl_list_remove(&damage->link);
+ pixman_region32_fini(&damage->region);
+ free(damage);
+}
+
+void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) {
+ if (scene_output == NULL) {
+ return;
+ }
+
+ wl_signal_emit_mutable(&scene_output->events.destroy, NULL);
+
+ scene_node_output_update(&scene_output->scene->tree.node,
+ &scene_output->scene->outputs, scene_output);
+
+ struct highlight_region *damage, *tmp_damage;
+ wl_list_for_each_safe(damage, tmp_damage, &scene_output->damage_highlight_regions, link) {
+ highlight_region_destroy(damage);
+ }
+
+ wlr_addon_finish(&scene_output->addon);
+ wlr_damage_ring_finish(&scene_output->damage_ring);
+ wl_list_remove(&scene_output->link);
+ wl_list_remove(&scene_output->output_commit.link);
+ wl_list_remove(&scene_output->output_mode.link);
+ wl_list_remove(&scene_output->output_damage.link);
+ wl_list_remove(&scene_output->output_needs_frame.link);
+
+ wl_array_release(&scene_output->render_list);
+ free(scene_output);
+}
+
+struct wlr_scene_output *wlr_scene_get_scene_output(struct wlr_scene *scene,
+ struct wlr_output *output) {
+ struct wlr_addon *addon =
+ wlr_addon_find(&output->addons, scene, &output_addon_impl);
+ if (addon == NULL) {
+ return NULL;
+ }
+ struct wlr_scene_output *scene_output =
+ wl_container_of(addon, scene_output, addon);
+ return scene_output;
+}
+
+void wlr_scene_output_set_position(struct wlr_scene_output *scene_output,
+ int lx, int ly) {
+ if (scene_output->x == lx && scene_output->y == ly) {
+ return;
+ }
+
+ scene_output->x = lx;
+ scene_output->y = ly;
+
+ scene_output_update_geometry(scene_output);
+}
+
+static bool scene_node_invisible(struct wlr_scene_node *node) {
+ if (node->type == WLR_SCENE_NODE_TREE) {
+ return true;
+ } else if (node->type == WLR_SCENE_NODE_RECT) {
+ struct wlr_scene_rect *rect = scene_rect_from_node(node);
+
+ return rect->color[3] == 0.f;
+ } else if (node->type == WLR_SCENE_NODE_BUFFER) {
+ struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
+
+ return buffer->buffer == NULL;
+ }
+
+ return false;
+}
+
+struct render_list_constructor_data {
+ struct wlr_box box;
+ struct wl_array *render_list;
+ bool calculate_visibility;
+};
+
+static bool construct_render_list_iterator(struct wlr_scene_node *node,
+ int lx, int ly, void *_data) {
+ struct render_list_constructor_data *data = _data;
+
+ if (scene_node_invisible(node)) {
+ return false;
+ }
+
+ // while rendering, the background should always be black.
+ // If we see a black rect, we can ignore rendering everything under the rect
+ // and even the rect itself.
+ if (node->type == WLR_SCENE_NODE_RECT && data->calculate_visibility) {
+ struct wlr_scene_rect *rect = scene_rect_from_node(node);
+ float *black = (float[4]){ 0.f, 0.f, 0.f, 1.f };
+
+ if (memcmp(rect->color, black, sizeof(float) * 4) == 0) {
+ return false;
+ }
+ }
+
+ pixman_region32_t intersection;
+ pixman_region32_init(&intersection);
+ pixman_region32_intersect_rect(&intersection, &node->visible,
+ data->box.x, data->box.y,
+ data->box.width, data->box.height);
+ if (!pixman_region32_not_empty(&intersection)) {
+ pixman_region32_fini(&intersection);
+ return false;
+ }
+
+ pixman_region32_fini(&intersection);
+
+ struct wlr_scene_node **entry = wl_array_add(data->render_list,
+ sizeof(struct wlr_scene_node *));
+ if (entry) {
+ *entry = node;
+ }
+ return false;
+}
+
+static bool scene_node_try_direct_scanout(struct wlr_scene_node *node,
+ struct wlr_scene_output *scene_output, struct wlr_box *box) {
+ if (!scene_output->scene->direct_scanout) {
+ return false;
+ }
+
+ if (scene_output->scene->debug_damage_option ==
+ WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
+ // We don't want to enter direct scan out if we have highlight regions
+ // enabled. Otherwise, we won't be able to render the damage regions.
+ return false;
+ }
+
+ if (node->type != WLR_SCENE_NODE_BUFFER) {
+ return false;
+ }
+
+ struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
+ struct wlr_output *output = scene_output->output;
+
+ struct wlr_fbox default_box = {0};
+ if (buffer->transform & WL_OUTPUT_TRANSFORM_90) {
+ default_box.width = buffer->buffer->height;
+ default_box.height = buffer->buffer->width;
+ } else {
+ default_box.width = buffer->buffer->width;
+ default_box.height = buffer->buffer->height;
+ }
+
+ if (!wlr_fbox_empty(&buffer->src_box) &&
+ !wlr_fbox_equal(&buffer->src_box, &default_box)) {
+ return false;
+ }
+
+ if (buffer->transform != output->transform) {
+ return false;
+ }
+
+ struct wlr_box node_box;
+ wlr_scene_node_coords(node, &node_box.x, &node_box.y);
+ scene_node_get_size(node, &node_box.width, &node_box.height);
+
+ if (!wlr_box_equal(box, &node_box)) {
+ return false;
+ }
+
+ wlr_output_attach_buffer(output, buffer->buffer);
+ if (!wlr_output_test(output)) {
+ wlr_output_rollback(output);
+ return false;
+ }
+
+ return wlr_output_commit(output);
+}
+
+bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
+ struct wlr_output *output = scene_output->output;
+ enum wlr_scene_debug_damage_option debug_damage =
+ scene_output->scene->debug_damage_option;
+
+ struct wlr_renderer *renderer = output->renderer;
+ assert(renderer != NULL);
+
+ struct render_list_constructor_data list_con = {
+ .box = { .x = scene_output->x, .y = scene_output->y },
+ .render_list = &scene_output->render_list,
+ .calculate_visibility = scene_output->scene->calculate_visibility,
+ };
+ wlr_output_effective_resolution(output,
+ &list_con.box.width, &list_con.box.height);
+
+ list_con.render_list->size = 0;
+ scene_nodes_in_box(&scene_output->scene->tree.node, &list_con.box,
+ construct_render_list_iterator, &list_con);
+ array_realloc(list_con.render_list, list_con.render_list->size);
+
+ int list_len = list_con.render_list->size / sizeof(struct wlr_scene_node *);
+ struct wlr_scene_node **list_data = list_con.render_list->data;
+
+ // if there is only one thing to render let's see if that thing can be
+ // directly scanned out
+ bool scanout = false;
+ if (list_len == 1) {
+ struct wlr_scene_node *node = list_data[0];
+ scanout = scene_node_try_direct_scanout(node, scene_output, &list_con.box);
+ }
+
+ if (scene_output->prev_scanout != scanout) {
+ scene_output->prev_scanout = scanout;
+ wlr_log(WLR_DEBUG, "Direct scan-out %s",
+ scanout ? "enabled" : "disabled");
+ // When exiting direct scan-out, damage everything
+ wlr_damage_ring_add_whole(&scene_output->damage_ring);
+ }
+
+ if (scanout) {
+ struct wlr_scene_node *node = list_data[0];
+
+ assert(node->type == WLR_SCENE_NODE_BUFFER);
+ struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
+ wl_signal_emit_mutable(&buffer->events.output_present, scene_output);
+ return true;
+ }
+
+ if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_RERENDER) {
+ wlr_damage_ring_add_whole(&scene_output->damage_ring);
+ }
+
+ struct timespec now;
+ if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
+ struct wl_list *regions = &scene_output->damage_highlight_regions;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ // add the current frame's damage if there is damage
+ if (pixman_region32_not_empty(&scene_output->damage_ring.current)) {
+ struct highlight_region *current_damage =
+ calloc(1, sizeof(*current_damage));
+ if (current_damage) {
+ pixman_region32_init(&current_damage->region);
+ pixman_region32_copy(&current_damage->region,
+ &scene_output->damage_ring.current);
+ current_damage->when = now;
+ wl_list_insert(regions, &current_damage->link);
+ }
+ }
+
+ pixman_region32_t acc_damage;
+ pixman_region32_init(&acc_damage);
+ struct highlight_region *damage, *tmp_damage;
+ wl_list_for_each_safe(damage, tmp_damage, regions, link) {
+ // remove overlaping damage regions
+ pixman_region32_subtract(&damage->region, &damage->region, &acc_damage);
+ pixman_region32_union(&acc_damage, &acc_damage, &damage->region);
+
+ // if this damage is too old or has nothing in it, get rid of it
+ struct timespec time_diff;
+ timespec_sub(&time_diff, &now, &damage->when);
+ if (timespec_to_msec(&time_diff) >= HIGHLIGHT_DAMAGE_FADEOUT_TIME ||
+ !pixman_region32_not_empty(&damage->region)) {
+ highlight_region_destroy(damage);
+ }
+ }
+
+ wlr_damage_ring_add(&scene_output->damage_ring, &acc_damage);
+ pixman_region32_fini(&acc_damage);
+ }
+
+ int buffer_age;
+ if (!wlr_output_attach_render(output, &buffer_age)) {
+ return false;
+ }
+
+ pixman_region32_t damage;
+ pixman_region32_init(&damage);
+ wlr_damage_ring_get_buffer_damage(&scene_output->damage_ring,
+ buffer_age, &damage);
+ if (!output->needs_frame && !pixman_region32_not_empty(
+ &scene_output->damage_ring.current)) {
+ pixman_region32_fini(&damage);
+ wlr_output_rollback(output);
+ return true;
+ }
+
+ wlr_renderer_begin(renderer, output->width, output->height);
+
+ pixman_region32_t background;
+ pixman_region32_init(&background);
+ pixman_region32_copy(&background, &damage);
+
+ // Cull areas of the background that are occluded by opaque regions of
+ // scene nodes above. Those scene nodes will just render atop having us
+ // never see the background.
+ if (scene_output->scene->calculate_visibility) {
+ float output_scale = scene_output->output->scale;
+
+ for (int i = list_len - 1; i >= 0; i--) {
+ struct wlr_scene_node *node = list_data[i];
+ int x, y;
+ wlr_scene_node_coords(node, &x, &y);
+
+ // We must only cull opaque regions that are visible by the node.
+ // The node's visibility will have the knowledge of a black rect
+ // that may have been omitted from the render list via the black
+ // rect optimization. In order to ensure we don't cull background
+ // rendering in that black rect region, consider the node's visibility.
+ pixman_region32_t opaque;
+ pixman_region32_init(&opaque);
+ scene_node_opaque_region(node, x, y, &opaque);
+ pixman_region32_intersect(&opaque, &opaque, &node->visible);
+
+ pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y);
+ wlr_region_scale(&opaque, &opaque, output_scale);
+ pixman_region32_subtract(&background, &background, &opaque);
+ pixman_region32_fini(&opaque);
+ }
+
+ if (floor(output_scale) != output_scale) {
+ wlr_region_expand(&background, &background, 1);
+
+ // reintersect with the damage because we never want to render
+ // outside of the damage region
+ pixman_region32_intersect(&background, &background, &damage);
+ }
+ }
+
+ int nrects;
+ pixman_box32_t *rects = pixman_region32_rectangles(&background, &nrects);
+ for (int i = 0; i < nrects; ++i) {
+ scissor_output(output, &rects[i]);
+ wlr_renderer_clear(renderer, (float[4]){ 0.0, 0.0, 0.0, 1.0 });
+ }
+ pixman_region32_fini(&background);
+
+ for (int i = list_len - 1; i >= 0; i--) {
+ struct wlr_scene_node *node = list_data[i];
+ scene_node_render(node, scene_output, &damage);
+ }
+
+ wlr_renderer_scissor(renderer, NULL);
+
+ if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
+ struct highlight_region *damage;
+ wl_list_for_each(damage, &scene_output->damage_highlight_regions, link) {
+ struct timespec time_diff;
+ timespec_sub(&time_diff, &now, &damage->when);
+ int64_t time_diff_ms = timespec_to_msec(&time_diff);
+ float alpha = 1.0 - (double)time_diff_ms / HIGHLIGHT_DAMAGE_FADEOUT_TIME;
+
+ int nrects;
+ pixman_box32_t *rects = pixman_region32_rectangles(&damage->region, &nrects);
+ for (int i = 0; i < nrects; ++i) {
+ struct wlr_box box = {
+ .x = rects[i].x1,
+ .y = rects[i].y1,
+ .width = rects[i].x2 - rects[i].x1,
+ .height = rects[i].y2 - rects[i].y1,
+ };
+
+ float color[4] = { alpha * .5, 0.0, 0.0, alpha * .5 };
+ wlr_render_rect(renderer, &box, color, output->transform_matrix);
+ }
+ }
+ }
+
+ wlr_output_render_software_cursors(output, &damage);
+
+ wlr_renderer_end(renderer);
+ pixman_region32_fini(&damage);
+
+ int tr_width, tr_height;
+ wlr_output_transformed_resolution(output, &tr_width, &tr_height);
+
+ enum wl_output_transform transform =
+ wlr_output_transform_invert(output->transform);
+
+ pixman_region32_t frame_damage;
+ pixman_region32_init(&frame_damage);
+ wlr_region_transform(&frame_damage,
+ &scene_output->damage_ring.current,
+ transform, tr_width, tr_height);
+ wlr_output_set_damage(output, &frame_damage);
+ pixman_region32_fini(&frame_damage);
+
+ bool success = wlr_output_commit(output);
+
+ if (success) {
+ wlr_damage_ring_rotate(&scene_output->damage_ring);
+ }
+
+ if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT &&
+ !wl_list_empty(&scene_output->damage_highlight_regions)) {
+ wlr_output_schedule_frame(scene_output->output);
+ }
+
+ return success;
+}
+
+static void scene_node_send_frame_done(struct wlr_scene_node *node,
+ struct wlr_scene_output *scene_output, struct timespec *now) {
+ if (!node->enabled) {
+ return;
+ }
+
+ if (node->type == WLR_SCENE_NODE_BUFFER) {
+ struct wlr_scene_buffer *scene_buffer =
+ wlr_scene_buffer_from_node(node);
+
+ if (scene_buffer->primary_output == scene_output) {
+ wlr_scene_buffer_send_frame_done(scene_buffer, now);
+ }
+ } else if (node->type == WLR_SCENE_NODE_TREE) {
+ struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_node *child;
+ wl_list_for_each(child, &scene_tree->children, link) {
+ scene_node_send_frame_done(child, scene_output, now);
+ }
+ }
+}
+
+void wlr_scene_output_send_frame_done(struct wlr_scene_output *scene_output,
+ struct timespec *now) {
+ scene_node_send_frame_done(&scene_output->scene->tree.node,
+ scene_output, now);
+}
+
+static void scene_output_for_each_scene_buffer(const struct wlr_box *output_box,
+ struct wlr_scene_node *node, int lx, int ly,
+ wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) {
+ if (!node->enabled) {
+ return;
+ }
+
+ lx += node->x;
+ ly += node->y;
+
+ if (node->type == WLR_SCENE_NODE_BUFFER) {
+ struct wlr_box node_box = { .x = lx, .y = ly };
+ scene_node_get_size(node, &node_box.width, &node_box.height);
+
+ struct wlr_box intersection;
+ if (wlr_box_intersection(&intersection, output_box, &node_box)) {
+ struct wlr_scene_buffer *scene_buffer =
+ wlr_scene_buffer_from_node(node);
+ user_iterator(scene_buffer, lx, ly, user_data);
+ }
+ } else if (node->type == WLR_SCENE_NODE_TREE) {
+ struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_node *child;
+ wl_list_for_each(child, &scene_tree->children, link) {
+ scene_output_for_each_scene_buffer(output_box, child, lx, ly,
+ user_iterator, user_data);
+ }
+ }
+}
+
+void wlr_scene_output_for_each_buffer(struct wlr_scene_output *scene_output,
+ wlr_scene_buffer_iterator_func_t iterator, void *user_data) {
+ struct wlr_box box = { .x = scene_output->x, .y = scene_output->y };
+ wlr_output_effective_resolution(scene_output->output,
+ &box.width, &box.height);
+ scene_output_for_each_scene_buffer(&box, &scene_output->scene->tree.node, 0, 0,
+ iterator, user_data);
+}
diff --git a/types/scene/xdg_shell.c b/types/scene/xdg_shell.c
new file mode 100644
index 0000000..1688348
--- /dev/null
+++ b/types/scene/xdg_shell.c
@@ -0,0 +1,124 @@
+#include <stdlib.h>
+#include <wlr/types/wlr_scene.h>
+#include <wlr/types/wlr_xdg_shell.h>
+
+struct wlr_scene_xdg_surface {
+ struct wlr_scene_tree *tree;
+ struct wlr_xdg_surface *xdg_surface;
+ struct wlr_scene_tree *surface_tree;
+
+ struct wl_listener tree_destroy;
+ struct wl_listener xdg_surface_destroy;
+ struct wl_listener xdg_surface_map;
+ struct wl_listener xdg_surface_unmap;
+ struct wl_listener xdg_surface_commit;
+};
+
+static void scene_xdg_surface_handle_tree_destroy(struct wl_listener *listener,
+ void *data) {
+ struct wlr_scene_xdg_surface *scene_xdg_surface =
+ wl_container_of(listener, scene_xdg_surface, tree_destroy);
+ // tree and surface_node will be cleaned up by scene_node_finish
+ wl_list_remove(&scene_xdg_surface->tree_destroy.link);
+ wl_list_remove(&scene_xdg_surface->xdg_surface_destroy.link);
+ wl_list_remove(&scene_xdg_surface->xdg_surface_map.link);
+ wl_list_remove(&scene_xdg_surface->xdg_surface_unmap.link);
+ wl_list_remove(&scene_xdg_surface->xdg_surface_commit.link);
+ free(scene_xdg_surface);
+}
+
+static void scene_xdg_surface_handle_xdg_surface_destroy(struct wl_listener *listener,
+ void *data) {
+ struct wlr_scene_xdg_surface *scene_xdg_surface =
+ wl_container_of(listener, scene_xdg_surface, xdg_surface_destroy);
+ wlr_scene_node_destroy(&scene_xdg_surface->tree->node);
+}
+
+static void scene_xdg_surface_handle_xdg_surface_map(struct wl_listener *listener,
+ void *data) {
+ struct wlr_scene_xdg_surface *scene_xdg_surface =
+ wl_container_of(listener, scene_xdg_surface, xdg_surface_map);
+ wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, true);
+}
+
+static void scene_xdg_surface_handle_xdg_surface_unmap(struct wl_listener *listener,
+ void *data) {
+ struct wlr_scene_xdg_surface *scene_xdg_surface =
+ wl_container_of(listener, scene_xdg_surface, xdg_surface_unmap);
+ wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, false);
+}
+
+static void scene_xdg_surface_update_position(
+ struct wlr_scene_xdg_surface *scene_xdg_surface) {
+ struct wlr_xdg_surface *xdg_surface = scene_xdg_surface->xdg_surface;
+
+ struct wlr_box geo = {0};
+ wlr_xdg_surface_get_geometry(xdg_surface, &geo);
+ wlr_scene_node_set_position(&scene_xdg_surface->surface_tree->node,
+ -geo.x, -geo.y);
+
+ if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
+ struct wlr_xdg_popup *popup = xdg_surface->popup;
+ wlr_scene_node_set_position(&scene_xdg_surface->tree->node,
+ popup->current.geometry.x, popup->current.geometry.y);
+ }
+}
+
+static void scene_xdg_surface_handle_xdg_surface_commit(struct wl_listener *listener,
+ void *data) {
+ struct wlr_scene_xdg_surface *scene_xdg_surface =
+ wl_container_of(listener, scene_xdg_surface, xdg_surface_commit);
+ scene_xdg_surface_update_position(scene_xdg_surface);
+}
+
+struct wlr_scene_tree *wlr_scene_xdg_surface_create(
+ struct wlr_scene_tree *parent, struct wlr_xdg_surface *xdg_surface) {
+ struct wlr_scene_xdg_surface *scene_xdg_surface =
+ calloc(1, sizeof(*scene_xdg_surface));
+ if (scene_xdg_surface == NULL) {
+ return NULL;
+ }
+
+ scene_xdg_surface->xdg_surface = xdg_surface;
+
+ scene_xdg_surface->tree = wlr_scene_tree_create(parent);
+ if (scene_xdg_surface->tree == NULL) {
+ free(scene_xdg_surface);
+ return NULL;
+ }
+
+ scene_xdg_surface->surface_tree = wlr_scene_subsurface_tree_create(
+ scene_xdg_surface->tree, xdg_surface->surface);
+ if (scene_xdg_surface->surface_tree == NULL) {
+ wlr_scene_node_destroy(&scene_xdg_surface->tree->node);
+ free(scene_xdg_surface);
+ return NULL;
+ }
+
+ scene_xdg_surface->tree_destroy.notify =
+ scene_xdg_surface_handle_tree_destroy;
+ wl_signal_add(&scene_xdg_surface->tree->node.events.destroy,
+ &scene_xdg_surface->tree_destroy);
+
+ scene_xdg_surface->xdg_surface_destroy.notify =
+ scene_xdg_surface_handle_xdg_surface_destroy;
+ wl_signal_add(&xdg_surface->events.destroy, &scene_xdg_surface->xdg_surface_destroy);
+
+ scene_xdg_surface->xdg_surface_map.notify =
+ scene_xdg_surface_handle_xdg_surface_map;
+ wl_signal_add(&xdg_surface->events.map, &scene_xdg_surface->xdg_surface_map);
+
+ scene_xdg_surface->xdg_surface_unmap.notify =
+ scene_xdg_surface_handle_xdg_surface_unmap;
+ wl_signal_add(&xdg_surface->events.unmap, &scene_xdg_surface->xdg_surface_unmap);
+
+ scene_xdg_surface->xdg_surface_commit.notify =
+ scene_xdg_surface_handle_xdg_surface_commit;
+ wl_signal_add(&xdg_surface->surface->events.commit,
+ &scene_xdg_surface->xdg_surface_commit);
+
+ wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, xdg_surface->mapped);
+ scene_xdg_surface_update_position(scene_xdg_surface);
+
+ return scene_xdg_surface->tree;
+}