Two field reports from the same afternoon, plus the doc rot each one exposed.
1. The takeover stopped the display manager, and that stranded the box
The managed gamescope takeover freed Steam by stopping the box's gaming session and — because a display-manager autologin puts that session straight back — by stopping the display manager too. That worked until the user asked for a desktop session: with no DM, nothing on the box can start one, so Steam's own "Switch to Desktop" sat on its modal until a reboot.
It also cannot be papered over with detection. Measured on .41, every trace a steamos-manager switch leaves is written by the display manager we had just stopped:
Candidate signal
Result
~/.config/steamos-session-select
never written — that's the ChimeraOS/Nobara layout, Bazzite execs steamosctl
/var/lib/sddm/state.conf
only advances when sddm actually starts a session; unchanged for 16 s under a takeover
steamosctl get-default-login-mode
stays game for a non-persistent switch
graphical-session.target → inactive
fires at takeover time too; timing-dependent on our own session start
box gaming unit → inactive
already inactive under a takeover
So the fix removes the deadlock rather than detecting it. The takeover now idles the box's autologin session for the stream's duration — a drop-in over the gamescope-session-plus@ template replacing ExecStart with a process that sleeps — and leaves the DM running. The autologin still SUCCEEDS, so there is no failed unit to relogin against (a masked one fails in milliseconds, which is the storm's engine); the session runs nothing, so Steam is free; and the DM is alive, so the box services the switch itself. No privilege, no DM-flavor matrix, no detection.
Measured on .41, both directions: takeover leaves steam down, sddm active, the unit active (running) with NRestarts=0; the switch that used to hang brings Plasma up in ~10 s; the restore puts Steam back within 5 s.
Placement details that are load-bearing: the drop-in lives under $XDG_RUNTIME_DIR (it replaces the box's game-mode ExecStart, so a copy outliving the host is a box whose Game Mode silently does nothing) and is swept unconditionally at startup; its removal sits above every early return in the restore, next to the bind drop-in's, because the desktop-active return is exactly the path that would leak it; and the restore restarts rather than starts, since start on an active-but-idle unit is a no-op that logs success over it.
2. A translated KWin refusal burned all 8 retries
is_permanent_build_error matched the English "could not find output". KWin sends the failed reason translated, so a pt-BR session fell through the short-circuit and spent ~11 s retrying a config fact. It now matches "KWin virtual output failed" — our own prefix, never translated.
Read against Plasma/6.4 → master: on 6.7 the DRM backend's createVirtualOutput cannot return null, so the only route to i18n("Could not find output") is workspace()->findOutput() returning null — wantsToManage() requires isEnabled(). KWin created the output and left it disabled. That hop is new in 6.6; 6.4/6.5 streamed a virtual output the workspace never managed. kde.md and virtual-displays.md now carry that plus what to check on the box. (The reporter's own box is still open — it needs their kwin journal.)
3. Doc rot both bugs exposed
gamescope.md, bazzite.md and the shipped packaging/bazzite/host.env all stated the takeover "has to stop the display manager" and needs the punktfunk group. Both now false; the group advice narrows to the usbip nodes the virtual Steam Deck pad attaches through, which is what it still gates.
punktfunk_connect_ex11's doc summary still stated the rule replaced in d09b4668 (2026-08-16) — "Passing anything other than 48000/16 sets CLIENT_CAP_AUDIO_HIRES" — contradicting the ⚠ note 15 lines below it and the implementation. It ships in the generated public C header, so an embedder following it would emit exactly the contradictory Hello the host warns about. Corrected at the source; header regenerated.
Verification
.41 (Bazzite, KWin 6.7.4, RTX 5070 Ti): takeover, in-stream desktop switch, and restore all exercised end to end.
permanent_errors_short_circuit_retry gains the pt-BR string; the timeout case stays transient.
cargo test -p pf-vdisplay and cargo check -p punktfunk-core (which regenerates include/punktfunk_core.h) on Linux; cargo fmt clean.
Two field reports from the same afternoon, plus the doc rot each one exposed.
## 1. The takeover stopped the display manager, and that stranded the box
The managed gamescope takeover freed Steam by stopping the box's gaming session and — because a display-manager autologin puts that session straight back — by stopping the display manager too. That worked until the user asked for a desktop session: with no DM, nothing on the box can start one, so Steam's own "Switch to Desktop" sat on its modal until a reboot.
It also cannot be papered over with detection. Measured on `.41`, every trace a `steamos-manager` switch leaves is written by the display manager we had just stopped:
| Candidate signal | Result |
|---|---|
| `~/.config/steamos-session-select` | never written — that's the ChimeraOS/Nobara layout, Bazzite execs `steamosctl` |
| `/var/lib/sddm/state.conf` | only advances when sddm actually *starts* a session; unchanged for 16 s under a takeover |
| `steamosctl get-default-login-mode` | stays `game` for a non-persistent switch |
| `graphical-session.target` → inactive | fires at takeover time too; timing-dependent on our own session start |
| box gaming unit → inactive | already inactive under a takeover |
So the fix removes the deadlock rather than detecting it. The takeover now **idles** the box's autologin session for the stream's duration — a drop-in over the `gamescope-session-plus@` template replacing `ExecStart` with a process that sleeps — and leaves the DM running. The autologin still SUCCEEDS, so there is no failed unit to relogin against (a masked one fails in milliseconds, which is the storm's engine); the session runs nothing, so Steam is free; and the DM is alive, so the box services the switch itself. No privilege, no DM-flavor matrix, no detection.
**Measured on `.41`, both directions:** takeover leaves `steam` down, `sddm` active, the unit `active (running)` with `NRestarts=0`; the switch that used to hang brings Plasma up in ~10 s; the restore puts Steam back within 5 s.
Placement details that are load-bearing: the drop-in lives under `$XDG_RUNTIME_DIR` (it replaces the box's game-mode `ExecStart`, so a copy outliving the host is a box whose Game Mode silently does nothing) and is swept unconditionally at startup; its removal sits above every early return in the restore, next to the bind drop-in's, because the desktop-active return is exactly the path that would leak it; and the restore `restart`s rather than `start`s, since `start` on an active-but-idle unit is a no-op that logs success over it.
## 2. A translated KWin refusal burned all 8 retries
`is_permanent_build_error` matched the English `"could not find output"`. KWin sends the `failed` reason **translated**, so a pt-BR session fell through the short-circuit and spent ~11 s retrying a config fact. It now matches `"KWin virtual output failed"` — our own prefix, never translated.
Read against Plasma/6.4 → master: on 6.7 the DRM backend's `createVirtualOutput` cannot return null, so the only route to `i18n("Could not find output")` is `workspace()->findOutput()` returning null — `wantsToManage()` requires `isEnabled()`. KWin created the output and left it disabled. That hop is **new in 6.6**; 6.4/6.5 streamed a virtual output the workspace never managed. `kde.md` and `virtual-displays.md` now carry that plus what to check on the box. (The reporter's own box is still open — it needs their kwin journal.)
## 3. Doc rot both bugs exposed
- `gamescope.md`, `bazzite.md` and the shipped `packaging/bazzite/host.env` all stated the takeover "has to stop the display manager" and needs the `punktfunk` group. Both now false; the group advice narrows to the usbip nodes the virtual Steam Deck pad attaches through, which is what it still gates.
- `punktfunk_connect_ex11`'s doc summary still stated the rule replaced in d09b4668 (2026-08-16) — *"Passing anything other than `48000`/`16` sets `CLIENT_CAP_AUDIO_HIRES`"* — contradicting the ⚠ note 15 lines below it and the implementation. It ships in the generated public C header, so an embedder following it would emit exactly the contradictory `Hello` the host warns about. Corrected at the source; header regenerated.
## Verification
- `.41` (Bazzite, KWin 6.7.4, RTX 5070 Ti): takeover, in-stream desktop switch, and restore all exercised end to end.
- `permanent_errors_short_circuit_retry` gains the pt-BR string; the timeout case stays transient.
- `cargo test -p pf-vdisplay` and `cargo check -p punktfunk-core` (which regenerates `include/punktfunk_core.h`) on Linux; `cargo fmt` clean.
KWin sends the `failed` reason on zkde_screencast localized. The retry
short-circuit matched the English "could not find output", so a pt-BR session
("Não foi possível encontrar saída") fell through to 8 attempts over ~11 s and
then reported "out of retries" — a config fact dressed up as a flaky one.
Match "KWin virtual output failed" instead: our own prefix, never translated.
Every `failed` KWin sends on this path is a backend/config fact (unsupported
compositing type, a backend without createVirtualOutput, an output the
workspace declined to enable), none of which a retry 500 ms later changes.
The message itself now says what KWin's untranslatable reason means, and the
KDE docs gain the KWin 6.6+ shape of it: KWin creates the output, leaves it
disabled, and workspace()->findOutput() returns null — reported identically to
a backend that cannot create one at all.
enricobuehler
changed title from A translated KWin refusal burned all 8 retries, and ex11's docs still stated a rule we replaced to A stopped display manager left the box unable to switch sessions, and a translated KWin refusal burned all 8 retries2026-08-19 16:58:53 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Two field reports from the same afternoon, plus the doc rot each one exposed.
1. The takeover stopped the display manager, and that stranded the box
The managed gamescope takeover freed Steam by stopping the box's gaming session and — because a display-manager autologin puts that session straight back — by stopping the display manager too. That worked until the user asked for a desktop session: with no DM, nothing on the box can start one, so Steam's own "Switch to Desktop" sat on its modal until a reboot.
It also cannot be papered over with detection. Measured on
.41, every trace asteamos-managerswitch leaves is written by the display manager we had just stopped:~/.config/steamos-session-selectsteamosctl/var/lib/sddm/state.confsteamosctl get-default-login-modegamefor a non-persistent switchgraphical-session.target→ inactiveSo the fix removes the deadlock rather than detecting it. The takeover now idles the box's autologin session for the stream's duration — a drop-in over the
gamescope-session-plus@template replacingExecStartwith a process that sleeps — and leaves the DM running. The autologin still SUCCEEDS, so there is no failed unit to relogin against (a masked one fails in milliseconds, which is the storm's engine); the session runs nothing, so Steam is free; and the DM is alive, so the box services the switch itself. No privilege, no DM-flavor matrix, no detection.Measured on
.41, both directions: takeover leavessteamdown,sddmactive, the unitactive (running)withNRestarts=0; the switch that used to hang brings Plasma up in ~10 s; the restore puts Steam back within 5 s.Placement details that are load-bearing: the drop-in lives under
$XDG_RUNTIME_DIR(it replaces the box's game-modeExecStart, so a copy outliving the host is a box whose Game Mode silently does nothing) and is swept unconditionally at startup; its removal sits above every early return in the restore, next to the bind drop-in's, because the desktop-active return is exactly the path that would leak it; and the restorerestarts rather thanstarts, sincestarton an active-but-idle unit is a no-op that logs success over it.2. A translated KWin refusal burned all 8 retries
is_permanent_build_errormatched the English"could not find output". KWin sends thefailedreason translated, so a pt-BR session fell through the short-circuit and spent ~11 s retrying a config fact. It now matches"KWin virtual output failed"— our own prefix, never translated.Read against Plasma/6.4 → master: on 6.7 the DRM backend's
createVirtualOutputcannot return null, so the only route toi18n("Could not find output")isworkspace()->findOutput()returning null —wantsToManage()requiresisEnabled(). KWin created the output and left it disabled. That hop is new in 6.6; 6.4/6.5 streamed a virtual output the workspace never managed.kde.mdandvirtual-displays.mdnow carry that plus what to check on the box. (The reporter's own box is still open — it needs their kwin journal.)3. Doc rot both bugs exposed
gamescope.md,bazzite.mdand the shippedpackaging/bazzite/host.envall stated the takeover "has to stop the display manager" and needs thepunktfunkgroup. Both now false; the group advice narrows to the usbip nodes the virtual Steam Deck pad attaches through, which is what it still gates.punktfunk_connect_ex11's doc summary still stated the rule replaced ind09b4668(2026-08-16) — "Passing anything other than48000/16setsCLIENT_CAP_AUDIO_HIRES" — contradicting the ⚠ note 15 lines below it and the implementation. It ships in the generated public C header, so an embedder following it would emit exactly the contradictoryHellothe host warns about. Corrected at the source; header regenerated.Verification
.41(Bazzite, KWin 6.7.4, RTX 5070 Ti): takeover, in-stream desktop switch, and restore all exercised end to end.permanent_errors_short_circuit_retrygains the pt-BR string; the timeout case stays transient.cargo test -p pf-vdisplayandcargo check -p punktfunk-core(which regeneratesinclude/punktfunk_core.h) on Linux;cargo fmtclean.KWin sends the `failed` reason on zkde_screencast localized. The retry short-circuit matched the English "could not find output", so a pt-BR session ("Não foi possível encontrar saída") fell through to 8 attempts over ~11 s and then reported "out of retries" — a config fact dressed up as a flaky one. Match "KWin virtual output failed" instead: our own prefix, never translated. Every `failed` KWin sends on this path is a backend/config fact (unsupported compositing type, a backend without createVirtualOutput, an output the workspace declined to enable), none of which a retry 500 ms later changes. The message itself now says what KWin's untranslatable reason means, and the KDE docs gain the KWin 6.6+ shape of it: KWin creates the output, leaves it disabled, and workspace()->findOutput() returns null — reported identically to a backend that cannot create one at all.A translated KWin refusal burned all 8 retries, and ex11's docs still stated a rule we replacedto A stopped display manager left the box unable to switch sessions, and a translated KWin refusal burned all 8 retries