The mid-stream session watcher (rebuild the backend in place when the box
flips Gaming↔Desktop) was opt-in via PUNKTFUNK_SESSION_WATCH, so it never
ran on a stock Bazzite/SteamOS box — switching modes froze the stream on the
now-dead compositor. Default it ON when os-release ID/ID_LIKE is
bazzite/steamos (the platforms that flip sessions); still off on plain
desktops. Also parse the env properly so PUNKTFUNK_SESSION_WATCH=0 actually
disables it (was: any value, including "0", enabled it).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two fixes from live Bazzite testing of the managed-Gaming + mid-stream work:
1. Input now FOLLOWS the active session. The host-lifetime injector was pinned to
the first backend it opened and only reopened on an inject FAILURE — but with
Feature A keeping the managed gamescope warm, its EIS socket stays alive, so a
switch to the KDE desktop + reconnect kept injecting into the idle gamescope
(input silently dead on KDE). injector_service_thread now compares the
resolved input backend (default_backend() ← PUNKTFUNK_INPUT_BACKEND, set per
connect by apply_input_env, and on a mid-stream switch) each event and reopens
when it changes. Fixes input on a Gaming->Desktop reconnect AND Feature B's
mid-stream input re-route, with no plumbing.
2. Debounced TV-restore no longer yanks you back to gaming. do_restore_tv_session
now checks detect_active_session(): if a desktop session is active (the user
switched), it tears down the idle managed gamescope but does NOT restart the
gaming autologin. Observed live: the restore fired and restarted
gamescope-session-plus@ogui-steam while the client was already on the KDE
desktop.
Also: document PUNKTFUNK_SESSION_WATCH (Feature B opt-in) in the Bazzite host.env
and correct the managed-default description. Compiles, clippy/fmt clean, 78 tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Feature A: in Gaming Mode, default to a host-managed gamescope at the CLIENT's
mode (tear the TV's autologin down on connect) instead of attaching to the
running TV session — so the client receives ITS resolution (capture == encode ==
client mode, fixing the InitializeEncoder size mismatch the attach path hit),
not the TV's 4K.
Reliability is the debounce: restore_managed_session() now SCHEDULES the TV
restore RESTORE_DEBOUNCE (5s) after the last disconnect via a host-lifetime
worker, instead of restoring immediately per-disconnect. A reconnect inside the
window cancels the pending restore and reuses the still-warm managed session
(create_managed_session clears PENDING_RESTORE at the top) — so a quick reconnect
(e.g. a controller hiccup) never triggers a gamescope stop/relaunch, which is the
per-connect churn that leaked NVIDIA GPU context on F44 (the black-screen
reconnect).
- vdisplay/gamescope.rs: PENDING_RESTORE + RESTORE_DEBOUNCE; schedule_restore_tv_session
(debounced), do_restore_tv_session (the actual restore, worker-driven),
start_restore_worker (100ms tick, RAII keepalive handle). create_managed_session
cancels the pending restore + reuse path unchanged.
- vdisplay.rs: apply_input_env flips gamescope to managed-DEFAULT; PUNKTFUNK_GAMESCOPE_ATTACH
(or an explicit _NODE) opts back to attach for couch-on-TV; _MANAGED forces managed.
restore_managed_session schedules; new start_restore_worker wrapper.
- m3.rs serve(): hold the restore worker for the host lifetime.
- bazzite host.env: document managed-default + the ATTACH opt-out.
Compiles, clippy-clean, 78 host tests pass. F44 single stop/start leak to be
verified live on the box.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bazzite/SteamOS boxes flip between Steam Gaming Mode (gamescope) and a
KDE/GNOME desktop. The host statically read PUNKTFUNK_COMPOSITOR /
XDG_CURRENT_DESKTOP once, so switching to Desktop Mode failed the stream, and
the gamescope managed-session path stopped+relaunched the autologin per connect
— leaking GPU context on F44 (reconnect → black screen).
Replace the static read with a runtime probe of the live session and route each
connect to the right backend, churn-free:
- vdisplay::detect_active_session() probes /proc for the running compositor of
our uid (gamescope|kwin_wayland|gnome-shell|sway, desktop outranks a leftover
gamescope) + scans the runtime dir for the live wayland-* socket. Returns an
ActiveKind + the SessionEnv (WAYLAND_DISPLAY/XDG_RUNTIME_DIR/DBUS/
XDG_CURRENT_DESKTOP) that targets it.
- apply_session_env() writes that into the process env per connect (host serves
one session at a time), so every backend (capture + input) opens against the
live session; apply_input_env() points input at the matching backend and
selects gamescope ATTACH (no managed restart) unless PUNKTFUNK_GAMESCOPE_MANAGED.
- resolve_compositor() (native path) auto-detects + applies; explicit
PUNKTFUNK_COMPOSITOR still wins (legacy/CI/forcing). detect() is now
active-aware for the GameStream/mgmt callers too.
- Bazzite host.env drops the static gamescope force; documents auto-detection
+ the optional overrides.
Result: Desktop Mode → KWin/Mutter virtual output at the client's mode
(churn-free, the reliable path); Gaming Mode → attach to the running gamescope
(no SIGSEGV/GPU leak on reconnect). Compiles + clippy-clean; 78 host tests pass.
Live validation on the Bazzite box pending (box offline).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Roadmap #3 (install on other devices). Bazzite already ships gamescope + PipeWire + the
NVIDIA stack, so the host slots in with minimal new deps (ffmpeg-libs from RPM Fusion + opus
+ libei).
- packaging/rpm/punktfunk.spec — builds punktfunk-host from source (cargo), installs the
binary + udev rule + systemd user unit + headless helpers; Requires/Recommends mapped from
the Ubuntu bootstrap deps to Fedora.
- packaging/bootc/Containerfile — layer punktfunk into a bazzite-nvidia bootc image for
atomic, image-based installs.
- packaging/bazzite/host.env — gamescope-default appliance config (spawned per session).
- packaging/copr/ + packaging/README.md — COPR build-from-SCM settings + install docs
(rpm-ostree and bootc paths), and why not Flatpak.
- LICENSE-MIT + LICENSE-APACHE — materialize the declared `MIT OR Apache-2.0` (was unfiled);
the RPM ships them.
Not buildable on the Ubuntu dev box (no rpm tooling) — the COPR/Fedora build is operator-run;
all spec-referenced files verified present and the cargo build is green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>