wayland: fix resource leaks causing freeze after extended use

Three leaks identified via static analysis:

1. frame_buf_destroy never freed fb->bg_pixmap. Every window with a
   textured titlebar leaked a full pixman_image_t on close.

2. handle_xwayland_destroy did not clean up frame_bufs (unlike the XDG
   path). Every XWayland window close leaked 4-5 pixman images (frame +
   titlebar + resizebar + buttons).

3. wl_deferred_decoration struct leaked if client disconnected before
   initial_commit fired. Added destroy listener to free it.
This commit is contained in:
lkn 2026-06-22 18:59:08 +02:00
parent c244148e31
commit 1728c38fbe
3 changed files with 37 additions and 0 deletions

View File

@ -28,6 +28,8 @@ extern void *W_GetViewBacking(unsigned long id);
void frame_buf_destroy(struct wlr_buffer *wlr_buf)
{
struct wl_frame_buf *fb = wl_container_of(wlr_buf, fb, base);
if (fb->bg_pixmap)
pixman_image_unref(fb->bg_pixmap);
if (fb->image)
pixman_image_unref(fb->image);
wfree(fb);

View File

@ -484,9 +484,12 @@ handle_toplevel_destroy(struct wl_listener *listener, void *data)
*/
struct wl_deferred_decoration {
struct wl_listener commit_listener;
struct wl_listener destroy_listener;
struct wlr_xdg_toplevel_decoration_v1 *deco;
};
static void handle_deferred_decoration_destroy(struct wl_listener *listener, void *data);
static void
handle_deferred_decoration_commit(struct wl_listener *listener, void *data)
{
@ -499,11 +502,23 @@ handle_deferred_decoration_commit(struct wl_listener *listener, void *data)
return;
wl_list_remove(&dd->commit_listener.link);
wl_list_remove(&dd->destroy_listener.link);
wlr_xdg_toplevel_decoration_v1_set_mode(dd->deco,
WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
wfree(dd);
}
static void
handle_deferred_decoration_destroy(struct wl_listener *listener, void *data)
{
struct wl_deferred_decoration *dd =
wl_container_of(listener, dd, destroy_listener);
(void)data;
wl_list_remove(&dd->commit_listener.link);
wl_list_remove(&dd->destroy_listener.link);
wfree(dd);
}
void
handle_new_toplevel_decoration(struct wl_listener *listener, void *data)
{
@ -519,6 +534,8 @@ handle_new_toplevel_decoration(struct wl_listener *listener, void *data)
dd->deco = deco;
dd->commit_listener.notify = handle_deferred_decoration_commit;
wl_signal_add(&surface->surface->events.commit, &dd->commit_listener);
dd->destroy_listener.notify = handle_deferred_decoration_destroy;
wl_signal_add(&deco->events.destroy, &dd->destroy_listener);
}
}

View File

@ -28,6 +28,8 @@ void view_attach_scene_destroy_listener(struct wl_toplevel_view *view);
void wm_dock_app_add(struct wl_toplevel_view *v);
void wm_dock_app_remove(WNativeWindow id);
void wl_pointer_warp_to(int x, int y);
extern void W_UnregisterBacking(unsigned long id);
struct wl_frame_buf *frame_buf_find(WNativeWindow id);
#define VIEW_FROM_LISTENER(ptr, field) \
((struct wl_toplevel_view *)((char *)(ptr) - offsetof(struct wl_toplevel_view, field)))
@ -237,7 +239,23 @@ handle_xwayland_destroy(struct wl_listener *listener, void *data)
wfree(view->wm_command[ci]);
wfree(view->wm_command);
}
/* Destroy frame_bufs belonging to this window (mirrors handle_toplevel_destroy) */
WNativeWindow frame_id = view->frame_id;
wfree(view);
if (frame_id) {
struct wl_frame_buf *_fb, *_next;
wl_list_for_each_safe(_fb, _next, &wl_state.frame_list, link) {
if (_fb->id == frame_id || _fb->parent_id == frame_id) {
if (_fb->scene_buf)
wlr_scene_node_destroy(&_fb->scene_buf->node);
if (_fb->tree)
wlr_scene_node_destroy(&_fb->tree->node);
W_UnregisterBacking(_fb->id);
wl_list_remove(&_fb->link);
wlr_buffer_drop(&_fb->base);
}
}
}
}
static void