feat(render): fullscreen headless receiver with letterboxing

A headless receiver has no window manager, so a windowed window is
meaningless there — the screencast should simply fill the screen. The
renderer now goes borderless fullscreen automatically when SDL runs
the KMSDRM backend, and --fullscreen forces the same behavior in
desktop sessions. The video keeps its aspect ratio via
SDL_SetRenderLogicalPresentation(LETTERBOX): a 4:3 desktop on a 16:9
TV renders with black bars instead of a stretched picture, the cursor
is hidden, and the clear-before-draw keeps the bars black.

Verified live on a 3440x1440 monitor: the --fullscreen receiver window
covered the entire display while a 4:3 synthetic feed rendered with
pure-black pillarbox bars (Y=0/SAT=0) and a saturated-red center
(V=254). meson test 5/5 in both build configurations.
This commit is contained in:
2026-09-07 13:18:08 +02:00
parent 47905004eb
commit 7c04506dde
8 changed files with 50 additions and 6 deletions
+11 -3
View File
@@ -34,9 +34,17 @@ loopback; current phase is Phase 7.
- **systemd autostart**: `systemd/screencast-receiver.service` is a
template (`__SC_RECEIVER_BIN__`/`__SC_RECEIVER_USER__`); the install
script substitutes and enables it (opt out: `SC_RECEIVER_SERVICE=0`),
adding the run user to video/render/input for headless KMSDRM.
Lifecycle-validated with a user unit on this machine: SIGTERM stop →
clean exit (success), mDNS withdrawn, no restart.
adding the run user to video/render/input for headless KMSDRM. The unit
gets a controlling tty (StandardInput=tty, tty1) because SDL's KMSDRM
backend needs one for VT handling.
- **Fullscreen headless rendering**: under the KMSDRM video driver (no
window manager) the renderer goes fullscreen automatically; --fullscreen
forces it on desktops. Aspect is preserved via
SDL_SetRenderLogicalPresentation(LETTERBOX) (verified: 4:3 feed on a
3440x1440 monitor rendered with black pillarbox bars; --fullscreen
window covered the full monitor). The service lifecycle was validated
with a user unit: SIGTERM stop → clean exit (success), mDNS withdrawn,
no restart.
- **mDNS operational lesson (hit during validation)**: `kill -9` on a
process holding an avahi registration leaves stale daemon records; the
service then browses but never resolves (timeout for every peer) until