fix(vdisplay/session): scrub the dead desktop's WAYLAND_DISPLAY from the user-manager env
ci / web (push) Successful in 1m2s
ci / docs-site (push) Successful in 1m14s
apple / swift (push) Successful in 1m22s
decky / build-publish (push) Successful in 27s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 19s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 17s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 12s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 12s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 12s
ci / bench (push) Successful in 5m44s
apple / screenshots (push) Successful in 6m18s
android / android (push) Successful in 13m0s
docker / deploy-docs (push) Successful in 27s
deb / build-publish (push) Successful in 13m36s
deb / build-publish-host (push) Successful in 14m44s
windows-host / package (push) Successful in 16m32s
arch / build-publish (push) Successful in 17m49s
ci / rust (push) Successful in 19m58s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m35s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 19m42s
ci / web (push) Successful in 1m2s
ci / docs-site (push) Successful in 1m14s
apple / swift (push) Successful in 1m22s
decky / build-publish (push) Successful in 27s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 19s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 17s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 12s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 12s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 12s
ci / bench (push) Successful in 5m44s
apple / screenshots (push) Successful in 6m18s
android / android (push) Successful in 13m0s
docker / deploy-docs (push) Successful in 27s
deb / build-publish (push) Successful in 13m36s
deb / build-publish-host (push) Successful in 14m44s
windows-host / package (push) Successful in 16m32s
arch / build-publish (push) Successful in 17m49s
ci / rust (push) Successful in 19m58s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m35s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 19m42s
settle_desktop_portal pushes the live desktop's WAYLAND_DISPLAY into the systemd --user manager (import-environment) so a re-activated portal inherits the right session — but the import PERSISTS after that desktop dies. Every later user unit then inherits the stale socket, including gamescope-session.target: gamescope sees WAYLAND_DISPLAY, runs NESTED, and aborts with "Failed to connect to wayland socket: wayland-0" — observed live on a Deck, where Game Mode could not start at all (autologin crash-looped) until the var was unset by hand. Two-layer fix, mirroring the protection launch_session already gives its transient unit: - observe_session_instance: when a desktop compositor instance goes away, unset WAYLAND_DISPLAY/DISPLAY from the manager env (a desktop bounce is harmless — the next portal settle re-imports). - write_steamos_dropin: UnsetEnvironment=DISPLAY WAYLAND_DISPLAY on the takeover drop-in, unit-scoped belt-and-suspenders for host-initiated restarts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -630,12 +630,17 @@ fn write_steamos_dropin(shim_dir: &std::path::Path, mode: Mode) -> Result<()> {
|
||||
if let Some(parent) = path.parent() {
|
||||
std::fs::create_dir_all(parent).with_context(|| format!("mkdir {}", parent.display()))?;
|
||||
}
|
||||
// UnsetEnvironment: the same headless-must-not-attach armor `launch_session` gives its
|
||||
// transient unit — the manager env can carry a stale desktop DISPLAY/WAYLAND_DISPLAY (from a
|
||||
// portal settle), and gamescope would abort trying to attach to it instead of becoming the
|
||||
// display server. Unit-scoped belt-and-suspenders on top of the observe_session_instance scrub.
|
||||
let body = format!(
|
||||
"[Service]\n\
|
||||
Environment=PATH={shim}:/usr/bin:/bin:/usr/local/bin\n\
|
||||
Environment=PF_W={w}\n\
|
||||
Environment=PF_H={h}\n\
|
||||
Environment=PF_HZ={hz}\n",
|
||||
Environment=PF_HZ={hz}\n\
|
||||
UnsetEnvironment=DISPLAY WAYLAND_DISPLAY\n",
|
||||
shim = shim_dir.display(),
|
||||
w = mode.width,
|
||||
h = mode.height,
|
||||
|
||||
@@ -57,6 +57,13 @@ pub fn observe_session_instance(active: &ActiveSession) {
|
||||
if let Some(old) = compositor_for_kind(prev.0) {
|
||||
registry::invalidate_backend(old.id());
|
||||
}
|
||||
// The dead desktop's socket vars may still sit in the systemd --user manager env
|
||||
// ([`settle_desktop_portal`]'s import-environment) — scrub them NOW, or the next
|
||||
// `gamescope-session.target` start inherits a stale WAYLAND_DISPLAY and gamescope
|
||||
// runs NESTED against the dead desktop socket instead of becoming the display
|
||||
// server ("Failed to connect to wayland socket: wayland-0" — kept a Deck's Game
|
||||
// Mode from starting at all, observed live 2026-07-21).
|
||||
scrub_desktop_manager_env();
|
||||
}
|
||||
let epoch = bump_session_epoch();
|
||||
tracing::info!(
|
||||
@@ -70,6 +77,23 @@ pub fn observe_session_instance(active: &ActiveSession) {
|
||||
*last = Some(cur);
|
||||
}
|
||||
|
||||
/// Counterpart to [`settle_desktop_portal`]'s `import-environment`: drop the desktop session's
|
||||
/// socket vars from the systemd `--user` manager env once that desktop instance is GONE. They
|
||||
/// persist in the manager otherwise, and every later user unit inherits them — including
|
||||
/// `gamescope-session.target`, whose gamescope then aborts trying to attach to the dead desktop
|
||||
/// socket. Best-effort; the D-Bus activation env has no unset op, but gamescope-session is
|
||||
/// systemd-started, so the manager scrub is the one that matters. (A desktop restart re-imports
|
||||
/// via the next [`settle_desktop_portal`], so scrubbing on a bounce is harmless.)
|
||||
#[cfg(target_os = "linux")]
|
||||
fn scrub_desktop_manager_env() {
|
||||
let _ = std::process::Command::new("systemctl")
|
||||
.args(["--user", "unset-environment", "WAYLAND_DISPLAY", "DISPLAY"])
|
||||
.status();
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn scrub_desktop_manager_env() {}
|
||||
|
||||
/// Is `kind` a **desktop** compositor (KWin / Mutter / wlroots) — one whose kept PipeWire outputs die
|
||||
/// with the compositor instance, so the session epoch tracks it? `Gaming` (gamescope) and `None` are
|
||||
/// not (gamescope spawns are independent nested sessions — see [`observe_session_instance`]).
|
||||
|
||||
Reference in New Issue
Block a user