feat(vdisplay): enumerate the host's physical monitors

P0 of per-monitor capture (design/per-monitor-portal-capture.md): nothing
in the tree could answer "what heads does this host have?", which both a
monitor pin and a console picker need first.

One read per compositor, each beside the code that already speaks that
dialect: KWin's kde_output_device_v2 (the topology session now also records
geometry + scale), Mutter's DisplayConfig.GetCurrentState, swaymsg
get_outputs, hyprctl monitors. Logical geometry throughout, because x/y is
what identifies a head — two monitors can share a size but never an origin.

PUNKTFUNK_CAPTURE_MONITOR parses here and is reported at startup, loudly
including that it is not yet enforced: a knob that is read but inert has to
say so, or "it didn't work" reads as a bug. GET /display/monitors always
answers 200 with an explained empty list, so a compositor-less host renders
as "nothing to pick", not as a broken console.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-27 23:51:41 +02:00
co-authored by Claude Opus 5
parent 3721b6816d
commit 95e3314d9a
11 changed files with 578 additions and 0 deletions
+1
View File
@@ -188,6 +188,7 @@ fn api_router_parts() -> (Router<Arc<MgmtState>>, utoipa::openapi::OpenApi) {
.routes(routes!(display::get_display_settings))
.routes(routes!(display::set_display_settings))
.routes(routes!(display::get_display_state))
.routes(routes!(display::get_display_monitors))
.routes(routes!(display::release_display))
.routes(routes!(display::set_display_layout))
.routes(routes!(