WP4 + WP5 of design/desktop-presentation-rebuild.md, on top of the WP1/WP2 engine. The engine shipped with no way to choose it and no way to see what it cost; this closes both. WP4 — the display stage splits into `pace` (decoded → present-submit, our own pipeline) + `latch` (submit → on-glass, the presentation queue and the vblank wait), off the `submitted_ns` stamp WP2 already carried. That split is what makes a high `display` self-diagnosing: latch dominating is the vsync floor or a standing queue, pace dominating is us. A `present:` line joins the Detailed tier naming the live swapchain mode — the answer to most "why is my latch a whole refresh" questions, since a MAILBOX request silently lands on FIFO wherever the driver has no mailbox — plus the engine's counters, rendered only when they are non-zero so a healthy latency session shows just the mode. Deviation from the plan: the planned `display_adj` twin is NOT here. It was specified as `display − latch_p50` for parity with the Apple HUD's shaved figure, but with a real per-sample `pace` percentile that twin is the same quantity derived worse (subtracting percentiles). `pace` IS the Apple-comparable number — Apple subtracts its OS present floor, the latch is ours — and the user docs now say exactly that. WP5 — Prioritize + Smoothness buffer on all three surfaces: the GTK dialog (a new Presentation group on the Display page), the WinUI settings page, and the console settings screen, which is the ONLY editor reachable in Gaming Mode and so the one that decides whether Deck users can reach this at all. The buffer control follows the intent the way echo cancellation follows the mic: hidden on the desktop shells, dimmed and inert on the console, where a row that vanished mid-list would shift everything under the cursor. The V-Sync and VRR rows are deliberately NOT here. Their settings exist and are profile-routed, but the swapchain does not honour them until WP3, and a toggle that does nothing is exactly how "Full chroma (4:4:4)" shipped inert on desktop for three releases after being announced. Buffer labels carry no millisecond hints (Apple/Android derive them from the session refresh): under a Native mode the shells do not know the refresh at settings time, so the captions state the cost as one refresh per frame rather than a confident wrong number. Docs: the stats page documents the split and the `present:` line, and stops claiming Linux/Windows measure to the present instant (untrue since present_wait); client-settings documents both new rows and drops the stale claim that the desktop 4:4:4 toggle has no effect (it was wired to VIDEO_CAP_444); configuration documents PUNKTFUNK_PRESENTER and PUNKTFUNK_PRESENT_DEBUG. Gates: punktfunk-rust-ci linux/amd64 — fmt, clippy -D warnings over pf-client-core, pf-presenter, pf-console-ui, the session binary and the GTK client, 158 tests. The WinUI leg cannot be reached by any Linux or macOS check, so it was compiled on the Windows runner .133: clippy -D warnings and tests both exit 0, against a tree proven by content to contain the edit. ⚠ The first run there reported a false pass — the script printed its done-marker while the log carried a test failure (a STATUS_DLL_NOT_FOUND launch failure, ffmpeg's DLLs missing from PATH); the harness now echoes each phase's exit code so the verdict is a fact in the log rather than an inference from a marker. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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 — the session presenter decodes via Vulkan Video on every GPU vendor (including NVIDIA), falling back to FFmpeg VAAPI → DRM-PRIME dmabuf and then software when Vulkan Video is unavailable.
- 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 (experimental, off by default) — "Browse library…" on a saved 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, FFmpeg 7 or 8 (with VAAPI for hardware decode), PipeWire, and SDL3 (with hidapi) development packages.
# 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, FFmpeg decode, 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.
Related
- Documentation — quick start, pairing, troubleshooting
- Steam Deck plugin — launches this client fullscreen in Gaming Mode
- Project README — the host, the other clients, and how it all fits together