From 3594bc029e7473f0c129ee1ee0d6aaad3a3420db Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 31 Jul 2026 19:31:34 +0200 Subject: [PATCH] fix(host): a host with no host.env streams a screen, not a test pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `PUNKTFUNK_VIDEO_SOURCE` had no default, and unset fell through to the synthetic test pattern. That was invisible while the systemd unit made host.env mandatory — you could not start the host without the file, and every copy of the file sets `virtual`. Now that the unit treats host.env as optional (020306b5), unset is reachable, and a packaged Linux install with no host.env would serve a test pattern to Moonlight clients. Default it to `virtual`, the flagship per-client virtual output — which is what every setter in the tree already says: packaging/bazzite/host.env, packaging/kde/host.env, both host.env.example files, the host.env the Windows installer generates, and scripts/bench/gpu-stream.sh. Nothing sets `synthetic` and nothing relied on unset. `synthetic` stays reachable by naming it, since any unrecognised value lands in that arm. Only one of the three readers changes behaviour: stream.rs's `== Some("virtual")` now matches when the variable is absent. gamestream/mod.rs already folded None and Some("virtual") into the same `_` arm for the HDR capability probe, and rtsp.rs only tests for `portal`. The native punktfunk/1 plane never read this — it always builds a virtual display — so this only ever affected GameStream/Moonlight sessions. host.env.example's header no longer claims this key is the one non-optional setting; the file is now overrides only, and the host runs without it. Co-Authored-By: Claude Opus 5 (1M context) --- crates/pf-host-config/src/lib.rs | 10 ++++++++-- docs-site/content/docs/configuration.md | 2 +- scripts/host.env.example | 10 ++++++---- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/crates/pf-host-config/src/lib.rs b/crates/pf-host-config/src/lib.rs index 5652add0..4595b726 100644 --- a/crates/pf-host-config/src/lib.rs +++ b/crates/pf-host-config/src/lib.rs @@ -101,7 +101,9 @@ pub struct HostConfig { pub chacha20: bool, /// `PUNKTFUNK_PERF` — per-stage timing instrumentation. pub perf: bool, - /// `PUNKTFUNK_VIDEO_SOURCE` — GameStream video source select (`virtual` / `portal` / unset → synthetic). + /// `PUNKTFUNK_VIDEO_SOURCE` — GameStream video source select. `virtual` (the default — a + /// per-client virtual output at the client's own mode) / `portal` (capture an existing + /// monitor); anything else, including the literal `synthetic`, gets the test pattern. pub video_source: Option, /// `PUNKTFUNK_CAPTURE_MONITOR` — pin capture at a NAMED physical monitor (`DP-1`, `HDMI-A-2`), /// instead of creating a virtual display or taking whichever head the portal hands back. The @@ -247,7 +249,11 @@ impl HostConfig { perf: flag("PUNKTFUNK_PERF"), // Default ON while the interval-stutter field program runs (see the field doc). stall_probes: env_on("PUNKTFUNK_STALL_PROBES").unwrap_or(true), - video_source: val("PUNKTFUNK_VIDEO_SOURCE"), + // Defaults to `virtual` — the flagship per-client virtual output. It used to be unset, + // which fell through to the synthetic test pattern: fine for a dev box that always has + // a host.env, wrong for a packaged install, whose unit no longer requires that file at + // all. `synthetic` is still reachable by naming it (any unrecognised value lands there). + video_source: val("PUNKTFUNK_VIDEO_SOURCE").or_else(|| Some("virtual".to_string())), // Trimmed + emptied-to-None: `PUNKTFUNK_CAPTURE_MONITOR=` in a host.env means "not // set", not "match the monitor named empty string". capture_monitor: val("PUNKTFUNK_CAPTURE_MONITOR") diff --git a/docs-site/content/docs/configuration.md b/docs-site/content/docs/configuration.md index 7a26a5cc..b45ef24e 100644 --- a/docs-site/content/docs/configuration.md +++ b/docs-site/content/docs/configuration.md @@ -59,7 +59,7 @@ redundant or stale. | Setting | Values | Meaning | |---|---|---| | `PUNKTFUNK_COMPOSITOR` | `kwin` · `mutter` · `gamescope` · `wlroots` · `hyprland` (aliases: `kde`/`plasma`, `gnome`, `sway`/`wlr`) | Which backend creates the virtual display. `wlroots` is sway/River; `hyprland` is its own backend. **Leave unset.** Setting it **pins** the backend and turns session-following **off** — per connect *and* mid-stream, so a Desktop ↔ Gaming switch kills the stream instead of being followed. For CI/tests and dedicated single-session appliances only. | -| `PUNKTFUNK_VIDEO_SOURCE` | `virtual` · `portal` | **GameStream/Moonlight sessions only** — it has no effect on the native `punktfunk/1` plane. `virtual` creates a per-client display at the client's exact mode (the normal choice); `portal` captures an existing monitor instead, and is what the GNOME 50+ HDR monitor mirror needs — see [HDR](/docs/hdr#linux--gnome). To stream a physical monitor to a Punktfunk app, use `PUNKTFUNK_CAPTURE_MONITOR` below, or the console's **Streamed screen**. | +| `PUNKTFUNK_VIDEO_SOURCE` | `virtual` (default) · `portal` | **GameStream/Moonlight sessions only** — it has no effect on the native `punktfunk/1` plane. `virtual` creates a per-client display at the client's exact mode (the normal choice); `portal` captures an existing monitor instead, and is what the GNOME 50+ HDR monitor mirror needs — see [HDR](/docs/hdr#linux--gnome). To stream a physical monitor to a Punktfunk app, use `PUNKTFUNK_CAPTURE_MONITOR` below, or the console's **Streamed screen**. | | `PUNKTFUNK_CAPTURE_MONITOR` | a connector name (`HDMI-A-1`, `DP-2`, …) | Stream a **physical** monitor this host already has instead of creating a virtual display — see [Streamed screen](/docs/virtual-displays#stream-a-real-monitor-instead). List the names with `punktfunk-host list-monitors`. Setting it here **outranks the web console's** choice, so an appliance stays aimed where its operator pointed it; leave it unset to steer from the console. A name that matches no monitor fails the session loudly rather than streaming a different screen. Linux only. | | `PUNKTFUNK_ZEROCOPY` | `1` · `0` *(default on)* | GPU zero-copy capture→encode (dmabuf → CUDA → NVENC, or D3D11 on Windows). **On by default** — no need to set it; it falls back to a CPU path automatically. Set `0` to force the CPU path. One exception: Windows **Intel/QSV** keeps the CPU path by default until zero-copy is validated on Intel hardware — set `1` to try it there. | | `PUNKTFUNK_INPUT_BACKEND` | `libei` · `kwin` · `gamescope` · `wlr` | How input is injected. `kwin` (KWin fake-input) for KDE — direct injection with no portal approval dialog, so it also works on a headless KDE box; `libei` (the RemoteDesktop portal) for GNOME; `gamescope` for Bazzite/gamescope; `wlr` for Sway/wlroots **and Hyprland**. Auto-detected with the compositor; a value that isn't one of the four is ignored and detection runs anyway. | diff --git a/scripts/host.env.example b/scripts/host.env.example index abd3e9a3..1f17208a 100644 --- a/scripts/host.env.example +++ b/scripts/host.env.example @@ -3,7 +3,8 @@ # YOU BARELY NEED THIS FILE. The host AUTO-DETECTS the live session per connect — which compositor # is running (KWin / Mutter / sway / Hyprland / gamescope), its Wayland socket, session bus, and the # matching input backend — and FOLLOWS the box when it switches between a desktop and Steam Gaming -# Mode, even mid-stream. Everything below except PUNKTFUNK_VIDEO_SOURCE is an optional override. +# Mode, even mid-stream. EVERYTHING below is an optional override — the host runs with sane +# defaults if this file does not exist at all (its systemd unit no longer requires it). # # Two rules that save debugging sessions: # * Keys are CASE-SENSITIVE. `punktfunk_gamescope_attach=1` sets nothing — use the exact @@ -13,9 +14,10 @@ # Mode mid-stream then kills the stream instead of being followed), and stale session vars # point detection at dead sockets. Those knobs are for CI and dedicated appliances (below). -# Video source: `virtual` creates a per-client virtual output at the client's exact -# resolution+refresh (the flagship mode); `portal` captures an existing monitor. -PUNKTFUNK_VIDEO_SOURCE=virtual +# Video source (GameStream/Moonlight sessions only): `virtual` creates a per-client virtual +# output at the client's exact resolution+refresh (the flagship mode, and the default); +# `portal` captures an existing monitor. +#PUNKTFUNK_VIDEO_SOURCE=virtual # GPU zero-copy capture (dmabuf → CUDA → NVENC / VAAPI / Vulkan) is ON by default and falls back to # CPU automatically. No need to set it. Set to 0 only to force the CPU path.