Expand description
Choosing a backend at runtime.
§The decision: runtime selection, one registry, Box<dyn Shell>
The windowing rules in docs/notes/backends.md require it in one sentence —
“backend selection
at runtime, not compile time, on Linux: try Wayland socket → fall back to
X11 (both compiled in; CRCBL_SHELL=x11 override)” — and that sentence has
consequences the API has to absorb now, because retrofitting them costs a
rewrite of every consumer:
Shellmust be object-safe. A genericShellwould make the backend a compile-time parameter, and “try Wayland, fall back to X11” would then have to monomorphise every consumer twice and pick between the two instantiations with anenumwrapper — which is a hand-written vtable with extra steps.crcbl-halreached the same conclusion for the same reason and states the costs; they apply here in a milder form, since the shell is called a handful of times per frame rather than per draw.- The entry point cannot be a constructor.
WaylandShell::new()puts the platform name in the caller’s source, which is the regression the whole seam exists to prevent. So the entry point isopen, and the only concrete shell type a consumer ever names isHeadlessShell— deliberately, because a test asking for determinism is asking for a specific implementation.
§What is registered today
Wayland then X11 on Linux, Win32 on Windows, AppKit on macOS, and
HeadlessShell everywhere — the last only when asked for by name. P0.6
confirmed the property this module was shaped to have and P5C confirmed it
twice more: adding a whole X11 backend touched REGISTRY and these
tests, and no other file outside src/x11/ (plus src/linux/, which is the
evdev table and libxkbcommon moving up one level to be shared rather than
copied); adding the Win32 backend touched this file, src/lib.rs and
src/win32/, and adding the AppKit one touched this file, src/lib.rs and
src/appkit/. No consumer changed, no trait method was added, and no
#[cfg] appeared anywhere above this crate.
Every variant of ShellBackend is now registered somewhere, which
changes how the “a backend this build does not have” property is tested. It
used to be asserted against whichever variant had not landed yet — X11 until
P0.6, Win32 until P5C, AppKit until this slice — and there is no fourth
candidate. The tests below now derive the absent set from the table itself
and assert the property per platform, which is what was actually meant all
along: asking for a backend this build lacks is an honest
UnknownBackend. It also cannot go stale
again — a future backend is covered the day it is added, on the platforms
that do not have it.
Headless is registered with auto: false, so open never selects it
implicitly. A game that silently ran headless because a compositor was
missing would look like a hang; failing with
ShellError::NoBackend names the actual problem.
§Why both Linux entries are dlopen-backed, and the Windows one is not
The registry is a fall-through list, so every entry has to be able to fail
at runtime. A backend linked against libwayland-client.so.0 — or
libxcb.so.1 — with DT_NEEDED cannot: the process dies in ld.so before
main, on any machine without the library, and this list never runs.
src/wayland/ffi.rs has the full argument and src/x11/ffi.rs sharpens it,
since X11 is the fallback and is reached only after Wayland has already
failed.
The Win32 and AppKit entries link their libraries instead, and the
difference is this list’s shape rather than a change of mind: each of those
platforms has one backend, so nothing falls through to anything, and neither
user32.dll nor AppKit can be missing from a system that is running a
process at all. src/win32/ffi.rs states it in full and src/appkit/ffi.rs
repeats it for the frameworks.
Enums§
- Shell
Backend - Which implementation is behind the
Shelltrait.
Constants§
- BACKEND_
ENV_ VAR - The environment variable that overrides automatic backend selection.
Functions§
- open
- Opens the best available shell for this process.
- open_
backend - Opens a specific backend, ignoring
BACKEND_ENV_VAR.