Tutorial 136: Running on Wayland through SDL3
What you’ll learn: how CNA reaches Wayland through SDL3’s wayland video driver, which packages the vendored SDL3 needs to get that driver, how to select it, how to confirm which window system a running game is on, and what CNA’s own tests prove about it.
Before you start — Tutorial 02: Setting Up Your Dev Environment for the workspace layout (CNA, sharp-runtime, easy-gl and meta-gl) and Tutorial 03: Your First CNA Window for the small game used in step 5. You need a Linux machine with a Wayland compositor (GNOME, KDE Plasma, Sway, Weston). Without one you can follow along with a nested or headless weston.
On a Wayland session a CNA game opens its window through SDL3’s wayland video driver, talking to the compositor directly rather than through Xwayland. SDL3 is CNA’s one windowing implementation, so there is nothing Wayland-specific to select in CNA itself: you make sure the vendored SDL3 is built with its Wayland driver, choose it, and let the renderer take the wl_display* and wl_surface* it needs from the SDL3 window.
What you are building
CNA’s own 3D house demo and 2D demo, built once with the default SDL3 platform and run on SDL’s wayland driver, then a few lines in your own game that report which window system it is on.
1. Install the packages before the first configure
The vendored SDL3 is configured by CNA’s own build and gets exactly the video drivers whose development files it finds at that moment. Install them first (the Debian and Ubuntu names CNA’s own platform workflow installs; Building has the full list for both Linux drivers and the GL/Vulkan/audio packages):
sudo apt install libwayland-dev wayland-protocols libxkbcommon-dev
Installing them later does not add the driver to an SDL that was already configured: start a fresh build directory.
2. Configure and build
cmake -S cna -B cna/build -G Ninja -DCNA_GRAPHICS_RENDERER=VULKAN \
-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
cmake --build cna/build --parallel
CNA_PLATFORM is left at its default, SDL3. Any renderer that runs in an SDL3 window works here; VULKAN and OPENGL33 are good 3D choices on a Linux desktop, and the default OPENGLES3 needs nothing extra.
3. Run on the wayland driver
SDL_VIDEODRIVER=wayland ./cna/build/cna_house3d_demo
SDL_VIDEODRIVER=wayland ./cna/build/cna_demo_2d
SDL picks a driver by itself when SDL_VIDEODRIVER is unset; setting it makes the choice explicit, and SDL reports an error if the driver is not in this build. SDL_VIDEODRIVER=x11 runs the same binary on the other Linux window system when its driver is present.
4. Your own game needs no change
A game written against the XNA API does not name the window system. The same executable runs on X11 and Wayland (and the same source on Windows and macOS) because the window comes from SDL3 and the renderer asks the platform for what it needs: EasyGL for a GL context, VULKAN for a surface, WEBGPU for the native handle.
5. Ask which window system you are on
GameWindow::GetNativeWindowHandleEXT() (a CNAEXT extension, so hidden in a CNA_STRICT_XNA_API build) returns the NativeWindowHandle the renderer received. Add this to LoadContent() of the game from Tutorial 03:
#include <iostream>
#include "CNA/Platform/NativeWindowHandle.hpp"
#include "CNA/Platform/NativeWindowSystem.hpp"
void MyGame::LoadContent()
{
const CNA::Platform::NativeWindowHandle handle =
getWindowProperty().GetNativeWindowHandleEXT();
std::cout << "window system: " << CNA::Platform::ToString(handle.system) << '\n';
CNA::Platform::WaylandNativeWindow wl{};
if (CNA::Platform::TryGetWayland(handle, wl)) {
std::cout << "wl_display* " << wl.display << ", wl_surface* " << wl.surface << '\n';
}
}
The TryGet… functions return true only when the window really is that system, so code written for one window system refuses another by name instead of misreading its pointers.
Troubleshooting
- The game opens through Xwayland instead. The vendored SDL3 has no
waylanddriver because the Wayland packages were missing when it was first configured; install them and reconfigure SDL from scratch.SDL_VIDEODRIVER=waylandthen makes the choice explicit. - No compositor.
WAYLAND_DISPLAYmust name a running compositor socket; a headlessweston --backend=headlessis enough for the demos. - A GPU renderer refuses to start. Check the renderer’s own requirement (a Vulkan loader for
VULKAN, theeasy-glandmeta-glsiblings for the GL renderers); the window system is rarely the cause.
What CNA’s tests cover — and what they do not
CnaPlatformSdl3WaylandTests runs the SDL3 window suite and the window conformance suite on a private headless Weston (tools/platform/wayland_test_server.sh) with SDL_VIDEODRIVER=wayland, and Sdl3WindowTest.NativeHandleMatchesTheVideoDriver asserts the handle a renderer receives. The platform workflow runs it on every push to CNA’s integration branches, and fails the step if it skips. CNA’s recorded local validation (2026-10-06) passed 30 of its tests with none skipped, and ran seven renderers’ 3D house and 2D demos on the wayland driver. Neither is a run on a real desktop session with a physical GPU; treat those as your own test.
Where to go next
- Tutorial 135: Running on X11 through SDL3 — the other Linux window system.
- Tutorial 137: Native Linux Audio with ALSA — sound through CNA’s own mixer, with or without SDL.
- Tutorial 138: Building Without SDL — windowless builds and the rules behind
CNA_ENABLE_SDL. - Windows, X11 and Wayland and Platform Support — native handles, the capability matrix and CI evidence.