aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/wlr/types/wlr_scene.h114
-rw-r--r--types/meson.build1
-rw-r--r--types/wlr_scene.c263
3 files changed, 378 insertions, 0 deletions
diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h
new file mode 100644
index 00000000..953b1b87
--- /dev/null
+++ b/include/wlr/types/wlr_scene.h
@@ -0,0 +1,114 @@
+/*
+ * This an unstable interface of wlroots. No guarantees are made regarding the
+ * future consistency of this API.
+ */
+#ifndef WLR_USE_UNSTABLE
+#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
+#endif
+
+#ifndef WLR_TYPES_WLR_SCENE_H
+#define WLR_TYPES_WLR_SCENE_H
+
+/**
+ * The scene-graph API provides a declarative way to display surfaces. The
+ * compositor creates a scene, adds surfaces, then renders the scene on
+ * outputs.
+ *
+ * The scene-graph API only supports basic 2D composition operations (like the
+ * KMS API or the Wayland protocol does). For anything more complicated,
+ * compositors need to implement custom rendering logic.
+ */
+
+#include <pixman.h>
+#include <wayland-server-core.h>
+#include <wlr/types/wlr_surface.h>
+
+struct wlr_output;
+
+enum wlr_scene_node_type {
+ WLR_SCENE_NODE_ROOT,
+ WLR_SCENE_NODE_SURFACE,
+};
+
+struct wlr_scene_node_state {
+ struct wl_list link; // wlr_scene_node_state.children
+
+ struct wl_list children; // wlr_scene_node_state.link
+
+ int x, y; // relative to parent
+};
+
+/** A node is an object in the scene. */
+struct wlr_scene_node {
+ enum wlr_scene_node_type type;
+ struct wlr_scene_node *parent;
+ struct wlr_scene_node_state state;
+
+ struct {
+ struct wl_signal destroy;
+ } events;
+};
+
+/** The root scene-graph node. */
+struct wlr_scene {
+ struct wlr_scene_node node;
+};
+
+/** A scene-graph node displaying a single surface. */
+struct wlr_scene_surface {
+ struct wlr_scene_node node;
+ struct wlr_surface *surface;
+
+ // private state
+
+ struct wl_listener surface_destroy;
+};
+
+/**
+ * Immediately destroy the scene-graph node.
+ */
+void wlr_scene_node_destroy(struct wlr_scene_node *node);
+/**
+ * Set the position of the node relative to its parent.
+ */
+void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y);
+/**
+ * Move the node right above the specified sibling.
+ */
+void wlr_scene_node_place_above(struct wlr_scene_node *node,
+ struct wlr_scene_node *sibling);
+/**
+ * Move the node right below the specified sibling.
+ */
+void wlr_scene_node_place_below(struct wlr_scene_node *node,
+ struct wlr_scene_node *sibling);
+/**
+ * Call `iterator` on each surface in the scene-graph, with the surface's
+ * position in layout coordinates. The function is called from root to leaves
+ * (in rendering order).
+ */
+void wlr_scene_node_for_each_surface(struct wlr_scene_node *node,
+ wlr_surface_iterator_func_t iterator, void *user_data);
+
+/**
+ * Create a new scene-graph.
+ */
+struct wlr_scene *wlr_scene_create(void);
+/**
+ * Manually render the scene-graph on an output. The compositor needs to call
+ * wlr_renderer_begin before and wlr_renderer_end after calling this function.
+ * Damage is given in output-buffer-local coordinates and can be set to NULL to
+ * disable damage tracking.
+ */
+void wlr_scene_render_output(struct wlr_scene *scene, struct wlr_output *output,
+ int lx, int ly, pixman_region32_t *damage);
+
+/**
+ * Add a node displaying a single surface to the scene-graph.
+ *
+ * The child sub-surfaces are ignored.
+ */
+struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_node *parent,
+ struct wlr_surface *surface);
+
+#endif
diff --git a/types/meson.build b/types/meson.build
index 6a650035..d297a753 100644
--- a/types/meson.build
+++ b/types/meson.build
@@ -49,6 +49,7 @@ wlr_files += files(
'wlr_primary_selection.c',
'wlr_region.c',
'wlr_relative_pointer_v1.c',
+ 'wlr_scene.c',
'wlr_screencopy_v1.c',
'wlr_server_decoration.c',
'wlr_surface.c',
diff --git a/types/wlr_scene.c b/types/wlr_scene.c
new file mode 100644
index 00000000..7b1690e9
--- /dev/null
+++ b/types/wlr_scene.c
@@ -0,0 +1,263 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <wlr/backend.h>
+#include <wlr/render/wlr_renderer.h>
+#include <wlr/types/wlr_matrix.h>
+#include <wlr/types/wlr_scene.h>
+#include <wlr/types/wlr_surface.h>
+#include "util/signal.h"
+
+static struct wlr_scene *scene_root_from_node(struct wlr_scene_node *node) {
+ assert(node->type == WLR_SCENE_NODE_ROOT);
+ return (struct wlr_scene *)node;
+}
+
+static struct wlr_scene_surface *scene_surface_from_node(
+ struct wlr_scene_node *node) {
+ assert(node->type == WLR_SCENE_NODE_SURFACE);
+ return (struct wlr_scene_surface *)node;
+}
+
+static void scene_node_state_init(struct wlr_scene_node_state *state) {
+ wl_list_init(&state->children);
+ wl_list_init(&state->link);
+}
+
+static void scene_node_state_finish(struct wlr_scene_node_state *state) {
+ wl_list_remove(&state->link);
+}
+
+static void scene_node_init(struct wlr_scene_node *node,
+ enum wlr_scene_node_type type, struct wlr_scene_node *parent) {
+ assert(type == WLR_SCENE_NODE_ROOT || parent != NULL);
+ assert(parent == NULL || parent->type == WLR_SCENE_NODE_ROOT);
+
+ node->type = type;
+ node->parent = parent;
+ scene_node_state_init(&node->state);
+ wl_signal_init(&node->events.destroy);
+
+ if (parent != NULL) {
+ wl_list_insert(parent->state.children.prev, &node->state.link);
+ }
+}
+
+static void scene_node_finish(struct wlr_scene_node *node) {
+ wlr_signal_emit_safe(&node->events.destroy, NULL);
+
+ struct wlr_scene_node *child, *child_tmp;
+ wl_list_for_each_safe(child, child_tmp,
+ &node->state.children, state.link) {
+ wlr_scene_node_destroy(child);
+ }
+
+ scene_node_state_finish(&node->state);
+}
+
+void wlr_scene_node_destroy(struct wlr_scene_node *node) {
+ if (node == NULL) {
+ return;
+ }
+
+ scene_node_finish(node);
+
+ switch (node->type) {
+ case WLR_SCENE_NODE_ROOT:;
+ struct wlr_scene *scene = scene_root_from_node(node);
+ free(scene);
+ break;
+ case WLR_SCENE_NODE_SURFACE:;
+ struct wlr_scene_surface *scene_surface = scene_surface_from_node(node);
+ wl_list_remove(&scene_surface->surface_destroy.link);
+ free(scene_surface);
+ break;
+ }
+}
+
+struct wlr_scene *wlr_scene_create(void) {
+ struct wlr_scene *scene = calloc(1, sizeof(struct wlr_scene));
+ if (scene == NULL) {
+ return NULL;
+ }
+ scene_node_init(&scene->node, WLR_SCENE_NODE_ROOT, NULL);
+
+ return scene;
+}
+
+static void scene_surface_handle_surface_destroy(struct wl_listener *listener,
+ void *data) {
+ struct wlr_scene_surface *scene_surface =
+ wl_container_of(listener, scene_surface, surface_destroy);
+ wlr_scene_node_destroy(&scene_surface->node);
+}
+
+struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_node *parent,
+ struct wlr_surface *surface) {
+ struct wlr_scene_surface *scene_surface =
+ calloc(1, sizeof(struct wlr_scene_surface));
+ if (scene_surface == NULL) {
+ return NULL;
+ }
+ scene_node_init(&scene_surface->node, WLR_SCENE_NODE_SURFACE, parent);
+
+ scene_surface->surface = surface;
+
+ scene_surface->surface_destroy.notify = scene_surface_handle_surface_destroy;
+ wl_signal_add(&surface->events.destroy, &scene_surface->surface_destroy);
+
+ return scene_surface;
+}
+
+void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) {
+ node->state.x = x;
+ node->state.y = y;
+}
+
+void wlr_scene_node_place_above(struct wlr_scene_node *node,
+ struct wlr_scene_node *sibling) {
+ assert(node->parent == sibling->parent);
+
+ wl_list_remove(&node->state.link);
+ wl_list_insert(&sibling->state.link, &node->state.link);
+}
+
+void wlr_scene_node_place_below(struct wlr_scene_node *node,
+ struct wlr_scene_node *sibling) {
+ assert(node->parent == sibling->parent);
+
+ wl_list_remove(&node->state.link);
+ wl_list_insert(sibling->state.link.prev, &node->state.link);
+}
+
+static void scene_node_for_each_surface(struct wlr_scene_node *node,
+ int lx, int ly, wlr_surface_iterator_func_t user_iterator,
+ void *user_data) {
+ lx += node->state.x;
+ ly += node->state.y;
+
+ if (node->type == WLR_SCENE_NODE_SURFACE) {
+ struct wlr_scene_surface *scene_surface = scene_surface_from_node(node);
+ user_iterator(scene_surface->surface, lx, ly, user_data);
+ }
+
+ struct wlr_scene_node *child;
+ wl_list_for_each(child, &node->state.children, state.link) {
+ scene_node_for_each_surface(child, lx, ly, user_iterator, user_data);
+ }
+}
+
+void wlr_scene_node_for_each_surface(struct wlr_scene_node *node,
+ wlr_surface_iterator_func_t user_iterator, void *user_data) {
+ scene_node_for_each_surface(node, 0, 0, user_iterator, user_data);
+}
+
+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);
+}
+
+static void scissor_output(struct wlr_output *output, pixman_box32_t *rect) {
+ struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend);
+ 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_texture(struct wlr_output *output,
+ pixman_region32_t *output_damage, struct wlr_texture *texture,
+ const struct wlr_box *box, const float matrix[static 9]) {
+ struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend);
+ assert(renderer);
+
+ pixman_region32_t damage;
+ pixman_region32_init(&damage);
+ pixman_region32_init_rect(&damage, box->x, box->y, box->width, box->height);
+ pixman_region32_intersect(&damage, &damage, output_damage);
+ if (pixman_region32_not_empty(&damage)) {
+ 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_texture_with_matrix(renderer, texture, matrix, 1.0);
+ }
+ }
+ pixman_region32_fini(&damage);
+}
+
+struct render_data {
+ struct wlr_output *output;
+ pixman_region32_t *damage;
+};
+
+static void render_surface_iterator(struct wlr_surface *surface,
+ int x, int y, void *_data) {
+ struct render_data *data = _data;
+ struct wlr_output *output = data->output;
+ pixman_region32_t *output_damage = data->damage;
+
+ struct wlr_texture *texture = wlr_surface_get_texture(surface);
+ if (texture == NULL) {
+ return;
+ }
+
+ struct wlr_box box = {
+ .x = x,
+ .y = y,
+ .width = surface->current.width,
+ .height = surface->current.height,
+ };
+ scale_box(&box, output->scale);
+
+ float matrix[9];
+ enum wl_output_transform transform =
+ wlr_output_transform_invert(surface->current.transform);
+ wlr_matrix_project_box(matrix, &box, transform, 0.0,
+ output->transform_matrix);
+
+ render_texture(output, output_damage, texture, &box, matrix);
+}
+
+void wlr_scene_render_output(struct wlr_scene *scene, struct wlr_output *output,
+ int lx, int ly, pixman_region32_t *damage) {
+ pixman_region32_t full_region;
+ pixman_region32_init_rect(&full_region, 0, 0, output->width, output->height);
+ if (damage == NULL) {
+ damage = &full_region;
+ }
+
+ struct wlr_renderer *renderer =
+ wlr_backend_get_renderer(output->backend);
+ assert(renderer);
+
+ if (output->enabled && pixman_region32_not_empty(damage)) {
+ struct render_data data = {
+ .output = output,
+ .damage = damage,
+ };
+ scene_node_for_each_surface(&scene->node, lx, ly,
+ render_surface_iterator, &data);
+ wlr_renderer_scissor(renderer, NULL);
+ }
+
+ pixman_region32_fini(&full_region);
+}