From 7e1da342be63c6bd6003995da10031561ab547fe Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Wed, 13 May 2026 14:16:47 +0200 Subject: [PATCH] Fix XWayland crash: switch to lazy mode XWayland was started eagerly (at compositor init) which caused a segfault when XWayland couldn't connect back to the Wayland socket (race condition in nested mode). Since the WM UI is now pure Wayland (no X11 rendering), XWayland is only needed for X11 client apps. Switch to lazy mode: XWayland starts only when the first X11 client connects. This eliminates the startup wait loop and the associated crash. Also set xwayland_ready=true in the server destroy handler as a safety net. --- src/backend/wayland/wl_backend.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index 3e89a3d..74a75bc 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -1349,6 +1349,7 @@ handle_xwayland_server_destroy(struct wl_listener *listener, void *data) (void)listener; (void)data; wwarning("wl_backend: XWayland server exited"); + wl_state.xwayland_ready = true; /* break the wait loop */ } static void @@ -2402,7 +2403,7 @@ wl_display_open(const char *name) * renders through XWayland, making the WM visible on-screen. * The ready signal fires once Xwayland's socket accepts connections. */ wl_state.xwayland = wlr_xwayland_create(wl_state.display, - wl_state.compositor, false); + wl_state.compositor, true); if (wl_state.xwayland) { /* Wire server destroy signal to detect early failure (e.g. stale * display lock → Xwayland exits before ready fires). */ @@ -2492,14 +2493,12 @@ wl_display_open(const char *name) * handle_xwayland_ready() calls XOpenDisplay and stores the result in * wl_state.x_display, then sets xwayland_ready = true to break the loop. * If XWayland is not available, return NULL (pure Wayland path). */ + /* XWayland is lazy — it starts when the first X11 client connects. + * No need to wait for it at startup. Mark as ready so the rest of + * init proceeds without blocking. */ if (wl_state.xwayland) { - wwarning("wl_backend: waiting for XWayland ready signal..."); - struct wl_event_loop *ev_loop = - wl_display_get_event_loop(wl_state.display); - while (!wl_state.xwayland_ready) { - wl_display_flush_clients(wl_state.display); - wl_event_loop_dispatch(ev_loop, 10); - } + wwarning("wl_backend: XWayland configured (lazy mode) — starts on first X11 client"); + wl_state.xwayland_ready = true; if (wl_state.x_display) { wwarning("wl_backend: XWayland bridge ready — Display*=%p, full WM UI enabled", (void *)wl_state.x_display);