From ea0c61fb0f6e6087449517d6f3e362b4424e53ea Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Mon, 27 Jul 2026 23:23:33 +0200 Subject: [PATCH] docs: streaming a real monitor, and the CLI that names one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The console grew a "Streamed screen" card and the host grew a PUNKTFUNK_CAPTURE_MONITOR knob and a list-monitors command, and the docs knew about none of it — a control with no explanation anywhere. virtual-displays.md gets the feature section: what it is for, that the monitor is never touched, that its resolution wins and a client scales, that a bad name is a hard error rather than a different screen, and that there is no chooser dialog on any of the four backends (which is what makes it work unattended). Plus the three troubleshooting entries the shape of the feature predicts: settings that do nothing while mirroring, a console card the env var has locked, and a pin that names no head. host-cli.md documents list-monitors and mirror-test; configuration.md gets the knob, including that it outranks the console on purpose; running-as-a-service.md gets the desktop-session drop-in and states that the host needs nothing exported to find its session. Co-Authored-By: Claude Opus 5 (1M context) --- docs-site/content/docs/configuration.md | 1 + docs-site/content/docs/host-cli.md | 27 ++++++ .../content/docs/running-as-a-service.md | 29 +++++++ docs-site/content/docs/virtual-displays.md | 83 +++++++++++++++++++ 4 files changed, 140 insertions(+) diff --git a/docs-site/content/docs/configuration.md b/docs-site/content/docs/configuration.md index 84dc0952..f811e8f2 100644 --- a/docs-site/content/docs/configuration.md +++ b/docs-site/content/docs/configuration.md @@ -36,6 +36,7 @@ redundant or stale. |---|---|---| | `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` | `virtual` creates a per-client display at the client's exact mode (the normal choice). `portal` captures an existing monitor instead. | +| `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` · `gamescope` · `wlr` · `uinput` | How input is injected. `libei` for GNOME/KDE, `gamescope` for Bazzite/gamescope, `wlr` for Sway/wlroots **and Hyprland**. Auto-detected with the compositor. | | `PUNKTFUNK_ENCODER` | `auto` · `nvenc` · `vaapi` (Linux) · `amf` · `qsv` (Windows) · `software` | Encoder backend. `auto` (default) detects the GPU vendor: NVIDIA→NVENC, AMD→VAAPI/AMF, Intel→VAAPI/QSV. `software` (aliases `sw`/`openh264`) is the GPU-less H.264 path on both platforms — on Windows `auto` falls back to it when no GPU is found; on Linux it is **explicit-only** (`auto` never picks it). On a multi-GPU Windows box a forced hardware backend whose vendor contradicts the selected GPU (web-console preference) is **overridden** — the adapter wins and the host logs a warning; remove the stale pin. | diff --git a/docs-site/content/docs/host-cli.md b/docs-site/content/docs/host-cli.md index d6863cc5..36f7edd8 100644 --- a/docs-site/content/docs/host-cli.md +++ b/docs-site/content/docs/host-cli.md @@ -86,6 +86,33 @@ hosts from another machine with `punktfunk-probe --discover`. Where multicast do Docker/VLAN setups), pass `--no-mdns` (or set `PUNKTFUNK_MDNS=0`) and add the host in the client by address instead. +## `list-monitors` + +`punktfunk-host list-monitors` prints the **physical** monitors this host's compositor has, by +connector name — which is how you name one for [Streamed +screen](/docs/virtual-displays#stream-a-real-monitor-instead) (in the console, or as +`PUNKTFUNK_CAPTURE_MONITOR`). + +```sh +punktfunk-host list-monitors +``` + +``` +Kwin: + HDMI-A-1 1920x1080@60 at +0,+0 scale 1 Dell U2412M [primary] + DP-2 2560x1440@144 at +1920,+0 scale 1 ACME 27 [PINNED] +``` + +Tags flag what's worth knowing before you pick: `primary`, `disabled` (nothing to stream), `punktfunk +virtual display` (one of ours, not a real head), and `PINNED` for the one currently selected. Linux +only — it reads the live compositor, so run it in (or with the environment of) the session you want +to stream. + +`punktfunk-host mirror-test --monitor [--seconds N] [--cpu]` then proves the whole path — +mirror, capture, frames — with no client involved. It reports the first frame, the frame count and +the negotiated size. Screen recording is damage-driven, so move the mouse on the host while it runs; +an idle desktop legitimately yields almost nothing. + ## `detect-conflicts` `punktfunk-host detect-conflicts` reports other Moonlight-compatible hosts (Sunshine, Apollo, and diff --git a/docs-site/content/docs/running-as-a-service.md b/docs-site/content/docs/running-as-a-service.md index bb06b16b..38521ae3 100644 --- a/docs-site/content/docs/running-as-a-service.md +++ b/docs-site/content/docs/running-as-a-service.md @@ -26,6 +26,35 @@ systemctl --user enable --now punktfunk-host The host now starts whenever you log in. Check it with `systemctl --user status punktfunk-host`. +**You don't need to export anything for it.** The host finds the live compositor session itself on +every connect and works out where to reach it (`WAYLAND_DISPLAY`, `XDG_RUNTIME_DIR`, the session bus, +sway's `SWAYSOCK`, Hyprland's instance signature) from the running compositor — so `host.env` is for +policy, not session plumbing, and `systemctl --user import-environment` is not a prerequisite. + +### Restart the host with your desktop + +Add one drop-in so the host follows your session's lifetime: + +```sh +mkdir -p ~/.config/systemd/user/punktfunk-host.service.d +# /usr/share/punktfunk/ on Fedora/Arch, /usr/share/punktfunk-host/ on Debian/Ubuntu, +# scripts/ in a source checkout +cp /usr/share/punktfunk/punktfunk-host-desktop-session.conf \ + ~/.config/systemd/user/punktfunk-host.service.d/desktop-session.conf +systemctl --user daemon-reload +systemctl --user reenable punktfunk-host +systemctl --user restart punktfunk-host +``` + +Without it, restarting Plasma or GNOME — a crash, a log out and back in, "restart the shell" — leaves +the host running against a compositor that no longer exists. It keeps listening and answering, and +every session after that fails at capture, which is a confusing way to find out. The drop-in makes a +compositor restart a host restart. + +Skip it on the headless/appliance route below (which has its own session unit), and on Sway or +Hyprland, which don't hand their session to systemd — start the host from the compositor's config +there instead, so it comes and goes with the session. + ## B. A headless, always-on host To run with **no monitor and no login** — a machine in a closet that's always ready — you need two diff --git a/docs-site/content/docs/virtual-displays.md b/docs-site/content/docs/virtual-displays.md index 00f312a5..ae70ec09 100644 --- a/docs-site/content/docs/virtual-displays.md +++ b/docs-site/content/docs/virtual-displays.md @@ -18,6 +18,10 @@ opened on. > Reach for a preset when you want a specific experience — a dedicated box you only stream from, a > desktop you also use in person, or a multi-monitor workstation. +If you'd rather stream a monitor the host **already has** than have punktfunk make one, that's a +different setting and it turns most of this page off — see +[Stream a real monitor instead](#stream-a-real-monitor-instead) right below. + > **What's live today:** **keep-alive** (linger, or **forever**), **topology** (extend / primary / > exclusive), **conflict handling**, **per-client identity + persistent scaling** (Windows, KDE/KWin > *and* GNOME/Mutter), and **multi-monitor layout** (several clients as monitors of one desktop) are @@ -25,6 +29,71 @@ opened on. > 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). +## Stream a real monitor instead + +Sometimes you don't want a new screen — you want *that* screen. A shop-floor PC on a wall mount, a +lab bench machine, a media box whose TV output you'd like to watch from the couch: what matters is +seeing the monitor that's already there, exactly as the person in front of it sees it. + +Set **Host → *Virtual displays* → Streamed screen** to one of the listed monitors and punktfunk stops +creating a virtual display altogether. It streams that physical monitor instead, and every client +sees it at *its* resolution — you're a viewer of a screen, not the owner of your own. + +- The monitor is **never touched**. punktfunk doesn't resize it, move it, disable it or restore it — + it only watches. Keep-alive, topology and the multi-monitor layout options above simply don't + apply, because there's no display of ours to apply them to. +- **The resolution is the monitor's**, not yours. A client asking for a different one is politely + told no and scales its own picture — reconfiguring the display someone is sitting at would be + rude, and the mid-stream resize machinery is switched off for this reason. +- **Every client sees the same screen.** Two clients means two viewers of one monitor, not two + desktops. +- Naming a monitor that isn't there is a **hard error**, not a fallback: the session fails with + `no monitor named "DP-9" — this host has: HDMI-1`. Showing you the wrong screen would be worse + than showing you none. +- Choosing **Virtual screen (default)** in the same card puts you back on the normal path. + +Supported on **KDE/KWin**, **GNOME/Mutter**, **Sway/wlroots** and **Hyprland** — each through the +compositor's own screen-recording API, so there is **no chooser dialog** and nothing to click. That +matters most for a host running unattended as a [service](/docs/running-as-a-service): a background +`systemd --user` daemon has nobody to answer a permission prompt, so the monitor has to be +configuration rather than a question. (A nested gamescope session has no physical monitors of its +own, so the setting doesn't appear there.) + +### Naming the monitor from the host + +Monitors are named by **connector** — `HDMI-A-1`, `DP-2`, `eDP-1`. List what this host has: + +```sh +punktfunk-host list-monitors +``` + +``` +Kwin: + HDMI-A-1 1920x1080@60 at +0,+0 scale 1 Dell U2412M [primary] + DP-2 2560x1440@144 at +1920,+0 scale 1 ACME 27 +``` + +To pin it **from the host's configuration** instead of the console — the appliance route — set it in +[`host.env`](/docs/configuration): + +```sh +PUNKTFUNK_CAPTURE_MONITOR=HDMI-A-1 +``` + +The environment variable **wins over the console setting**, deliberately: an operator who declared +the answer in the unit's environment shouldn't have it re-aimed by a click. The console shows the +Streamed screen card as locked while it's set. Leave it unset on a machine you want to steer from the +console. + +Check the whole path — mirror, capture, frames — without a client involved: + +```sh +punktfunk-host mirror-test --monitor HDMI-A-1 --seconds 20 +``` + +Compositor screen recording is **damage-driven**: an idle desktop legitimately produces almost no +frames, so move the mouse on the host while it runs or a working mirror reads as a stall. + ## 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 @@ -252,6 +321,20 @@ landing on a dead stream — and switching between game mode and the KDE / GNOME follows the switch. If a launched game **exits**, a dedicated session ends and returns you to your library; a game mode / desktop session keeps streaming. +**My keep-alive / topology / layout settings do nothing.** Check whether **Streamed screen** is set +to a real monitor. Those options are all about a display punktfunk created; when it's mirroring one +of yours there is nothing of ours to keep alive or rearrange. Switch the card back to *Virtual screen +(default)* to get them back. + +**The console won't let me change Streamed screen.** `PUNKTFUNK_CAPTURE_MONITOR` is set in this +host's [`host.env`](/docs/configuration) and deliberately outranks the console. Unset it (and restart +the host) to choose from the console instead. + +**My session fails with "no monitor named …".** The pinned connector isn't among this host's +monitors — it was renamed, unplugged, or the host is now in a different session. Run +`punktfunk-host list-monitors` on the host to see the real names. punktfunk will not quietly stream a +different screen instead. + **My couch box's TV stayed on the streamed session after I disconnected.** With the **Headless box** preset (keep alive = *forever*), a managed Steam session is held indefinitely so a reconnect resumes instantly — return to game mode on the box (or restart the host) to hand the TV back.