Files
KioskConfig/PLAN-drm-kms.md
T
NeilClack 06c2a7cd0a
build-prod.yml / update-and-build (push) Canceled after 0s
Add plan to migrate to DRM
2026-08-17 10:44:36 -05:00

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 as configuration.nix but without pkgs.cage
  • environment.loginShellInit = mkForce ''... — same script but:
    • Sets SDL_VIDEODRIVER=drm instead of wayland
    • Removes WLR_RENDERER, WLR_NO_HARDWARE_CURSORS, QT_QPA_PLATFORM, XDG_SESSION_TYPE, GODEBUG
    • Launches exec /run/current-system/sw/bin/radiantwave directly (no dbus-run-session cage --)
    • Keeps: network wait, audio init, Plymouth quit
  • 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.nix to extraModules for radiantwavekiosk-dev
  • dev-iso.nix already targets radiantwavekiosk-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.nix to extraModules for radiantwavekiosk-commercial-dev
  • Create commercial-dev-iso.nix (same pattern as dev-iso.nix, targeting radiantwavekiosk-commercial-dev)
  • Add commercial-dev-iso config to flake.nix

Verify: nix build .#nixosConfigurations.radiantwavekiosk-commercial-dev.config.system.build.toplevel


Phase 3: radiantwavekiosk-android-dev (rotated display)

Files:

  • Create radiantwavekiosk-android-dev in flake.nix (currently only production radiantwavekiosk-android exists)
  • 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 modetest from libdrm to 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.nix to extraModules for radiantwavekiosk-rpi-dev
  • Modify build-rpi.sh: update to build radiantwavekiosk-rpi-dev instead of radiantwavekiosk-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

  1. SDL3 DRM backend maturity — Less commonly used than the Wayland backend. Needs testing on NUC (Intel i915) and RPi 5 (VC4/V3D) hardware.

  2. Android rotation — wlr-randr won't work without Wayland. Needs an alternative rotation strategy.

  3. Input device permissions — SDL3's DRM backend reads /dev/input/* directly. Need to verify input group is sufficient and udev rules don't block access.

  4. Multi-GPU/multi-output — DRM/KMS needs to select the right connector. SDL3 handles this, but edge cases exist (e.g., multiple monitors).

  5. 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.