fix(host/linux): headless gamescope must not inherit a desktop DISPLAY/WAYLAND_DISPLAY
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 <noreply@anthropic.com>
This commit is contained in:
@@ -1233,6 +1233,10 @@ fn launch_session(client: &str, unit_name: &str, mode: Mode) -> Result<u32> {
|
||||
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));
|
||||
|
||||
Reference in New Issue
Block a user