From 97a9209b4f0621980d18bff9abb9f287bb31e703 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Fri, 27 Mar 2026 23:06:49 +0100 Subject: [PATCH] =?UTF-8?q?wayland:=20Phase=2034=20=E2=80=94=20Wayland=20s?= =?UTF-8?q?ocket,=20output=20layout,=20real=20monitors=5Fquery?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create the compositor UNIX socket via wl_display_add_socket_auto so Wayland clients can connect; store socket name and set WAYLAND_DISPLAY. Add wlr_output_layout for multi-head tracking and implement monitors_query to return real 1920x1080 head geometry from the headless output. wl-smoke-test now verifies the socket file exists in XDG_RUNTIME_DIR. Co-Authored-By: Claude Sonnet 4.6 --- src/backend/wayland/wl_backend.c | 83 +++++++++++++++++++++++++++++--- tests/wl-smoke-test.sh | 36 ++++++++++++-- 2 files changed, 108 insertions(+), 11 deletions(-) diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index eb6e4f3..9f31f6c 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -6,6 +6,8 @@ * wlroots implementations one functional area at a time. * Phase 32: display_open/close/screen_count implemented with wlroots headless * backend. + * Phase 34: Wayland compositor socket created (clients can connect); + * wlr_output_layout added; monitors_query returns real head geometry. */ #ifdef HAVE_CONFIG_H @@ -15,12 +17,15 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include #include "../../WindowMaker.h" @@ -34,22 +39,26 @@ /* ------------------------------------------------------------------ */ struct wl_compositor_state { - struct wl_display *display; - struct wlr_backend *backend; - struct wlr_renderer *renderer; - struct wlr_output *output; /* headless output (screen 0) */ - int started; /* non-zero after successful display_open */ + struct wl_display *display; + struct wlr_backend *backend; + struct wlr_renderer *renderer; + struct wlr_output *output; /* headless output (screen 0) */ + struct wlr_output_layout *output_layout; /* Phase 34 */ + char socket_name[64]; /* Phase 34: e.g. "wayland-0" */ + int started; /* non-zero after successful display_open */ }; static struct wl_compositor_state wl_state; /* ------------------------------------------------------------------ */ -/* Display lifecycle (Phase 32) */ +/* Display lifecycle (Phase 32 / Phase 34) */ /* ------------------------------------------------------------------ */ static void * wl_display_open(const char *name) { + const char *sock; + (void)name; /* Wayland compositor picks its own socket name */ wlr_log_init(WLR_ERROR, NULL); @@ -89,8 +98,29 @@ wl_display_open(const char *name) return NULL; } + /* Phase 34: create the UNIX socket so Wayland clients can connect. */ + sock = wl_display_add_socket_auto(wl_state.display); + if (!sock) { + wwarning("wl_backend: failed to create Wayland compositor socket"); + wlr_renderer_destroy(wl_state.renderer); + wlr_backend_destroy(wl_state.backend); + wl_display_destroy(wl_state.display); + wl_state.renderer = NULL; + wl_state.backend = NULL; + wl_state.display = NULL; + return NULL; + } + snprintf(wl_state.socket_name, sizeof(wl_state.socket_name), "%s", sock); + setenv("WAYLAND_DISPLAY", wl_state.socket_name, 1); + wwarning("wl_backend: compositor socket WAYLAND_DISPLAY=%s", wl_state.socket_name); + + /* Phase 34: output layout for multi-head geometry tracking. */ + wl_state.output_layout = wlr_output_layout_create(); + /* Add a single 1920×1080 headless output (screen 0). */ wl_state.output = wlr_headless_add_output(wl_state.backend, 1920, 1080); + if (wl_state.output && wl_state.output_layout) + wlr_output_layout_add_auto(wl_state.output_layout, wl_state.output); wl_state.started = 1; return NULL; /* no X11 Display* in the Wayland path */ @@ -101,15 +131,21 @@ wl_display_close(void) { if (!wl_state.started) return; + if (wl_state.output_layout) { + wlr_output_layout_destroy(wl_state.output_layout); + wl_state.output_layout = NULL; + } if (wl_state.renderer) wlr_renderer_destroy(wl_state.renderer); if (wl_state.backend) wlr_backend_destroy(wl_state.backend); if (wl_state.display) wl_display_destroy(wl_state.display); + unsetenv("WAYLAND_DISPLAY"); wl_state.renderer = NULL; wl_state.backend = NULL; wl_state.display = NULL; + wl_state.output = NULL; wl_state.started = 0; } @@ -527,7 +563,40 @@ static void wl_refresh_desktop(WScreen *scr) { (void)scr; } static void wl_monitors_query(WScreen *scr, WHeadGeometry **heads_out, int *count_out) -{ (void)scr; *heads_out = NULL; *count_out = 0; } +{ + (void)scr; + + if (!wl_state.output) { + *heads_out = NULL; + *count_out = 0; + return; + } + + /* Phase 34: return the single headless output as head 0. */ + WHeadGeometry *heads = wmalloc(sizeof(WHeadGeometry)); + + if (wl_state.output_layout) { + const struct wlr_box *box = + wlr_output_layout_get_box(wl_state.output_layout, wl_state.output); + if (box) { + heads[0].x = box->x; + heads[0].y = box->y; + heads[0].width = box->width; + heads[0].height = box->height; + } else { + heads[0].x = heads[0].y = 0; + heads[0].width = (int)wl_state.output->width; + heads[0].height = (int)wl_state.output->height; + } + } else { + heads[0].x = heads[0].y = 0; + heads[0].width = (int)wl_state.output->width; + heads[0].height = (int)wl_state.output->height; + } + + *heads_out = heads; + *count_out = 1; +} static void wl_monitors_select_events(WScreen *scr) { (void)scr; } diff --git a/tests/wl-smoke-test.sh b/tests/wl-smoke-test.sh index 203aa60..c691f01 100755 --- a/tests/wl-smoke-test.sh +++ b/tests/wl-smoke-test.sh @@ -9,6 +9,7 @@ # 2. wmaker links against wlroots # 3. wmaker starts with WMAKER_USE_WAYLAND=1 and reaches the event loop # without crashing (verified by sending SIGTERM and checking exit status) +# 4. (Phase 34) A Wayland compositor socket is created in XDG_RUNTIME_DIR # # This test grows as wl_backend vtable slots are implemented. # @@ -49,12 +50,13 @@ ldd "$WMAKER" 2>/dev/null | grep -qi 'wlroots\|libwlr' \ # WMAKER_USE_WAYLAND=1 selects wl_backend. # # The test waits for the process to be alive (in the event loop) then -# sends SIGTERM and checks for clean exit. +# verifies the Wayland socket was created, sends SIGTERM, and checks +# for clean exit. WMAKER_USE_WAYLAND=1 src/wmaker \ --no-dock --no-clip --no-autolaunch \ --dont-restore --static \ - --for-real & + --for-real 2>/tmp/wl-smoke-stderr & WM_PID=$! # Wait up to 5s for wmaker to reach the event loop (stay alive) @@ -68,7 +70,33 @@ done kill -0 "$WM_PID" 2>/dev/null \ || die "wmaker exited prematurely before SIGTERM (crashed during startup)" -# Send SIGTERM and wait for clean exit +# -- 4. Wayland compositor socket must exist in XDG_RUNTIME_DIR ----------- +# +# Phase 34: wl_display_open() calls wl_display_add_socket_auto() and logs: +# "wl_backend: compositor socket WAYLAND_DISPLAY=" +# Parse the socket name from stderr and verify the socket file exists. + +RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" + +WL_SOCK="" +i=0 +while [ $i -lt 20 ]; do + WL_SOCK=$(grep -o 'WAYLAND_DISPLAY=[^ ]*' /tmp/wl-smoke-stderr 2>/dev/null \ + | head -1 | sed 's/WAYLAND_DISPLAY=//') + [ -n "$WL_SOCK" ] && break + sleep 0.1 + i=$(( i + 1 )) +done + +[ -n "$WL_SOCK" ] \ + || die "wmaker did not log WAYLAND_DISPLAY socket name in stderr" + +[ -S "$RUNTIME_DIR/$WL_SOCK" ] \ + || die "Wayland compositor socket $RUNTIME_DIR/$WL_SOCK not found" + +echo " socket check: $RUNTIME_DIR/$WL_SOCK exists" + +# -- Send SIGTERM and wait for clean exit --------------------------------- kill -TERM "$WM_PID" 2>/dev/null WM_EXIT=0 wait "$WM_PID" 2>/dev/null || WM_EXIT=$? @@ -81,5 +109,5 @@ WM_PID="" || die "wmaker exited with status $WM_EXIT after SIGTERM (expected 0)" # -- Done ----------------------------------------------------------------- -echo "PASS (wl_backend compositor starts and exits cleanly)" +echo "PASS (wl_backend compositor starts, socket created, exits cleanly)" exit 0