Windows, X11 and Wayland
One windowing implementation: SDL3
A platform implementation owns windows, events, input, timing and host services (clipboard, dialogs, displays, controllers). CNA selects exactly one at configure time with CNA_PLATFORM, and offers three: SDL3, the windowing implementation and the default everywhere, and the windowless HEADLESS and TERMINAL hosts. All of them implement the same CNA::Platform::IPlatform contract and report what they can do through 32 capability flags.
Windows, X11 and Wayland are reached through SDL3. SDL chooses its video driver at run time from the drivers it was built with and what the session offers; SDL_VIDEODRIVER=windows, x11 or wayland selects one explicitly. Window-system specifics — the X selections, Wayland’s output scale, Windows’ per-monitor DPI — are SDL’s to implement, and CNA consumes them through the same IPlatform services on every host.
| Host | SDL video driver | NativeWindowSystem and handles | Read directly by |
|---|---|---|---|
| Windows | windows | Win32: HWND | DIRECTX9, DIRECTX11, WEBGPU |
| Linux, X11 session (or Xwayland) | x11 | X11: Display* + Window XID | WEBGPU |
| Linux, Wayland session | wayland | Wayland: wl_display* + wl_surface* | WEBGPU |
| macOS / iOS | cocoa / uikit | Cocoa: NSWindow*; UIKit: UIWindow* | METAL, WEBGPU |
| Android | android | Android: ANativeWindow* | WEBGPU |
The handle arrives through IPlatformWindow::GetNativeHandle() as a NativeWindowHandle; TryGetWin32, TryGetX11 and TryGetWayland return a validated view only when the window really is that system, so a renderer that expected an HWND refuses an X11 window by name instead of dereferencing it. An X11 Window is an integer XID and stays in windowId, never in a pointer field. The experimental C API exposes the same discriminator as CNA_NATIVE_WINDOW_SYSTEM_*.
Choosing a configuration
| If you want… | Use | Why |
|---|---|---|
| A windowed game on Windows, an X11 or Wayland desktop, macOS, iOS, Android or the web | SDL3 (default) | It is the one windowing implementation; the full capability set (gamepads and sensors, IME, drag-and-drop, tray, camera) comes with it. |
| A particular Linux window system | SDL3 and SDL_VIDEODRIVER=x11 or wayland | Both drivers are in the vendored SDL3 when their development packages were installed before its first configure. |
| A binary with no SDL in it | CNA_ENABLE_SDL=OFF with HEADLESS or TERMINAL | An SDL-free build has no window. See Building without SDL. |
| No window at all (servers, tests, batch rendering) | HEADLESS or TERMINAL | See Platform Support. |
Requested means required. CNA_PLATFORM accepts SDL3, HEADLESS and TERMINAL (the last only off Windows). Any other value fails configuration by name and points at SDL3; nothing is substituted silently.
X11
On an Xorg session SDL uses its x11 driver; on a Wayland desktop it may use Xwayland through the same driver when its wayland driver is unavailable or not chosen. Renderers receive the Display* and the window XID: WEBGPU creates its surface from them, VULKAN asks the platform’s Vulkan-surface service and the EasyGL renderers its GL-context service, both implemented over SDL for the same window. The vendored SDL3 gets its X11 driver only when the X11 development packages (libX11, libXext, libXrandr, libXi, libXcursor, libXfixes, libXss, libXtst and xkbcommon) are present when it is first configured; Building lists the packages.
Wayland
On a Wayland session SDL uses its wayland driver when it was built with one, and the renderer receives the wl_display* and wl_surface*. The Wayland driver exists only if the wayland-client, wayland-protocols and xkbcommon development files were installed before the vendored SDL3 was first configured; a build without them still runs on a Wayland desktop through Xwayland. Output scaling and the other compositor specifics are SDL’s.
Windows
On Windows SDL uses its windows driver, and the HWND it owns is what the Direct3D 9 and 11 renderers create their swap chains for and what WEBGPU makes its surface from. CNA does not take over main() on Windows: a game keeps an ordinary main, and cna_windows_gui_executable() builds it for the GUI subsystem. HEADLESS is the windowless alternative; TERMINAL is not offered on Windows. An SDL-free Windows build is therefore windowless, and its only audio choice is NULL.
What each renderer takes from the window
| Renderer | Takes | On |
|---|---|---|
OPENGLES3, OPENGL33 | A GL context from the platform’s IPlatformGlContext service | Any SDL3 window |
VULKAN | A surface from the platform’s IPlatformVulkanSurface service | Any SDL3 window where a Vulkan loader is available |
DIRECTX9, DIRECTX11 | The HWND | Windows |
WEBGPU | The native handle of the window system it is on | Windows, X11, Wayland, macOS, Android; the browser canvas on the web |
METAL | The Cocoa or UIKit window, for its CAMetalLayer-backed view | macOS, iOS |
SDL_RENDERER, SDL_GPU, FNA3D | The SDL window itself | Any SDL3 window |
SOFTWARE, HEADLESS, STUB | Nothing (off-screen; SOFTWARE is displayed by TERMINAL) | Any platform |
A GPU renderer on HEADLESS or TERMINAL has no native window to draw into and is refused where that is known, rather than failing on a null handle later.
Capabilities
The SDL3 platform reports one capability set whichever video driver it runs on; the full 32-flag table, with Headless and Terminal beside it, is on Platform Support. Whether a particular service works in a particular session (a global pointer position on Wayland, a system tray on a desktop without one) is decided by SDL and the window system at run time, and a service that is not available fails at the call rather than pretending to succeed.
Building without SDL
CNA_ENABLE_SDL takes AUTO (the default), ON or OFF. With OFF SDL is not downloaded, built, found or linked, and any selection that genuinely needs it is refused at configure time with the reason named: CNA_PLATFORM=SDL3, CNA_AUDIO_PLATFORM=SDL3, and the renderers SDL_RENDERER, SDL_GPU and FNA3D. Because SDL3 is the only windowing implementation, an SDL-free build is a windowless one — a game server, a test runner, a batch renderer or a terminal game:
# Linux: no SDL anywhere, ALSA audio through CNA's own mixer
cmake -S . -B build-sdl-free -G Ninja \
-DCNA_ENABLE_SDL=OFF \
-DCNA_PLATFORM=HEADLESS \
-DCNA_AUDIO_PLATFORM=ALSA \
-DCNA_GRAPHICS_RENDERER=HEADLESS
# A terminal game: CPU pixels presented as character cells
cmake -S . -B build-terminal -G Ninja \
-DCNA_ENABLE_SDL=OFF \
-DCNA_PLATFORM=TERMINAL \
-DCNA_AUDIO_PLATFORM=NULL \
-DCNA_GRAPHICS_RENDERER=SOFTWARE
To prove a binary contains no SDL, check ldd (no libSDL3) and the build tree (no SDL target was configured); the platform workflow’s SDL-free job does both. Tutorial 138 walks through it.
Build recipes
# Linux desktop: SDL3 with both of its Linux video drivers. Install the X11 and Wayland
# development packages (see Building) BEFORE the first configure, so the vendored SDL3 gets them.
cmake -S . -B build -G Ninja -DCNA_GRAPHICS_RENDERER=VULKAN
cmake --build build --parallel
SDL_VIDEODRIVER=wayland ./build/cna_house3d_demo # or x11
# Windows, natively with MSVC (SDL3 is the platform; the HWND reaches DIRECTX11)
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=DIRECTX11
cmake --build build --config Release
Cross-compiling for Windows from Linux uses the mingw-w64 toolchain file (-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/mingw-w64.cmake), with the same SDL3 platform; Wine (with DXVK for Direct3D) runs the result. Building has the sibling checkouts, presets and packages.
Validation
SDL3 on a real window system is tested on servers private to the run, never on the developer’s own desktop. CnaPlatformSdl3X11Tests runs the SDL3 window suite and the window conformance suite on a private Xvfb (tools/platform/x11_test_server.sh) with SDL_VIDEODRIVER=x11, and CnaPlatformSdl3WaylandTests does the same on a private headless Weston (tools/platform/wayland_test_server.sh) with SDL_VIDEODRIVER=wayland; Sdl3WindowTest.NativeHandleMatchesTheVideoDriver asserts the native handle each driver hands a renderer. Each skips (exit 77) where its server is not installed.
CNA’s recorded validation of this arrangement (2026-10-06, Linux x86-64 with a Radeon 780M; plans/plan_platform.md section 11c) ran CnaPlatformSdl3X11Tests (38 passed) and CnaPlatformSdl3WaylandTests (30 passed) with nothing skipped; seven renderers — OPENGLES3, OPENGL33, VULKAN, WEBGPU, SDL_GPU, FNA3D, SOFTWARE — ran the 3D house and 2D demos on both SDL drivers and exited cleanly; a mingw-w64 SDL3 cross-build ran the SDL3 window suite under Wine (30 passed, the handle reported as Win32) and the Direct3D 11 smoke presented into, and resized, an SDL3 window’s HWND under Wine + DXVK. On GitHub’s windows-latest image the manually dispatched platform workflow passed all 11 jobs, its native-MSVC sdl3-windows job among them.
What CI is configured to check
Job (platform-ci.yml) | Trigger | What it does |
|---|---|---|
Platform contract, SDL3 cells (OPENGLES3, VULKAN, SOFTWARE) | Push / PR | Builds and runs the platform suites, then CnaPlatformSdl3X11Tests and CnaPlatformSdl3WaylandTests on a private Xvfb and headless Weston; a skip fails the step, because both servers are installed. |
sdl-free (Headless + Headless + ALSA) | Push / PR | CNA_ENABLE_SDL=OFF build; checks that nothing links SDL. |
sdl-enable-matrix | Push / PR | Configure-only: AUTO, ON and OFF succeed where they should, and OFF with the SDL3 platform fails. |
sdl3-windows (native MSVC, windows-latest) | Manual | A real MSVC configure and build of CNA and the platform module suite on SDL’s windows driver. |
Every Linux run uses virtual servers with Mesa’s software drivers. The Direct3D renderers’ Windows job (d3d-windows-ci.yml) is also manual; see Graphics Renderers.
Honest limits
- No CI job runs on a real desktop compositor (GNOME, KDE) or on physical Windows hardware; the Windows jobs run only when dispatched by hand.
- Which Linux video drivers a build has is fixed when the vendored SDL3 is first configured: install the X11 and Wayland development packages first.
- An SDL-free build cannot open a window on any operating system.
- Window-system behaviour (IME, DPI, clipboard formats, cursor capture) is SDL’s implementation; CNA’s tests pin the contract it consumes, not every compositor’s behaviour.
Further reading
- Platform Support — the axes, the capability matrix, per-OS status and renderer availability.
- Building CNA — branches, siblings, submodules, presets, packages and every CMake option.
- Audio System — ALSA, CNA's own mixer and
CNA_AUDIO_DEVICE. - Tutorials: 135 Running on X11, 136 Running on Wayland, 137 ALSA audio, 138 SDL-free build, 139 Terminal platform.
- CNA's own manual at this snapshot:
platform-abstraction.md.
Deep dives on this topic
Long-form pages that explain the exact semantics, invariants and evidence behind this subject.
- The cross-platform contract: axes, composition and evidence per route — How target OS, platform implementation, renderer set, audio implementation and the XNA surface compose in CNA, what IPlatform owns, and why each platform claim is an evidence vector.