Duplicated the entire v0.20.1 fix bundle verbatim instead of following house convention (each vX.Y.Z.md covers only what changed since the immediately preceding release file — see v0.19.1 -> v0.19.2, neither restates the other). v0.20.1 is a real, already-tagged release with its own notes file; 0.21.0 only needed to add the monitor-streaming feature, the KDE registry fix found while building it, and the CI-only winget fix, with a pointer back to v0.20.1 for the rest. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
6.9 KiB
Update whenever it suits you — the app and the machine you stream from can be updated one at a time, and everything already paired keeps working.
The headline: on Linux, Punktfunk's host can now stream one of your machine's own physical monitors instead of always creating a virtual one. This release also carries a KDE Plasma fix found while building it: on Plasma 6.7 and newer, the host had silently been falling back to a slower, less reliable way of arranging displays on every single session.
New: stream one of your actual monitors, not just a virtual screen
Until now, Punktfunk's Linux host always created its own virtual display to stream from — even on a machine you're sitting in front of, with real monitors already showing something. You can now point it at one of your machine's real monitors instead: pick it from a new Streamed screen card in the console, or pin it once in the host's configuration for an unattended box.
The monitor keeps working exactly as it always did — Punktfunk mirrors it rather than taking it over, so nothing about your own setup changes. It streams at that monitor's own resolution and refresh rate; your device scales to fit. Works with KWin, GNOME, sway and Hyprland, in both the Punktfunk app and Moonlight, and applies whether you connect straight away or pick up a saved display preset later.
A machine you pin from the host's own configuration shows the choice in the console as locked, with the reason, rather than offering a control that would silently lose to it. And on a machine you actually log into day to day (rather than a dedicated streaming box), the host now restarts itself when your desktop session does, so it recovers on its own instead of quietly going stale until the next stream fails to start.
For headless and unattended setups, two new commands help you find and verify the right monitor without a screen attached: punktfunk-host list-monitors lists what the machine has (names, geometry, which one is pinned), and punktfunk-host mirror-test confirms frames are actually flowing from the one you picked.
Fixed: newer KDE Plasma silently fell back to a slower, less reliable way to arrange displays
On current KDE Plasma (6.7 and newer), Punktfunk's direct way of talking to the desktop about display layout stopped seeing any displays at all, with no visible error — every session quietly fell back to an external helper tool that's known to hang under load, exactly the thing the previous release's Plasma fix was meant to stop relying on. Fixed: Punktfunk now recognizes both the way older and newer Plasma releases announce their displays.
Under the hood (for developers)
- Nothing changed on the wire. Streaming protocol stays at version 2, the embeddable C ABI at 13, the Windows virtual-display driver protocol at 6 — 0.18–0.21 hosts and clients keep mixing freely.
GET /display/monitorsis a new endpoint (always 200, an explained empty list on a compositor-less host or a nested gamescope session); the display policy gains an optionalcapture_monitorfield, resolved env-over-policy so a host.env pin outranks a console change. Both are additive. - Monitor enumeration is one read per compositor, beside the code that already speaks its dialect: KWin's
kde_output_device_v2, Mutter'sDisplayConfig.GetCurrentState,swaymsg get_outputs(through aswaymsg_queryhelper after the bare command form mis-parsed-t get_outputsas an unknown command),hyprctl monitors. Logical geometry throughout — origin, not size, is what identifies a head. - Mirroring routes through
vdisplay::openon all four backends, arriving as aVirtualDisplaywithDisplayOwnership::External, which by construction gets no keep-alive, no topology participation and no reuse, and ignores the client's requested mode — a real monitor runs at the mode its owner set. KWin useszkde_screencast'sstream_output(the connector name is the selection, no dialog); Mutter uses the same private ScreenCast session the virtual path drives,RecordMonitorinstead ofRecordVirtual, deliberately outside the topology lock since mirroring adds or removes nothing; sway and Hyprland reuse their existing portal-chooser config (xdpw'sMonitor: NAME,xdph's[SELECTION]screen:NAME), now serialized against the handshake that reads it. A mirrored display refuses a mid-stream reconfigure (Linux only — a pin on Windows streams nothing different). - The GameStream/Moonlight portal source now honors the pin too — it previously always went straight to the portal chooser regardless — reusing the same
MirrorDisplaypath, with which screen a pooled capturer is showing added as a reuse key beside HDR-ness and cursor mode, so a live pin change between two connects can't leave a client streaming the previous screen. - libei absolute-coordinate resolution changed from matching by mode size to
mapping_id→ origin → size → first, since two outputs can share a size but never a top-left, and a mirrored head's region is not the client's stream size at all. A named anchor that matches nothing falls back down the ladder rather than stranding input, and both a miss and a match now log once per distinct answer.punktfunk-host anchor-testexercises the ladder against a live compositor with two same-sized outputs — the one case a unit test can only simulate. SWAYSOCKis now derived rather than required to be inherited — by the compositor's known PID, then the newest socket owned by the host's user — closing the last session variable asystemd --userhost lacked for sway. A new drop-in (PartOf/WantedByongraphical-session.target, opt-in, shipped under/usr/share) restarts a desktop-login host with its desktop, so a Wayland socket and portal connection that die with a Plasma/GNOME restart don't leave the daemon silently unable to recover.- KWin's in-process output-management path now also binds
kde_output_device_registry_v2, not just the per-outputkde_output_device_v2globals it originally shipped with — KWin 6.7 stopped advertising the latter, so the module written specifically to avoid shelling out tokscreen-doctor(0.19.x) saw zero devices and silently degraded to it on every session. Both models are supported now; registry-sourced devices arrive one Wayland round-trip later, so the handshake gains one conditional extra barrier. - CI-only fix: the winget release-verification step's
envs:allow-list was a step-level sibling ofwith:/env:instead of nested insidewith:, soappleboy/ssh-actionnever actually received it as an input and the step kept failing on every tag. Moved to match howREGISTRY_TOKENis already forwarded elsewhere.
See v0.20.1 for the fixes carried by that release (Windows install/winget, GameStream opt-in default, the gamescope Game Mode takeover hardening, a laptop-panel stall fix, and the PyroWave latency-creep bundle) — all included here too, since this release supersedes it.