38078fe7ee
ci / rust (push) Successful in 1m54s
ci / web (push) Successful in 54s
ci / docs-site (push) Successful in 1m2s
windows-host / package (push) Successful in 6m43s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m12s
ci / bench (push) Successful in 4m47s
apple / swift (push) Successful in 1m9s
android / android (push) Successful in 3m33s
deb / build-publish (push) Successful in 4m36s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 6s
decky / build-publish (push) Successful in 15s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m10s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 51s
release / apple (push) Successful in 8m30s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 48s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 53s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m15s
flatpak / build-publish (push) Successful in 4m4s
apple / screenshots (push) Successful in 5m31s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m48s
docker / deploy-docs (push) Successful in 24s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 9m20s
A controller-driven, chrome-less library launcher for the Steam Deck flow
(the Decky plugin's "Open library on screen" + pinned games, 8470419):
`--browse host[:port]` opens a paired host's game library as a coverflow
over a drifting aurora — A streams the focused title (the id rides the
Hello), session end returns to the launcher, B quits back to Gaming Mode.
`--connect` gains `--launch <id>` for direct-to-game starts; `--mgmt`
overrides the library port. Scope is deliberately library-only: host
selection/settings stay in the touch UI, pairing stays in the plugin (no
dialog can map under gamescope — every state renders in-page).
- gamepad.rs menu mode: the worker holds the active pad open while idle
(WITHOUT the Valve HIDAPI drivers — Deck lizard mode survives) and
translates it through a pure MenuNav state machine: edge-triggered
buttons, held-state snapshot on entry/detach (the escape chord that ends
a stream can't ghost-fire in the menu), 380/160 ms stick/dpad repeat,
menu rumble ticks. Keyboard fallback (arrows/Enter/Esc) drives the same
handler — fully usable with no pad, no host (PUNKTFUNK_FAKE_LIBRARY).
- Coverflow: ±38° corridor-facing tilt under per-card perspective
(gsk rotate_3d), dense overlapping side shelves with paint-order
restacking (gtk::Fixed draws in child order), opaque card faces + a
darkening veil for the recede (translucency would bleed the stack
through). The strip lives in an External-policy ScrolledWindow because
a bare gtk::Fixed measures its TRANSFORMED children and inflates the
page min-width past the window.
- Spring-driven motion: semi-implicit Euler in ≤8 ms substeps (a raw
50 ms frame leaves the stiff recoil spring ringing at ω·dt ≈ 1.2 —
regression-tested), ζ≈0.85 cursor chase + ζ≈0.55 boundary wobble;
velocity carries across retargets so held-repeat scrolling glides.
- Shot scene `gamepad-library` (GTK animations force-disabled in shot mode
— nav transitions froze mid-slide in headless captures); shared poster
fetch extracted to library::spawn_art_fetch.
Verified here: 21 unit tests (MenuNav, cursor stepping, spring
convergence/stability), clippy -D warnings clean, screenshot scene
pixel-checked, --browse smoke runs (fake-library + unpaired) on the
headless session. On-Deck validation pending (virtual-pad input, lizard
mode, rumble via Steam Input, full Decky→browse→stream→launcher loop).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
95 lines
5.5 KiB
Markdown
95 lines
5.5 KiB
Markdown
# punktfunk — Linux client
|
|
|
|
The native **Linux** app for streaming a punktfunk host to your desktop, laptop, or Steam Deck.
|
|
It's a clean GTK4/libadwaita app that finds hosts on your network, pairs with a PIN, and puts a
|
|
low-latency stream on glass at your display's own resolution and refresh rate.
|
|
|
|
Built in Rust, it links the shared **`punktfunk-core`** directly (no C ABI) and speaks the fast
|
|
**`punktfunk/1`** protocol — QUIC control plane, GF(2¹⁶) FEC + AES-GCM data plane.
|
|
|
|
## Features
|
|
|
|
- **Zero-copy hardware decode** — FFmpeg VAAPI decode → DRM-PRIME dmabuf → `GdkDmabufTexture`
|
|
(Tier-1 zero-copy on Intel and AMD), with an automatic software-HEVC fallback on NVIDIA or when
|
|
VAAPI 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 coverflow of
|
|
a paired host's library (drifting aurora backdrop, center-focus posters, button hints): 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`](../../packaging/flatpak/README.md) |
|
|
| **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`](../../packaging/arch/README.md) |
|
|
|
|
Per-device install steps and pairing walkthrough:
|
|
**[docs.punktfunk.unom.io/docs/install-client](https://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.
|
|
|
|
```sh
|
|
# 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`**. Handy flags: `--connect host[:port]` (start a session
|
|
immediately — for scripting and the Steam Deck launcher) with optional `--launch <id>` (ask the
|
|
host to launch that library title, id from `--library`), `--browse host[:port]` (the gamepad
|
|
library launcher; `--mgmt <port>` overrides the management port it fetches from),
|
|
`--pair <PIN> --connect host[:port]` (run the pairing ceremony headlessly), and
|
|
`--library host[:mgmt_port]` (print a host's game library headlessly). Force a decoder with
|
|
`PUNKTFUNK_DECODER=software|vaapi`; `PUNKTFUNK_FAKE_LIBRARY=<file.json>` feeds the launcher
|
|
canned entries for UI work with no host.
|
|
|
|
## Layout
|
|
|
|
```
|
|
src/
|
|
main.rs · app.rs entry point, GTK application, primary menu, CSS
|
|
cli.rs CLI paths (--connect/--launch, --browse, headless --pair, screenshot scenes)
|
|
ui_hosts.rs host card grids (saved + discovered) · add-host dialog · banner
|
|
ui_library.rs game-library poster grid (per-host, launches titles)
|
|
ui_gamepad_library.rs the --browse gamepad launcher (aurora · coverflow · hint bar)
|
|
ui_trust.rs TOFU / PIN-pairing / request-access dialogs
|
|
ui_settings.rs resolution · refresh · decoder · bitrate · compositor · mic
|
|
ui_stream.rs the stream window (GtkGraphicsOffload present) + input capture
|
|
launch.rs · session.rs session launch/UI glue; lifecycle over the NativeClient connector
|
|
video.rs FFmpeg VAAPI / software decode → dmabuf / texture
|
|
audio.rs PipeWire playback + mic uplink
|
|
gamepad.rs · keymap.rs SDL3 controllers + feedback; keyboard VK mapping
|
|
trust.rs · discovery.rs persistent identity, known hosts + settings, mDNS browse
|
|
library.rs mgmt-API library client (mTLS + pinned fingerprint, art proxy)
|
|
tools/screenshots.sh store screenshot capture (app self-capture; Xvfb fallback)
|
|
```
|
|
|
|
## Related
|
|
|
|
- **[Documentation](https://docs.punktfunk.unom.io)** — quick start, pairing, troubleshooting
|
|
- **[Steam Deck plugin](../decky/README.md)** — launches this client fullscreen in Gaming Mode
|
|
- **[Project README](../../README.md)** — the host, the other clients, and how it all fits together
|