diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index 93da985..99a9b0c 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -300,6 +300,13 @@ struct wl_frame_buf { pixman_image_t *image; /* ARGB32 pixel data */ struct wlr_scene_buffer *scene_buf; /* scene graph node */ struct wlr_scene_tree *tree; /* grouping tree (top-level frames only) */ + /* Destroy listeners on scene_buf and tree so they get NULL'd when + * wlroots auto-destroys them (e.g. via cascade from parent tree + * destruction during wl_frame_destroy). Without these, code that + * later tries to wlr_scene_node_destroy them again segfaults inside + * wl_list_remove on already-zeroed list links. */ + struct wl_listener scene_buf_destroy; + struct wl_listener tree_destroy; WNativeWindow id; /* synthetic ID (>= 0x10000000) */ WNativeWindow parent_id; /* parent frame_buf ID (0 = root) */ int x, y; /* position in output coords */ @@ -4023,6 +4030,46 @@ frame_buf_find(WNativeWindow id) return NULL; } +/* + * fb_scene_buf_attach / fb_tree_attach + * + * Register destroy listeners on a scene_buf or tree so that fb->scene_buf + * (or fb->tree) is NULL'd when wlroots auto-destroys the node — typically + * via cascade when an ancestor scene_tree is destroyed. Without this, + * follow-up cleanup paths (wl_view_destroy_backing_impl, wl_frame_destroy) + * call wlr_scene_node_destroy on a freed node and segfault inside + * wl_list_remove against a zeroed list link. + */ +static void fb_scene_buf_destroy_handler(struct wl_listener *l, void *d) +{ + (void)d; + struct wl_frame_buf *fb = wl_container_of(l, fb, scene_buf_destroy); + fb->scene_buf = NULL; + wl_list_remove(&fb->scene_buf_destroy.link); +} +static void fb_tree_destroy_handler(struct wl_listener *l, void *d) +{ + (void)d; + struct wl_frame_buf *fb = wl_container_of(l, fb, tree_destroy); + fb->tree = NULL; + /* The tree's destroy cascades into scene_buf — its destroy handler + * will fire too and NULL fb->scene_buf separately. */ + wl_list_remove(&fb->tree_destroy.link); +} +static void fb_attach_scene_buf_listener(struct wl_frame_buf *fb) +{ + if (!fb || !fb->scene_buf) return; + fb->scene_buf_destroy.notify = fb_scene_buf_destroy_handler; + wl_signal_add(&fb->scene_buf->node.events.destroy, + &fb->scene_buf_destroy); +} +static void fb_attach_tree_listener(struct wl_frame_buf *fb) +{ + if (!fb || !fb->tree) return; + fb->tree_destroy.notify = fb_tree_destroy_handler; + wl_signal_add(&fb->tree->node.events.destroy, &fb->tree_destroy); +} + /* Recursively update fb->x, fb->y for all descendants of `parent`, used * to keep absolute-position tracking in sync after the parent moves. * Hit-testing (frame_buf_at, frame_buf_button_at) and click coordinate @@ -4229,9 +4276,11 @@ wl_frame_create_toplevel(WScreen *scr, ? wl_state.level_trees[WL_LAYER_NORMAL] : &wl_state.scene->tree; fb->tree = wlr_scene_tree_create(_parent); if (fb->tree) { + fb_attach_tree_listener(fb); wlr_scene_node_set_position(&fb->tree->node, x, y); fb->scene_buf = wlr_scene_buffer_create(fb->tree, &fb->base); if (fb->scene_buf) { + fb_attach_scene_buf_listener(fb); wlr_scene_node_set_position(&fb->scene_buf->node, 0, 0); wlr_scene_node_set_enabled(&fb->scene_buf->node, false); } @@ -4296,6 +4345,7 @@ wl_frame_create_child(WNativeWindow parent_win, WScreen *scr, ? parent->tree : &wl_state.scene->tree; fb->scene_buf = wlr_scene_buffer_create(ptree, &fb->base); if (fb->scene_buf) { + fb_attach_scene_buf_listener(fb); wlr_scene_node_set_position(&fb->scene_buf->node, x, y); wlr_scene_node_set_enabled(&fb->scene_buf->node, false); } @@ -7013,6 +7063,7 @@ wl_client_reparent(WNativeWindow client, WNativeWindow new_parent, int x, int y) : &wl_state.scene->tree); fb->scene_buf = wlr_scene_buffer_create(_wp, &fb->base); if (fb->scene_buf) { + fb_attach_scene_buf_listener(fb); /* If under the frame tree, use relative coords; otherwise absolute. */ if (parent_fb->tree) wlr_scene_node_set_position(&fb->scene_buf->node, x, y); @@ -7072,7 +7123,12 @@ void wl_view_destroy_backing_impl(unsigned long id) struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)id); if (!fb) return; wl_list_remove(&fb->link); - wlr_scene_node_destroy(&fb->scene_buf->node); + /* fb->scene_buf may have been auto-destroyed by wlroots already if a + * parent tree was destroyed earlier (e.g. wl_frame_destroy on the + * surrounding WMaker frame). The destroy listener NULLs scene_buf + * in that case. */ + if (fb->scene_buf) + wlr_scene_node_destroy(&fb->scene_buf->node); wlr_buffer_drop(&fb->base); } @@ -7250,9 +7306,11 @@ static WNativeWindow wl_fake_leader_create(WScreen *scr) ? wl_state.level_trees[WL_LAYER_NORMAL] : &wl_state.scene->tree; fb->tree = wlr_scene_tree_create(_pp); if (fb->tree) { + fb_attach_tree_listener(fb); wlr_scene_node_set_position(&fb->tree->node, 0, 0); fb->scene_buf = wlr_scene_buffer_create(fb->tree, &fb->base); if (fb->scene_buf) { + fb_attach_scene_buf_listener(fb); wlr_scene_node_set_position(&fb->scene_buf->node, 0, 0); wlr_scene_node_set_enabled(&fb->scene_buf->node, false); } @@ -7480,9 +7538,12 @@ wl_screen_create_internals(WScreen *scr) ? wl_state.level_trees[WL_LAYER_OVERLAY] : &wl_state.scene->tree; fb->tree = wlr_scene_tree_create(parent); if (fb->tree) { + fb_attach_tree_listener(fb); fb->scene_buf = wlr_scene_buffer_create(fb->tree, &fb->base); - if (fb->scene_buf) + if (fb->scene_buf) { + fb_attach_scene_buf_listener(fb); wlr_scene_node_set_enabled(&fb->scene_buf->node, false); + } wlr_scene_node_set_enabled(&fb->tree->node, false); } wl_list_insert(&wl_state.frame_list, &fb->link); @@ -8583,10 +8644,13 @@ static void wl_balloon_init(WScreen *scr) ? wl_state.level_trees[WL_LAYER_OVERLAY] : &wl_state.scene->tree; fb->tree = wlr_scene_tree_create(parent); if (fb->tree) { + fb_attach_tree_listener(fb); wlr_scene_node_set_position(&fb->tree->node, 0, 0); fb->scene_buf = wlr_scene_buffer_create(fb->tree, &fb->base); - if (fb->scene_buf) + if (fb->scene_buf) { + fb_attach_scene_buf_listener(fb); wlr_scene_node_set_position(&fb->scene_buf->node, 0, 0); + } wlr_scene_node_set_enabled(&fb->tree->node, false); }