feat(gamescope): end dedicated stream on Steam game exit + auto --steam
ci / web (push) Successful in 54s
ci / docs-site (push) Successful in 1m12s
apple / swift (push) Successful in 1m19s
decky / build-publish (push) Successful in 32s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 11s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
ci / bench (push) Successful in 6m57s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 4m51s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 14s
apple / screenshots (push) Successful in 6m13s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m1s
deb / build-publish (push) Successful in 9m37s
docker / deploy-docs (push) Successful in 29s
deb / build-publish-host (push) Successful in 9m29s
windows-host / package (push) Successful in 16m24s
arch / build-publish (push) Successful in 17m40s
android / android (push) Successful in 18m22s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 14m39s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 18m52s
ci / rust (push) Successful in 26m16s
ci / web (push) Successful in 54s
ci / docs-site (push) Successful in 1m12s
apple / swift (push) Successful in 1m19s
decky / build-publish (push) Successful in 32s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 11s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
ci / bench (push) Successful in 6m57s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 4m51s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 14s
apple / screenshots (push) Successful in 6m13s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m1s
deb / build-publish (push) Successful in 9m37s
docker / deploy-docs (push) Successful in 29s
deb / build-publish-host (push) Successful in 9m29s
windows-host / package (push) Successful in 16m24s
arch / build-publish (push) Successful in 17m40s
android / android (push) Successful in 18m22s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 14m39s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 18m52s
ci / rust (push) Successful in 26m16s
Dedicated single-game sessions now end cleanly when the launched game quits, and Steam launches get gamescope's Steam integration without an operator env knob. End-on-exit: the existing APP_EXITED close only fired when gamescope's PipeWire node vanished, which never happens for Steam — the nested `steam` is the resident singleton client and stays up after a game quits, so gamescope (and its node) never die and the stream sat on a hidden Steam session forever. Add a Steam AppId reaper watcher (pf-vdisplay .../gamescope/discovery.rs): steam_appid_from_launch() parses steam://rungameid/<id>; wait_for_steam_game_exit() waits for the game to start (<=300s grace) then exit (3s confirm); steam_game_running() scans same-uid /proc for Steam's launch reaper matching both the `SteamLaunch` and `AppId=<id>` argv tokens (exact-match; reaper lifetime == game lifetime, so shader precompile can't false-trigger). The host spawns a pf1-gamewatch thread for nested Steam launches that closes the connection with APP_EXITED (launcher clients return to their library) and sets quit/stop; cancelled via stop if the session ends first. Non-Steam nested launches keep the node-death path (gamescope's child IS the game). Flags: auto-enable `--steam` whenever the launch is a Steam launch (was only the global PUNKTFUNK_GAMESCOPE_STEAM knob, default-off) — in-game overlay / Steam+X / gamepad-UI nav for Steam titles with no operator config. New opt-in PUNKTFUNK_GAMESCOPE_GRAB_CURSOR adds `--force-grab-cursor` for a real game launch (FPS mouselook); default OFF because it forces relative mode, which breaks absolute-pointer games/menus. Verified: fmt clean; clippy -D warnings clean on the three crates; pf-vdisplay 64/0 (incl. new steam-appid parse test); punktfunk-host builds + 186/0; reaper /proc detection smoke-tested (detect, exact non-match, gone-after-exit). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -978,6 +978,45 @@ pub(super) fn virtual_stream(ctx: SessionContext, prepared: Option<PreparedDispl
|
||||
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
|
||||
let _ = &launch;
|
||||
|
||||
// Dedicated Steam launch: end the stream cleanly when the LAUNCHED GAME exits. The node-death
|
||||
// check in the capture-loss branch below can't see this for Steam — the nested `steam` is the
|
||||
// resident client and stays up after a game quits, so gamescope (and its node) never dies and the
|
||||
// stream would sit on a hidden Steam session forever. Watch the game process directly (Steam's
|
||||
// `SteamLaunch AppId=<id>` reaper, whose lifetime == the game's) and close with APP_EXITED when
|
||||
// it's gone, so a launcher client returns to its library. Non-Steam nested launches keep the
|
||||
// node-death path (gamescope's child IS the game). Cancelled via `stop` when the session ends for
|
||||
// another reason first; the thread self-terminates, so we don't join it.
|
||||
#[cfg(target_os = "linux")]
|
||||
let _game_watch = launch
|
||||
.as_deref()
|
||||
.filter(|_| crate::vdisplay::launch_is_nested(compositor))
|
||||
.and_then(crate::vdisplay::steam_appid_from_launch)
|
||||
.map(|appid| {
|
||||
let conn = conn.clone();
|
||||
let stop = stop.clone();
|
||||
let quit = quit.clone();
|
||||
std::thread::Builder::new()
|
||||
.name("pf1-gamewatch".into())
|
||||
.spawn(move || {
|
||||
if crate::vdisplay::watch_steam_game_exit(appid, &stop) {
|
||||
tracing::info!(
|
||||
appid,
|
||||
"dedicated Steam game exited — ending the session cleanly (APP_EXITED)"
|
||||
);
|
||||
// Close FIRST so APP_EXITED is the winning close code (quinn keeps the first
|
||||
// application close), then set the flags: `quit` skips the display lease's
|
||||
// keep-alive linger and `stop` wakes the encode/send loops out.
|
||||
conn.close(
|
||||
punktfunk_core::quic::APP_EXITED_CLOSE_CODE.into(),
|
||||
b"game exited",
|
||||
);
|
||||
quit.store(true, Ordering::SeqCst);
|
||||
stop.store(true, Ordering::SeqCst);
|
||||
}
|
||||
})
|
||||
.ok()
|
||||
});
|
||||
|
||||
let perf = pf_host_config::config().perf;
|
||||
// Microburst cap (applied in send_loop/paced_submit): a frame ≤ the cap bursts out
|
||||
// immediately; only a bigger frame's overflow is spread. `None` = auto — max(128 KB, the
|
||||
|
||||
Reference in New Issue
Block a user