Files
enricobuehler 77834be4e4 feat(clients)!: retire the "Show game library" toggle — the library is simply there now
The console's Interface tab still offered an enable/disable switch for the game
library, left over from when the feature was experimental. Removing it turned out
to be the opposite of a one-line deletion.

The console never read the setting. Its row rendered a value and flipped a bool,
and every library affordance in the console — the home Y hint, the Y press, the
`--browse <host>` deep link — is gated on PAIRING and nothing else. So the row
showed "Game library · Off" while the same shell handed you the library on Y. The
docs already said as much.

What the flag actually gated was the other two desktop shells, GTK and WinUI,
which hide "Browse library…" unless it is on — and its stored default is `false`.
So deleting only the console row would have left every user who never found this
switch with a hidden library in the desktop apps, while removing the last place in
Gaming Mode where it could be turned on. The letter of the request, and the exact
opposite of its intent.

So the field goes, not just the row: `trust::Settings::library_enabled` and all
four reads of it. No migration code and no serde alias is needed, and that is by
construction rather than by luck — `Settings` is `#[serde(default)]` with a
`#[serde(flatten)] extra` map, so a stored `"library_enabled": false` parses into
`extra`, round-trips untouched, and is ignored. Everyone who had it off now has
the library, and a downgraded binary still finds its old value under the same key.
A test pins that contract, since it is the whole reason this is safe.

At the two GTK and two WinUI menu sites the flag is replaced by the PAIRING
predicate rather than dropped for an unconditional item. The library fetch
authenticates with the paired identity, and GTK's saved cards include trusted-but-
unpaired hosts, so an unconditional menu entry would promote a documented latent
bug — a fetch that cannot authenticate — into the default experience on every
install.

Apple and Android keep their own independent toggles for now, deliberately: both
already default TRUE, so nobody there loses anything by our not touching them, and
their removal is a follow-up rather than a rushed edit across two more UI
frameworks in this change.

VERIFICATION. The console and GTK legs are gate-green in the Linux container
(clippy `-D warnings`, a plain non-test build, and the test suites), and the GTK
leg needed that plain build: deleting the read orphaned the struct field that held
the settings store, which `--all-targets` hides and a shipping build raises.
The WinUI edits are READ-VERIFIED ONLY — `punktfunk-client-windows` is
`cfg(windows)` and compiles on neither the Mac nor the Linux container, so they
are unproven until a Windows runner sees them.
2026-08-16 19:03:53 +02:00
..

punktfunk — Linux client

The native Linux app for streaming a punktfunk host to your desktop, laptop, or Steam Deck. It's a clean relm4/GTK4/libadwaita shell that finds hosts on your network, pairs with a PIN, and manages your settings and library — the stream itself runs in the sibling punktfunk-session Vulkan binary (clients/session), which the shell spawns, putting the picture on glass at your display's own resolution and refresh rate.

Built in Rust end to end (no C ABI): the shell shares its plumbing with the session binary through crates/pf-client-core, which links the punktfunk-core protocol crate and speaks the fast punktfunk/1 protocol — QUIC control plane, GF(2¹⁶) FEC + AES-GCM data plane.

Features

  • Zero-copy hardware decode, and it's ours — the session presenter decodes with Punktfunk's own decoders; no FFmpeg is linked or bundled. Vulkan Video (pf-vkdecode, decoding onto the presenter's own device) leads on NVIDIA and AMD, VAAPI (pf-vaadec driving a dlopen'd libva, exporting DRM-PRIME dmabufs) leads on Intel, whichever isn't first is the fallback, and an OpenH264/rav1d CPU rung is last.
  • Your display's native mode — the host builds a virtual output at exactly your WxH@Hz; no scaling, no letterboxing. Steady 60 fps at 1080p60, ~6 ms capture→decoded on the LAN.
  • Audio both ways — PipeWire playback with a jitter ring, plus mic uplink to the host.
  • Full controller support — SDL3 gamepads with rumble and DualSense fidelity (lightbar, player LEDs, touchpad, motion, adaptive-trigger replay). Click-to-capture keyboard and mouse, with a release chord (Ctrl+Alt+Shift+Q) and focus-loss release.
  • Find hosts automatically — mDNS discovery lists hosts on your LAN; saved hosts persist. First connect does a one-time SPAKE2 PIN pairing (or TOFU on trusted LANs), then reconnects on a pinned identity.
  • Per-host speed test to pick a bitrate, plus compositor and mode preferences in Settings.
  • Game library browser — "Browse library…" on a paired host shows its games (Steam + custom) as a poster grid; click one to launch it in the session. Fetched from the host's management API over mTLS — paired devices are authorized by their certificate, no extra host setup.
  • Gamepad library launcher (--browse host) — a console-style, controller-driven library view of a paired host's games, rendered by the session binary's Skia console UI: A plays the focused title, B quits, L1/R1 jump. Built for the Steam Deck plugin's "Open library" launch; session end returns to the launcher. Arrow keys/Enter/Esc drive it too (no pad needed).

Get it

Most people should install a package rather than build from source:

Distro Install
Flatpak (any distro, Steam Deck) io.unom.Punktfunk — see packaging/flatpak
Ubuntu / Debian (apt) sudo apt install punktfunk-client (after adding the repo)
Fedora / Bazzite (rpm) rpm-ostree install punktfunk-client
Arch (PKGBUILD) see packaging/arch

Per-device install steps and pairing walkthrough: docs.punktfunk.unom.io/docs/install-client.

Build & run from source

Requires GTK ≥ 4.16, libadwaita ≥ 1.5, PipeWire, and SDL3 (with hidapi) development packages, plus a C compiler (the CPU rung builds OpenH264 from source). No decoder development package is needed: libva and the Vulkan loader are both opened at runtime rather than linked, so hardware decode is a fact about the box you run on — a Vulkan loader and your GPU's driver, and libva for the VAAPI rung — not about the one you build on.

# from the repo root
cargo run -p punktfunk-client-linux                 # launch the app
cargo run -p punktfunk-client-linux -- --connect HOST[:PORT]   # skip the host list and connect
cargo run -p punktfunk-client-linux -- --browse HOST           # the gamepad library launcher

The binary is named punktfunk-client — the relm4/libadwaita desktop shell (hosts, pairing/trust, settings, the desktop library page). Every stream and the console game library run in the sibling punktfunk-session Vulkan binary; the shell spawns it for connects, and --connect/--browse on the shell exec it directly (so the Decky wrapper keeps working unchanged). Headless flags stay in the shell: --pair <PIN> --connect host[:port] (pairing ceremony), --wake host[:port], and --library host[:mgmt_port] (print a host's game library).

Layout

src/
  main.rs · app.rs        entry point, relm4 AppModel (window, trust gate, session child
                          lifecycle, typed messages), primary menu, CSS
  cli.rs                  headless paths (--pair/--wake/--library), the --connect/--browse
                          exec handoff to punktfunk-session, screenshot scenes
  ui_hosts.rs             hosts page component (FactoryVecDeque cards, saved + discovered
                          grids, add-host dialog, banner)
  ui_library.rs           game-library poster grid (per-host, launches titles)
  ui_trust.rs             TOFU / PIN-pairing / request-access dialogs
  ui_settings.rs          resolution · refresh · decoder · bitrate · compositor · mic
  spawn.rs                the session-child plumbing (stdout contract → AppMsg)
tools/screenshots.sh      store screenshot capture (app self-capture; Xvfb fallback)

The UI-agnostic plumbing — session pump, the native decode ladder (Vulkan Video · VAAPI · OpenH264/rav1d), PipeWire audio, SDL3 gamepads + keymap, trust store, mDNS discovery, library client, Wake-on-LAN — lives in crates/pf-client-core, shared with the Vulkan session binary.