Compare commits

...
Author SHA1 Message Date
enricobuehler a3e073fd3a Merge pull request 'The host-audio switch reaches every client, not just the desktop ones' (#423) from worktree-keep-host-audio-parity into main
ci / docs-drift (push) Successful in 28s
ci / bun-nix (push) Successful in 31s
ci / docs-site (push) Successful in 1m8s
apple / swift (push) Successful in 2m10s
ci / web (push) Successful in 2m6s
deb / build-publish-gamescope (push) Successful in 52s
ci / rust-arm64 (push) Successful in 2m48s
deb / build-publish-client-arm64 (push) Successful in 1m18s
deb / build-publish (push) Successful in 6m34s
windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 7m30s
arch / build-publish (push) Successful in 9m7s
android / android (push) Successful in 9m28s
ci / rust (push) Successful in 9m43s
decky / build-publish (push) Successful in 55s
deb / build-publish-host (push) Successful in 8m51s
flatpak / build-publish (push) Successful in 4m45s
deb / smoke-install (push) Successful in 2m21s
apple / distribute (push) Successful in 11m15s
windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 9m33s
apple / screenshots (push) Successful in 10m0s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 20m6s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 21m13s
windows-host / package (push) Successful in 14m54s
windows-host / winget-source (push) Skipped
windows-host / canary-manifest (push) Successful in 31s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 18s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 50s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 14s
docker / builders (ci/flatpak-ci.Dockerfile, punktfunk-flatpak-ci) (push) Successful in 1m14s
docker / builders (ci/gamescope-trixie.Dockerfile, punktfunk-gamescope-trixie) (push) Successful in 16s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 15s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 15s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 1m1s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m37s
docker / deploy-docs (push) Successful in 39s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 13s
docker / builders-arm64cross (push) Successful in 11s
2026-08-27 23:26:41 +00:00
enricobuehler 1391de87d6 Merge pull request 'A prep entry stops naming a device, and the docs show it' (#422) from worktree-prep-mode-env-example into main
ci / bun-nix (push) Successful in 36s
ci / docs-drift (push) Successful in 35s
ci / web (push) Successful in 1m5s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 16s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 23s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 33s
ci / docs-site (push) Successful in 1m47s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 38s
docker / builders (ci/flatpak-ci.Dockerfile, punktfunk-flatpak-ci) (push) Successful in 34s
docker / builders (ci/gamescope-trixie.Dockerfile, punktfunk-gamescope-trixie) (push) Successful in 26s
ci / rust-arm64 (push) Successful in 2m24s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 14s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 35s
docker / builders-arm64cross (push) Successful in 18s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 1m4s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 2m19s
docker / deploy-docs (push) Successful in 45s
ci / rust (push) Successful in 8m13s
Reviewed-on: #422
2026-08-27 22:50:03 +00:00
enricobuehler 3b0e880b4c docs(automation): a worked example for the prep mode env
ci / docs-drift (pull_request) Successful in 28s
ci / bun-nix (pull_request) Successful in 30s
ci / docs-site (pull_request) Successful in 1m3s
ci / web (pull_request) Successful in 1m5s
ci / rust-arm64 (pull_request) Successful in 1m58s
ci / rust (pull_request) Successful in 7m36s
v0.32.0 gave per-app prep do/undo steps PF_STREAM_WIDTH/_HEIGHT/_REFRESH/_HDR
(hooks::prep_mode_env, both serving planes), but the docs' only example was a
one-liner. Add the entry+script pair that shows the point: the prep entry stops
naming a device and reads the negotiated mode instead - an FPS cap at the
client's own refresh, an HDR arm taken only when the session negotiated it, and
the raster for whatever wants pixels.

Written so the traps are visible rather than explained: undo sees the values its
do saw, HDR is 1/0 (the marker file's spelling) not PF_EVENT_*'s true/false, the
app identity is PF_APP_ID or PF_APP_TITLE by plane, and set -u makes an older
host fail the step loudly instead of capping at 'fps_limit='.

Verified: the fenced script extracted from the doc and run under sh - do writes
the cap and takes the HDR arm, undo restores and reverses it, an SDR session
skips HDR, an empty identity falls through to the literal, and an unset
environment exits non-zero.
2026-08-28 00:45:17 +02:00
+64
View File
@@ -164,6 +164,70 @@ per-mode frame cap is one step for every device —
`{ "do": "rtss-cli property:set Global FramerateLimit $PF_STREAM_REFRESH" }` — instead of one
hard-coded entry per client.
### One entry, every client
The point of those four variables is that the *entry* stops describing a device. Attach one script
to the title and let the session tell it what it got — 60 Hz on the phone, 4K120 HDR on the TV, from
the same two lines:
```json
{ "id": 2, "title": "Cyberpunk 2077", "cmd": "steam -applaunch 1091500",
"prep": [
{ "do": "/home/me/.config/punktfunk/scripts/mode.sh do",
"undo": "/home/me/.config/punktfunk/scripts/mode.sh undo" }
] }
```
```sh
#!/bin/sh
# ~/.config/punktfunk/scripts/mode.sh — run as `mode.sh do` before the title, `mode.sh undo`
# at session end. Nothing here names a client: the negotiated mode arrives in the environment.
set -eu
CONF="${XDG_CONFIG_HOME:-$HOME/.config}/MangoHud/MangoHud.conf"
case "${1:-}" in
do)
# Cap the game at the refresh this client actually negotiated.
cp -f "$CONF" "$CONF.pf-bak"
printf 'fps_limit=%s\n' "$PF_STREAM_REFRESH" >>"$CONF"
# Light the panel's HDR only when the session really negotiated it.
if [ "$PF_STREAM_HDR" = 1 ]; then
kscreen-doctor output.HDMI-A-1.hdr.enable
fi
# The raster, for anything that wants pixels — a launcher's window size, a per-mode
# config profile, or just a line in the journal naming what launched.
logger -t punktfunk \
"prep ${PF_APP_ID:-${PF_APP_TITLE:-desktop}}: ${PF_STREAM_WIDTH}x${PF_STREAM_HEIGHT}@${PF_STREAM_REFRESH}"
;;
undo)
mv -f "$CONF.pf-bak" "$CONF"
if [ "$PF_STREAM_HDR" = 1 ]; then
kscreen-doctor output.HDMI-A-1.hdr.disable
fi
;;
esac
```
Four things that example is quietly relying on:
- **`undo` sees exactly what its `do` saw.** The values are captured once, at launch, and held for
the session — so teardown can branch on `PF_STREAM_HDR` and reach the same answer however the
stream ended.
- **`PF_STREAM_HDR` is `1`/`0`**, the stream-marker file's spelling, not the `true`/`false` that
`PF_EVENT_*` uses. One script can be written against either.
- **The app identity depends on the plane**: `PF_APP_ID` on a native client's launch,
`PF_APP_TITLE` from a Moonlight one. `${PF_APP_ID:-${PF_APP_TITLE:-desktop}}` reads whichever one
is set, and `:-` also catches the empty string a launch with no title of its own leaves behind.
- **`set -u` is doing work.** An older host doesn't set these, and the step then fails loudly (and
disarms its own `undo`) instead of silently capping the game at `fps_limit=`.
The same `prep` array works on a custom `library.json` entry, where the identity arrives as
`PF_APP_ID`. The console's Library form has no input for prep steps and **clears** them on save, so
edit that file directly.
## Reacting to a game, not a stream
`stream.stopped` tells you the *stream* ended; `game.exited` tells you the *game* did. Often the