wayland: NULL view->scene_node when wlroots auto-destroys it
Crash on closing a toplevel: handle_toplevel_destroy called wlr_scene_node_destroy on view->scene_node, but the node had already been destroyed by wlroots a few stack frames earlier when the WMaker frame's tree was destroyed (frame_destroy → tree destroy cascades to children). Fix: register a destroy listener on view->scene_node when it's created. When wlroots destroys the node (typically because its parent tree is destroyed during unmanage), the listener fires and NULLs view->scene_node, so handle_toplevel_destroy's existing 'if (view->scene_node)' check correctly skips the redestroy. handle_toplevel_destroy itself removes the listener before its own wlr_scene_node_destroy call so we don't double-remove the listener entry from the wlroots signal list.
This commit is contained in:
parent
27fa3f78c6
commit
8cb56fc141
|
|
@ -255,6 +255,12 @@ struct wl_toplevel_view {
|
|||
struct wl_listener xw_geometry_listener;
|
||||
/* Phase 18: scene graph tree for this surface (NULL if scene unavailable). */
|
||||
struct wlr_scene_tree *scene_node;
|
||||
/* Listener fires when scene_node is destroyed (e.g. when a parent
|
||||
* tree is destroyed by wl_frame_destroy as part of unmanage).
|
||||
* Without it, view->scene_node becomes a dangling pointer and the
|
||||
* later handle_toplevel_destroy crashes calling wlr_scene_node_destroy
|
||||
* a second time. */
|
||||
struct wl_listener scene_destroy_listener;
|
||||
WNativeWindow id; /* synthetic handle, >= 0x10000000 */
|
||||
WNativeWindow frame_id; /* frame_buf that owns this view (0 = none) */
|
||||
int frame_rel_x, frame_rel_y; /* offset within frame */
|
||||
|
|
@ -1079,6 +1085,29 @@ handle_toplevel_set_app_id(struct wl_listener *listener, void *data)
|
|||
/*
|
||||
* Phase 38: toplevel map — surface is ready to be managed.
|
||||
*/
|
||||
static void
|
||||
handle_toplevel_scene_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
(void)data;
|
||||
struct wl_toplevel_view *view =
|
||||
VIEW_FROM_LISTENER(listener, scene_destroy_listener);
|
||||
/* wlroots is destroying view->scene_node (typically because its
|
||||
* parent tree — the WMaker frame's fb->tree — is being destroyed
|
||||
* as part of unmanage). NULL the pointer so the later
|
||||
* handle_toplevel_destroy doesn't try to redestroy it. */
|
||||
view->scene_node = NULL;
|
||||
wl_list_remove(&view->scene_destroy_listener.link);
|
||||
}
|
||||
|
||||
static void
|
||||
view_attach_scene_destroy_listener(struct wl_toplevel_view *view)
|
||||
{
|
||||
if (!view || !view->scene_node) return;
|
||||
view->scene_destroy_listener.notify = handle_toplevel_scene_destroy;
|
||||
wl_signal_add(&view->scene_node->node.events.destroy,
|
||||
&view->scene_destroy_listener);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_toplevel_map(struct wl_listener *listener, void *data)
|
||||
{
|
||||
|
|
@ -1119,6 +1148,7 @@ handle_toplevel_map(struct wl_listener *listener, void *data)
|
|||
view->scene_node = wlr_scene_subsurface_tree_create(
|
||||
_vp, view->xdg_surface->surface);
|
||||
if (view->scene_node) {
|
||||
view_attach_scene_destroy_listener(view);
|
||||
/* Deferred reparent: move client surface under its
|
||||
* frame grouping tree now that the node exists. */
|
||||
if (view->frame_id) {
|
||||
|
|
@ -1232,6 +1262,10 @@ handle_toplevel_destroy(struct wl_listener *listener, void *data)
|
|||
wl_list_remove(&view->link);
|
||||
/* Phase 18: remove scene node. */
|
||||
if (view->scene_node) {
|
||||
/* Remove our destroy listener BEFORE destroying so the listener
|
||||
* doesn't fire and try to remove itself from a list we're
|
||||
* already cleaning up. */
|
||||
wl_list_remove(&view->scene_destroy_listener.link);
|
||||
wlr_scene_node_destroy(&view->scene_node->node);
|
||||
view->scene_node = NULL;
|
||||
}
|
||||
|
|
@ -1405,8 +1439,10 @@ handle_xwayland_map(struct wl_listener *listener, void *data)
|
|||
struct wlr_scene_tree *_vp = wl_state.level_trees[WL_LAYER_NORMAL]
|
||||
? wl_state.level_trees[WL_LAYER_NORMAL] : &wl_state.scene->tree;
|
||||
view->scene_node = wlr_scene_subsurface_tree_create(_vp, xw->surface);
|
||||
if (view->scene_node)
|
||||
if (view->scene_node) {
|
||||
view_attach_scene_destroy_listener(view);
|
||||
wlr_scene_node_set_position(&view->scene_node->node, view->x, view->y);
|
||||
}
|
||||
} else if (!xw->surface) {
|
||||
wwarning("wl_backend: xwayland map id=0x%lx: surface is NULL, window will not render",
|
||||
view->id);
|
||||
|
|
|
|||
Loading…
Reference in New Issue