6.4 KiB
DRM/KMS Direct Mode — Deferred to Post-Launch
Status: Deferred (too much risk before launch) Created: 2026-08-17
Goal
Remove Cage (Wayland compositor) from -dev kiosk variants and run RadiantWave
directly on DRM/KMS via SDL3's built-in SDL_VIDEODRIVER=drm backend. This
simplifies the stack, removes compositor overhead, and is better suited for
embedded/hardware kiosks.
Decision: Deferring until after the current launch. Cage works, and the transition touches every variant's boot pipeline.
What Cage Currently Provides
| Feature | How Cage Does It | DRM/KMS Replacement |
|---|---|---|
| Display mode setting | Opens /dev/dri/card* via libseat, selects connector/CRTC/mode |
SDL3 DRM backend opens DRM devices directly |
| Fullscreen surface | Creates Wayland surface, manages lifecycle | SDL3 creates DRM framebuffer surface |
| Page flipping / VSync | Handles drmHandlePageFlip() |
SDL3 DRM backend handles page flip |
| Input devices | Opens /dev/input/* via libseat, generates Wayland events |
SDL3 DRM backend reads evdev directly |
| Keyboard mapping | XKB keymap loading, key repeat | SDL3 handles keymap translation |
| Seat management | Connects to seatd via libseat | Not needed — group permissions (video, input) suffice |
What Stays Safe Without Cage
| Concern | Why It's Safe |
|---|---|
| Audio | PipeWire/ALSA works independently of the compositor. App uses SDL3_mixer -> ALSA. Audio init service still runs before launch. |
| WiFi | NetworkManager uses system bus (sd_bus_open_system() in network_manager.cpp). No session bus needed. networkmanager group handles D-Bus access. |
| Keyboard/input | SDL3's DRM backend reads /dev/input/* evdev directly. input group grants access. |
| DRM/display | SDL3's DRM backend opens /dev/dri/card* directly. video group grants access. |
| Plymouth | sudo -n plymouth quit --wait still works — Plymouth runs as root on DRM/KMS. |
| Auto-login | getty autologin still works on tty1. |
| Health check | pgrep -f '.radiantwave-unwrapped' still works — no wrapper changes. |
| Backup service | Runs as kiosk user, rsyncs ~/.local/radiantwave. No compositor dependency. |
Implementation Plan — 4 Phases
All changes are in kioskconfig only. The radiantwave app needs no code
changes — SDL3 handles DRM/KMS internally when SDL_VIDEODRIVER=drm is set.
The NixOS system already provides Mesa/EGL/GBM via hardware.graphics.enable = true.
Only -dev variants are modified. Production configs stay on Cage.
New module: kiosk-drm.nix
A NixOS module that overrides the Wayland/cage setup:
environment.systemPackages = mkForce [...]— same list asconfiguration.nixbut withoutpkgs.cageenvironment.loginShellInit = mkForce ''...— same script but:- Sets
SDL_VIDEODRIVER=drminstead ofwayland - Removes
WLR_RENDERER,WLR_NO_HARDWARE_CURSORS,QT_QPA_PLATFORM,XDG_SESSION_TYPE,GODEBUG - Launches
exec /run/current-system/sw/bin/radiantwavedirectly (nodbus-run-session cage --) - Keeps: network wait, audio init, Plymouth quit
- Sets
services.seatd.enable = mkForce false— nothing uses libseat anymore
Phase 1: radiantwavekiosk-dev (Intel NUC)
Files:
- Create
kiosk-drm.nix - Modify
flake.nix: add./kiosk-drm.nixtoextraModulesforradiantwavekiosk-dev dev-iso.nixalready targetsradiantwavekiosk-dev— no change needed
Verify: nix build .#nixosConfigurations.radiantwavekiosk-dev.config.system.build.toplevel
Phase 2: radiantwavekiosk-commercial-dev (commercial HW)
Files:
- Modify
flake.nix: add./kiosk-drm.nixtoextraModulesforradiantwavekiosk-commercial-dev - Create
commercial-dev-iso.nix(same pattern asdev-iso.nix, targetingradiantwavekiosk-commercial-dev) - Add
commercial-dev-isoconfig toflake.nix
Verify: nix build .#nixosConfigurations.radiantwavekiosk-commercial-dev.config.system.build.toplevel
Phase 3: radiantwavekiosk-android-dev (rotated display)
Files:
- Create
radiantwavekiosk-android-devinflake.nix(currently only productionradiantwavekiosk-androidexists) - Create
android-kiosk-drm.nix— DRM/KMS mode + display rotation
Challenge: rotated_display.nix uses wlr-randr (Wayland tool) for
90-degree rotation. Without Cage/Wayland, this won't work. Alternatives:
- Option A: Use
modetestfromlibdrmto set DRM rotation property - Option B: Handle rotation in the app (query display orientation, render rotated)
- Option C: Kernel DRM rotation via modprobe params or udev rule
This is the hardest phase — needs investigation.
Verify: nix build .#nixosConfigurations.radiantwavekiosk-android-dev.config.system.build.toplevel
Phase 4: radiantwavekiosk-rpi-dev (RPi 5)
Files:
- Modify
flake.nix: add./kiosk-drm.nixtoextraModulesforradiantwavekiosk-rpi-dev - Modify
build-rpi.sh: update to buildradiantwavekiosk-rpi-devinstead ofradiantwavekiosk-rpi
RPi 5 uses VC4/V3D GPU driver — hardware.graphics.enable = true +
nixos-raspberrypi.nixosModules.raspberry-pi-5.display-vc4 already handles this.
Verify: ./build-rpi.sh (builds SD card image)
Risks and Unknowns
-
SDL3 DRM backend maturity — Less commonly used than the Wayland backend. Needs testing on NUC (Intel i915) and RPi 5 (VC4/V3D) hardware.
-
Android rotation —
wlr-randrwon't work without Wayland. Needs an alternative rotation strategy. -
Input device permissions — SDL3's DRM backend reads
/dev/input/*directly. Need to verifyinputgroup is sufficient and udev rules don't block access. -
Multi-GPU/multi-output — DRM/KMS needs to select the right connector. SDL3 handles this, but edge cases exist (e.g., multiple monitors).
-
Plymouth -> app handoff — Plymouth runs on DRM/KMS too. The handoff should work, but timing might differ without a Wayland compositor in between.
radiantwave Repo — No Changes Needed
The app doesn't directly use DRM/KMS APIs. SDL3 handles the DRM backend
internally when SDL_VIDEODRIVER=drm is set. The Nix package doesn't need
changes — SDL3 dynamically loads DRM libraries at runtime.
The only change is the environment variable in the kiosk startup script
(SDL_VIDEODRIVER=drm vs wayland), which lives in kioskconfig.