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;
|
struct wl_listener xw_geometry_listener;
|
||||||
/* Phase 18: scene graph tree for this surface (NULL if scene unavailable). */
|
/* Phase 18: scene graph tree for this surface (NULL if scene unavailable). */
|
||||||
struct wlr_scene_tree *scene_node;
|
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 id; /* synthetic handle, >= 0x10000000 */
|
||||||
WNativeWindow frame_id; /* frame_buf that owns this view (0 = none) */
|
WNativeWindow frame_id; /* frame_buf that owns this view (0 = none) */
|
||||||
int frame_rel_x, frame_rel_y; /* offset within frame */
|
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.
|
* 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
|
static void
|
||||||
handle_toplevel_map(struct wl_listener *listener, void *data)
|
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(
|
view->scene_node = wlr_scene_subsurface_tree_create(
|
||||||
_vp, view->xdg_surface->surface);
|
_vp, view->xdg_surface->surface);
|
||||||
if (view->scene_node) {
|
if (view->scene_node) {
|
||||||
|
view_attach_scene_destroy_listener(view);
|
||||||
/* Deferred reparent: move client surface under its
|
/* Deferred reparent: move client surface under its
|
||||||
* frame grouping tree now that the node exists. */
|
* frame grouping tree now that the node exists. */
|
||||||
if (view->frame_id) {
|
if (view->frame_id) {
|
||||||
|
|
@ -1232,6 +1262,10 @@ handle_toplevel_destroy(struct wl_listener *listener, void *data)
|
||||||
wl_list_remove(&view->link);
|
wl_list_remove(&view->link);
|
||||||
/* Phase 18: remove scene node. */
|
/* Phase 18: remove scene node. */
|
||||||
if (view->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);
|
wlr_scene_node_destroy(&view->scene_node->node);
|
||||||
view->scene_node = NULL;
|
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]
|
struct wlr_scene_tree *_vp = wl_state.level_trees[WL_LAYER_NORMAL]
|
||||||
? wl_state.level_trees[WL_LAYER_NORMAL] : &wl_state.scene->tree;
|
? wl_state.level_trees[WL_LAYER_NORMAL] : &wl_state.scene->tree;
|
||||||
view->scene_node = wlr_scene_subsurface_tree_create(_vp, xw->surface);
|
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);
|
wlr_scene_node_set_position(&view->scene_node->node, view->x, view->y);
|
||||||
|
}
|
||||||
} else if (!xw->surface) {
|
} else if (!xw->surface) {
|
||||||
wwarning("wl_backend: xwayland map id=0x%lx: surface is NULL, window will not render",
|
wwarning("wl_backend: xwayland map id=0x%lx: surface is NULL, window will not render",
|
||||||
view->id);
|
view->id);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue