Files
punktfunk/crates
enricobuehler b2c03f1904 fix(pf-vdisplay): a managed launch blocked the shutdown restore that was meant to rescue it, and re-moding could flip the operator's own screen
The gamescope subsystem — the crate's largest and fastest-churning area, and the one the 2026-07-28
sweep predates most of.

* **`MANAGED_SESSION` was held across the ~90 s managed launch**, and the shutdown/idle restore
  blocks on that same lock — *after* it has already stopped our unit. So the display-manager restore
  never ran and the box was left with no session at all. `create_managed_session` now decides under
  the guard and acts outside it, re-acquiring only to store the result; `do_restore_tv_session`
  consumes the record in a short scope at the top. Same shape the SteamOS twin already used.

* **The physical-display guard was bypassed whenever no gamescope node happened to be published.**
  `if physical_display_connected() { if let Some(node) = find_gamescope_node() { … } }` fell through
  to `set-environment SCREEN_WIDTH/HEIGHT/CUSTOM_REFRESH_RATES` + `restart` when the node was
  momentarily absent — gamescope restarting between titles, or built without PipeWire — flipping the
  operator's own screen to the client's resolution and bouncing a DM-driven login session. The guard
  now refuses instead of falling through, and the forced `SCREEN_*` values (which were never unset,
  so every later session on the box inherited them) are tracked and `unset-environment`ed on restore.

* **`current_gamescope_output_size()` reported an arbitrary gamescope's `-W`/`-H`** — whichever
  `/proc` enumerated first — and four consumers treated it as this session's output size. It now
  answers only when every gamescope on the box agrees, and `None` ("cannot tell") when they differ.
  `heads.rs` no longer takes it at all: it reads the size off the DRM-backed argv it already
  selected. Its test previously passed `None`, which is why the hazard was invisible.

Resource and honesty fixes: the ATTACH path armed the box's own session-unit bind drop-in and no
in-process path ever removed it (now tracked and disarmed on both restore arms); `wait_for_node`
never called `try_wait`, so a gamescope that died at `vkCreateDevice` was polled for the full 15 s
and the error then blamed headless capture support; `do_restore_tv_session` deleted its crash-recovery
state *before* the unbounded work that state records, so a grace-period expiry in that window left
the DM down with nothing on disk to heal it; the SteamOS takeover's two failure arms never armed the
TV restore though the session-plus twin does; the TV-session restore logged success with the
`systemctl` status discarded; the `steam -shutdown` child was dropped un-reaped; and a managed
session that took nothing over was never persisted, so a host crash orphaned the transient unit.

Item 8.1: the unbounded `pw-dump`, `systemctl`, `loginctl` and `pkexec` calls in this subsystem now
go through `proc::{status_within, output_within}` with per-call budgets. `pw-dump` is polled from
three separate 45 s loops against the very daemon this file documents gamescope as head-blocking,
and until now a hang there pinned the session's stream thread forever.
2026-08-11 09:22:09 +02:00
..