Files
punktfunk/crates
enricobuehlerandClaude Opus 5 028462ef6d feat(vdisplay/gamescope): a Game Mode session drives a real head — list it, and mirror it
The per-monitor capture feature shipped with gamescope written off as "nested,
therefore no physical heads of its own", and `monitors::list` returned a
hard-coded empty Vec for it. That premise holds for a gamescope nested in another
compositor and for the headless ones this crate spawns — but not for the
deployment people actually stream from: a Bazzite/SteamOS Game Mode session is
the DRM master and drives the TV directly. On such a box the console's picker was
permanently empty, on exactly the hardware the feature was asked for.

`gamescope/heads.rs` answers the narrow question instead — is this gamescope on
the DRM backend, and which connector is it lighting? — from `/proc/<pid>/cmdline`
(the backend flag and `--prefer-output`; gamescope publishes no protocol that
reports its own output) and `/sys/class/drm` (what is plugged in, plus each
connector's EDID for the picker label and the panel's preferred timing). The
nested/headless shapes still answer an empty list, which is what they always
meant.

Mirroring needs no new capture code: gamescope composites its one head into a
PipeWire node it already publishes, so the mirror arm is an ATTACH to that node —
the gaming session is not stopped, relaunched or re-moded, which is the whole
difference between "stream the panel I am looking at" and the MANAGED takeover
that deliberately blanks it.

Refresh comes from the EDID and never from `--nested-refresh`: that flag is the
rate gamescope composites the nested game at, and reading it as the connector's
rate would let a session launched at 60 cap a 120 Hz TV for the whole stream.

Verified against home-bazzite-1's live EDID: manufacturer `1e6d` -> GSM, name
descriptor -> "LG TV SSCR2", preferred timing 594 MHz / 4400x2250 -> 3840x2160@60.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 12:23:32 +02:00
..