Summary
On unified-memory hardware the shell dies after a variable number of layer-window opens (launcher, dock, sidebar, workspace pager):
Gdk-Message: Error 71 (Protocol error) dispatching to Wayland display.
wl_display error 2: zwlr_layer_surface_v1 has never been configured
The whole UX resets. It is not reproducible on a fixed count — it can be 5 opens or 50 — which makes it easy to dismiss as a one-off.
Filing this as a findable record, because the error string currently returns nothing useful and anyone bringing Singularity up on an ARM SBC, Apple silicon or an Intel/AMD iGPU is likely to hit it.
Cause
GTK keeps one wl_surface alive across hide/show and tears down only the role object. gtk4-layer-shell then builds a new zwlr_layer_surface_v1 over that same surface on the next open. wl_surface state is persistent, so a frame queued by a closing animation that lands after the unmap re-attaches a live buffer, and the next role creation inherits it — which the layer-shell protocol explicitly forbids.
Measured at ~0.7 ms after unmap with WAYLAND_DEBUG=1. It is a race on wl_buffer.release, which is why the count varies.
Why unified memory matters
On unified-memory GPUs the buffer is the same physical page the compositor samples, dmabuf-imported with no copy, so wl_buffer.release can return almost immediately and a late animation frame fits in the window. On a discrete GPU the buffer sits behind an explicit VRAM transfer and the window may never open wide enough. This is a hypothesis — we have only tested unified-memory hardware — but it matches the gl/vulkan-only pattern reported upstream in wmww/gtk4-layer-shell#94.
Practical consequence: a developer on a discrete-GPU workstation may never see this, while every SBC user does.
Fixes
Two, at different layers:
Verification
CIX Sky1 (Radxa Orion O6N), labwc/wlroots, GTK4 4.22.4, gtk4-layer-shell 1.3.0, GLES on Mali.
An identical shell binary with no client-side changes crashes immediately against stock gtk4-layer-shell and survives repeated launcher and workspace-pager cycling against the patched library — same kernel, same session, minutes apart, library as the only variable.
Environment note for anyone reproducing: exercise the four-finger gesture dismissal and the dock autohide reveal as well as the launcher toggle. Those are separate close paths and we initially missed both.
Summary
On unified-memory hardware the shell dies after a variable number of layer-window opens (launcher, dock, sidebar, workspace pager):
The whole UX resets. It is not reproducible on a fixed count — it can be 5 opens or 50 — which makes it easy to dismiss as a one-off.
Filing this as a findable record, because the error string currently returns nothing useful and anyone bringing Singularity up on an ARM SBC, Apple silicon or an Intel/AMD iGPU is likely to hit it.
Cause
GTK keeps one
wl_surfacealive across hide/show and tears down only the role object. gtk4-layer-shell then builds a newzwlr_layer_surface_v1over that same surface on the next open.wl_surfacestate is persistent, so a frame queued by a closing animation that lands after the unmap re-attaches a live buffer, and the next role creation inherits it — which the layer-shell protocol explicitly forbids.Measured at ~0.7 ms after unmap with
WAYLAND_DEBUG=1. It is a race onwl_buffer.release, which is why the count varies.Why unified memory matters
On unified-memory GPUs the buffer is the same physical page the compositor samples, dmabuf-imported with no copy, so
wl_buffer.releasecan return almost immediately and a late animation frame fits in the window. On a discrete GPU the buffer sits behind an explicit VRAM transfer and the window may never open wide enough. This is a hypothesis — we have only tested unified-memory hardware — but it matches the gl/vulkan-only pattern reported upstream in wmww/gtk4-layer-shell#94.Practical consequence: a developer on a discrete-GPU workstation may never see this, while every SBC user does.
Fixes
Two, at different layers:
GdkSurface(gtk_widget_unrealize()) afterhide()on all 24 layer-window close paths, so each open gets a freshwl_surface. Works against the library as it ships today.get_layer_surface. Fixes it once for every client, and is the real fix; if it lands and distros pick it up, shell: drop the GdkSurface when a layer-shell window closes #15 largely stops mattering.Verification
CIX Sky1 (Radxa Orion O6N), labwc/wlroots, GTK4 4.22.4, gtk4-layer-shell 1.3.0, GLES on Mali.
An identical shell binary with no client-side changes crashes immediately against stock gtk4-layer-shell and survives repeated launcher and workspace-pager cycling against the patched library — same kernel, same session, minutes apart, library as the only variable.
Environment note for anyone reproducing: exercise the four-finger gesture dismissal and the dock autohide reveal as well as the launcher toggle. Those are separate close paths and we initially missed both.