feat(vdisplay): ship keep_alive=forever (gaming-rig) — Windows MgrState::Pinned

Completes the last §6A-era preset. The Linux registry already resolved forever→Pinned (pure
lifecycle machine); the blockers were the Windows manager, the mgmt reject, and the console tag:

- Windows manager: new `MgrState::Pinned { mon }` — the last-released monitor under keep_alive=forever
  is kept indefinitely (like Lingering but the linger timer never fires). A reconnect preempts +
  recreates it (same as Lingering — a reused IddCx swap-chain is dead), snapshot reports "pinned",
  and `force_release` (POST /display/release, the §8 escape hatch) frees a pinned monitor. release()
  branches on the new `keep_alive_forever()`; all MgrState matches made exhaustive over Pinned.
- mgmt PUT /display/settings: stop rejecting keep_alive=forever (now honored on both platforms with a
  release path). OpenAPI regenerated.
- web: un-disable the gaming-rig preset (DISABLED_PRESETS now empty) — one-click applies.

Linux paths + web/tsc/openapi green; 47 vdisplay tests pass. The Windows manager.rs is #[cfg(windows)]
(not compilable on the Linux dev box) — build-verified + on-glass validation on .173 to follow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-05 17:26:43 +00:00
parent a0546b36b6
commit ccbd7e8880
4 changed files with 85 additions and 55 deletions
+4 -3
View File
@@ -572,9 +572,10 @@ const apiErrorMessage = (err: unknown): string | undefined => {
return err ? String(err) : undefined;
};
/** `gaming-rig` expands to `keep_alive: forever`, which the host still rejects (Windows has no
* Pinned state yet) — surface it, but disabled, rather than let the one-click apply 400. */
const DISABLED_PRESETS: ReadonlySet<string> = new Set(["gaming-rig"]);
/** Presets the host can't honor yet (one-click apply would 400) are surfaced but disabled. Empty
* now that `gaming-rig` (`keep_alive: forever`) ships: the display is Pinned (Linux + Windows) and
* freed via Release. */
const DISABLED_PRESETS: ReadonlySet<string> = new Set<string>();
const PRESET_LABEL: Record<string, () => string> = {
custom: m.display_preset_custom,