feat(linux): log if unsupported / too low gamescope version discovered
apple / swift (push) Successful in 2m29s
android / android (push) Successful in 12m52s
arch / build-publish (push) Successful in 12m59s
ci / web (push) Successful in 56s
ci / docs-site (push) Successful in 1m6s
ci / bench (push) Successful in 5m30s
ci / rust (push) Successful in 19m51s
decky / build-publish (push) Successful in 20s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 12s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 10s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 21s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 17s
deb / build-publish (push) Successful in 8m43s
apple / screenshots (push) Successful in 20m59s
deb / build-publish-host (push) Successful in 9m25s
docker / deploy-docs (push) Successful in 24s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 16m22s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m22s
apple / swift (push) Successful in 2m29s
android / android (push) Successful in 12m52s
arch / build-publish (push) Successful in 12m59s
ci / web (push) Successful in 56s
ci / docs-site (push) Successful in 1m6s
ci / bench (push) Successful in 5m30s
ci / rust (push) Successful in 19m51s
decky / build-publish (push) Successful in 20s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 12s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 10s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 21s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 17s
deb / build-publish (push) Successful in 8m43s
apple / screenshots (push) Successful in 20m59s
deb / build-publish-host (push) Successful in 9m25s
docker / deploy-docs (push) Successful in 24s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 16m22s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m22s
This commit is contained in:
@@ -2652,6 +2652,18 @@ mod pipewire {
|
|||||||
build_default_format_obj(preferred)
|
build_default_format_obj(preferred)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// gamescope trap — the Steam overlay's presence in the stream is decided HERE by omission:
|
||||||
|
// gamescope's `paint_pipewire()` composites the overlay (Shift+Tab / Quick Access Menu) into
|
||||||
|
// the node it hands us ONLY when the consumer-negotiated `gamescope_focus_appid` is 0 — the
|
||||||
|
// default, and the "mirror the focused window + overlay" branch (gamescope ≥ 3.16.23; see
|
||||||
|
// `MIN_GAMESCOPE_OVERLAY`). None of the EnumFormat pods below advertise the
|
||||||
|
// `SPA_FORMAT_VIDEO_gamescope_focus_appid` property, so gamescope reads 0 and paints the
|
||||||
|
// overlay for us for free. DO NOT add a non-zero focus-appid (e.g. to "isolate the game" in a
|
||||||
|
// dedicated session) — that flips gamescope into the Remote-Play branch that deliberately
|
||||||
|
// drops the overlay (and all host chrome) back out of the capture. The cursor, external
|
||||||
|
// overlay (MangoHUD), and notifications are excluded from the node on EVERY gamescope
|
||||||
|
// version and are composited host-side instead (see `xfixes_cursor.rs`).
|
||||||
|
//
|
||||||
// When zero-copy is on, offer ONLY a BGRx dmabuf format with our EGL-importable modifiers
|
// When zero-copy is on, offer ONLY a BGRx dmabuf format with our EGL-importable modifiers
|
||||||
// (offering shm too makes the compositor pick shm). The modifier list is advertised with
|
// (offering shm too makes the compositor pick shm). The modifier list is advertised with
|
||||||
// DONT_FIXATE so the compositor's allocator chooses one; we re-emit the fixated format in
|
// DONT_FIXATE so the compositor's allocator chooses one; we re-emit the fixated format in
|
||||||
|
|||||||
@@ -340,9 +340,19 @@ pub(crate) fn is_available() -> bool {
|
|||||||
/// against PipeWire ≥ 1.6 (a loop-lock bug) and a stuck link head-blocks the whole daemon.
|
/// against PipeWire ≥ 1.6 (a loop-lock bug) and a stuck link head-blocks the whole daemon.
|
||||||
const MIN_GAMESCOPE: (u32, u32, u32) = (3, 16, 22);
|
const MIN_GAMESCOPE: (u32, u32, u32) = (3, 16, 22);
|
||||||
|
|
||||||
/// Best-effort: warn loudly if the installed gamescope is older than [`MIN_GAMESCOPE`]. Parsing
|
/// First gamescope that paints the Steam overlay (Shift+Tab / Quick Access Menu) into its built-in
|
||||||
/// failures are silent (don't block a possibly-fine custom build) — this is a diagnostic, not a
|
/// PipeWire node. `paint_pipewire()` is a *separate, reduced* composite from the display scanout;
|
||||||
/// gate. Returns the parsed version when it could read one.
|
/// the overlay-window paint (gated on the consumer negotiating `gamescope_focus_appid == 0`, which
|
||||||
|
/// we do by never advertising that property — see the capturer's EnumFormat builders) first ships
|
||||||
|
/// in 3.16.23 (gamescope commits `ccd62074` + `f8b33d38`). Below this the overlay is *never* in the
|
||||||
|
/// node, so it cannot appear in the stream no matter what the host does. The cursor and
|
||||||
|
/// external-overlay / notification layers are excluded on *every* version (handled host-side).
|
||||||
|
const MIN_GAMESCOPE_OVERLAY: (u32, u32, u32) = (3, 16, 23);
|
||||||
|
|
||||||
|
/// Best-effort: warn if the installed gamescope is older than [`MIN_GAMESCOPE`] (capture is
|
||||||
|
/// unreliable) or than [`MIN_GAMESCOPE_OVERLAY`] (capture works but the Steam overlay can't reach
|
||||||
|
/// the stream). Parsing failures are silent (don't block a possibly-fine custom build) — this is a
|
||||||
|
/// diagnostic, not a gate. Returns the parsed version when it could read one.
|
||||||
pub(super) fn check_gamescope_version() -> Option<(u32, u32, u32)> {
|
pub(super) fn check_gamescope_version() -> Option<(u32, u32, u32)> {
|
||||||
let out = Command::new("gamescope").arg("--version").output().ok()?;
|
let out = Command::new("gamescope").arg("--version").output().ok()?;
|
||||||
// gamescope prints the version banner to stderr on some builds, stdout on others.
|
// gamescope prints the version banner to stderr on some builds, stdout on others.
|
||||||
@@ -360,6 +370,18 @@ pub(super) fn check_gamescope_version() -> Option<(u32, u32, u32)> {
|
|||||||
capture deadlock against PipeWire ≥ 1.6 (a wedged link head-blocks the daemon); \
|
capture deadlock against PipeWire ≥ 1.6 (a wedged link head-blocks the daemon); \
|
||||||
upgrade gamescope or use PUNKTFUNK_COMPOSITOR=kwin|mutter"
|
upgrade gamescope or use PUNKTFUNK_COMPOSITOR=kwin|mutter"
|
||||||
);
|
);
|
||||||
|
} else if ver < MIN_GAMESCOPE_OVERLAY {
|
||||||
|
// Capture is fine; the Steam overlay just won't be in the frame gamescope hands us.
|
||||||
|
tracing::warn!(
|
||||||
|
found = %format!("{}.{}.{}", ver.0, ver.1, ver.2),
|
||||||
|
min = %format!(
|
||||||
|
"{}.{}.{}",
|
||||||
|
MIN_GAMESCOPE_OVERLAY.0, MIN_GAMESCOPE_OVERLAY.1, MIN_GAMESCOPE_OVERLAY.2
|
||||||
|
),
|
||||||
|
"gamescope is older than the first version that paints the Steam overlay (Shift+Tab / \
|
||||||
|
Quick Access Menu) into its PipeWire node — the overlay will be absent from the \
|
||||||
|
stream until you upgrade gamescope (the cursor is composited host-side regardless)"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Some(ver)
|
Some(ver)
|
||||||
}
|
}
|
||||||
@@ -379,7 +401,7 @@ fn parse_version(text: &str) -> Option<(u32, u32, u32)> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{parse_version, steam_appid_from_launch, MIN_GAMESCOPE};
|
use super::{parse_version, steam_appid_from_launch, MIN_GAMESCOPE, MIN_GAMESCOPE_OVERLAY};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_steam_appid_from_launch() {
|
fn parses_steam_appid_from_launch() {
|
||||||
@@ -425,4 +447,15 @@ mod tests {
|
|||||||
assert!(parse_version("gamescope version 3.16.22").unwrap() >= MIN_GAMESCOPE);
|
assert!(parse_version("gamescope version 3.16.22").unwrap() >= MIN_GAMESCOPE);
|
||||||
assert!(parse_version("gamescope version 3.17.0").unwrap() >= MIN_GAMESCOPE);
|
assert!(parse_version("gamescope version 3.17.0").unwrap() >= MIN_GAMESCOPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn overlay_threshold_brackets_the_fix() {
|
||||||
|
// 3.16.22 captures fine but predates the overlay-in-pipewire paint — it sits in the
|
||||||
|
// "capture works, overlay absent" window `[MIN_GAMESCOPE, MIN_GAMESCOPE_OVERLAY)`, which is
|
||||||
|
// exactly the `else if` warn arm; 3.16.23 is the first to include the overlay.
|
||||||
|
assert!(parse_version("gamescope version 3.16.22").unwrap() >= MIN_GAMESCOPE);
|
||||||
|
assert!(parse_version("gamescope version 3.16.22").unwrap() < MIN_GAMESCOPE_OVERLAY);
|
||||||
|
assert!(parse_version("gamescope version 3.16.23").unwrap() >= MIN_GAMESCOPE_OVERLAY);
|
||||||
|
assert!(parse_version("gamescope version 3.16.25").unwrap() >= MIN_GAMESCOPE_OVERLAY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user