diff --git a/WINGs/WINGs/WINGsP.h.in b/WINGs/WINGs/WINGsP.h.in index e1a59ef..e6236d6 100644 --- a/WINGs/WINGs/WINGsP.h.in +++ b/WINGs/WINGs/WINGsP.h.in @@ -746,6 +746,46 @@ void W_InitIM(WMScreen *scr); WMScreen *W_X11OpenDisplay(const char *display); void W_X11PlatformInit(W_Screen *scrPtr); +/* + * Platform backend vtable (phase ζ). + * + * Each platform-open backend (X11 in widgets_x11.c, Wayland-client in + * wclient_wl.c) provides one instance of W_PlatformBackend. WMOpenScreen + * walks an ordered list of backends, asking each .detect() whether it + * wants to handle the requested display, and calls .open() on the first + * that does. + * + * Backends are referenced via weak symbols from widgets.c so excluded + * backends (e.g. --disable-x11-backend) link cleanly with the symbol + * resolving to NULL. + */ +typedef struct W_PlatformBackend { + const char *name; /* "x11" / "wayland-client" */ + + /* Return 1 if this backend should handle the given display string + * (and current env state). Return 0 otherwise. WMOpenScreen tries + * each backend in order; the first to return 1 wins. */ + int (*detect)(const char *display); + + /* Open the platform-native display and return a populated WMScreen. + * Returns NULL on failure (e.g. compositor refused the connection). + * The caller may then try the next backend in the list. */ + WMScreen *(*open)(const char *display); +} W_PlatformBackend; + +/* + * WMScreenKind — selects which widget backends WMCreateScreenWithRContext + * installs on the next screen creation. Set by the platform backend + * before calling WMCreateScreenWithRContext. This replaces the prior + * WMAKER_USE_WAYLAND env-var dispatch. + */ +typedef enum { + WM_SCREEN_KIND_X11 = 0, + WM_SCREEN_KIND_WAYLAND, +} WMScreenKind; + +void W_SetNextScreenKind(WMScreenKind kind); + /* * W_NativeInternAtom — wraps XInternAtom for the X11 backend so that * shared code (e.g. wtextfield.c selection request handler) can intern diff --git a/WINGs/wclient_wl.c b/WINGs/wclient_wl.c index 1793930..e3a74f4 100644 --- a/WINGs/wclient_wl.c +++ b/WINGs/wclient_wl.c @@ -1020,6 +1020,66 @@ W_WLClientShutdown(struct W_Screen *scrPtr) wfree(ctx); } +/* ------------------------------------------------------------------ */ +/* W_PlatformBackend instance — registered in widgets.c via weak ref. */ +/* ------------------------------------------------------------------ */ + +/* + * wlclient_open — adapter that matches the W_PlatformBackend.open + * signature. Creates a Wayland-mode WMScreen and runs W_WLClientInit + * on it. Returns NULL on failure so WMOpenScreen can try the next + * backend. + */ +static WMScreen *wlclient_open(const char *display) +{ + /* Tell WMCreateScreenWithRContext to install the Wayland WINGs + * backends (font/event/render/view/color all -> _WL variants). */ + W_SetNextScreenKind(WM_SCREEN_KIND_WAYLAND); + WMScreen *scr = WMCreateScreenWithRContext(NULL, 0, NULL); + if (!scr) return NULL; + if (!W_WLClientInit(scr, display)) { + /* TODO: WMCloseScreen(scr) — currently leaks the failed screen. */ + return NULL; + } + scr->aflags.hasAppIcon = 0; + scr->aflags.simpleApplication = 1; + return scr; +} + +/* + * wlclient_detect — returns 1 when this backend should handle the + * requested display. The rules favour explicit configuration first, + * then fall back to standard Wayland-vs-X autodetection (matching + * GTK / Qt behaviour). + */ +static int wlclient_detect(const char *display) +{ + /* Explicit "wayland" or "wayland:NAME". */ + if (display && strncmp(display, "wayland", 7) == 0) + return 1; + + /* Explicit X display string overrides everything else. */ + if (display && display[0]) + return 0; + + /* WMAKER_USE_WAYLAND=1 env override. */ + const char *force = getenv("WMAKER_USE_WAYLAND"); + if (force && force[0] && force[0] != '0') + return 1; + + /* Standard autodetect: WAYLAND_DISPLAY set, DISPLAY unset. */ + if (getenv("WAYLAND_DISPLAY") && !getenv("DISPLAY")) + return 1; + + return 0; +} + +const W_PlatformBackend W_PlatformBackendWLClient = { + .name = "wayland-client", + .detect = wlclient_detect, + .open = wlclient_open, +}; + #else /* !USE_WAYLAND_CLIENT */ #include "WINGsP.h" diff --git a/WINGs/widgets.c b/WINGs/widgets.c index be2b7aa..dd8c565 100644 --- a/WINGs/widgets.c +++ b/WINGs/widgets.c @@ -518,68 +518,52 @@ static Bool loadPixmaps(WMScreen * scr) } /* - * Decide whether to attempt a native Wayland connection. + * Platform backends (phase ζ). Each is a W_PlatformBackend instance + * defined in its own backend file: * - * The rules favour explicit configuration but fall back to the standard - * Wayland-vs-X autodetection used by toolkits like GTK/Qt: + * W_PlatformBackendX11 — widgets_x11.c + * W_PlatformBackendWLClient — wclient_wl.c (only when --enable-wayland-client) * - * 1. If the application passed a display string starting with - * "wayland", use that (strip the prefix; the rest is the - * WAYLAND_DISPLAY socket name). - * 2. If WMAKER_USE_WAYLAND=1 is set, force Wayland. - * 3. Otherwise, if WAYLAND_DISPLAY is set and DISPLAY is not, use - * Wayland. - * 4. Otherwise, X11. + * Referenced as weak symbols here so a build that excludes a backend + * (e.g. --disable-x11-backend) links cleanly with the symbol resolving + * to NULL. WMOpenScreen iterates the registry below. Order matters: + * the first .detect() to return non-zero wins, so the more specific + * backend (Wayland-client) is listed before the X11 fallback. */ -static const char * -wings_detect_wayland_display(const char *display) -{ - if (display && strncmp(display, "wayland", 7) == 0) { - const char *rest = display + 7; - if (*rest == ':') return rest + 1; /* "wayland:NAME" */ - if (*rest == 0) return NULL; /* "wayland"; default */ - return display; /* something exotic — pass through */ - } - if (display) - return NULL; /* explicit X display */ +extern const W_PlatformBackend W_PlatformBackendWLClient __attribute__((weak)); +extern const W_PlatformBackend W_PlatformBackendX11 __attribute__((weak)); - const char *force = getenv("WMAKER_USE_WAYLAND"); - if (force && force[0] && force[0] != '0') - return getenv("WAYLAND_DISPLAY"); - - if (getenv("WAYLAND_DISPLAY") && !getenv("DISPLAY")) - return getenv("WAYLAND_DISPLAY"); - - return NULL; -} +static const W_PlatformBackend *const wm_platform_backends[] = { + &W_PlatformBackendWLClient, + &W_PlatformBackendX11, + NULL +}; WMScreen *WMOpenScreen(const char *display) { - const char *wl_disp = wings_detect_wayland_display(display); - -#ifdef USE_WAYLAND_CLIENT - if (wl_disp != NULL || (display == NULL && getenv("WAYLAND_DISPLAY"))) { - /* Tell WMCreateScreenWithRContext to install Wayland backends. */ - setenv("WMAKER_USE_WAYLAND", "1", 0); - WMScreen *scr = WMCreateScreenWithRContext(NULL, 0, NULL); - if (!scr) { - wwarning(_("WINGs: WMCreateScreenWithRContext failed in Wayland mode")); - /* Fall through to X11. */ - } else if (W_WLClientInit(scr, wl_disp)) { - scr->aflags.hasAppIcon = 0; - scr->aflags.simpleApplication = 1; - return scr; - } else { - wwarning(_("WINGs: Wayland connection failed; falling back to X11")); - /* TODO: WMCloseScreen(scr) — currently leaks the failed screen. */ + /* First pass: ask each available backend if this display is theirs. */ + for (int i = 0; wm_platform_backends[i]; i++) { + const W_PlatformBackend *b = wm_platform_backends[i]; + if (!b || !b->detect || !b->open) + continue; /* weak-undefined: backend not linked in */ + if (b->detect(display)) { + WMScreen *scr = b->open(display); + if (scr) return scr; + wwarning(_("WINGs: %s backend failed to open display, trying next"), + b->name); } } -#else - (void)wl_disp; -#endif - /* X11 path lives in widgets_x11.c so this file stays Xlib-free. */ - return W_X11OpenDisplay(display); + /* Second pass: nobody claimed it explicitly — try each in order. */ + for (int i = 0; wm_platform_backends[i]; i++) { + const W_PlatformBackend *b = wm_platform_backends[i]; + if (!b || !b->open) continue; + WMScreen *scr = b->open(display); + if (scr) return scr; + } + + wwarning(_("WINGs: no platform backend could open the display")); + return NULL; } WMScreen *WMCreateSimpleApplicationScreen(Display * display) @@ -599,11 +583,31 @@ WMScreen *WMCreateScreen(Display * display, int screen) return WMCreateScreenWithRContext(display, screen, RCreateContext(display, screen, NULL)); } +/* + * W_screen_next_kind — communicates which widget backends the next + * WMCreateScreenWithRContext call should install. Set by the platform + * backend (e.g. wlclient_open in wclient_wl.c) before it calls + * WMCreateScreenWithRContext. Default is X11. + * + * Replaces the previous WMAKER_USE_WAYLAND env-var dispatch — env + * detection belongs in the platform backend's .detect() function, not + * in shared code. + */ +static WMScreenKind wm_screen_next_kind = WM_SCREEN_KIND_X11; + +void W_SetNextScreenKind(WMScreenKind kind) +{ + wm_screen_next_kind = kind; +} + WMScreen *WMCreateScreenWithRContext(Display * display, int screen, RContext * context) { W_Screen *scrPtr; static int initialized = 0; - int wayland = (getenv("WMAKER_USE_WAYLAND") != NULL); + int wayland = (wm_screen_next_kind == WM_SCREEN_KIND_WAYLAND); + /* Reset for the next call so leakage between WMOpenScreen invocations + * can't accidentally pick the wrong backend set. */ + wm_screen_next_kind = WM_SCREEN_KIND_X11; if (!initialized) { initialized = 1; diff --git a/WINGs/widgets_x11.c b/WINGs/widgets_x11.c index 76f228a..d08795a 100644 --- a/WINGs/widgets_x11.c +++ b/WINGs/widgets_x11.c @@ -231,6 +231,35 @@ WMScreen *W_X11OpenDisplay(const char *display) return WMCreateSimpleApplicationScreen(dpy); } +/* + * W_PlatformBackendX11.detect — return 1 if this is an X11 connection. + * + * Rules: + * - explicit display string that does NOT start with "wayland" → X11. + * - display==NULL but DISPLAY env set → X11. + * - everything else → 0 (let the next backend try). + * + * The Wayland-client backend's detect() is consulted FIRST (registered + * earlier in W_PlatformBackends), so the typical "WAYLAND_DISPLAY set, + * DISPLAY unset" case picks Wayland. X11 is the fallback. + */ +static int x11_detect(const char *display) +{ + if (display && strncmp(display, "wayland", 7) == 0) + return 0; + if (display && display[0]) + return 1; /* explicit X display */ + if (getenv("DISPLAY")) + return 1; /* legacy: DISPLAY set */ + return 0; /* no display info; not us */ +} + +const W_PlatformBackend W_PlatformBackendX11 = { + .name = "x11", + .detect = x11_detect, + .open = W_X11OpenDisplay, +}; + /* * W_NativeInternAtom — atom-intern wrapper used by shared code that * needs an Atom value at run-time (e.g. wtextfield.c's selection diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index a946b46..56c7da1 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -3954,6 +3954,10 @@ wl_screen_init_display(WScreen *scr, int screen_number) scr->w_depth = 32; scr->rcontext = RCreateContextWayland(scr->scr_width, scr->scr_height, NULL); + /* WINGs widget backends are selected via W_SetNextScreenKind — the + * compositor uses the Wayland-mode set even though wmaker itself is + * the compositor (rendering goes through pixman → wlroots scenes). */ + W_SetNextScreenKind(WM_SCREEN_KIND_WAYLAND); scr->wmscreen = WMCreateScreenWithRContext(NULL, 0, scr->rcontext); if (!scr->wmscreen) { wfatal(_("could not initialize WINGs widget set"));