Files
punktfunk/clients/linux
enricobuehler 9514a8c0e2
ci / web (push) Successful in 51s
ci / docs-site (push) Successful in 56s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 8s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 8s
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 10s
decky / build-publish (push) Successful in 52s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 8s
apple / swift (push) Successful in 4m39s
ci / bench (push) Successful in 6m27s
windows-host / package (push) Successful in 8m34s
flatpak / build-publish (push) Successful in 7m15s
docker / deploy-docs (push) Successful in 24s
android / android (push) Successful in 12m49s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 4m16s
deb / build-publish (push) Successful in 12m50s
arch / build-publish (push) Successful in 16m32s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m53s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 19m44s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 5m14s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 17m53s
ci / rust (push) Successful in 25m18s
release / apple (push) Successful in 22m3s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m57s
apple / screenshots (push) Successful in 18m56s
fix(client): correct Linux/Windows "Forwarded controller" copy for multi-controller
The relm4 (Linux) and native (Windows) shells spawn punktfunk-session, which
since the native-plane rework forwards ALL controllers by default — but the
"Forwarded controller" settings dropdowns still described the pre-rework world
("Automatic (most recent)", "Exactly one controller is forwarded to the host").

The dropdown already lists every detected pad and wires set_pinned(None)=all /
set_pinned(key)=single-player; this fixes only the misleading labels, subtitle,
tooltip, and stale leading comments to match: Automatic forwards every real
controller (each its own player); pick one to force single-player.

cargo check -p punktfunk-client-linux green; Windows is windows-gated (pure
string edits, CI windows.yml).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 21:53:07 +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 — 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.