wayland: Phase 33 — event_loop_run/atom_intern vtable; wmaker starts and exits cleanly under Wayland
Add display_default_screen, atom_intern, and event_loop_run vtable slots. Guard all X11-only paths in startup.c/screen.c/shutdown.c/event.c/xdnd.c with if (!dpy) so the Wayland path reaches wl_display_run() and exits cleanly (exit 0) on SIGTERM. wl-smoke-test.sh now PASSES. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d07362b90e
commit
f7dfc50f26
|
|
@ -72,6 +72,29 @@ typedef struct WMBackend {
|
|||
*/
|
||||
int (*display_screen_count)(void);
|
||||
|
||||
/*
|
||||
* Return the default screen index for the display connection.
|
||||
* X11: DefaultScreen(dpy). Wayland: 0.
|
||||
* Called from StartUp() to pass to wScreenInit().
|
||||
*/
|
||||
int (*display_default_screen)(void);
|
||||
|
||||
/*
|
||||
* Intern an atom by name.
|
||||
* X11: XInternAtom(dpy, name, False) -- returns Atom (unsigned long).
|
||||
* Wayland: returns 0 (atoms have no meaning in native Wayland).
|
||||
* Called from StartUp() to populate w_global.atom.*.
|
||||
*/
|
||||
unsigned long (*atom_intern)(const char *name);
|
||||
|
||||
/*
|
||||
* Run the backend's main event loop until shutdown.
|
||||
* X11: no-op; EventLoop() drives the WINGs/X11 event pump.
|
||||
* Wayland: wl_display_run(display) -- blocks until wl_display_terminate().
|
||||
* Called from EventLoop() when dpy == NULL (Wayland path).
|
||||
*/
|
||||
void (*event_loop_run)(void);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Screen lifecycle (Phase 2) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include <wlr/backend.h>
|
||||
#include <wlr/backend/headless.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
||||
#include "../../WindowMaker.h"
|
||||
|
|
@ -36,6 +37,7 @@ 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 */
|
||||
};
|
||||
|
||||
|
|
@ -87,6 +89,9 @@ wl_display_open(const char *name)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* Add a single 1920×1080 headless output (screen 0). */
|
||||
wl_state.output = wlr_headless_add_output(wl_state.backend, 1920, 1080);
|
||||
|
||||
wl_state.started = 1;
|
||||
return NULL; /* no X11 Display* in the Wayland path */
|
||||
}
|
||||
|
|
@ -114,11 +119,43 @@ wl_display_screen_count(void)
|
|||
return wl_state.started ? 1 : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
wl_display_default_screen(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
wl_atom_intern(const char *name)
|
||||
{
|
||||
(void)name;
|
||||
return 0; /* atoms have no meaning in native Wayland */
|
||||
}
|
||||
|
||||
static void
|
||||
wl_event_loop_run(void)
|
||||
{
|
||||
if (wl_state.display)
|
||||
wl_display_run(wl_state.display);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Screen open/close */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void wl_screen_open(WScreen *scr) { (void)scr; }
|
||||
static void
|
||||
wl_screen_open(WScreen *scr)
|
||||
{
|
||||
/* Set geometry from the headless output created in display_open(). */
|
||||
scr->scr_width = wl_state.output ?
|
||||
(int)wl_state.output->width : 1920;
|
||||
scr->scr_height = wl_state.output ?
|
||||
(int)wl_state.output->height : 1080;
|
||||
scr->depth = 24;
|
||||
scr->root_win = WNATIVE_NONE;
|
||||
scr->colormap = 0;
|
||||
}
|
||||
|
||||
static void wl_screen_close(WScreen *scr) { (void)scr; }
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -468,8 +505,10 @@ static void wl_fake_leader_destroy(WNativeWindow win) { (void)win; }
|
|||
/* Screen internals */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static int wl_screen_replace_wm(WScreen *scr) { (void)scr; return 0; }
|
||||
static int wl_screen_select_input(WScreen *scr, long event_mask) { (void)scr; (void)event_mask; return 0; }
|
||||
/* Wayland compositor is always the sole WM — succeed unconditionally. */
|
||||
static int wl_screen_replace_wm(WScreen *scr) { (void)scr; return 1; }
|
||||
/* No X11 event mask selection needed; succeed. */
|
||||
static int wl_screen_select_input(WScreen *scr, long event_mask) { (void)scr; (void)event_mask; return 1; }
|
||||
static void wl_screen_set_root_cursor(WScreen *scr, int cursor_id) { (void)scr; (void)cursor_id; }
|
||||
static void wl_screen_set_icon_sizes(WScreen *scr, int min_size, int max_size) { (void)scr; (void)min_size; (void)max_size; }
|
||||
static void wl_screen_setup_noticeboard(WScreen *scr) { (void)scr; }
|
||||
|
|
@ -530,9 +569,12 @@ static void wl_register_error_handlers(void) {}
|
|||
/* ------------------------------------------------------------------ */
|
||||
|
||||
const WMBackend wl_backend = {
|
||||
.display_open = wl_display_open,
|
||||
.display_close = wl_display_close,
|
||||
.display_screen_count = wl_display_screen_count,
|
||||
.display_open = wl_display_open,
|
||||
.display_close = wl_display_close,
|
||||
.display_screen_count = wl_display_screen_count,
|
||||
.display_default_screen = wl_display_default_screen,
|
||||
.atom_intern = wl_atom_intern,
|
||||
.event_loop_run = wl_event_loop_run,
|
||||
|
||||
.screen_open = wl_screen_open,
|
||||
.screen_close = wl_screen_close,
|
||||
|
|
|
|||
|
|
@ -102,6 +102,24 @@ x11_display_screen_count(void)
|
|||
return dpy ? ScreenCount(dpy) : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
x11_display_default_screen(void)
|
||||
{
|
||||
return dpy ? DefaultScreen(dpy) : 0;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
x11_atom_intern(const char *name)
|
||||
{
|
||||
return (unsigned long)XInternAtom(dpy, name, False);
|
||||
}
|
||||
|
||||
static void
|
||||
x11_event_loop_run(void)
|
||||
{
|
||||
/* no-op: EventLoop() in event.c drives the X11/WINGs event pump */
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Screen lifecycle */
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
@ -2358,9 +2376,12 @@ x11_register_error_handlers(void)
|
|||
/* -------------------------------------------------------------------- */
|
||||
|
||||
const WMBackend x11_backend = {
|
||||
.display_open = x11_display_open,
|
||||
.display_close = x11_display_close,
|
||||
.display_screen_count = x11_display_screen_count,
|
||||
.display_open = x11_display_open,
|
||||
.display_close = x11_display_close,
|
||||
.display_screen_count = x11_display_screen_count,
|
||||
.display_default_screen = x11_display_default_screen,
|
||||
.atom_intern = x11_atom_intern,
|
||||
.event_loop_run = x11_event_loop_run,
|
||||
.screen_open = x11_screen_open,
|
||||
.screen_close = x11_screen_close,
|
||||
.frame_create_toplevel = x11_frame_create_toplevel,
|
||||
|
|
|
|||
|
|
@ -366,7 +366,15 @@ noreturn void EventLoop(void)
|
|||
struct timeval time;
|
||||
fd_set rfds;
|
||||
int retVal = 0;
|
||||
#endif
|
||||
|
||||
/* Wayland path: delegate to the backend's event loop (wl_display_run). */
|
||||
if (!dpy) {
|
||||
wm_backend->event_loop_run();
|
||||
Exit(0);
|
||||
}
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
if (w_global.inotify.fd_event_queue < 0 || w_global.inotify.wd_defaults < 0)
|
||||
retVal = -1;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -198,6 +198,9 @@ static int initWVisualID(const char *user_str)
|
|||
|
||||
noreturn void Exit(int status)
|
||||
{
|
||||
if (wm_backend)
|
||||
wm_backend->display_close();
|
||||
|
||||
if (dpy)
|
||||
XCloseDisplay(dpy);
|
||||
|
||||
|
|
|
|||
|
|
@ -158,6 +158,10 @@ WScreen *wScreenInit(int screen_number)
|
|||
wm_backend->monitors_select_events(scr);
|
||||
wm_backend->screen_set_root_cursor(scr, WCUR_ROOT);
|
||||
|
||||
/* Wayland path: screen_open() sets geometry; X11/wrlib context not needed. */
|
||||
if (!dpy)
|
||||
goto screen_done;
|
||||
|
||||
/* screen descriptor for raster graphic library */
|
||||
rattr.flags = RC_RenderMode | RC_ColorsPerChannel | RC_StandardColormap;
|
||||
rattr.render_mode = wPreferences.no_dithering ? RBestMatchRendering : RDitheredRendering;
|
||||
|
|
@ -265,6 +269,7 @@ WScreen *wScreenInit(int screen_number)
|
|||
|
||||
wScreenUpdateUsableArea(scr);
|
||||
|
||||
screen_done:
|
||||
return scr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,8 @@ void Shutdown(WShutdownMode mode)
|
|||
if (scr->helper_pid)
|
||||
kill(scr->helper_pid, SIGKILL);
|
||||
|
||||
wScreenSaveState(scr);
|
||||
if (dpy) /* Wayland: skip X11 session state save */
|
||||
wScreenSaveState(scr);
|
||||
|
||||
if (mode == WSKillMode)
|
||||
wipeDesktop(scr);
|
||||
|
|
@ -99,7 +100,8 @@ void Shutdown(WShutdownMode mode)
|
|||
if (scr) {
|
||||
if (scr->helper_pid)
|
||||
kill(scr->helper_pid, SIGKILL);
|
||||
wScreenSaveState(scr);
|
||||
if (dpy) /* Wayland: skip X11 session state save */
|
||||
wScreenSaveState(scr);
|
||||
RestoreDesktop(scr);
|
||||
}
|
||||
}
|
||||
|
|
@ -157,6 +159,9 @@ static void restoreWindows(WMBag * bag, WMBagIterator iter)
|
|||
*/
|
||||
void RestoreDesktop(WScreen * scr)
|
||||
{
|
||||
if (!dpy)
|
||||
return; /* Wayland: no X11 clients to unmanage */
|
||||
|
||||
if (scr->helper_pid > 0) {
|
||||
kill(scr->helper_pid, SIGTERM);
|
||||
scr->helper_pid = 0;
|
||||
|
|
@ -192,6 +197,9 @@ static void wipeDesktop(WScreen * scr)
|
|||
{
|
||||
WWindow *wwin;
|
||||
|
||||
if (!dpy)
|
||||
return; /* Wayland: no X11 clients to wipe */
|
||||
|
||||
wwin = scr->focused_window;
|
||||
while (wwin) {
|
||||
if (wwin->protocols.DELETE_WINDOW)
|
||||
|
|
|
|||
|
|
@ -177,6 +177,9 @@ static void getOffendingModifiers(void)
|
|||
Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
|
||||
};
|
||||
|
||||
if (!dpy)
|
||||
return; /* Wayland: no modifier mapping to query */
|
||||
|
||||
nlock = wm_backend->keysym_to_keycode(WM_KEY_NUM_LOCK);
|
||||
slock = wm_backend->keysym_to_keycode(WM_KEY_SCROLL_LOCK);
|
||||
|
||||
|
|
@ -376,18 +379,11 @@ void StartUp(Bool defaultScreenOnly)
|
|||
w_global.context.app_win = XUniqueContext();
|
||||
w_global.context.stack = XUniqueContext();
|
||||
|
||||
/* _XA_VERSION = XInternAtom(dpy, "VERSION", False); */
|
||||
|
||||
#ifdef HAVE_XINTERNATOMS
|
||||
XInternAtoms(dpy, atomNames, wlengthof(atomNames), False, atom);
|
||||
#else
|
||||
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < wlengthof(atomNames); i++)
|
||||
atom[i] = XInternAtom(dpy, atomNames[i], False);
|
||||
int ai;
|
||||
for (ai = 0; ai < (int)wlengthof(atomNames); ai++)
|
||||
atom[ai] = (Atom)wm_backend->atom_intern(atomNames[ai]);
|
||||
}
|
||||
#endif
|
||||
|
||||
w_global.atom.wm.state = atom[0];
|
||||
w_global.atom.wm.change_state = atom[1];
|
||||
|
|
@ -478,8 +474,9 @@ void StartUp(Bool defaultScreenOnly)
|
|||
/* handle X shutdowns and errors via backend */
|
||||
wm_backend->register_error_handlers();
|
||||
|
||||
/* set hook for out event dispatcher in WINGs event dispatcher */
|
||||
WMHookEventHandler(DispatchEvent);
|
||||
/* set hook for our event dispatcher in WINGs event dispatcher (X11 only) */
|
||||
if (dpy)
|
||||
WMHookEventHandler(DispatchEvent);
|
||||
|
||||
/* initialize defaults stuff */
|
||||
w_global.domain.wmaker = wDefaultsInitDomain("WindowMaker", True);
|
||||
|
|
@ -535,7 +532,7 @@ void StartUp(Bool defaultScreenOnly)
|
|||
/* manage the screens */
|
||||
for (j = 0; j < max; j++) {
|
||||
if (defaultScreenOnly || max == 1) {
|
||||
wScreen[w_global.screen_count] = wScreenInit(DefaultScreen(dpy));
|
||||
wScreen[w_global.screen_count] = wScreenInit(wm_backend->display_default_screen());
|
||||
if (!wScreen[w_global.screen_count]) {
|
||||
wfatal(_("it seems that there is already a window manager running"));
|
||||
Exit(1);
|
||||
|
|
@ -550,9 +547,11 @@ void StartUp(Bool defaultScreenOnly)
|
|||
w_global.screen_count++;
|
||||
}
|
||||
|
||||
InitializeSwitchMenu();
|
||||
if (dpy)
|
||||
InitializeSwitchMenu();
|
||||
|
||||
/* initialize/restore state for the screens */
|
||||
/* initialize/restore state for the screens (X11 only for now) */
|
||||
if (dpy) {
|
||||
for (j = 0; j < w_global.screen_count; j++) {
|
||||
int lastDesktop;
|
||||
|
||||
|
|
@ -605,6 +604,7 @@ void StartUp(Bool defaultScreenOnly)
|
|||
else
|
||||
wSessionRestoreLastWorkspace(wScreen[j]);
|
||||
}
|
||||
} /* end if (dpy) */
|
||||
|
||||
if (w_global.screen_count == 0) {
|
||||
wfatal(_("could not manage any screen"));
|
||||
|
|
@ -612,8 +612,8 @@ void StartUp(Bool defaultScreenOnly)
|
|||
}
|
||||
|
||||
#ifndef HAVE_INOTIFY
|
||||
/* setup defaults file polling */
|
||||
if (!wPreferences.flags.noupdates)
|
||||
/* setup defaults file polling (X11/WINGs timer only) */
|
||||
if (dpy && !wPreferences.flags.noupdates)
|
||||
WMAddTimerHandler(3000, wDefaultsCheckDomains, NULL);
|
||||
#endif
|
||||
|
||||
|
|
@ -647,7 +647,10 @@ static void manageAllWindows(WScreen * scr, int crashRecovery)
|
|||
unsigned int i, j;
|
||||
WWindow *wwin;
|
||||
|
||||
XGrabServer(dpy);
|
||||
if (!dpy)
|
||||
return; /* Wayland: no pre-existing X11 clients to adopt */
|
||||
|
||||
wm_backend->server_grab();
|
||||
XQueryTree(dpy, scr->root_win, &root, &parent, &children, &nchildren);
|
||||
|
||||
scr->flags.startup = 1;
|
||||
|
|
@ -709,7 +712,7 @@ static void manageAllWindows(WScreen * scr, int crashRecovery)
|
|||
}
|
||||
}
|
||||
}
|
||||
XUngrabServer(dpy);
|
||||
wm_backend->server_ungrab();
|
||||
|
||||
/* hide apps */
|
||||
wwin = scr->focused_window;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,9 @@ static Atom selected_typelist;
|
|||
|
||||
void wXDNDInitializeAtoms(void)
|
||||
{
|
||||
if (!dpy)
|
||||
return; /* Wayland: XDND is X11-only */
|
||||
|
||||
_XA_XdndAware = XInternAtom(dpy, "XdndAware", False);
|
||||
_XA_XdndEnter = XInternAtom(dpy, "XdndEnter", False);
|
||||
_XA_XdndLeave = XInternAtom(dpy, "XdndLeave", False);
|
||||
|
|
|
|||
|
|
@ -7,11 +7,10 @@
|
|||
# Currently verifies:
|
||||
# 1. wmaker was built with Wayland backend support (symbol present)
|
||||
# 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)
|
||||
#
|
||||
# This test grows as wl_backend vtable slots are implemented.
|
||||
# When the compositor can open a headless display, tests for window
|
||||
# management, EWMH properties, etc. will be added here in the same
|
||||
# style as tests/x11-smoke-test.sh.
|
||||
#
|
||||
# Exit 0 = PASS, non-zero = FAIL.
|
||||
|
||||
|
|
@ -21,6 +20,13 @@ set -e
|
|||
WMAKER=src/.libs/wmaker
|
||||
[ -x "$WMAKER" ] || WMAKER=src/wmaker
|
||||
|
||||
WM_PID=""
|
||||
|
||||
cleanup() {
|
||||
[ -n "$WM_PID" ] && kill "$WM_PID" 2>/dev/null || true
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
die() {
|
||||
echo "FAIL: $*" >&2
|
||||
exit 1
|
||||
|
|
@ -36,6 +42,44 @@ nm "$WMAKER" 2>/dev/null | grep -q 'wl_backend' \
|
|||
ldd "$WMAKER" 2>/dev/null | grep -qi 'wlroots\|libwlr' \
|
||||
|| die "wmaker does not link against wlroots"
|
||||
|
||||
# -- Done (headless compositor tests to be added as vtable is implemented) --
|
||||
echo "PASS (wl_backend symbol + wlroots link verified; full headless tests pending)"
|
||||
# -- 3. Start wmaker with Wayland backend; verify it reaches event loop ---
|
||||
#
|
||||
# We use --no-dock --no-clip --no-autolaunch --static to avoid config
|
||||
# file writes and autolaunch; --dont-restore skips session state reads.
|
||||
# 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.
|
||||
|
||||
WMAKER_USE_WAYLAND=1 src/wmaker \
|
||||
--no-dock --no-clip --no-autolaunch \
|
||||
--dont-restore --static \
|
||||
--for-real &
|
||||
WM_PID=$!
|
||||
|
||||
# Wait up to 5s for wmaker to reach the event loop (stay alive)
|
||||
i=0
|
||||
while [ $i -lt 50 ]; do
|
||||
kill -0 "$WM_PID" 2>/dev/null || break
|
||||
sleep 0.1
|
||||
i=$(( i + 1 ))
|
||||
done
|
||||
|
||||
kill -0 "$WM_PID" 2>/dev/null \
|
||||
|| die "wmaker exited prematurely before SIGTERM (crashed during startup)"
|
||||
|
||||
# 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=$?
|
||||
WM_PID=""
|
||||
|
||||
# SIGTERM causes exit(0) via the Shutdown(WSExitMode) path;
|
||||
# shell reports signal-terminated processes as 128+signal, but
|
||||
# wmaker catches SIGTERM and calls Exit(0) so we expect 0.
|
||||
[ "$WM_EXIT" -eq 0 ] \
|
||||
|| die "wmaker exited with status $WM_EXIT after SIGTERM (expected 0)"
|
||||
|
||||
# -- Done -----------------------------------------------------------------
|
||||
echo "PASS (wl_backend compositor starts and exits cleanly)"
|
||||
exit 0
|
||||
|
|
|
|||
Loading…
Reference in New Issue