diff --git a/crates/punktfunk-host/src/m3.rs b/crates/punktfunk-host/src/m3.rs index 6875c6f..bb128bc 100644 --- a/crates/punktfunk-host/src/m3.rs +++ b/crates/punktfunk-host/src/m3.rs @@ -988,21 +988,36 @@ const INJECTOR_REOPEN_BACKOFF: std::time::Duration = std::time::Duration::from_s /// sender have dropped (host shutdown), which drops the injector and closes its portal session. fn injector_service_thread(rx: std::sync::mpsc::Receiver) { let mut injector: Option> = None; + let mut open_backend: Option = None; let mut last_failed: Option = None; for ev in rx { + // The resolved input backend (PUNKTFUNK_INPUT_BACKEND, set per connect by apply_input_env, + // also on a mid-stream session switch) may have changed since we opened. Reopen against it + // so input FOLLOWS the active session instead of injecting into a stale, still-warm backend + // (e.g. the managed gamescope's EIS socket after the user switched to the KDE desktop). + let want = crate::inject::default_backend(); + if injector.is_some() && open_backend != Some(want) { + tracing::info!( + ?open_backend, + ?want, + "input: backend changed — reopening injector for the active session" + ); + injector = None; + last_failed = None; // re-resolve immediately + } if injector.is_none() { // Open on the first event; after a failure wait out the backoff before retrying (a // few events drop during setup — acceptable, input is lossy). let ready = last_failed.is_none_or(|t| t.elapsed() >= INJECTOR_REOPEN_BACKOFF); if ready { - let backend = crate::inject::default_backend(); - match crate::inject::open(backend) { + match crate::inject::open(want) { Ok(i) => { tracing::info!( - ?backend, + backend = ?want, "punktfunk/1 input injector ready (host-lifetime)" ); injector = Some(i); + open_backend = Some(want); last_failed = None; } Err(e) => { @@ -1018,6 +1033,7 @@ fn injector_service_thread(rx: std::sync::mpsc::Receiver) { // a later event (covers a gamescope EIS socket that respawns with its session). tracing::warn!(error = %format!("{e:#}"), "inject failed — reopening injector"); injector = None; + open_backend = None; last_failed = Some(std::time::Instant::now()); } } diff --git a/crates/punktfunk-host/src/vdisplay/gamescope.rs b/crates/punktfunk-host/src/vdisplay/gamescope.rs index e61b6b2..2f877c9 100644 --- a/crates/punktfunk-host/src/vdisplay/gamescope.rs +++ b/crates/punktfunk-host/src/vdisplay/gamescope.rs @@ -266,6 +266,19 @@ fn do_restore_tv_session() { } stop_session(SESSION_UNIT); // our gamescope/Steam session, so Steam is free for the autologin *MANAGED_SESSION.lock().unwrap_or_else(|e| e.into_inner()) = None; + // Only bring the gaming autologin BACK if the box is still meant to be in gaming mode. If the + // user switched to a desktop session (KDE/GNOME/wlroots) in the meantime, don't yank them back + // to gaming — leave the desktop alone. (We still stopped our idle managed session above.) + use super::ActiveKind; + if matches!( + super::detect_active_session().kind, + ActiveKind::DesktopKde | ActiveKind::DesktopGnome | ActiveKind::DesktopWlroots + ) { + tracing::info!( + "gamescope: a desktop session is active — not restoring the TV gaming session" + ); + return; + } for unit in units { let _ = Command::new("systemctl") .args(["--user", "start", &unit]) diff --git a/packaging/bazzite/host.env b/packaging/bazzite/host.env index 9f6b70c..2fbbbd6 100644 --- a/packaging/bazzite/host.env +++ b/packaging/bazzite/host.env @@ -1,8 +1,8 @@ # punktfunk host config for Bazzite (~/.config/punktfunk/host.env). # # The compositor + input backend are AUTO-DETECTED per connect from the ACTIVE session: the host -# follows the box as you flip between Steam Gaming Mode (gamescope — attached to the running -# session, no churn) and a KDE/GNOME Desktop (KWin/Mutter virtual output at the client's mode). +# follows the box as you flip between Steam Gaming Mode (gamescope — a managed session at the +# CLIENT's resolution) and a KDE/GNOME Desktop (KWin/Mutter virtual output at the client's mode). # So nothing here forces a backend — only the trustworthy anchors stay. XDG_RUNTIME_DIR=/run/user/1000 @@ -26,3 +26,6 @@ PUNKTFUNK_ZEROCOPY=1 # stays live on the panel, no Steam restart), set: # PUNKTFUNK_GAMESCOPE_ATTACH=1 # PUNKTFUNK_GAMESCOPE_APP=steam -gamepadui # only for an ad-hoc bare-spawn fallback +# +# Follow a Gaming<->Desktop switch MID-STREAM (rebuild the backend in place, no reconnect): +# PUNKTFUNK_SESSION_WATCH=1