Files
punktfunk/clients/session
enricobuehlerandClaude Opus 5 9c5af8d7e1
ci / web (push) Successful in 58s
ci / docs-site (push) Successful in 2m50s
ci / rust-arm64 (push) Successful in 2m55s
deb / build-publish-host (push) Successful in 4m16s
deb / build-publish-client-arm64 (push) Successful in 4m56s
apple / swift (push) Successful in 4m41s
flatpak / build-publish (push) Successful in 6m32s
deb / build-publish (push) Successful in 3m44s
android / android (push) Successful in 8m2s
ci / rust (push) Successful in 11m18s
arch / build-publish (push) Successful in 10m24s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m42s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m10s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 19m24s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 4m1s
apple / screenshots (push) Successful in 20m36s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 4m40s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 4m9s
fix(clients): the settings you chose reach the stream again, and four more that never did
Since spec mode (`f32c3aaa`, shipped in 0.22.0) the GTK shell has handed the session
a `--resolved-spec` built from `Settings::default()`. A session running from a spec
performs ZERO store reads by design, so every stream since has run at the defaults:
`bitrate_kbps: 0` reaches the host, which reads it as its 20 Mbps fallback — the
field report this starts from, "my bitrate seems to be stuck at 20 MBPS" on 0.22.3 —
and with it the resolution, refresh, render scale, codec, decoder, HDR, 4:4:4, audio
channels, mic, touch/mouse mode, invert scroll, stats tier, match-window and gamepad
type. Profiles never applied at all: the spec's `profile` was None, and spec mode
ignores `--profile`. The tell in the wild is that the GPU and audio-device pickers
kept working — the session reads those three off disk into env vars before it reads
the spec.

The plan carried a comment explaining that `settings` "carries only what the argv
needs (the fullscreen flag)". That was true when it was written and stopped being
true one commit later, in another file. `ConnectPlan::for_target` answers that shape
of bug: a front-end holding its own request type resolves through the same helper the
session's compat path uses, so there is no hand-built `Settings` left to go stale.
The GTK shell's `fullscreen_on_stream` parameter goes with it — that is a tier-P
field, and a shell-read global was beating a profile that set it. The Windows shell
was never affected (it spawns with no spec) but had the same fullscreen-vs-profile
bug, so it moves onto the resolver too.

The audit that followed found four more settings stored, rendered in two UIs, and
read by nothing:

- `enable_444` never became `VIDEO_CAP_444`. "Full chroma (4:4:4)" is announced in
  the 0.22.0 notes for Linux and Windows and did nothing on either; only Apple
  advertised the bit. The host already gates it on its own policy, its capturer,
  HEVC and a real GPU probe, and answers the resolved chroma in the Welcome before
  we build a decoder — the client only has to ask. There is no desktop decode probe
  (Apple has `Stage444Probe`), but the presenter bails cleanly on a plane format it
  doesn't know and demotes to software, so the downside is a slow decode, not a
  broken one.
- `session_params` picked the HEIGHT fallback off `settings.width == 0`.
- `cli::exec_session` dropped `--profile` from its forward list, so
  `punktfunk --connect … --profile Work` from a script or a Decky wrapper streamed
  with the host's binding instead.
- `ResolvedSpec::write_temp` named the spec by the SPAWNER's pid alone, so a
  cancelled connect and the retry behind it shared one path — and the first child's
  exit deleted the file the second was still starting up to read.

The regression test asserts the plan carries the profile-overlaid bitrate and that
the spec equals it. On the old code both are 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 19:32:51 +02:00
..

punktfunk-session

The Vulkan session binary: one stream per invocation in an SDL3 window — no UI toolkit, no widgets, terminal stats. The power-user / gamescope stream client, and the stage-2 presenter of the Linux client re-architecture (punktfunk-planning: linux-client-rearchitecture.md).

This binary is deliberately dumb: a renderer the front-ends call INTO — the GTK shell (punktfunk-client), the WinUI shell, and the punktfunk CLI all spawn it through the same brain (pf_client_core::orchestrate), which resolves policy (profiles, settings, wake) and hands the result down, normally as a --resolved-spec file. It reads the shared stores only as the compat fallback for a bare hand-launched invocation.

punktfunk-session --connect host[:port] [--fp HEX] [--launch id] [--fullscreen] [--stats]
punktfunk-session --browse host[:port] [--mgmt PORT] [--fullscreen]

--browse opens the console game library (the Skia coverflow over the animated aurora) instead of connecting: A launches the focused title as a stream in the same window, session end returns to the library, B quits (Gaming Mode returns). Paired hosts only — pairing is the desktop client / Decky plugin's job. PUNKTFUNK_FAKE_LIBRARY=<file.json> feeds canned entries with no host (portrait paths starting with / load from disk).

Reads the same identity / known-hosts / settings stores as the desktop client (punktfunk-client), so enrolling on either side makes the other work; this binary never connects to a host it has no pinned fingerprint for (--fp HEX overrides the store).

Pairing is punktfunk pair <host> — the CLI, which ships alongside this binary in every package and needs no window and no toolkit either. punktfunk-session --pair still works for one release (someone's provisioning script calls it today) but prints a deprecation notice: pairing is a trust ceremony and belongs to the brain, not a renderer.

Stdout is the machine interface: {"ready":true} after the first presented frame, stats: … once per second while the overlay tier isn't Off (always the full detailed text, whatever the OSD shows; --stats forces the overlay on), one {"error"|"ended": …} JSON line on the way out. Logs go to stderr. Exit codes: 0 clean end, 2 connect failed, 3 trust rejected / pairing required, 4 presenter init failed.

In-stream keys match the desktop client: click captures input (Ctrl+Alt+Shift+Q releases), Ctrl+Alt+Shift+D disconnects, F11 toggles fullscreen; the controller escape chord (L1+R1+Start+Select, hold to disconnect) works the same.

The default build carries the Skia console UI (ui feature): the stats OSD and capture hint render in-window. Ctrl+Alt+Shift+S cycles the OSD tier live — Off → Compact (one line: fps · latency · Mb/s) → Normal (mode + end-to-end percentiles) → Detailed (decoder path + per-stage latency equation); any tier but Off also emits the stdout mirror. --no-default-features is the ~5 MB power-user build — same streaming, stats on stdout only, no Skia anywhere in the dependency tree.

Decode follows the Settings preference (auto: Vulkan Video → VAAPI → software on Linux, Vulkan Video → D3D11VA → software on Windows): FFmpeg's Vulkan Video decoder runs on the presenter's own device where the stack supports it (every vendor, zero copy); VAAPI dmabufs import per-plane elsewhere (D3D11VA textures on Windows); software is the universal fallback. 10-bit Main10 and HDR10 are advertised (VIDEO_CAP_10BIT|HDR): P010 decodes through all three paths, and PQ streams present on an HDR10/ST.2084 swapchain when the desktop offers one (KDE HDR, gamescope) or tone-map in-shader to SDR when it doesn't (PUNKTFUNK_TONEMAP_PEAK tunes the rolloff, default ≈1000 nits). The host still gates the upgrade behind its PUNKTFUNK_10BIT policy.

Debug/bisect knobs: PUNKTFUNK_DECODER=vulkan|vaapi|d3d11va|software, PUNKTFUNK_PRESENT_MODE= mailbox|immediate (default FIFO), PUNKTFUNK_VK_DEVICE=<index> (multi-GPU), and PUNKTFUNK_HW_FAULT=import (fault every VAAPI dmabuf import — proves the three-strike demotion to software on healthy hardware).