types/scene: split wlr_scene into modular components

Refactor the scene graph implementation by splitting wlr_scene.c
into multiple focused units: buffer, node, rect and tree. Introduce
corresponding public headers for each component.

This reduces the size and complexity of the core scene file, improves
code organization and makes individual parts of the scene graph easier
to maintain and extend.

As part of this refactor, drop unnecessary pointer indirection when
passing scene trees (e.g. &scene->tree → scene->tree) and update all
call sites accordingly.

No functional changes intended.
This commit is contained in:
YaoBing Xiao 2026-04-26 21:55:22 +08:00
parent b806de1d8c
commit 04501f8d8b
21 changed files with 2970 additions and 1999 deletions

View file

@ -135,7 +135,7 @@ struct wlr_scene_output_layout *wlr_scene_attach_output_layout(struct wlr_scene
wl_signal_add(&output_layout->events.change, &sol->layout_change);
sol->scene_destroy.notify = scene_output_layout_handle_scene_destroy;
wl_signal_add(&scene->tree.node.events.destroy, &sol->scene_destroy);
wl_signal_add(&scene->tree->node.events.destroy, &sol->scene_destroy);
return sol;
}

View file

@ -327,7 +327,7 @@ static struct wlr_scene_subsurface_tree *get_subsurface_tree_from_node(
static bool subsurface_tree_set_clip(struct wlr_scene_node *node,
const struct wlr_box *clip) {
if (node->type != WLR_SCENE_NODE_TREE) {
if (wlr_scene_node_get_children(node) == NULL) {
return false;
}
@ -350,9 +350,9 @@ static bool subsurface_tree_set_clip(struct wlr_scene_node *node,
subsurface_tree_reconfigure_clip(tree);
}
struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wl_list *childrens = wlr_scene_node_get_children(node);
struct wlr_scene_node *child;
wl_list_for_each(child, &scene_tree->children, link) {
wl_list_for_each(child, childrens, link) {
discovered_subsurface_tree |= subsurface_tree_set_clip(child, clip);
}

View file

@ -95,7 +95,7 @@ static void handle_scene_buffer_outputs_update(
struct wlr_scene_surface *surface =
wl_container_of(listener, surface, outputs_update);
struct wlr_scene_outputs_update_event *event = data;
struct wlr_scene *scene = scene_node_get_root(&surface->buffer->node);
struct wlr_scene *scene = surface->buffer->node.scene;
// If the surface is no longer visible on any output, keep the last sent
// preferred configuration to avoid unnecessary redraws

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,505 @@
#include <KHR/khrplatform.h>
#include <sched.h>
#include <wlr/types/wlr_scene_node.h>
#include <wlr/types/wlr_scene_tree.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/region.h>
#include <wlr/util/transform.h>
#include "types/wlr_scene.h"
#include "types/wlr_output.h"
#include <wayland-server-core.h>
#include <wayland-util.h>
#include <stdlib.h>
#include <assert.h>
#include <wlr/config.h>
void wlr_scene_node_init(struct wlr_scene_node *node,
const struct wlr_scene_node_impl *impl,
struct wlr_scene_tree *parent) {
assert(impl->destroy);
*node = (struct wlr_scene_node){
.impl = impl,
.parent = parent,
.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);
node->scene = parent->node.scene;
}
wlr_addon_set_init(&node->addons);
}
void wlr_scene_node_destroy(struct wlr_scene_node *node) {
if (node == NULL) {
return;
}
wl_signal_emit_mutable(&node->events.destroy, NULL);
assert(wl_list_empty(&node->events.destroy.listener_list));
wlr_addon_set_finish(&node->addons);
wlr_scene_node_set_enabled(node, false);
wl_list_remove(&node->link);
pixman_region32_fini(&node->visible);
if (node->impl->destroy != NULL) {
node->impl->destroy(node);
}
}
void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled) {
if (node->impl->set_enabled == NULL) {
if (node->enabled == enabled) {
return;
}
int x, y;
pixman_region32_t visible;
pixman_region32_init(&visible);
if (wlr_scene_node_coords(node, &x, &y)) {
wlr_scene_node_visibility(node, &visible);
}
node->enabled = enabled;
wlr_scene_node_update(node, &visible);
return;
}
node->impl->set_enabled(node, enabled);
}
void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) {
if (node->impl->set_position == NULL) {
if (node->x == x && node->y == y) {
return;
}
node->x = x;
node->y = y;
wlr_scene_node_update(node, NULL);
return;
}
node->impl->set_position(node, x, y);
}
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);
wlr_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);
wlr_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)) {
wlr_scene_node_visibility(node, &visible);
}
wl_list_remove(&node->link);
node->parent = new_parent;
wl_list_insert(new_parent->children.prev, &node->link);
wlr_scene_node_update(node, &visible);
}
void wlr_scene_node_get_size(struct wlr_scene_node *node,
int *width, int *height) {
if (node->impl->get_size == NULL) {
*width = 0;
*height = 0;
return;
}
node->impl->get_size(node, width, height);
}
void wlr_scene_node_bounds(struct wlr_scene_node *node,
int x, int y, pixman_region32_t *visible) {
if (node->impl->bounds == NULL) {
if (!node->enabled) {
return;
}
int width, height;
wlr_scene_node_get_size(node, &width, &height);
pixman_region32_union_rect(visible, visible, x, y, width, height);
return;
}
node->impl->bounds(node, x, y, visible);
}
bool wlr_scene_node_coords(struct wlr_scene_node *node, int *lx_ptr, int *ly_ptr) {
if (node->impl->coords == NULL) {
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;
}
return node->impl->coords(node, lx_ptr, ly_ptr);
}
struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node,
double lx, double ly, double *nx, double *ny) {
if (node->impl->at == NULL) {
return NULL;
}
return node->impl->at(node, lx, ly, nx, ny);
}
bool wlr_scene_node_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box,
scene_node_box_iterator_func_t iterator, void *user_data) {
if (node->impl->in_box == NULL) {
return false;
}
return node->impl->in_box(node, box, iterator, user_data);
}
void wlr_scene_node_opaque_region(struct wlr_scene_node *node, int x, int y,
pixman_region32_t *opaque) {
if (node->impl->opaque_region == NULL) {
int width, height;
wlr_scene_node_get_size(node, &width, &height);
pixman_region32_fini(opaque);
pixman_region32_init_rect(opaque, x, y, width, height);
return;
}
return node->impl->opaque_region(node, x, y, opaque);
}
void wlr_scene_node_update_outputs(struct wlr_scene_node *node,
struct wl_list *outputs, struct wlr_scene_output *ignore,
struct wlr_scene_output *force) {
if (node->impl->update_outputs == NULL) {
return;
}
return node->impl->update_outputs(node, outputs, ignore, force);
}
static void scale_region(pixman_region32_t *region, float scale, bool round_up) {
wlr_region_scale(region, region, scale);
if (round_up && floor(scale) != scale) {
wlr_region_expand(region, region, 1);
}
}
static bool scene_node_update_iterator(struct wlr_scene_node *node,
int lx, int ly, void *_data) {
struct wlr_scene_update_data *data = _data;
struct wlr_box box = { .x = lx, .y = ly };
wlr_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);
wlr_scene_node_opaque_region(node, lx, ly, &opaque);
pixman_region32_subtract(data->visible, data->visible, &opaque);
pixman_region32_fini(&opaque);
}
wlr_scene_node_update_outputs(node, data->outputs, NULL, NULL);
#if WLR_HAS_XWAYLAND
if (data->restack_xwayland_surfaces) {
wlr_scene_node_restack_xwayland_surface(node, &box, data);
}
#endif
return false;
}
static void scene_update_region(struct wlr_scene *scene,
const pixman_region32_t *update_region) {
pixman_region32_t visible;
pixman_region32_init(&visible);
pixman_region32_copy(&visible, update_region);
struct pixman_box32 *region_box = pixman_region32_extents(update_region);
struct wlr_scene_update_data data = {
.visible = &visible,
.update_region = update_region,
.update_box = {
.x = region_box->x1,
.y = region_box->y1,
.width = region_box->x2 - region_box->x1,
.height = region_box->y2 - region_box->y1,
},
.outputs = &scene->outputs,
.calculate_visibility = scene->calculate_visibility,
.restack_xwayland_surfaces = scene->restack_xwayland_surfaces,
};
// update node visibility and output enter/leave events
wlr_scene_node_nodes_in_box(&scene->tree->node, &data.update_box, scene_node_update_iterator, &data);
pixman_region32_fini(&visible);
}
static void scene_output_damage(struct wlr_scene_output *scene_output,
const pixman_region32_t *damage) {
struct wlr_output *output = scene_output->output;
pixman_region32_t clipped;
pixman_region32_init(&clipped);
pixman_region32_intersect_rect(&clipped, damage, 0, 0, output->width, output->height);
if (!pixman_region32_empty(&clipped)) {
wlr_output_schedule_frame(scene_output->output);
wlr_damage_ring_add(&scene_output->damage_ring, &clipped);
pixman_region32_union(&scene_output->pending_commit_damage,
&scene_output->pending_commit_damage, &clipped);
}
pixman_region32_fini(&clipped);
}
static void output_to_buffer_coords(pixman_region32_t *damage, struct wlr_output *output) {
int width, height;
wlr_output_transformed_resolution(output, &width, &height);
wlr_region_transform(damage, damage,
wlr_output_transform_invert(output->transform), width, height);
}
static void scene_damage_outputs(struct wlr_scene *scene, const pixman_region32_t *damage) {
if (pixman_region32_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_region(&output_damage, scene_output->output->scale, true);
output_to_buffer_coords(&output_damage, scene_output->output);
scene_output_damage(scene_output, &output_damage);
pixman_region32_fini(&output_damage);
}
}
void wlr_scene_node_update(struct wlr_scene_node *node,
pixman_region32_t *damage) {
if (node->impl->update == NULL) {
struct wlr_scene *scene = node->scene;
int x, y;
if (!wlr_scene_node_coords(node, &x, &y)) {
// We assume explicit damage on a disabled tree means the node was just
// disabled.
if (damage) {
wlr_scene_node_cleanup_when_disabled(node, scene->restack_xwayland_surfaces, &scene->outputs);
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);
wlr_scene_node_visibility(node, &visible);
damage = &visible;
}
pixman_region32_t update_region;
pixman_region32_init(&update_region);
pixman_region32_copy(&update_region, damage);
wlr_scene_node_bounds(node, x, y, &update_region);
scene_update_region(scene, &update_region);
pixman_region32_fini(&update_region);
wlr_scene_node_visibility(node, damage);
scene_damage_outputs(scene, damage);
pixman_region32_fini(damage);
return;
}
node->impl->update(node, damage);
}
void wlr_scene_node_visibility(struct wlr_scene_node *node,
pixman_region32_t *visible) {
if (node->impl->visibility == NULL) {
return;
}
node->impl->visibility(node, visible);
}
void wlr_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->impl->frame_done == NULL) {
return;
}
node->impl->frame_done(node, scene_output, now);
}
bool wlr_scene_node_invisible(struct wlr_scene_node *node) {
if (node->impl->invisible == NULL) {
return false;
}
return node->impl->invisible(node);
}
bool wlr_scene_node_construct_render_list_iterator(struct wlr_scene_node *node,
int lx, int ly, void *_data) {
if (node->impl->construct_render_list_iterator == NULL) {
return false;
}
return node->impl->construct_render_list_iterator(node, lx, ly, _data);
}
void wlr_scene_node_render(struct wlr_render_list_entry *entry, const struct wlr_render_data *data) {
if (entry->node->impl->render == NULL) {
return;
}
entry->node->impl->render(entry, data);
}
void wlr_scene_node_dmabuf_feedback(struct wlr_render_list_entry *entry,
struct wlr_scene_output *scene_output) {
if (entry->node->impl->dmabuf_feedback == NULL) {
return;
}
entry->node->impl->dmabuf_feedback(entry, scene_output);
}
void wlr_scene_node_get_extents(struct wlr_scene_node *node, int lx, int ly,
int *x_min, int *y_min, int *x_max, int *y_max) {
if (node->impl->get_extents == NULL) {
return;
}
node->impl->get_extents(node, lx, ly, x_min, y_min, x_max, y_max);
}
struct wl_list *wlr_scene_node_get_children(struct wlr_scene_node *node) {
if (node->impl->get_children == NULL) {
return NULL;
}
return node->impl->get_children(node);
}
void wlr_scene_node_restack_xwayland_surface(struct wlr_scene_node *node,
struct wlr_box *box, struct wlr_scene_update_data *data) {
if (node->impl->restack_xwayland_surface == NULL) {
return;
}
node->impl->restack_xwayland_surface(node, box, data);
}
void wlr_scene_node_cleanup_when_disabled(struct wlr_scene_node *node,
bool xwayland_restack, struct wl_list *outputs) {
if (node->impl->cleanup_when_disabled == NULL) {
return;
}
node->impl->cleanup_when_disabled(node, xwayland_restack, outputs);
}

View file

@ -0,0 +1,364 @@
#include <sched.h>
#include <wlr/types/wlr_scene_rect.h>
#include "types/wlr_scene.h"
#include <wlr/util/region.h>
#include <wlr/util/transform.h>
#include "types/wlr_output.h"
#include "types/wlr_scene.h"
#include <wayland-server-core.h>
#include <wayland-util.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <wlr/config.h>
static void scene_node_visibility(struct wlr_scene_node *node,
pixman_region32_t *visible);
static void scene_node_get_size(struct wlr_scene_node *node,
int *width, int *height);
static void scene_node_destroy(struct wlr_scene_node *node) {
struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node);
free(scene_rect);
}
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;
at_data->rx = rx;
at_data->ry = ry;
at_data->node = node;
return true;
}
static struct wlr_scene_node *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 (wlr_scene_node_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 scene_node_get_size(struct wlr_scene_node *node,
int *width, int *height) {
struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node);
*width = scene_rect->width;
*height = scene_rect->height;
}
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;
}
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;
}
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) {
int width, height;
scene_node_get_size(node, &width, &height);
struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node);
if (scene_rect->color[3] != 1) {
return;
}
pixman_region32_fini(opaque);
pixman_region32_init_rect(opaque, x, y, width, height);
}
static void scale_region(pixman_region32_t *region, float scale, bool round_up) {
wlr_region_scale(region, region, scale);
if (round_up && floor(scale) != scale) {
wlr_region_expand(region, region, 1);
}
}
static void scene_node_visibility(struct wlr_scene_node *node,
pixman_region32_t *visible) {
if (!node->enabled) {
return;
}
pixman_region32_union(visible, visible, &node->visible);
}
static bool scene_node_invisible(struct wlr_scene_node *node) {
struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
return rect->color[3] == 0.f;
}
static bool construct_render_list_iterator(struct wlr_scene_node *node,
int lx, int ly, void *_data) {
struct wlr_render_list_constructor_data *data = _data;
if (wlr_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
// unless fractional scale is used even the rect itself (to avoid running
// into issues regarding damage region expansion).
if (data->calculate_visibility &&
(!data->fractional_scale || data->render_list->size == 0)) {
struct wlr_scene_rect *rect = wlr_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_empty(&intersection)) {
pixman_region32_fini(&intersection);
return false;
}
pixman_region32_fini(&intersection);
struct wlr_render_list_entry *entry = wl_array_add(data->render_list, sizeof(*entry));
if (entry == NULL) {
return false;
}
*entry = (struct wlr_render_list_entry){
.node = node,
.x = lx,
.y = ly,
.highlight_transparent_region = data->highlight_transparent_region,
};
return false;
}
static void logical_to_buffer_coords(pixman_region32_t *region, const struct wlr_render_data *data,
bool round_up) {
enum wl_output_transform transform = wlr_output_transform_invert(data->transform);
scale_region(region, data->scale, round_up);
wlr_region_transform(region, region, transform, data->trans_width, data->trans_height);
}
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 transform_output_box(struct wlr_box *box, const struct wlr_render_data *data) {
enum wl_output_transform transform = wlr_output_transform_invert(data->transform);
scale_box(box, data->scale);
wlr_box_transform(box, box, transform, data->trans_width, data->trans_height);
}
static void scene_node_render(struct wlr_render_list_entry *entry, const struct wlr_render_data *data) {
struct wlr_scene_node *node = entry->node;
pixman_region32_t render_region;
pixman_region32_init(&render_region);
pixman_region32_copy(&render_region, &node->visible);
pixman_region32_translate(&render_region, -data->logical.x, -data->logical.y);
logical_to_buffer_coords(&render_region, data, true);
pixman_region32_intersect(&render_region, &render_region, &data->damage);
if (pixman_region32_empty(&render_region)) {
pixman_region32_fini(&render_region);
return;
}
int x = entry->x - data->logical.x;
int y = entry->y - data->logical.y;
struct wlr_box dst_box = {
.x = x,
.y = y,
};
scene_node_get_size(node, &dst_box.width, &dst_box.height);
transform_output_box(&dst_box, data);
pixman_region32_t opaque;
pixman_region32_init(&opaque);
scene_node_opaque_region(node, x, y, &opaque);
logical_to_buffer_coords(&opaque, data, false);
pixman_region32_subtract(&opaque, &render_region, &opaque);
struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node);
wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){
.box = dst_box,
.color = {
.r = scene_rect->color[0],
.g = scene_rect->color[1],
.b = scene_rect->color[2],
.a = scene_rect->color[3],
},
.clip = &render_region,
});
pixman_region32_fini(&opaque);
pixman_region32_fini(&render_region);
}
static void get_scene_node_extents(struct wlr_scene_node *node, int lx, int ly,
int *x_min, int *y_min, int *x_max, int *y_max) {
struct wlr_box node_box = { .x = lx, .y = ly };
scene_node_get_size(node, &node_box.width, &node_box.height);
if (node_box.x < *x_min) {
*x_min = node_box.x;
}
if (node_box.y < *y_min) {
*y_min = node_box.y;
}
if (node_box.x + node_box.width > *x_max) {
*x_max = node_box.x + node_box.width;
}
if (node_box.y + node_box.height > *y_max) {
*y_max = node_box.y + node_box.height;
}
}
static void scene_node_cleanup_when_disabled(struct wlr_scene_node *node,
bool xwayland_restack, struct wl_list *outputs) {
pixman_region32_clear(&node->visible);
wlr_scene_node_update_outputs(node, outputs, NULL, NULL);
}
static const struct wlr_scene_node_impl scene_node_impl = {
.destroy = scene_node_destroy,
.set_enabled = NULL,
.set_position = NULL,
.bounds = NULL,
.get_size = scene_node_get_size,
.coords = NULL,
.at = scene_node_at,
.in_box = scene_nodes_in_box,
.opaque_region = scene_node_opaque_region,
.update_outputs = NULL,
.update = NULL,
.visibility = scene_node_visibility,
.frame_done = NULL,
.invisible = scene_node_invisible,
.construct_render_list_iterator = construct_render_list_iterator,
.render = scene_node_render,
.get_extents = get_scene_node_extents,
.get_children = NULL,
.restack_xwayland_surface = NULL,
.cleanup_when_disabled = scene_node_cleanup_when_disabled,
};
struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent,
int width, int height, const float color[static 4]) {
assert(parent);
assert(width >= 0 && height >= 0);
struct wlr_scene_rect *scene_rect = calloc(1, sizeof(*scene_rect));
if (scene_rect == NULL) {
return NULL;
}
wlr_scene_node_init(&scene_rect->node, &scene_node_impl, parent);
scene_rect->width = width;
scene_rect->height = height;
memcpy(scene_rect->color, color, sizeof(scene_rect->color));
wlr_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;
}
assert(width >= 0 && height >= 0);
rect->width = width;
rect->height = height;
wlr_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));
wlr_scene_node_update(&rect->node, NULL);
}
bool wlr_scene_node_is_rect(const struct wlr_scene_node *node) {
return node->impl == &scene_node_impl;
}
struct wlr_scene_rect *wlr_scene_rect_from_node(struct wlr_scene_node *node) {
assert(node->impl == &scene_node_impl);
struct wlr_scene_rect *scene_rect =
wl_container_of(node, scene_rect, node);
return scene_rect;
}

View file

@ -0,0 +1,352 @@
#include <wlr/types/wlr_scene_tree.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
#include <wlr/util/region.h>
#include <wlr/util/transform.h>
#include "types/wlr_output.h"
#include "types/wlr_scene.h"
#include <wayland-server-core.h>
#include <wayland-util.h>
#include <assert.h>
#include <stdlib.h>
#include <wlr/config.h>
static void scene_node_visibility(struct wlr_scene_node *node,
pixman_region32_t *visible);
static void scene_node_destroy(struct wlr_scene_node *node) {
struct wlr_scene *scene = node->scene;
struct wlr_scene_tree *scene_tree = wlr_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->linux_dmabuf_v1_destroy.link);
wl_list_remove(&scene->gamma_control_manager_v1_destroy.link);
wl_list_remove(&scene->gamma_control_manager_v1_set_gamma.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);
}
free(scene_tree);
}
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;
at_data->rx = rx;
at_data->ry = ry;
at_data->node = node;
return true;
}
static struct wlr_scene_node *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 (wlr_scene_node_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 scene_node_bounds(struct wlr_scene_node *node,
int x, int y, pixman_region32_t *visible) {
if (!node->enabled) {
return;
}
struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each(child, &scene_tree->children, link) {
wlr_scene_node_bounds(child, x + child->x, y + child->y, visible);
}
}
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;
}
struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each_reverse(child, &scene_tree->children, link) {
if (wlr_scene_node_nodes_in_box(child, box, iterator, user_data)) {
return true;
}
}
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 scale_region(pixman_region32_t *region, float scale, bool round_up) {
wlr_region_scale(region, region, scale);
if (round_up && floor(scale) != scale) {
wlr_region_expand(region, region, 1);
}
}
static void scene_node_visibility(struct wlr_scene_node *node,
pixman_region32_t *visible) {
if (!node->enabled) {
return;
}
struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each(child, &scene_tree->children, link) {
wlr_scene_node_visibility(child, visible);
}
}
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;
}
struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each(child, &scene_tree->children, link) {
wlr_scene_node_send_frame_done(child, scene_output, now);
}
}
static bool scene_node_invisible(struct wlr_scene_node *node) {
return true;
}
static bool construct_render_list_iterator(struct wlr_scene_node *node,
int lx, int ly, void *_data) {
struct wlr_render_list_constructor_data *data = _data;
if (wlr_scene_node_invisible(node)) {
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_empty(&intersection)) {
pixman_region32_fini(&intersection);
return false;
}
pixman_region32_fini(&intersection);
struct wlr_render_list_entry *entry = wl_array_add(data->render_list, sizeof(*entry));
if (entry == NULL) {
return false;
}
*entry = (struct wlr_render_list_entry){
.node = node,
.x = lx,
.y = ly,
.highlight_transparent_region = data->highlight_transparent_region,
};
return false;
}
static void logical_to_buffer_coords(pixman_region32_t *region, const struct wlr_render_data *data,
bool round_up) {
enum wl_output_transform transform = wlr_output_transform_invert(data->transform);
scale_region(region, data->scale, round_up);
wlr_region_transform(region, region, transform, data->trans_width, data->trans_height);
}
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 transform_output_box(struct wlr_box *box, const struct wlr_render_data *data) {
enum wl_output_transform transform = wlr_output_transform_invert(data->transform);
scale_box(box, data->scale);
wlr_box_transform(box, box, transform, data->trans_width, data->trans_height);
}
static void scene_node_render(struct wlr_render_list_entry *entry, const struct wlr_render_data *data) {
struct wlr_scene_node *node = entry->node;
pixman_region32_t render_region;
pixman_region32_init(&render_region);
pixman_region32_copy(&render_region, &node->visible);
pixman_region32_translate(&render_region, -data->logical.x, -data->logical.y);
logical_to_buffer_coords(&render_region, data, true);
pixman_region32_intersect(&render_region, &render_region, &data->damage);
if (pixman_region32_empty(&render_region)) {
pixman_region32_fini(&render_region);
return;
}
int x = entry->x - data->logical.x;
int y = entry->y - data->logical.y;
struct wlr_box dst_box = {
.x = x,
.y = y,
};
wlr_scene_node_get_size(node, &dst_box.width, &dst_box.height);
transform_output_box(&dst_box, data);
pixman_region32_t opaque;
pixman_region32_init(&opaque);
wlr_scene_node_opaque_region(node, x, y, &opaque);
logical_to_buffer_coords(&opaque, data, false);
pixman_region32_subtract(&opaque, &render_region, &opaque);
assert(false);
pixman_region32_fini(&opaque);
pixman_region32_fini(&render_region);
}
static void get_scene_node_extents(struct wlr_scene_node *node, int lx, int ly,
int *x_min, int *y_min, int *x_max, int *y_max) {
struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each(child, &scene_tree->children, link) {
wlr_scene_node_get_extents(child, lx + child->x, ly + child->y, x_min, y_min, x_max, y_max);
}
}
static struct wl_list *scene_node_get_children(struct wlr_scene_node *node) {
struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
return &scene_tree->children;
}
static void scene_node_cleanup_when_disabled(struct wlr_scene_node *node,
bool xwayland_restack, struct wl_list *outputs) {
struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each(child, &scene_tree->children, link) {
if (!child->enabled) {
continue;
}
wlr_scene_node_cleanup_when_disabled(child, xwayland_restack, outputs);
}
return;
}
static const struct wlr_scene_node_impl scene_node_impl = {
.destroy = scene_node_destroy,
.set_enabled = NULL,
.set_position = NULL,
.bounds = scene_node_bounds,
.get_size = NULL,
.coords = NULL,
.at = scene_node_at,
.in_box = scene_nodes_in_box,
.opaque_region = NULL,
.update_outputs = NULL,
.update = NULL,
.visibility = scene_node_visibility,
.frame_done = scene_node_send_frame_done,
.invisible = scene_node_invisible,
.construct_render_list_iterator = construct_render_list_iterator,
.render = scene_node_render,
.get_extents = get_scene_node_extents,
.get_children = scene_node_get_children,
.restack_xwayland_surface = NULL,
.cleanup_when_disabled = scene_node_cleanup_when_disabled,
};
static struct wlr_scene_tree *scene_tree_create(struct wlr_scene_tree *parent) {
struct wlr_scene_tree *tree = calloc(1, sizeof(*tree));
if (tree == NULL) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return NULL;
}
wlr_scene_node_init(&tree->node, &scene_node_impl, parent);
wl_list_init(&tree->children);
return tree;
}
struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent) {
assert(parent);
return scene_tree_create(parent);
}
struct wlr_scene_tree *wlr_root_scene_tree_create(struct wlr_scene *scene) {
struct wlr_scene_tree *tree = scene_tree_create(NULL);
tree->node.scene = scene;
return tree;
}
bool wlr_scene_node_is_tree(const struct wlr_scene_node *node) {
return node->impl == &scene_node_impl;
}
struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node) {
assert(node->impl == &scene_node_impl);
struct wlr_scene_tree *scene_tree =
wl_container_of(node, scene_tree, node);
return scene_tree;
}