Compare commits

...
Author SHA1 Message Date
enricobuehler 00f9c1f4d3 Merge pull request '0.29.0' (#242) from worktree-release-0290 into main
audit / bun-audit (sdk) (push) Successful in 26s
audit / cargo-audit (push) Successful in 38s
audit / pnpm-audit (push) Successful in 12s
audit / bun-audit (plugin-kit) (push) Successful in 1m40s
audit / docs-site-audit (push) Successful in 1m39s
audit / bun-audit (web) (push) Successful in 1m39s
audit / license-gate (push) Successful in 4m34s
audit / miri (push) Successful in 5m4s
ci / web (push) Successful in 1m2s
ci / bun-nix (push) Successful in 16s
ci / docs-site (push) Successful in 1m13s
audit / c-abi-asan (push) Successful in 11m33s
ci / rust-arm64 (push) Successful in 12m19s
nix / flake (push) Successful in 15m45s
ci / rust (push) Successful in 31m23s
deb / build-publish-gamescope (push) Successful in 36s
decky / build-publish (push) Successful in 34s
deb / build-publish-client-arm64 (push) Successful in 1m19s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 8s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 8s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 8s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 10s
docker / builders (ci/gamescope-trixie.Dockerfile, punktfunk-gamescope-trixie) (push) Successful in 9s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 9s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
apple / swift (push) Successful in 2m12s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 55s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m20s
deb / build-publish-host (push) Successful in 4m13s
windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 5m26s
linux-client-screenshots / screenshots (push) Successful in 3m14s
android-screenshots / screenshots (push) Successful in 7m2s
sbom / sbom (push) Successful in 42s
deb / build-publish (push) Successful in 9m22s
docker / builders-arm64cross (push) Successful in 23s
docker / deploy-docs (push) Successful in 59s
arch / build-publish (push) Successful in 12m48s
web-screenshots / screenshots (push) Successful in 6m8s
android / android (push) Successful in 14m5s
flatpak / build-publish (push) Successful in 11m30s
windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 9m5s
apple / distribute (push) Successful in 13m54s
deb / smoke-install (push) Successful in 6m26s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 18m33s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 17m52s
apple / screenshots (push) Successful in 9m36s
windows-host / package (push) Successful in 19m10s
windows-host / canary-manifest (push) Skipped
windows-host / winget-source (push) Successful in 20s
2026-08-14 23:18:48 +00:00
enricobuehler 78efedc0d8 release: 0.29.0 — version bump, notes, CHANGELOG, Play notes
apple / swift (pull_request) Successful in 2m3s
apple / distribute (pull_request) Skipped
apple / screenshots (pull_request) Skipped
windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (pull_request) Successful in 2m52s
android / android (pull_request) Successful in 7m14s
ci / web (pull_request) Successful in 1m23s
ci / bun-nix (pull_request) Successful in 17s
windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (pull_request) Successful in 6m27s
ci / docs-site (pull_request) Successful in 1m27s
ci / rust-arm64 (pull_request) Successful in 15m57s
nix / flake (pull_request) Successful in 13m40s
ci / rust (pull_request) Successful in 31m1s
53 commits since v0.28.1 (36 non-merge). Cut from origin/main 8c6099da.

THE NUMBER: 0.29.0 is forced, not chosen. The C ABI moved 19 -> 20 (#230
added punktfunk_connection_mgmt_port for the in-band mgmt-port advert),
and the Windows MSIX package identity changed with the Azure signing
move (#228) — either alone rules out a patch. scripts/ci/pf-version.sh
derives the canary base as latest-stable + one minor, so canaries move
from 0.29.x to 0.30.x after the tag; no collision either way.

Version table measured, not copied forward: wire stays 2 (Welcome grew
a trailing u16 older peers never read, with an explicit cipher byte
whenever a port rides along so offset 68 keeps meaning cipher), driver
protocol 6/min 3 (pf-driver-proto has no diff against the v0.28.1 tag),
gamepad channel 3, plugin index schema 1, edition 2024, MSRV 1.85, 27
crate dirs, gamescope +pfhdr7 (patch series untouched), SDK 0.1.4,
plugin-kit 0.4.1. api/openapi.json stays stamped 0.28.0 — the mgmt API
surface did not change this cycle — and docs-site/public/openapi.json
is byte-identical to it, so no re-sync is owed for once.

Re-synced once as main moved (b5cace3a -> 8c6099da, PRs #237–#241):
the Hyprland six-fix arc and the Windows mgmt-port completion joined
the notes and CHANGELOG; contract surfaces (include/, pf-driver-proto,
sdk, plugin-kit, api/) show no diff from the extra commits, so every
version-table row survived the re-sync unchanged.

Gates run on this tree: cargo fmt --all --check clean; cargo metadata
--locked ok; Cargo.lock diff versions-only (36/36 lines); cargo test
-p punktfunk-core green including the c_abi harness (the header with
the v20 symbol compiles and round-trips); Play whatsnew 444/500 chars
(counted as characters, not bytes) and not byte-identical to any prior
release's; notes voice scan finds internal names only in the For
developers section.
2026-08-15 00:26:56 +02:00
5 changed files with 261 additions and 38 deletions
+177 -1
View File
@@ -12,7 +12,183 @@ with the version table of the release you are moving to, then read **Breaking ch
---
## v0.28.1
## v0.29.0
53 commits since v0.28.1 (36 non-merge).
The headline contract change is one **additive** C ABI bump: the host now tells the client, in-band,
where its management API lives, and the connection grew an accessor for it. The wire protocol, the
driver protocol and the plugin contract do not move; every 0.28.x host, client, driver and plugin
keeps interoperating with 0.29.0 in both directions, with no re-pairing. The one thing that needs an
operator's hand is on Windows: the MSIX package identity changed with the move to a publicly
trusted signing certificate, so that install path needs a one-time uninstall + reinstall.
### Versions
| | v0.28.1 | v0.29.0 | Notes |
|---|---|---|---|
| Wire protocol | 2 | **2** | unchanged — `Welcome` grew a trailing field older peers never read (below) |
| C ABI | 19 | **20** | one symbol added: `punktfunk_connection_mgmt_port` (below) |
| Rust edition | 2024 | **2024** | unchanged |
| MSRV (`rust-version`) | 1.85 | **1.85** | unchanged |
| Workspace crate dirs | 27 | **27** | unchanged |
| Virtual-display driver protocol | 6 | **6** | unchanged (minimum accepted still 3); `pf-driver-proto` shows no diff against the v0.28.1 tag |
| Windows virtual-gamepad channel | 3 | **3** | unchanged |
| Plugin index schema | 1 | **1** | unchanged |
| `api/openapi.json` | 0.28.0 | **0.28.0** | the management API surface did not change; the file keeps the stamp it was regenerated under |
| gamescope patch level (`+pfhdrN`) | 7 | **7** | unchanged — the patch series is untouched |
| `@punktfunk/host` (SDK) | 0.1.4 | **0.1.4** | unchanged |
| `@punktfunk/plugin-kit` | 0.4.1 | **0.4.1** | unchanged |
### ⚠ Breaking changes
- **C ABI 19 → 20, addition only.** `include/punktfunk_core.h` gains exactly one declaration,
`punktfunk_connection_mgmt_port(const PunktfunkConnection *, uint16_t *)` — the management-API
port the host advertised in its `Welcome`, or the documented default when it advertised none.
Nothing is removed or reshaped; an embedder that compares `PUNKTFUNK_ABI_VERSION` at build time
rebuilds against the new header and is done. Nothing in-tree compares it at runtime.
- **The Windows MSIX package identity changed.** Releases are now signed by Azure Artifact Signing
(below), and the MSIX manifest `Publisher` must equal the signer subject byte-for-byte — so it
moved from the self-signed `CN=unom` to the verified subject. Package identity is Name +
Publisher: Windows treats the new package as a different app, and an in-place upgrade is
impossible by design. One-time uninstall + reinstall for MSIX installs; the `.exe` installer and
winget-via-installer paths upgrade normally.
- **Android embedder edge, additive:** `NativeBridge` gains `nativeHostMgmtPort`, and the native
discovery record gains its 9th field, `mgmt` (the record's append-only rule; 0, non-numeric and
out-of-range all parse as unknown). Out-of-tree JNI callers are unaffected unless they want the
value.
### The management port is movable, survives, and is learned in-band
47990 is the management API's port and also the web-UI port of Sunshine and its forks — with the
GameStream planes off, the only port the two still contend for. Moving it now actually works, end
to end:
- **`PUNKTFUNK_MGMT_BIND` joins `host.env`** (the `PUNKTFUNK_GAMESTREAM` shape: env or CLI flag,
the flag wins), so the choice survives package upgrades that rewrite the unit file. `serve`
publishes the port it *actually bound* to `~/.config/punktfunk/mgmt-endpoint` (KEY=VALUE, written
write-then-rename), and the console, the Windows service and the unit files all derive from that
one file; the six hardcoded 47990 literals survive only as the old-host fallback.
- **`Welcome.mgmt_port`** — a trailing `u16` after the cipher block, the same additive discipline
as the eight fields before it, so `WIRE_VERSION` stays 2 and an older peer stops earlier and uses
the default. ⚠ One encode subtlety, pinned by test: `cipher` used to be emitted only when
non-default, and appending the port to an AES `Welcome` would land its low byte at offset 68 —
exactly where every shipped 0.28.x client reads `cipher`, fail-closed. `encode` therefore writes
an explicit cipher byte whenever a port rides along; a host advertising no port still emits
exactly 68 bytes. The standalone `punktfunk1-host` binary advertises `0` (it has no management
API).
- **Clients persist it**: `KnownHost.mgmt_port` + `effective_mgmt_port()` across the Rust clients
(three-rung: live advert → stored → default), the session console, Android (through
`DiscoveredHost`), and Apple — where `StoredHost.mgmtPort` had existed all along but nothing ever
wrote it, so every Apple client resolved 47990 regardless. A host that has never been seen over
mDNS (VPN, routed subnet, multicast-dead network) now learns the port from the authenticated
connection itself.
- **`PUNKTFUNK_NATIVE_PORT`** completes the pair for the data plane — `--native-port` was CLI-only
and died on upgrade. A bad value is a startup **error**, not a silent fall back to 9777.
- The Windows shell's half of the client-side learn landed separately (#241): `trust.rs` re-exports
`learn_mgmt_port`, the shell's own mDNS browser parses the `mgmt` TXT, and `HostTarget` carries
the port like the mac client's target does.
### Linux thread priority: the renice was a no-op on every install to date
`boost_thread_priority`'s `setpriority()` needs `CAP_SYS_NICE` or a raised `RLIMIT_NICE`; no
channel granted either, and the host binary can never carry a file capability (a capped process's
`/proc/<pid>/exe` is unreadable to KWin — the 0.26.0-1 incident). So capture/encode/send ran at
nice 0, and a shader-compile storm could deschedule them hard enough to stutter audio and drag ABR
to its floor at zero loss. Now:
- **RealtimeKit fallback** — `MakeThreadHighPriorityWithPID`, the same unprivileged broker
PipeWire clients use. Only the nice verb, never `MakeThreadRealtime`; nothing enters the
permitted set, KWin identification is untouched.
- **The audio plane is boosted at all, for the first time**: the 5 ms Opus
capture→encode→send loop, the PipeWire capture mainloop, and the pad-audio streamer (on Windows
too, via the existing `SetThreadPriority` arm).
- **Packaging ships headroom for rtkit-less boxes**: `packaging/linux/50-punktfunk-nice.conf`
(`user@.service.d`, `LimitNICE=-15` — a limit, not a grant; effective from next login) on rpm,
Arch and deb, written to `/etc/systemd/system/user@.service.d` by the Steam Deck installer; deb
and rpm gain a weak `Recommends: rtkit`, Arch an optdepends hint, and the NixOS module sets
`security.rtkit.enable = mkDefault true`.
### Host capture gain works on `punktfunk/1`, and boosting no longer hard-clips
`PUNKTFUNK_AUDIO_GAIN` existed only on the GameStream plane, and where it applied it was a hard
`clamp(-1.0, 1.0)` — flat-topping, so pushing past ~1.5× sounded broken long before it got loud
(WASAPI loopback taps upstream of the endpoint's master volume, so the host's own slider never
changes the sent level either). `punktfunk_core::audio::apply_gain` now serves **both planes** with
a tanh soft knee above `SOFT_LIMIT_KNEE` (0.7, ≈−3.1 dBFS): C1-continuous, bounded by
construction, odd-symmetric, memoryless (zero added latency). Unity is a no-op inside the function
itself, so the default wire stays byte-for-byte identical. `capture_gain` rejects non-positive
values and caps at 8.0 (+18 dB). This buys headroom, not loudness — it is deliberately not a
compressor, and the docs say so. `SOFT_LIMIT_KNEE` is excluded from cbindgen on purpose.
### Windows binaries are signed by Azure Artifact Signing
Account `unomsigning`, profile `unom-io`, signed by a service principal holding only the
profile-scoped signer role. Azure mints a **per-request leaf that expires in ~3 days**, which
changes two rules: a timestamped countersignature is now *mandatory* (the old retry-without-
timestamp fallback is a hard failure in Azure mode — it would ship an artifact that goes untrusted
days later, everywhere at once), and leaf pinning is structurally impossible (the updater's
`AUTHENTICODE_SHA256` note claiming otherwise is corrected). `pack-msix.ps1` reads the signature
back off the packed `.msix` and fails on Publisher drift. Driver catalogs are deliberately
untouched: they keep the `DRIVER_CERT_*` cert and the installer still plants it as a machine root
(PnP trust is independent of SmartScreen/UAC trust). Canary and fork builds keep the `.pfx` and
ephemeral fallbacks.
### Library: a launcher the host cannot open no longer costs the whole sync
`valid_launcher_ui` conflated vocabulary with environment. It is now split: `known_launcher_ui`
(an unknown launcher kind is a plugin bug — still a hard 400) and `resolvable_launcher_ui` (the
launcher just is not installed on this box — the entry is dropped with one warn and the games
sync). Same shape as the unservable-cover fix, on the launch side. And Playnite is actually
findable now: the old lookup read the LocalSystem service's own HKCU and `%LOCALAPPDATA%` (the
SYSTEM profile — a per-user Playnite is invisible there) and matched a registry key name Inno Setup
never writes. Now: every loaded hive under `HKEY_USERS` plus both HKLM views, matched on
`DisplayName`, then `C:\Users\*\AppData\Local\Playnite`.
### Hyprland/sway capture: six defects, all ours, and streaming now survives past one session
The wlr portal route looked environmental and never was. Measured on Hyprland 0.55.4 +
xdg-desktop-portal-hyprland 1.3.12, fixed in one arc (#240):
- **The dmabuf pod offered `BGRx`; xdph offers `BGRA`.** The modifier lists intersect perfectly,
the fourcc never does, so PipeWire failed the link itself (`no more input formats`) — and the
pods live only in the PipeWire *daemon's* log, which is why it read as a GPU/modifier problem.
- **A per-cast tokio runtime orphaned ashpd's process-global D-Bus connection.** ashpd caches its
connection in a `OnceLock`; the first cast's runtime hosted zbus's reader task and then died
with the cast, so the first stream of a host process worked and every later one went black.
Both wlr backends now share one long-lived portal runtime.
- **Teardown removed the captured output before closing the cast**, and xdph spun on the wreckage;
the order is now cast-then-output.
- **A hung portal handshake leaked its thread** and the leak poisoned every later cast; the
handshake is now bounded.
- **The wlr absolute-motion injector aimed at the operator's head**, never the streamed one; the
pointer is now bound to the streamed output.
- **The cursor park schedule read a missing cursor overlay as a lost pointer** — an Embedded-mode
portal never sends one.
### Everything else an integrator might notice
- **vdisplay/KDE:** a bare-spawn gamescope session under an exclusive topology now darkens the
physical panels over `org_kde_kwin_dpms` (new in-process `kwin_dpms` module,
`kscreen-doctor --dpms` fallback), refcounted host-wide so concurrent spawns compose; DPMS is
non-persistent, so a dead host leaves nothing to journal. Managed and Attach routes untouched.
- **macOS client:** `Settings::inhibit_shortcuts` is finally implemented on Apple — a local
keyDown monitor claims every ⌘ chord while input is captured and forwards it host-side (AppKit
dispatches menu key equivalents before the stream view sees them, so ⌘Q used to quit the
client). ⌘⎋ and ⌃⌘F stay client-side; ⌘Tab/⌘Space/Mission Control are out of reach without a
CGEventTap. Chord matching no longer compares Caps Lock and `.function`/`.numericPad` bits raw.
- **Android client:** `Gamepad.padButtonBit` resolves a gamepad-sourced `KEYCODE_BACK` to
`BTN_BACK` — pads that report Select as plain BACK (the Android-TV shape) no longer quit the
stream on one press, and the Select chords (exit chord, mic mute, stats tier) become reachable
on exactly those pads. `FLAG_FALLBACK` events stay excluded.
- **CI:** Android canaries now feed Play **open testing (beta) and closed testing (alpha)** from
one Play edit (`play-upload.py --also-track`); tags still publish production only, and a manual
`android.yml` dispatch can now opt into publishing (`publish=true`), so a lost merge run is no
longer a dead end. Windows
runners provision the .NET 8 runtime and a machine-wide signing client (a mixed-mode dlib with
no runtime makes signtool exit 3 in silence).
60 commits since v0.28.0.
Generated
+36 -36
View File
@@ -1090,7 +1090,7 @@ dependencies = [
[[package]]
name = "cursor-probe"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"anyhow",
"pf-capture",
@@ -1222,7 +1222,7 @@ dependencies = [
[[package]]
name = "display-disturb"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"pf-win-display",
"windows 0.62.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2343,7 +2343,7 @@ dependencies = [
[[package]]
name = "latency-probe"
version = "0.28.1"
version = "0.29.0"
[[package]]
name = "lazy_static"
@@ -2446,7 +2446,7 @@ dependencies = [
[[package]]
name = "libvpl-sys"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"bindgen",
"cmake",
@@ -2475,7 +2475,7 @@ checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
[[package]]
name = "loss-harness"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"punktfunk-core",
]
@@ -2967,7 +2967,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "pf-bitstream"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"cros-codecs",
"tracing",
@@ -2975,7 +2975,7 @@ dependencies = [
[[package]]
name = "pf-capture"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"anyhow",
"ashpd",
@@ -2996,7 +2996,7 @@ dependencies = [
[[package]]
name = "pf-client-core"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"anyhow",
"ash",
@@ -3031,7 +3031,7 @@ dependencies = [
[[package]]
name = "pf-clipboard"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"anyhow",
"ashpd",
@@ -3049,7 +3049,7 @@ dependencies = [
[[package]]
name = "pf-console-ui"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"anyhow",
"ash",
@@ -3071,7 +3071,7 @@ dependencies = [
[[package]]
name = "pf-dxvadec"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"cros-codecs",
"pf-bitstream",
@@ -3081,7 +3081,7 @@ dependencies = [
[[package]]
name = "pf-encode"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"anyhow",
"ash",
@@ -3107,7 +3107,7 @@ dependencies = [
[[package]]
name = "pf-frame"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"anyhow",
"libc",
@@ -3120,7 +3120,7 @@ dependencies = [
[[package]]
name = "pf-gpu"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"anyhow",
"pf-host-config",
@@ -3134,11 +3134,11 @@ dependencies = [
[[package]]
name = "pf-host-config"
version = "0.28.1"
version = "0.29.0"
[[package]]
name = "pf-inject"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"anyhow",
"ashpd",
@@ -3167,14 +3167,14 @@ dependencies = [
[[package]]
name = "pf-paths"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"tracing",
]
[[package]]
name = "pf-presenter"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"anyhow",
"ash",
@@ -3189,7 +3189,7 @@ dependencies = [
[[package]]
name = "pf-update"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"serde",
"serde_json",
@@ -3197,7 +3197,7 @@ dependencies = [
[[package]]
name = "pf-update-check"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"anyhow",
"aws-lc-rs",
@@ -3209,7 +3209,7 @@ dependencies = [
[[package]]
name = "pf-vaadec"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"cros-codecs",
"pf-bitstream",
@@ -3218,7 +3218,7 @@ dependencies = [
[[package]]
name = "pf-vdisplay"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"anyhow",
"ashpd",
@@ -3251,7 +3251,7 @@ dependencies = [
[[package]]
name = "pf-vkdecode"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"ash",
"cros-codecs",
@@ -3262,7 +3262,7 @@ dependencies = [
[[package]]
name = "pf-win-display"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"pf-paths",
"punktfunk-core",
@@ -3273,7 +3273,7 @@ dependencies = [
[[package]]
name = "pf-zerocopy"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"anyhow",
"ash",
@@ -3485,7 +3485,7 @@ dependencies = [
[[package]]
name = "punktfunk-cli"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"pf-client-core",
"punktfunk-core",
@@ -3495,7 +3495,7 @@ dependencies = [
[[package]]
name = "punktfunk-client-android"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"android_logger",
"jni",
@@ -3513,7 +3513,7 @@ dependencies = [
[[package]]
name = "punktfunk-client-linux"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"anyhow",
"async-channel",
@@ -3530,7 +3530,7 @@ dependencies = [
[[package]]
name = "punktfunk-client-session"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"pf-client-core",
"pf-console-ui",
@@ -3544,7 +3544,7 @@ dependencies = [
[[package]]
name = "punktfunk-client-windows"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"async-channel",
"mdns-sd",
@@ -3562,7 +3562,7 @@ dependencies = [
[[package]]
name = "punktfunk-core"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"aes-gcm",
"cbindgen",
@@ -3594,7 +3594,7 @@ dependencies = [
[[package]]
name = "punktfunk-encode-worker"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"pf-encode",
"tracing",
@@ -3603,7 +3603,7 @@ dependencies = [
[[package]]
name = "punktfunk-host"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"aes",
"aes-gcm",
@@ -3673,7 +3673,7 @@ dependencies = [
[[package]]
name = "punktfunk-probe"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"anyhow",
"mdns-sd",
@@ -3687,7 +3687,7 @@ dependencies = [
[[package]]
name = "punktfunk-tray"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"anyhow",
"ksni",
@@ -3710,7 +3710,7 @@ checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea"
[[package]]
name = "pyrowave-sys"
version = "0.28.1"
version = "0.29.0"
dependencies = [
"bindgen",
"cmake",
+1 -1
View File
@@ -65,7 +65,7 @@ exclude = [
ndk = { path = "clients/android/native/vendor/ndk" }
[workspace.package]
version = "0.28.1"
version = "0.29.0"
edition = "2024"
rust-version = "1.85"
license = "MIT OR Apache-2.0"
+45
View File
@@ -0,0 +1,45 @@
Wire-compatible with 0.28.x — everything you have already paired keeps working, and you can update one side at a time. An old client on a new host, or the other way round, streams exactly as it does today.
This release is about getting along: with the other streaming host on your machine, with the game that is hogging your processor, and with Windows itself. Punktfunk can now share a computer with Sunshine and its forks, Linux hosts keep the stream smooth while a game hammers the CPU, streaming from a Hyprland desktop finally works — and keeps working past the first session — and Windows downloads are now signed under a publicly verified publisher.
## TL;DR
- **Punktfunk and Sunshine (or Apollo, or another fork) can now live on the same machine.** Move one port in Punktfunk's settings and you are done — every client finds the new port by itself.
- **Linux hosts stop stuttering when a game hogs the processor.** The streaming threads were supposed to run at high priority and never actually did, on any install to date.
- **If your Windows client is the app package (the `.msix` download), this one update needs an uninstall first** — see *Before you update*. The regular installer updates normally.
- **Streaming from a Hyprland desktop actually works now — and keeps working.** Before, the picture could refuse to appear at all, and at best the first session of a host's lifetime was also its last good one.
- **A controller whose Select button reports itself as "Back" no longer quits your Android stream on one press.**
- **Mac clients can finally capture system shortcuts:** ⌘Q reaches the host's desktop instead of closing the client.
## Before you update
- **Windows client installed as the app package (the `.msix` download):** Windows will refuse to update it in place this one time, because the package's signing identity changed with the move to a verified certificate. Uninstall the old package, then install the new one. This does not apply to the regular installer, and future updates go back to normal.
## New
- **Share a machine with Sunshine, Apollo and friends.** With Moonlight-compatible connections switched off, the two only ever fought over a single network port — and moving Punktfunk's was barely possible: the setting did not survive updates, and clients had no way to find the moved port unless local network discovery happened to be working. All of that is fixed. The port is now a real setting in the host's configuration file, it survives upgrades, the web console follows it, and every client learns it directly from the host the moment it connects — so it also works over a VPN or across subnets, where discovery has never worked. The connection port can be moved the same way.
- **Capture system shortcuts, now on Mac too.** The setting existed on every other client; on a Mac, ⌘Q quit the client itself and other ⌘ shortcuts went to the client's menus instead of your game. While your input is captured, ⌘ shortcuts are now sent to the host — with the escape shortcuts kept local, so there is always a way out. On by default, like everywhere else.
## Improved
- **Linux hosts keep the stream smooth under load.** The threads that capture, encode and send your stream were meant to run ahead of ordinary programs and never did — the request was silently refused on every install channel. A game's shader-compilation storm could starve them, stutter the audio and drag the picture quality to the floor on a connection with nothing wrong. The host now asks the system's scheduling service the way audio apps do, the packages ship the needed headroom for machines without it, and — for the first time — the audio path itself is prioritized, which matters most because a late sound packet is directly audible.
- **The host's volume boost is now trustworthy.** It quietly did nothing on normal Punktfunk connections, and where it did work, turning it up clipped the sound into harsh distortion long before it got loud. It now applies everywhere, and levels above the safe range are rounded off gently instead of clipped — boosting a quiet desktop mix finally sounds like turning it up, not breaking it. It buys headroom for quiet sources; it is deliberately not a loudness compressor.
- **Windows downloads are signed under a publicly verified certificate.** The installer and the app package now carry a verified publisher signature instead of a self-signed one — what Windows shows when it asks "do you trust this?" is now an identity a certificate authority actually checked.
## Fixed
- **Streaming your desktop from Hyprland works now, and keeps working.** Six separate faults, every one of them ours, stacked up on this route: the capture could fail to start at all because Punktfunk offered the picture in a format the desktop's screen-sharing service never accepts; when it did start, only the first session of the host's lifetime worked and every later one was black; ending a stream could wedge the screen-sharing service itself; a stalled start could quietly poison every session after it; your mouse pointer was aimed at the host's own monitor instead of the streamed one; and the host misread the service's normal behaviour as a lost pointer. All six are fixed — measured against a fully up-to-date Hyprland, where it now streams session after session.
- **Your Playnite library now shows up on Windows.** Playnite installs per-user by default, and the host — which runs as a system service — was looking in places where a per-user install can never be found, so syncing failed. Worse, the one tile it could not open took every game down with it. Both are fixed: a normal Playnite install is found, and a shortcut that cannot open on your machine is skipped with a note instead of emptying the library.
- **One press of Select no longer ends your Android stream.** Some controllers — especially the Android TV shape — report their Select button as the same "Back" every TV remote has. That press fell through to "leave the stream", and every shortcut built on Select (the emergency exit hold, mic mute, the stats overlay) was unreachable on exactly those pads. Select now works as Select on them; a remote's Back still exits, as the on-screen hint says.
- **Streaming a game in its own full-screen session from a KDE desktop no longer leaves your monitor lit.** With the host set to take the machine over exclusively, this one way of launching a game kept the physical monitor showing the idle desktop for the whole stream. It now goes dark, wakes when you touch the local mouse or keyboard, and never wakes from anything happening inside the stream.
- **Mac keyboard edge cases in a stream:** with Caps Lock on, the escape shortcuts were not recognized at all, and a shortcut's key could stay "stuck" until pressed again. Both fixed alongside the shortcut-capture work.
## Thanks
The Linux priority fix exists because someone measured their stutter carefully enough — down to which seconds it happened in and what else the machine was doing — that the cause could be found in a place nobody was looking. The Select-button fix likewise arrived as a precise field report. Thank you; that detail is what makes a fault findable.
## For developers
Protocol, ABI, driver and embedder detail — including the version table and the breaking-changes list — is in [CHANGELOG.md](https://git.unom.io/unom/punktfunk/src/tag/v0.29.0/CHANGELOG.md).
Two things deserve a headline even here: the C ABI moves 19 → 20 by pure addition (one new accessor for the host's advertised management port), and the Windows MSIX package identity changed with the signing move — that is the uninstall note above, and it is one-time.
+2
View File
@@ -0,0 +1,2 @@
• One press of Select no longer ends your stream. Some controllers report Select as the "Back" every TV remote has; that press quit the session, and the Select shortcuts (the emergency exit hold, mic mute, stats) never worked on those pads. Fixed — a remote's Back still exits.
• The app now learns a host's settings port from the host itself when it connects, so hosts reached over a VPN or another network work fully, without local discovery.