diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index fc460f2..7d277e7 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -1301,11 +1301,16 @@ wl_display_open(const char *name) wl_signal_add(&wl_state.xdg_shell->events.new_surface, &wl_state.xdg_new_surface_listener); - /* Phase 17: start XWayland so X11 clients (e.g. GNUstep apps) can run - * under the Wayland compositor. The third argument (lazy=false) starts - * the Xwayland process eagerly so DISPLAY is available immediately. */ + /* Phase 17: register XWayland for X11 client support. + * Using lazy=true: the Xwayland process does NOT start at compositor + * boot. It starts automatically on the first X11 client connect. + * This avoids a startup race where Xwayland's Wayland client tries to + * negotiate globals before the compositor's event loop is running, + * causing "Broken pipe" and "xcb connect failed" on the first frame. + * The display number is pre-allocated immediately so DISPLAY can be + * set for child processes even before any X11 client connects. */ wl_state.xwayland = wlr_xwayland_create(wl_state.display, - wl_state.compositor, false); + wl_state.compositor, true); if (wl_state.xwayland) { wl_state.xwayland_new_surface_listener.notify = handle_xwayland_new_surface; wl_signal_add(&wl_state.xwayland->events.new_surface, @@ -1313,7 +1318,7 @@ wl_display_open(const char *name) wlr_xwayland_set_seat(wl_state.xwayland, wl_state.seat); if (wl_state.xwayland->display_name) { setenv("DISPLAY", wl_state.xwayland->display_name, 1); - wwarning("wl_backend: XWayland started DISPLAY=%s", + wwarning("wl_backend: XWayland DISPLAY=%s (lazy: starts on first X11 client)", wl_state.xwayland->display_name); } else { wwarning("wl_backend: XWayland created (display_name pending)"); @@ -1405,6 +1410,7 @@ wl_display_close(void) * still valid because the event loop is stopped and the socket closure * has not been dispatched, so wlroots' wl_client_destroy() succeeds. */ if (wl_state.xwayland->server) { + int xdisp = wl_state.xwayland->server->display; if (wl_state.xwayland->server->pid > 0) kill(wl_state.xwayland->server->pid, SIGKILL); /* wlroots 0.15 bug: after any Wayland client has connected and @@ -1417,6 +1423,17 @@ wl_display_close(void) * clean it up; if it is stale, it has already been removed from * the display's client list and nothing more is needed. */ wl_state.xwayland->server->client = NULL; + /* Clean up X lock file and Unix socket left by SIGKILL'd Xwayland. + * Without this, the next run may find a stale lock file whose PID + * has been reused by an unrelated process, causing wlroots to + * refuse to reclaim the display number → "xcb connect failed". */ + { + char path[64]; + snprintf(path, sizeof(path), "/tmp/.X%d-lock", xdisp); + unlink(path); + snprintf(path, sizeof(path), "/tmp/.X11-unix/X%d", xdisp); + unlink(path); + } } wl_state.xwayland = NULL; unsetenv("DISPLAY"); diff --git a/tests/wl-smoke-test.sh b/tests/wl-smoke-test.sh index cf17316..c85cfae 100755 --- a/tests/wl-smoke-test.sh +++ b/tests/wl-smoke-test.sh @@ -118,10 +118,11 @@ # so handle_backend_new_output finds a valid scene when it fires. # Verified by "scene graph created" + "scene output created" + # "headless compositor mode" logs. -# 39. (Phase 17) XWayland started: wlr_xwayland_create() is called in -# wl_display_open(); compositor logs "XWayland started DISPLAY=:" -# so X11 clients (GNUstep apps) can connect. DISPLAY env var is set. -# Verified by "XWayland started DISPLAY=" log. +# 39. (Phase 17) XWayland display allocated (lazy start): wlr_xwayland_create() +# is called with lazy=true; Xwayland only starts on first X11 client. +# Compositor logs "XWayland DISPLAY=: (lazy: starts on first X11 client)". +# DISPLAY env var is set from the pre-allocated display number. +# Verified by "XWayland DISPLAY=.*lazy" log. # 38. (Phase 11) noclip/nodock set in wl_screen_create_internals: wDockCreate # requires X11 rendering resources unavailable in Wayland mode; disabling # the dock/clip prevents crashes when wWorkspaceNew is called to create @@ -267,14 +268,16 @@ grep -q 'dock/clip disabled in Wayland mode' /tmp/wl-smoke-stderr \ || die "compositor did not log dock/clip disabled (noclip/nodock not set in wl_screen_create_internals)" echo " nodock check: dock/clip disabled in Wayland mode (noclip=1 nodock=1)" -# -- 4g. Phase 17: XWayland started and DISPLAY set ---------------------- +# -- 4g. Phase 17: XWayland display number allocated (lazy start) -------- # -# wl_display_open() logs "XWayland started DISPLAY=:" when -# wlr_xwayland_create() succeeds; verify the log line is present. +# wl_display_open() logs "XWayland DISPLAY=: (lazy: starts on first +# X11 client)" when wlr_xwayland_create() succeeds with lazy=true. +# The Xwayland process does not start until the first X11 client connects, +# avoiding the startup race that caused "Broken pipe" with lazy=false. # (Falls back gracefully if XWayland is unavailable.) -if grep -q 'XWayland started DISPLAY=' /tmp/wl-smoke-stderr; then +if grep -q 'XWayland DISPLAY=.*lazy' /tmp/wl-smoke-stderr; then XDISP=$(grep -o 'DISPLAY=:[0-9]*' /tmp/wl-smoke-stderr | head -1 | sed 's/DISPLAY=//') - echo " xwayland check: XWayland started DISPLAY=$XDISP" + echo " xwayland check: XWayland DISPLAY=$XDISP (lazy start)" elif grep -q 'XWayland unavailable' /tmp/wl-smoke-stderr; then echo " xwayland check: XWayland unavailable (skipped)" else