From 85513d1697f98fa263916ea2ea03c50c2b48655b Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Tue, 14 Jul 2026 19:07:41 +0200 Subject: [PATCH] fix(host/linux): headless gamescope must not inherit a desktop DISPLAY/WAYLAND_DISPLAY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A host (re)started after a desktop login inherits the user manager's compositor env; a stale WAYLAND_DISPLAY makes headless gamescope 3.16 exit at startup ('Failed to connect to wayland socket') before its PipeWire node appears. Unset both on the systemd-run transient unit (UnsetEnvironment=) and the direct spawn (env_remove) — gamescope exports its own DISPLAY/GAMESCOPE_WAYLAND_DISPLAY to the nested app. Co-Authored-By: Claude Fable 5 --- .../src/vdisplay/linux/gamescope.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/punktfunk-host/src/vdisplay/linux/gamescope.rs b/crates/punktfunk-host/src/vdisplay/linux/gamescope.rs index 9d4aa8e7..5ca00f3e 100644 --- a/crates/punktfunk-host/src/vdisplay/linux/gamescope.rs +++ b/crates/punktfunk-host/src/vdisplay/linux/gamescope.rs @@ -1233,6 +1233,10 @@ fn launch_session(client: &str, unit_name: &str, mode: Mode) -> Result { let start_unit = || -> Result<()> { let status = Command::new("systemd-run") .args(["--user", "--collect", &format!("--unit={unit_name}")]) + // Same headless-must-not-attach rule as [`spawn`]: the transient unit inherits the + // user manager env, which can carry a (possibly stale) desktop DISPLAY/WAYLAND_DISPLAY + // that would abort gamescope at startup. + .arg("--property=UnsetEnvironment=DISPLAY WAYLAND_DISPLAY") .arg("--setenv=BACKEND=headless") .arg(format!("--setenv=SCREEN_WIDTH={}", mode.width)) .arg(format!("--setenv=SCREEN_HEIGHT={}", mode.height)) @@ -1398,7 +1402,16 @@ fn spawn(w: u32, h: u32, hz: u32, cmd: Option<&str>, log: &std::path::Path) -> R ]) .args(app.split_whitespace()) // Prefer the NVIDIA GL vendor for the nested session (harmless on a pure-NVIDIA box). - .env("__GLX_VENDOR_LIBRARY_NAME", "nvidia"); + .env("__GLX_VENDOR_LIBRARY_NAME", "nvidia") + // A HEADLESS gamescope must never attach to a parent compositor. A host (re)started after + // a desktop login inherits the user manager's DISPLAY/WAYLAND_DISPLAY — and a stale + // WAYLAND_DISPLAY (e.g. a leftover `wayland-kde` in the manager env from a past session) + // makes gamescope 3.16 exit at startup with "Failed to connect to wayland socket" before + // its PipeWire node ever appears (observed 2026-07-14; the boot-started host never saw the + // bug because it predates any login's env import). gamescope exports its own DISPLAY / + // GAMESCOPE_WAYLAND_DISPLAY to the nested app, so the child loses nothing. + .env_remove("DISPLAY") + .env_remove("WAYLAND_DISPLAY"); if let Ok(logf) = std::fs::File::create(log) { if let Ok(log2) = logf.try_clone() { cmd.stdout(Stdio::from(logf)).stderr(Stdio::from(log2));