docs(display-management): Stage 8 consolidation — docs page, README, CLAUDE.md, host.env

Bring the user-facing + project docs in line with the shipped, on-glass-validated state (Stages
0-5 §6A + keep-alive hardening + gaming-rig) ahead of a merge decision:

- docs-site/virtual-displays.md: drop the now-false "stored but not yet enforced / following
  release" caveats — conflict handling, per-client identity + KDE scaling round-trip, and §6A
  multi-monitor layout are all live; gaming-rig/forever ships (freed via Release); document the
  reconnect-always-resumes + deliberate-quit-skips-linger behavior and the PUNKTFUNK_IDLE_TIMEOUT_MS
  knob. KDE persistent scaling →  today (validated); Windows primary → shipped; Sway exclusive
  stays "following release".
- README: a "displays you configure, not just create" differentiator bullet.
- CLAUDE.md: the display-management invariant now reflects Stages 0-5 shipped (all axes enforced,
  forever/Pinned, hardened reconnect) instead of "Stage 0 shipped".
- host.env.example: document PUNKTFUNK_IDLE_TIMEOUT_MS + that display policy lives in the console.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-05 17:50:32 +00:00
parent fad1e01408
commit 468a60c88a
3 changed files with 51 additions and 19 deletions
+5
View File
@@ -29,6 +29,11 @@ protocol, FEC, and crypto, linked into the host and every client over a stable C
- **Your device's exact mode.** For each client that connects, the host spins up a virtual display
sized to that device — 1080p60 to a laptop, 1440p120 to a desktop, 4K to a TV, all at once. No
letterboxing, no scaling, no rearranging your real monitors.
- **Displays you configure, not just create.** Keep a game's display (and the game) alive across
disconnects so a reconnect drops straight back in; make the stream your sole desktop or extend
alongside your monitors; let several devices become monitors of one desktop; keep each client's
scaling. One-click presets in the console — a dedicated couch box, a shared desktop, a multi-monitor
workstation. See [Virtual displays](docs-site/content/docs/virtual-displays.md).
- **A real virtual display on Windows, too.** On Linux the host uses per-compositor virtual outputs;
on Windows you get the same on-the-fly virtual display — at the client's exact mode, no physical
monitor or dummy HDMI plug, even on the secure desktop (UAC / lock screen). It also has **its own
+37 -19
View File
@@ -18,11 +18,12 @@ opened on.
> Reach for a preset when you want a specific experience — a dedicated couch/gaming box, a desktop
> you also use in person, or a multi-monitor workstation.
> **What's live today:** this release wires **keep-alive** (linger duration) and **topology**
> (extend / primary / exclusive). The other options below — conflict handling, identity/scaling
> persistence on Linux, and multi-monitor layout — are **stored but not yet enforced**; they arrive
> in following releases. The console marks them accordingly. Windows already persists per-client
> scaling (see [Persistent scaling](#persistent-scaling)).
> **What's live today:** **keep-alive** (linger, or **forever**), **topology** (extend / primary /
> exclusive), **conflict handling**, **per-client identity + persistent scaling** (Windows *and*
> KDE/KWin), and **multi-monitor layout** (several clients as monitors of one desktop) are all
> enforced. A reconnect always resumes the kept display — even a fast one — instead of spawning a
> second. The remaining gaps are noted inline: the Linux `primary` physical-keep *effect*, Sway
> `exclusive`, and multi-display for a *single* client (that last is the next stage).
## Pick a preset
@@ -32,7 +33,7 @@ the individual options documented further down.
| Preset | What it's for |
|---|---|
| **Default** | Today's behavior. A short linger absorbs reconnects, the streamed output becomes the sole desktop, and extra clients each get their own view. |
| **Gaming rig** | A dedicated couch/headless box. The game and its display survive disconnects indefinitely, and whoever connects takes the box over. *(Arrives with the keep-alive stage.)* |
| **Gaming rig** | A dedicated couch/headless box. The game and its display survive disconnects indefinitely (keep-alive **forever**), and whoever connects takes the box over. Release it from the console when you're done. |
| **Shared desktop** | A desktop you also use in person. punktfunk never blanks your real monitors and never leaves a ghost display behind; concurrent viewers each get a view. |
| **Hot-desk** | One user at a time with fast reattach — roaming between your own devices. A second user is told the box is busy, and each device+resolution keeps its own scaling. |
| **Workstation** | The multi-monitor daily driver. Your displays come back exactly where you arranged them, with per-client identity and an exclusive desktop. |
@@ -49,12 +50,19 @@ this also keeps the **game itself running** so you can reconnect straight back i
- **Off** — tear the display down at session end (nothing lingers).
- **A duration** (seconds) — keep it for that long; a reconnect inside the window drops you straight
back in, with no re-negotiation and no desktop reshuffle.
- **Forever** — keep it until you stop the host or release it from the console. *(Arrives with the
keep-alive lifecycle stage; the console won't let you save it before then.)*
- **Forever** — keep it until you stop the host or **release it** from the console (Host → *Virtual
displays* → *Release*). This is the gaming-rig model.
Default: **10 seconds**. Windows has always lingered 10 s; the Linux backends previously tore down
immediately — a short linger makes reconnects smoother on both.
**A reconnect always resumes the kept display** — the host recognises your device and hands back the
same display, even if you reconnect a second or two after dropping (before it has noticed you left).
**Deliberately quitting** (closing the client, not a network drop) tears the display down at once,
skipping the linger, so you don't leave a ghost behind. How quickly a *dropped* client is noticed is
the QUIC idle timeout — 8 s by default, tunable with `PUNKTFUNK_IDLE_TIMEOUT_MS` (see
[Legacy environment knobs](#legacy-environment-knobs)) if you want kept displays freed sooner.
> **Keep-alive + Exclusive keeps your physical monitors dark after you disconnect**, until the
> linger expires or you release the display. That's intentional for a dedicated gaming box, but
> don't set a long/forever keep-alive together with Exclusive on a machine whose monitors you also
@@ -78,23 +86,25 @@ Per-backend support:
| | KWin | Mutter/GNOME | Sway/wlroots | Windows |
|---|---|---|---|---|
| Extend | ✅ | ✅ | ✅ | ✅ |
| Primary | ✅ | ✅ | ⚠️ treated as Extend | ✅ *(following release)* |
| Exclusive | ✅ | ✅ | *(following release)* | ✅ |
| Primary | ✅ | ✅ | ⚠️ treated as Extend | ✅ |
| Exclusive | ✅ | ✅ | following release | ✅ |
### Conflict handling · identity · layout
These are **stored but not yet enforced** — they're documented here so you know what's coming and
can set them ahead of the release that turns them on:
- **Conflict handling** — what happens when a *different* client connects while one is already
streaming and asks for a different resolution: give it its own display (**separate**), take the
box over (**steal**), share the existing display at its current mode (**join**), or refuse it
(**reject**).
(**reject**). On Linux, `separate` gives each client its own display on the shared desktop. On
**Windows** a second client is **rejected** (a clean "host busy") even under `separate` — two
clients can't yet share one virtual display's capture there (that's a later stage), so the live
session is protected instead. A same-client *reconnect* never conflicts — it resumes.
- **Identity** — whether each client gets a **stable display identity** so your desktop environment
remembers its settings (see below): one shared identity, one **per client**, or one **per client +
resolution**.
- **Layout / max displays** — how multiple virtual displays are arranged (for multi-monitor), and an
upper bound on how many can be live at once.
remembers its settings (see [Persistent scaling](#persistent-scaling)): one shared identity, one
**per client**, or one **per client + resolution**.
- **Layout / max displays** — when several clients each become a monitor of one desktop, this places
them side by side (**auto**) or exactly where you arrange them in the console (**manual**, keyed to
each client), up to **max displays**. Arrange them under Host → *Virtual displays* once two or more
are streaming.
## Persistent scaling
@@ -104,7 +114,7 @@ client a *stable display identity*, so your desktop environment keys its per-mon
| Host | Supported | How |
|---|---|---|
| **Windows** | ✅ today | Connect, set scaling in Settings while streaming — Windows remembers it per client. |
| **KDE / KWin** | ⏳ following release | A stable per-client output name lets KWin persist scale/mode per client. |
| **KDE / KWin** | ✅ today | Set scaling in System Settings while streaming; KWin keys it to a stable per-client output name and reapplies it on reconnect. Validated live (150 %/125 % survive a full disconnect + reconnect). |
| **GNOME / Mutter** | ❌ | GNOME's virtual-monitor API exposes no stable identity to key config on. |
| **Sway / wlroots** | ❌ | Headless outputs can't carry a stable identity; pin scale in your sway config instead. |
@@ -119,6 +129,14 @@ them — when a settings file exists, it wins.
| `PUNKTFUNK_NO_ISOLATE` | **Topology** → Extend *(Windows)* |
| `PUNKTFUNK_KWIN_VIRTUAL_PRIMARY` / `PUNKTFUNK_MUTTER_VIRTUAL_PRIMARY` | **Topology** → Exclusive (when set) / Extend (when `0`) |
One knob has no console equivalent — it's a transport tuning, not display policy:
- **`PUNKTFUNK_IDLE_TIMEOUT_MS`** (host, default `8000`) — how long the host waits before declaring a
*dropped* client gone, which is when a kept display starts its linger (or is freed). Lower it (e.g.
`3000`) to reclaim kept displays sooner after an ungraceful drop; it's clamped to ≥1 s and its
keep-alive ping scales with it, so a live session never false-disconnects. A deliberate quit is
instant regardless. Also `--idle-timeout-ms` on `punktfunk1-host`.
## Troubleshooting
**My physical monitors stayed off after I disconnected.** You have keep-alive set together with
+9
View File
@@ -48,6 +48,15 @@ PUNKTFUNK_ZEROCOPY=1
#PUNKTFUNK_INPUT_BACKEND=libei # wlr | libei | gamescope | uinput
#PUNKTFUNK_FEC_PCT=20 # video FEC overhead percent
#PUNKTFUNK_PERF=1 # per-stage timing logs
# Display-management policy (keep-alive · topology · conflict · identity · layout · max) is set in the
# web console (Host → Virtual displays) → ~/.config/punktfunk/display-settings.json, NOT here; a
# settings file supersedes the legacy PUNKTFUNK_MONITOR_LINGER_MS / _NO_ISOLATE / *_VIRTUAL_PRIMARY
# knobs. One transport knob has no console equivalent:
#PUNKTFUNK_IDLE_TIMEOUT_MS=8000 # disconnect-detection latency: how long before a DROPPED client is
# declared gone (a kept display then starts its linger, or frees).
# Lower (e.g. 3000) to reclaim kept displays sooner after an
# ungraceful drop; clamped ≥1s, keep-alive ping scales with it so a
# live session never false-disconnects. A deliberate quit is instant.
# Full-chroma 4:4:4 (HEVC Range Extensions) — sharper text/desktop, no chroma loss. Honored only on
# the punktfunk/1 native path when the client advertises 4:4:4 AND the GPU supports it (probed; else
# the session stays 4:2:0). HEVC-only; independent of 10-bit. NVENC (NVIDIA) is the validated path;