From 0c2eed533eeb6a8d7dbe0a0378930b3a01be557d Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 31 May 2022 21:40:03 +0300 Subject: [PATCH] scene/output-layout: improve ownership logic This commit ensures that outputs that weren't created by the output layout helper aren't destroyed on the output layout change. Consider the following piece of logic: // struct wlr_output *o1, *o2; // struct wlr_scene *scene; // struct wlr_output_layout *layout; wlr_scene_attach_output_layout(scene, layout); wlr_output_layout_add_auto(layout, o1); struct wlr_scene_output *so2 = wlr_scene_output_create(scene, o2); wlr_output_layout_move(layout, o1, 100, 200); // so2 is invalid now --- include/wlr/types/wlr_scene.h | 6 +- types/scene/output_layout.c | 110 +++++++++++++++++++++++----------- 2 files changed, 80 insertions(+), 36 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 4b8ea517..49804d83 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -406,8 +406,10 @@ struct wlr_scene_output *wlr_scene_get_scene_output(struct wlr_scene *scene, /** * Attach an output layout to a scene. * - * Outputs in the output layout are automatically added to the scene. Any - * change to the output layout is mirrored to the scene-graph outputs. + * Adding, removing, or repositioning an output in the output layout + * will respectively add, remove or reposition a corresponding + * scene-graph output. When the output layout is destroyed, scene-graph + * outputs which were created by this helper will be destroyed. */ bool wlr_scene_attach_output_layout(struct wlr_scene *scene, struct wlr_output_layout *output_layout); diff --git a/types/scene/output_layout.c b/types/scene/output_layout.c index e2327eb9..21223114 100644 --- a/types/scene/output_layout.c +++ b/types/scene/output_layout.c @@ -6,36 +6,58 @@ 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; }; -static void scene_output_layout_destroy(struct wlr_scene_output_layout *sol) { - wl_list_remove(&sol->layout_destroy.link); - wl_list_remove(&sol->layout_add.link); - wl_list_remove(&sol->layout_change.link); - wl_list_remove(&sol->scene_destroy.link); - free(sol); +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) { + wlr_scene_output_destroy(solo->scene_output); + wl_list_remove(&solo->layout_output_destroy.link); + wl_list_remove(&solo->scene_output_destroy.link); + wl_list_remove(&solo->link); + free(solo); } -static void scene_output_layout_handle_layout_destroy( +static void scene_output_layout_output_handle_layout_output_destroy( struct wl_listener *listener, void *data) { - struct wlr_scene_output_layout *sol = - wl_container_of(listener, sol, layout_destroy); + struct wlr_scene_output_layout_output *solo = + wl_container_of(listener, solo, layout_output_destroy); + scene_output_layout_output_destroy(solo); +} - // Remove all outputs managed by the output layout - struct wlr_scene_output *scene_output, *tmp; - wl_list_for_each_safe(scene_output, tmp, &sol->scene->outputs, link) { - struct wlr_output_layout_output *lo = - wlr_output_layout_get(sol->layout, scene_output->output); - if (lo != NULL) { - wlr_scene_output_destroy(scene_output); - } - } +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); +} - scene_output_layout_destroy(sol); +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( @@ -43,17 +65,10 @@ static void scene_output_layout_handle_layout_change( struct wlr_scene_output_layout *sol = wl_container_of(listener, sol, layout_change); - struct wlr_scene_output *scene_output, *tmp; - wl_list_for_each_safe(scene_output, tmp, &sol->scene->outputs, link) { - struct wlr_output_layout_output *lo = - wlr_output_layout_get(sol->layout, scene_output->output); - if (lo == NULL) { - // Output has been removed from the layout - wlr_scene_output_destroy(scene_output); - continue; - } - - wlr_scene_output_set_position(scene_output, lo->x, lo->y); + 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); } } @@ -63,13 +78,38 @@ static void scene_output_layout_handle_layout_add( wl_container_of(listener, sol, layout_add); struct wlr_output_layout_output *lo = data; - struct wlr_scene_output *scene_output = - wlr_scene_output_create(sol->scene, lo->output); - if (scene_output == NULL) { + struct wlr_scene_output_layout_output *solo = calloc(1, sizeof(*solo)); + if (solo == NULL) { return; } - wlr_scene_output_set_position(scene_output, lo->x, lo->y); + 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_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( @@ -89,6 +129,8 @@ bool wlr_scene_attach_output_layout(struct wlr_scene *scene, 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);