feat(vdisplay): lifecycle state machine + display state/release API (Stage 1)

Stage 1 of design/display-management.md — the lifecycle core + the display
management surface:

- vdisplay/lifecycle.rs: pure per-slot state machine (Idle/Active{refs}/
  Lingering{until}/Pinned) with acquire/release/expiry/force-release
  transitions. No I/O, no OS types — the platform-neutral distillation of the
  Windows manager's model. Unit + a 200k-iteration seeded property walk
  (no leaks / double-frees / refcount underflow across arbitrary interleavings).
- vdisplay/registry.rs: neutral snapshot/release facade over the per-OS
  lifecycle owners. Windows reads/controls the VirtualDisplayManager; Linux
  keep-alive (a per-session pool) lands in a following increment (needs GPU-box
  validation).
- windows/manager.rs: additive snapshot() + force_release() (no behavior change
  to the on-glass-validated path).
- mgmt: GET /api/v1/display/state (live/kept displays) + POST /api/v1/display/release
  (tear down lingering/pinned now; refuses active). OpenAPI regenerated.
- web console: Virtual displays card gains a live-display list (polled) with
  per-row + release-all buttons and a linger countdown.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-04 20:32:03 +00:00
parent bbd98241e4
commit 87f0ce7997
9 changed files with 889 additions and 1 deletions
+9
View File
@@ -68,6 +68,15 @@
"display_save": "Save",
"display_effective": "In effect",
"display_pending_note": "Conflict handling, identity, and layout are stored but not enforced yet — they arrive in later releases.",
"display_live": "Live displays",
"display_none_live": "No virtual displays right now.",
"display_state_active": "Active",
"display_state_lingering": "Lingering",
"display_state_pinned": "Pinned",
"display_release_btn": "Release",
"display_release_all": "Release all kept",
"display_expires_in": "tears down in {sec}s",
"display_sessions": "{count} streaming",
"clients_title": "Paired clients",
"clients_empty": "No paired clients yet.",
"clients_name": "Name",