Tutorial 136: Running on Wayland through SDL3

CNA Tutorials  ·  CNA snapshot c1c316b9

ℹ

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 wayland driver because the Wayland packages were missing when it was first configured; install them and reconfigure SDL from scratch. SDL_VIDEODRIVER=wayland then makes the choice explicit.
  • No compositor. WAYLAND_DISPLAY must name a running compositor socket; a headless weston --backend=headless is enough for the demos.
  • A GPU renderer refuses to start. Check the renderer’s own requirement (a Vulkan loader for VULKAN, the easy-gl and meta-gl siblings 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