wayland: Phase 34 — Wayland socket, output layout, real monitors_query

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 <noreply@anthropic.com>
This commit is contained in:
Levi Neely 2026-03-27 23:06:49 +01:00
parent f7dfc50f26
commit 97a9209b4f
2 changed files with 108 additions and 11 deletions

View File

@ -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 <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/backend/headless.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#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; }

View File

@ -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=<name>"
# 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