From 0b053233c2d2de10103955b9fdea4c204582bcd3 Mon Sep 17 00:00:00 2001 From: dvh Date: Wed, 11 Nov 2026 10:07:03 +0100 Subject: [PATCH] remove the popup commit listener when the popup is destroyed The listener was only removed after the popup's initial commit was handled. If the popup went away before that (closed before its first commit, no parent, or destroyed by commitpopup() because its client has no monitor), it stayed on the surface: wlroots then asserts when the surface is destroyed, and a commit in between dereferences a NULL popup. --- dwl.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index 6662022..6dbcebb 100644 --- a/dwl.c +++ b/dwl.c @@ -246,6 +246,11 @@ typedef struct { struct wl_listener destroy; } PointerConstraint; +typedef struct { + struct wl_listener commit; + struct wl_listener destroy; +} Popup; + typedef struct { const char *id; const char *title; @@ -307,6 +312,7 @@ static void destroylock(SessionLock *lock, int unlocked); static void destroylocksurface(struct wl_listener *listener, void *data); static void destroynotify(struct wl_listener *listener, void *data); static void destroypointerconstraint(struct wl_listener *listener, void *data); +static void destroypopup(struct wl_listener *listener, void *data); static void destroysessionlock(struct wl_listener *listener, void *data); static void destroykeyboardgroup(struct wl_listener *listener, void *data); static Monitor *dirtomon(enum wlr_direction dir); @@ -1054,6 +1060,7 @@ commitpopup(struct wl_listener *listener, void *data) { struct wlr_surface *surface = data; struct wlr_xdg_popup *popup = wlr_xdg_popup_try_from_wlr_surface(surface); + Popup *p = wl_container_of(listener, p, commit); LayerSurface *l = NULL; Client *c = NULL; struct wlr_box box; @@ -1075,8 +1082,7 @@ commitpopup(struct wl_listener *listener, void *data) box.x -= (type == LayerShell ? l->scene->node.x : c->geom.x - c->surface.xdg->geometry.x); box.y -= (type == LayerShell ? l->scene->node.y : c->geom.y - c->surface.xdg->geometry.y); wlr_xdg_popup_unconstrain_from_box(popup, &box); - wl_list_remove(&listener->link); - free(listener); + destroypopup(&p->destroy, NULL); } void @@ -1370,7 +1376,9 @@ createpopup(struct wl_listener *listener, void *data) /* This event is raised when a client (either xdg-shell or layer-shell) * creates a new popup. */ struct wlr_xdg_popup *popup = data; - LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup); + Popup *p = ecalloc(1, sizeof(*p)); + LISTEN(&popup->base->surface->events.commit, &p->commit, commitpopup); + LISTEN(&popup->events.destroy, &p->destroy, destroypopup); } void @@ -1559,6 +1567,15 @@ destroypointerconstraint(struct wl_listener *listener, void *data) free(pointer_constraint); } +void +destroypopup(struct wl_listener *listener, void *data) +{ + Popup *p = wl_container_of(listener, p, destroy); + wl_list_remove(&p->commit.link); + wl_list_remove(&p->destroy.link); + free(p); +} + void destroysessionlock(struct wl_listener *listener, void *data) {