Exclusive topology left the KDE panel lit under a gamescope spawn — DPMS it dark #227

Merged
enricobuehler merged 1 commits from worktree-gamescope-exclusive-dpms into main 2026-08-14 17:14:43 +00:00
Owner

Problem

With topology=exclusive on a KDE box, launching a gamescope session (dedicated game session / bare spawn) correctly puts the stream into its own headless gamescope — but the KDE session keeps driving the physical display with the idle desktop for the whole stream. The policy says the virtual display should be the only thing alive; the panel stays lit. gamescope.rs was the only Linux backend that never consulted effective_topology().

Why not disable the outputs like the KWin route does

That door is closed on this route: KWin refuses an output configuration with zero enabled outputs, and a bare-spawn session has no KWin output of its own to leave enabled. (Game mode "solves" this by stopping KDE outright — exactly what a dedicated session must not do.)

Fix

DPMS is the honest translation of exclusive for this route: the desktop stays exactly where it is (no topology churn, no window re-homing), the panels go dark, local input wakes them, and stream input never does — it is injected into gamescope's own EIS socket and never passes through KWin's libinput.

  • New kwin_dpms module drives the vendored org_kde_kwin_dpms protocol in-process over the desktop's own Wayland — same stack and same wedge-immunity rationale as kwin_output_mgmt — with a kscreen-doctor --dpms fallback on kwin.rs's shared verdict/budget helper.
  • The darken is refcounted host-wide rather than floated through the registry's per-group restore: every gamescope spawn is its own group (registry::group_key), so the §6.1 float alone would re-light the panel when the FIRST of two concurrent spawns ends, under the second's still-live stream. Each exclusive spawn takes one hold and registers the release as its per-display take_topology_restore, so the registry still times every release and the last one out re-lights only what the first darken actually turned off.
  • Hooked after the spawn succeeds (a failed create never blanks the screen). Managed (its takeover already stopped the desktop) and Attach (may be mirroring a gamescope that itself drives the physical panel) are deliberately excluded.
  • Self-gating: no Wayland session / non-KDE desktop declines quietly at debug level — headless plain-distro spawns are unchanged.
  • Crash-safe by construction: DPMS is non-persistent, so a dead host leaves nothing to journal — the panel re-lights on the next local input or compositor restart (contrast the Windows pnp_disable_monitors journal).

Note on defaults: Auto resolves to Exclusive on unpinned desktop boxes, so a default-config KDE box with game_session=dedicated gets the darken too — consistent with what Auto already means on the KWin route (physicals disabled). Under a PUNKTFUNK_COMPOSITOR pin Auto resolves to Extend, so CI/test postures are untouched.

Verification

  • scripts/xcheck.sh linux check + clippy: clean, 0 warnings; macOS cargo check -p pf-vdisplay + cargo fmt --check: clean.
  • Full pf-vdisplay suite in a Linux container: 230 passed, 0 failed, including 5 new refcount-edge tests (first-acquire darkens / only-last-release re-lights / no-op darken restores nothing / unbalanced release never underflows / full cycle re-arms).
  • ⚠ No on-glass verify yet: needs a KDE Wayland box streaming a dedicated gamescope session — panel off at create, back on at last teardown, staying dark across a 2-client overlap.

Future work: the same hook generalizes to Hyprland/wlroots/GNOME desktops under a spawn (hyprctl dispatch dpms off / wlr-output-power-management-v1 / Mutter PowerSaveMode); today those decline quietly.

## Problem With `topology=exclusive` on a KDE box, launching a gamescope session (dedicated game session / bare spawn) correctly puts the stream into its own headless gamescope — but the KDE session keeps driving the physical display with the idle desktop for the whole stream. The policy says the virtual display should be the only thing alive; the panel stays lit. `gamescope.rs` was the only Linux backend that never consulted `effective_topology()`. ## Why not disable the outputs like the KWin route does That door is closed on this route: KWin refuses an output configuration with zero enabled outputs, and a bare-spawn session has no KWin output of its own to leave enabled. (Game mode "solves" this by stopping KDE outright — exactly what a dedicated session must not do.) ## Fix DPMS is the honest translation of `exclusive` for this route: the desktop stays exactly where it is (no topology churn, no window re-homing), the panels go dark, local input wakes them, and **stream input never does** — it is injected into gamescope's own EIS socket and never passes through KWin's libinput. - New `kwin_dpms` module drives the vendored `org_kde_kwin_dpms` protocol in-process over the desktop's own Wayland — same stack and same wedge-immunity rationale as `kwin_output_mgmt` — with a `kscreen-doctor --dpms` fallback on `kwin.rs`'s shared verdict/budget helper. - The darken is **refcounted host-wide** rather than floated through the registry's per-group restore: every gamescope spawn is its own group (`registry::group_key`), so the §6.1 float alone would re-light the panel when the FIRST of two concurrent spawns ends, under the second's still-live stream. Each exclusive spawn takes one hold and registers the release as its per-display `take_topology_restore`, so the registry still times every release and the last one out re-lights only what the first darken actually turned off. - Hooked after the spawn succeeds (a failed create never blanks the screen). Managed (its takeover already stopped the desktop) and Attach (may be mirroring a gamescope that itself drives the physical panel) are deliberately excluded. - Self-gating: no Wayland session / non-KDE desktop declines quietly at debug level — headless plain-distro spawns are unchanged. - Crash-safe by construction: DPMS is non-persistent, so a dead host leaves nothing to journal — the panel re-lights on the next local input or compositor restart (contrast the Windows `pnp_disable_monitors` journal). Note on defaults: `Auto` resolves to `Exclusive` on unpinned desktop boxes, so a default-config KDE box with `game_session=dedicated` gets the darken too — consistent with what Auto already means on the KWin route (physicals disabled). Under a `PUNKTFUNK_COMPOSITOR` pin Auto resolves to `Extend`, so CI/test postures are untouched. ## Verification - `scripts/xcheck.sh linux check` + `clippy`: clean, 0 warnings; macOS `cargo check -p pf-vdisplay` + `cargo fmt --check`: clean. - Full `pf-vdisplay` suite in a Linux container: **230 passed, 0 failed**, including 5 new refcount-edge tests (first-acquire darkens / only-last-release re-lights / no-op darken restores nothing / unbalanced release never underflows / full cycle re-arms). - ⚠ No on-glass verify yet: needs a KDE Wayland box streaming a dedicated gamescope session — panel off at create, back on at last teardown, staying dark across a 2-client overlap. Future work: the same hook generalizes to Hyprland/wlroots/GNOME desktops under a spawn (`hyprctl dispatch dpms off` / `wlr-output-power-management-v1` / Mutter PowerSaveMode); today those decline quietly.
enricobuehler added 1 commit 2026-08-14 16:36:19 +00:00
fix(vdisplay): exclusive topology left the KDE panel lit under a gamescope spawn — DPMS it dark
ci / bun-nix (pull_request) Successful in 1m42s
ci / web (pull_request) Successful in 3m41s
ci / rust-arm64 (pull_request) Successful in 6m2s
android / android (pull_request) Successful in 7m12s
ci / docs-site (pull_request) Successful in 7m45s
ci / rust (pull_request) Successful in 23m56s
79114891df
A bare-spawn gamescope session is its own headless compositor, so it was the one
Linux route that never consulted effective_topology(): on a KDE desktop box the
physical panel kept showing the idle desktop for the whole stream while the
policy said exclusive. The KWin route's mechanism (disable the physicals) is
closed on this route — KWin refuses a configuration with zero enabled outputs
and no output on that desktop is ours to leave enabled — so the honest
translation is DPMS: the desktop stays exactly where it is, the panels go dark,
local input wakes them, and stream input never does (it enters gamescope's own
EIS socket, not KWin's libinput).

New kwin_dpms module drives the vendored org_kde_kwin_dpms protocol in-process
over the desktop's own Wayland (the kwin_output_mgmt stack and rationale), with
a kscreen-doctor --dpms fallback on kwin.rs's shared verdict/budget. The darken
is refcounted host-wide rather than floated through the registry's per-group
restore, because every gamescope spawn is its own group — the float alone would
re-light the panel when the first of two concurrent spawns ends. Each exclusive
spawn registers the release as its per-display topology restore, so the
registry still times every release (§6.1) and the last one out re-lights only
what the first darken actually turned off. Crash-safe by construction: DPMS is
non-persistent, so a dead host leaves nothing to journal — the panel re-lights
on the next local input.

Managed and Attach are deliberately untouched: managed's takeover already
stopped the desktop, and attach may be mirroring a gamescope that is itself
driving the physical panel.
enricobuehler merged commit 8ee963b2b0 into main 2026-08-14 17:14:43 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/punktfunk#227