• v0.18.0
    apple / swift (push) Successful in 1m21s
    ci / web (push) Successful in 51s
    ci / docs-site (push) Successful in 57s
    apple / screenshots (push) Successful in 6m41s
    ci / bench (push) Successful in 6m22s
    ci / rust (push) Successful in 24m42s
    docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 12s
    docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 11s
    decky / build-publish (push) Successful in 28s
    docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 11s
    docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 11s
    docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 12s
    android-screenshots / screenshots (push) Successful in 3m10s
    docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 4m41s
    windows-host / package (push) Successful in 9m53s
    linux-client-screenshots / screenshots (push) Successful in 7m11s
    deb / build-publish (push) Successful in 11m1s
    docker / deploy-docs (push) Successful in 24s
    deb / build-publish-host (push) Successful in 11m55s
    windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 2m31s
    arch / build-publish (push) Successful in 12m57s
    web-screenshots / screenshots (push) Successful in 3m12s
    android / android (push) Successful in 13m46s
    windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 2m34s
    rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 15m0s
    release / apple (push) Successful in 10m32s
    rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m45s
    flatpak / build-publish (push) Successful in 5m51s
    Stable

    enricobuehler released this 2026-07-22 23:13:14 +00:00 | 0 commits to main since this release

    Wire-compatible with 0.17.x — existing pairings and clients keep working. Every protocol addition in this release is capability-negotiated or safely ignored by older peers: a 0.17 host never advertises the new text-input or cursor capabilities (clients keep their fallbacks), and a 0.17 client never sees a byte it can't parse. WIRE_VERSION stays 2; the C ABI moves to 12 for embedders (cursor channel + render-flip surface), and the Windows virtual-display driver protocol moves to 6 with the compat floor still at 3 — an un-upgraded driver keeps pairing, it just can't serve the new hardware-cursor channel.

    The cursor goes client-side

    A remote desktop lives and dies by its pointer. New in this release, the cursor channel: the host ships the pointer's shape (RGBA + hotspot, cached by serial) and per-frame position/visibility as metadata, and capable clients draw it locally — pixel-crisp at any stream bitrate, immune to encode artifacts, and a round-trip ahead of the video under load. Capability-negotiated per session (CLIENT_CAP_CURSORHOST_CAP_CURSOR); when both sides agree the host stops compositing the pointer into the video, and everything falls back to today's host-drawn cursor otherwise.

    • Desktop (absolute) mouse mode on the desktop clients and macOS: point at the remote desktop like a local one — no capture required — with a host-driven mouse-model flip so host-side policy can switch a session between absolute and relative live.
    • Windows host: the virtual-display driver grows a hardware-cursor channel (driver protocol v6), a GDI shape poller covers masked/monochrome cursors, and sessions without the channel get deterministic host-side compositing — including HDR streams, where the composited pointer now matches the target's SDR white level instead of searing at max brightness.
    • Linux host: Mutter and KWin virtual monitors ship the cursor as PipeWire SPA_META_Cursor metadata; gamescope sessions — whose capture paints no cursor at all — get it via XFixes shape + position queries; and sessions without the channel composite the pointer on a sticky-declared target.
    • NVENC blend rewritten: the cursor blend on the NVIDIA encode path is now a SPIR-V compute pass over Vulkan-allocated input slots — the PTX kernels (and their driver-portability hazards) are retired.
    • Apple client: renders the channel cursor via NSCursor on macOS with per-session state fully reset between streams.

    Also on the Linux host: KWin virtual outputs now stream above 60 Hz — the output is born at a sacrificial mode and renegotiated live to the client's real refresh (240 Hz verified on-glass), where KWin previously froze the offer at birth.

    Android: mouse & keyboard, all in

    The Android client — phone, tablet, and Android TV — now covers the "desktop client" use case properly.

    • Physical mice work like mice. A USB/Bluetooth mouse points at the desktop absolutely (hover, drags, all five buttons — back/forward included — and a high-resolution wheel with fractional accumulation). The local cursor hides over the stream; the host's cursor is the one you see.
    • Pointer capture for games. The new "Capture pointer for games" setting locks the mouse to the stream and forwards raw relative motion for mouse-look. Ctrl+Alt+Shift+Q — the same chord as the desktop clients — toggles capture live, clicking the stream re-captures, and held buttons are always released on capture loss so nothing sticks on the host. A mouse's back button can no longer yank you out of the stream.
    • Android TV remote as a pointer. Hold SELECT ≈ 1 s and a D-pad-only remote drives the host cursor with ramping acceleration — SELECT clicks, play/pause right-clicks, holding play/pause summons the keyboard. The Siri-remote pointer's Android sibling, for TV boxes where the remote is the only thing in hand.
    • The soft keyboard finally types like it means it. Against hosts that advertise the new text-input capability (below), the IME runs its full machinery — autocorrect, gesture typing, non-Latin composition, emoji — mirrored to the host live as corrections happen. Older hosts keep the raw-key path unchanged.
    • Shared clipboard (text). Text copied on the device pastes on the host and vice versa, opt-in per session and only when the host offers it. Local copies cross lazily (only when the host actually pastes); host copies land in the Android clipboard immediately.
    • Quality of life: invert-scroll setting (wheel and two-finger touch alike), media keys now reach the host instead of the system, and a TV remote's SELECT maps to Enter while streaming.

    A first-class text-input plane

    Layout-independent key events can't express what an input method commits — autocorrect, gesture typing, non-Latin scripts, emoji. New in the protocol:

    • InputKind::TextInput carries committed text one Unicode scalar per event; HOST_CAP_TEXT_INPUT advertises it, and only where the host's inject backend can genuinely type text: Windows (KEYEVENTF_UNICODE, surrogate-pair aware) and Linux wlroots sessions (a dedicated virtual keyboard whose xkb keymap grows Unicode keysyms on demand — keymap re-uploads never disturb the main keyboard's modifier state). KWin, GNOME/libei and gamescope sessions don't set the bit; clients fall back to key-event synthesis there.
    • Moonlight clients benefit too: the GameStream UTF-8 text packet — previously recognized and dropped — now injects through the same plane, so a Moonlight client's on-screen keyboard types on punktfunk hosts.

    ChaCha20-Poly1305 session cipher for soft-AES clients

    Clients without hardware AES (webOS-class TVs and other soft-AES ARM targets) can now request ChaCha20-Poly1305 for the encrypted session plane — 4–7× faster than software AES-GCM at these bitrates. Support-plus-request rides one capability bit; the host answers with the negotiated cipher and a dedicated key in the Welcome, and every other client stays byte-identical AES-128-GCM. Both AEADs are full-strength — this is purely a performance choice, negotiated over the pinned-TLS control channel with no downgrade surface. Host kill-switch: PUNKTFUNK_CHACHA20 (default on).

    SteamOS / Steam Deck host: a season of reliability

    • Dead-session self-heal. A headless SteamOS box whose gamescope session died (crash, OS update, suspend wedge) now detects the corpse at connect time and restarts the session instead of failing the stream with SetupFailed.
    • Surviving SteamOS A/B updates. System tuning registers on the atomic-update keep list and a post-update rebuild check re-provisions what the update wiped — the host comes back streamable after an OS update instead of silently degraded.
    • Installer hardening: cmake dependency added to preflight, sudo acquired up front (prompting instead of requiring passwordless), controller passthrough set up with the required reboot surfaced, plugin runner installed, and update.sh repaired.
    • Deck-as-client typing on the on-glass keyboard hardened, and the host's EIS injection now handles gamescope's degenerate regions: absolute pointer/touch coordinates scale by the session's real output size and map into the streamed output's region, region-less EIS devices get absolute pointer emission, and touch degrades gracefully to absolute pointing when the EIS offers no touchscreen device.
    • The host holds a logind sleep/idle inhibitor while clients stream — a Linux box no longer dozes off mid-session under desktop power settings.

    GameStream / Moonlight interop

    • HDR actually engages on TVs. The host now sends the async HDR-mode control message (0x010e + HDR10 metadata) that Moonlight clients wait for before switching the display into HDR picture mode. Streams negotiated as BT.2020 PQ no longer play back in SDR on stock Moonlight.
    • Legacy clients pair again. moonlight-embedded-family clients still mint X.509 v2 self-signed certs, which the TLS stack rejects before checking the signature. Pairing now re-runs the same cryptographic proof-of-possession check with a version-agnostic parser — a full signature verification, never a bypass; identity remains the SHA-256-pinned cert plus the PIN ceremony.

    Fixes across the board

    • Windows host: virtual-display mode indexes are un-pinned on paths the CCD isolate deactivates — no more 0x57 on multi-display topology changes; the HDR SDR-white scale is actually queried on refresh; a mode-switch replacement display inherits its group topology.
    • Linux host: the GPU clock pin is scoped to live client sessions instead of host lifetime.
    • Wake-on-LAN launches: the client retries the QUIC dial across the whole connect budget, so waking a sleeping host from the couch survives the host's slow resume instead of dying on the first refused dial.
    • Game library: Steam's "Add a Non-Steam Game" entries — which carry no appmanifest — now appear in the streamed library, read from each account's binary shortcuts.vdf along with their grid artwork. Best-effort: hidden shortcuts and duplicates are dropped, and an unreadable file contributes nothing.
    • Decky plugin: the published plugin zip actually ships its shortcut artwork and Steam Input layout; the library shortcut re-creates itself when its cached appId goes stale.
    • Web console: the 2026-07-22 JS advisory wave (node-tar, sharp/libvips, undici, and friends) cleared via dependency overrides — bun audit is clean.
    • CI: the Android leg no longer downloads the unused emulator and retries its SDK fetches; the runner fleet got a local DNS cache so release fan-outs stop tripping over their own load.

    Versions

    WIRE_VERSION 2 (unchanged — 0.17.x clients and hosts interoperate) · C ABI 12 (cursor channel + mid-stream cursor-render flip; embedders recompile) · Windows display driver protocol 6, compat floor 3 (an existing driver keeps working; the hardware-cursor channel needs the new driver). Windows drivers ship separately, as always.

    Downloads