Files
punktfunk/docs-site/content/docs/virtual-displays.md
T
enricobuehler bbd98241e4 feat(vdisplay): display-management policy surface (Stage 0)
A user-configurable policy layer above the per-compositor VirtualDisplay
backends: keep-alive, topology, conflict, identity, layout, max-displays —
persisted to display-settings.json, editable from the web console, applied
per connect. Design: design/display-management.md.

Stage 0 stands up the surface and wires the two behaviors the existing code
can already express — the Windows monitor linger duration and the
"make the streamed output the sole desktop" topology — through it; every
other option is stored + echoed but not yet enforced (later stages). An
unconfigured host (no display-settings.json) keeps today's exact behavior.

- vdisplay/policy.rs: pure DisplayPolicy + 5 presets + JSON store (gpu-settings
  pattern) + EffectivePolicy; 9 unit tests.
- vdisplay.rs: resolve_topology(Auto); apply_session_env drives *_VIRTUAL_PRIMARY
  from the policy only when a settings file exists.
- windows/manager.rs: linger_ms() + should_isolate() read the policy when configured.
- mgmt: GET/PUT /api/v1/display/settings (bearer-only); PUT rejects keep_alive
  forever until the lifecycle stage. OpenAPI regenerated.
- web console: Host → Virtual displays card (preset picker + custom fields); en+de.
- docs-site: virtual-displays.md + configuration.md cross-links.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 19:44:18 +00:00

7.6 KiB

title, description
title description
Virtual displays Control how punktfunk creates, keeps alive, and arranges the virtual displays it streams — presets, keep-alive, exclusive vs. extend, and persistent per-client scaling.

When a client connects, punktfunk creates a virtual display sized to exactly that client's resolution and refresh, renders your desktop or game onto it, and streams it. This page is about the policy for that display: how long it survives a disconnect, whether it takes over your physical monitors, what happens when a second client connects, and how desktop environments remember per-client settings like scaling.

You set this policy in the web console (Host → Virtual displays), or by editing ~/.config/punktfunk/display-settings.json directly (%ProgramData%\punktfunk\display-settings.json on Windows). A change applies to the next connection — a running session keeps the display it opened on.

You rarely need to touch this. The default behavior matches how punktfunk has always worked. 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).

Pick a preset

A preset is the easy way in — select one in the console and you're done. Each expands to a bundle of 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.)
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.

Options reference

Choose Custom in the console to set these directly.

Keep alive

How long the virtual display survives after your last session disconnects. On a gamescope game host, this also keeps the game itself running so you can reconnect straight back into it.

  • 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.)

Default: 10 seconds. Windows has always lingered 10 s; the Linux backends previously tore down immediately — a short linger makes reconnects smoother on both.

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 use in person — use Shared desktop there instead.

Topology

What punktfunk does with your monitor layout while it streams.

  • Extend — add the virtual display alongside your real monitors; touch nothing else.
  • Primary — make the virtual display your primary output; your physical monitors stay on.
  • Exclusive — the virtual display becomes your only enabled output (physical monitors are disabled, then restored when streaming ends). This is what makes the streamed surface be the desktop, so panels and windows land on it.
  • Automatic (default) — Exclusive on Windows and on an auto-detected KDE/GNOME desktop ("stream this desktop" means the streamed output is the desktop); Extend when you've pinned a specific compositor with PUNKTFUNK_COMPOSITOR (a test/CI posture).

Per-backend support:

KWin Mutter/GNOME Sway/wlroots Windows
Extend
Primary ⚠️ treated as Extend (following release)
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).
  • 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.

Persistent scaling

Set your display scaling once and have it stick across reconnects. This works by giving each client a stable display identity, so your desktop environment keys its per-monitor settings to it.

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.
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.

Legacy environment knobs

These PUNKTFUNK_* variables still work, but the console (and display-settings.json) supersede them — when a settings file exists, it wins.

Legacy knob Now expressed as
PUNKTFUNK_MONITOR_LINGER_MS Keep alive → duration (Windows)
PUNKTFUNK_NO_ISOLATE Topology → Extend (Windows)
PUNKTFUNK_KWIN_VIRTUAL_PRIMARY / PUNKTFUNK_MUTTER_VIRTUAL_PRIMARY Topology → Exclusive (when set) / Extend (when 0)

Troubleshooting

My physical monitors stayed off after I disconnected. You have keep-alive set together with Exclusive topology — the display (and your isolated desktop) is being kept for the linger window. Release it from the console (Host → Virtual displays), or switch to the Shared desktop preset so streaming never disables your real monitors.

The virtual output shows only my wallpaper. Your topology is Extend, so the streamed display is an empty extension. Use Primary or Exclusive so your desktop actually lands on it.

KWin virtual outputs need KWin ≥ 6.5.6. Older KWin can't create the virtual output at all — see requirements.