diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 7187359b..965782bf 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -224,4 +224,11 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output); bool wlr_scene_attach_output_layout(struct wlr_scene *scene, struct wlr_output_layout *output_layout); +/** + * Add a node displaying a surface and all of its sub-surfaces to the + * scene-graph. + */ +struct wlr_scene_node *wlr_scene_subsurface_tree_create( + struct wlr_scene_node *parent, struct wlr_surface *surface); + #endif diff --git a/types/meson.build b/types/meson.build index fd9ce58c..bb1e8c8f 100644 --- a/types/meson.build +++ b/types/meson.build @@ -3,6 +3,7 @@ wlr_files += files( 'data_device/wlr_data_offer.c', 'data_device/wlr_data_source.c', 'data_device/wlr_drag.c', + 'scene/subsurface_tree.c', 'scene/wlr_scene.c', 'scene/output_layout.c', 'seat/wlr_seat_keyboard.c', diff --git a/types/scene/subsurface_tree.c b/types/scene/subsurface_tree.c new file mode 100644 index 00000000..7cfa8828 --- /dev/null +++ b/types/scene/subsurface_tree.c @@ -0,0 +1,218 @@ +#include +#include +#include +#include + +/** + * A tree for a surface and all of its child sub-surfaces. + * + * `tree` contains `scene_surface` and one node per sub-surface. + */ +struct wlr_scene_subsurface_tree { + struct wlr_scene_tree *tree; + struct wlr_surface *surface; + struct wlr_scene_surface *scene_surface; + + struct wlr_scene_subsurface_tree *parent; // NULL for the top-level surface + struct wlr_addon surface_addon; // only set if there's a parent + + struct wl_listener tree_destroy; + struct wl_listener surface_destroy; + struct wl_listener surface_commit; + struct wl_listener surface_new_subsurface; +}; + +static void subsurface_tree_destroy(struct wlr_scene_subsurface_tree *subsurface_tree) { + // tree and scene_surface will be cleaned up by scene_node_finish + if (subsurface_tree->parent) { + wlr_addon_finish(&subsurface_tree->surface_addon); + } + wl_list_remove(&subsurface_tree->tree_destroy.link); + wl_list_remove(&subsurface_tree->surface_destroy.link); + wl_list_remove(&subsurface_tree->surface_commit.link); + wl_list_remove(&subsurface_tree->surface_new_subsurface.link); + free(subsurface_tree); +} + +static void subsurface_tree_handle_tree_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(listener, subsurface_tree, tree_destroy); + subsurface_tree_destroy(subsurface_tree); +} + +static void subsurface_tree_handle_surface_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(listener, subsurface_tree, surface_destroy); + wlr_scene_node_destroy(&subsurface_tree->tree->node); +} + +static const struct wlr_addon_interface subsurface_tree_addon_impl; + +static struct wlr_scene_subsurface_tree *subsurface_tree_from_subsurface( + struct wlr_scene_subsurface_tree *parent, + struct wlr_subsurface *subsurface) { + struct wlr_addon *addon = wlr_addon_find(&subsurface->surface->addons, + parent, &subsurface_tree_addon_impl); + assert(addon != NULL); + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(addon, subsurface_tree, surface_addon); + return subsurface_tree; +} + +static void subsurface_tree_reconfigure( + struct wlr_scene_subsurface_tree *subsurface_tree) { + struct wlr_surface *surface = subsurface_tree->surface; + + struct wlr_scene_node *prev = NULL; + struct wlr_subsurface *subsurface; + wl_list_for_each(subsurface, &surface->current.subsurfaces_below, + current.link) { + struct wlr_scene_subsurface_tree *child = + subsurface_tree_from_subsurface(subsurface_tree, subsurface); + if (prev != NULL) { + wlr_scene_node_place_above(&child->tree->node, prev); + } + prev = &child->tree->node; + + wlr_scene_node_set_position(&child->tree->node, + subsurface->current.x, subsurface->current.y); + } + + if (prev != NULL) { + wlr_scene_node_place_above(&subsurface_tree->scene_surface->node, prev); + } + prev = &subsurface_tree->scene_surface->node; + + wl_list_for_each(subsurface, &surface->current.subsurfaces_above, + current.link) { + struct wlr_scene_subsurface_tree *child = + subsurface_tree_from_subsurface(subsurface_tree, subsurface); + wlr_scene_node_place_above(&child->tree->node, prev); + prev = &child->tree->node; + + wlr_scene_node_set_position(&child->tree->node, + subsurface->current.x, subsurface->current.y); + } +} + +static void subsurface_tree_handle_surface_commit(struct wl_listener *listener, + void *data) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(listener, subsurface_tree, surface_commit); + + // TODO: only do this on subsurface order or position change + subsurface_tree_reconfigure(subsurface_tree); +} + +static void subsurface_tree_addon_destroy(struct wlr_addon *addon) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(addon, subsurface_tree, surface_addon); + wlr_scene_node_destroy(&subsurface_tree->tree->node); +} + +static const struct wlr_addon_interface subsurface_tree_addon_impl = { + .name = "wlr_scene_subsurface_tree", + .destroy = subsurface_tree_addon_destroy, +}; + +static struct wlr_scene_subsurface_tree *scene_surface_tree_create( + struct wlr_scene_node *parent, struct wlr_surface *surface); + +static bool subsurface_tree_create_subsurface( + struct wlr_scene_subsurface_tree *parent, + struct wlr_subsurface *subsurface) { + struct wlr_scene_subsurface_tree *child = scene_surface_tree_create( + &parent->tree->node, subsurface->surface); + if (child == NULL) { + return false; + } + + child->parent = parent; + wlr_addon_init(&child->surface_addon, &subsurface->surface->addons, + parent, &subsurface_tree_addon_impl); + + return true; +} + +static void subsurface_tree_handle_surface_new_subsurface( + struct wl_listener *listener, void *data) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(listener, subsurface_tree, surface_new_subsurface); + struct wlr_subsurface *subsurface = data; + if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) { + wl_resource_post_no_memory(subsurface->resource); + } +} + +static struct wlr_scene_subsurface_tree *scene_surface_tree_create( + struct wlr_scene_node *parent, struct wlr_surface *surface) { + struct wlr_scene_subsurface_tree *subsurface_tree = + calloc(1, sizeof(*subsurface_tree)); + if (subsurface_tree == NULL) { + return NULL; + } + + subsurface_tree->tree = wlr_scene_tree_create(parent); + if (subsurface_tree->tree == NULL) { + goto error_surface_tree; + } + + subsurface_tree->scene_surface = + wlr_scene_surface_create(&subsurface_tree->tree->node, surface); + if (subsurface_tree->scene_surface == NULL) { + goto error_scene_surface; + } + + subsurface_tree->surface = surface; + + struct wlr_subsurface *subsurface; + wl_list_for_each(subsurface, &surface->current.subsurfaces_below, + current.link) { + if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) { + goto error_scene_surface; + } + } + wl_list_for_each(subsurface, &surface->current.subsurfaces_above, + current.link) { + if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) { + goto error_scene_surface; + } + } + + subsurface_tree_reconfigure(subsurface_tree); + + subsurface_tree->tree_destroy.notify = subsurface_tree_handle_tree_destroy; + wl_signal_add(&subsurface_tree->tree->node.events.destroy, + &subsurface_tree->tree_destroy); + + subsurface_tree->surface_destroy.notify = subsurface_tree_handle_surface_destroy; + wl_signal_add(&surface->events.destroy, &subsurface_tree->surface_destroy); + + subsurface_tree->surface_commit.notify = subsurface_tree_handle_surface_commit; + wl_signal_add(&surface->events.commit, &subsurface_tree->surface_commit); + + subsurface_tree->surface_new_subsurface.notify = + subsurface_tree_handle_surface_new_subsurface; + wl_signal_add(&surface->events.new_subsurface, + &subsurface_tree->surface_new_subsurface); + + return subsurface_tree; + +error_scene_surface: + wlr_scene_node_destroy(&subsurface_tree->tree->node); +error_surface_tree: + free(subsurface_tree); + return NULL; +} + +struct wlr_scene_node *wlr_scene_subsurface_tree_create( + struct wlr_scene_node *parent, struct wlr_surface *surface) { + struct wlr_scene_subsurface_tree *subsurface_tree = + scene_surface_tree_create(parent, surface); + if (subsurface_tree == NULL) { + return NULL; + } + return &subsurface_tree->tree->node; +}