feat(gamescope/hdr): HDR is attempted by default and every install path ships the patched gamescope
ci / web (push) Successful in 1m9s
ci / docs-site (push) Successful in 1m14s
ci / rust-arm64 (push) Successful in 3m28s
deb / build-publish-client-arm64 (push) Successful in 2m28s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 10s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 9s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 11s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 9s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 12s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 29s
android / android (push) Successful in 5m47s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m16s
docker / builders-arm64cross (push) Successful in 9s
deb / build-publish (push) Successful in 6m5s
docker / deploy-docs (push) Successful in 35s
deb / build-publish-host (push) Successful in 6m35s
ci / rust (push) Successful in 6m38s
arch / build-publish (push) Successful in 9m13s
apple / swift (push) Failing after 13m37s
apple / screenshots (push) Skipped
windows-host / package (push) Successful in 17m4s
windows-host / winget-source (push) Skipped
windows-host / canary-manifest (push) Successful in 20s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 16m17s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 39s
ci / web (push) Successful in 1m9s
ci / docs-site (push) Successful in 1m14s
ci / rust-arm64 (push) Successful in 3m28s
deb / build-publish-client-arm64 (push) Successful in 2m28s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 10s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 9s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 11s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 9s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 12s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 29s
android / android (push) Successful in 5m47s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m16s
docker / builders-arm64cross (push) Successful in 9s
deb / build-publish (push) Successful in 6m5s
docker / deploy-docs (push) Successful in 35s
deb / build-publish-host (push) Successful in 6m35s
ci / rust (push) Successful in 6m38s
arch / build-publish (push) Successful in 9m13s
apple / swift (push) Failing after 13m37s
apple / screenshots (push) Skipped
windows-host / package (push) Successful in 17m4s
windows-host / winget-source (push) Skipped
windows-host / canary-manifest (push) Successful in 20s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 16m17s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 39s
Field report (RX 5700 / SteamOS): 10-bit HEVC "unsupported" — but the GPU was
never the blocker (RADV probed Main10 fine on that box). The session stayed SDR
because stock gamescope offers no 10-bit PQ capture formats and nothing shipped
our pipewire-hdr build outside the Bazzite sysext, while PUNKTFUNK_GAMESCOPE_HDR
additionally defaulted off everywhere. Close all three gaps:
- PUNKTFUNK_GAMESCOPE_HDR now defaults ON (explicit-off grammar, the flip the
config comment planned post-canary). Safe by construction: the +pfhdr banner
probe, managed-spawn term, and client 10-bit cap keep stock-gamescope boxes on
the exact 8-bit path. The nix module's gamescopeHdr (default true now) only
controls whether the patched binary is on PATH; it no longer sets the env.
- SteamOS: new scripts/steamdeck/build-gamescope.sh builds the HDR gamescope in
the same pf2 trixie distrobox (apt dep list validated by a full build in a
debian:trixie container), installs ~/.local/bin/punktfunk-gamescope, and
maintains PUNKTFUNK_GAMESCOPE_BIN in host.env ONLY while the binary passes its
on-glass --version/+pfhdr check — a stale absolute override would break
session spawning, not just HDR. Wired into install.sh (§3b), update.sh
(retrofit), and rebuild-check.sh (ldd-probes it like the host binary).
- Arch: build-sysext.sh gains a --gamescope fold-in (banner-verified by
execution, host-image-only), and punktfunk-host optdepends the companion.
Deliberately NO CI-published host sysext: per the 2026-07-22 packaging
verdict a prebuilt SteamOS host breaks on A/B soname bumps (and /var is
per-partition-set), so arch.yml records that instead — SteamOS hosts use the
distrobox flow above.
Also fixes a latent bug that made build-sysext.sh fail every invocation as
documented: a literal `}` inside `${1:?usage …{host,client}…}` terminates the
expansion early and corrupts $PKG (caught by the new synthetic-package test,
which also covers the fold-in, the unmarked-gamescope refusal, and the
client-image guard).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -40,9 +40,10 @@ serving HTTPS (HTTP/1.1 over TLS) with the host's identity cert), so its service
|
||||
|
||||
| Script | What it does |
|
||||
|--------|--------------|
|
||||
| `install.sh` | Idempotent installer: ensure the `pf2` distrobox + toolchain → build host + web + **plugin runner** → write config → tune sysctl + udev + `vhci-hcd` + `input` group and **register it on SteamOS's atomic-update keep list** (sudo) → install + start `punktfunk-host` / `punktfunk-web` systemd **user** services with linger, plus the **rebuild check** below. |
|
||||
| `update.sh` | Rebuild everything from the current source and restart the services (config + pairings persist). `--pull` does `git pull` first. Also retrofits anything a newer install.sh writes (runner, keep-list registration, rebuild check) onto older installs. |
|
||||
| `rebuild-check.sh` | The post-OS-update self-heal (run by `punktfunk-rebuild-check.service` before the host at session start): `ldd`-probes the binary — milliseconds when healthy, a full `update.sh` rebuild only when a SteamOS update actually broke its library links. |
|
||||
| `install.sh` | Idempotent installer: ensure the `pf2` distrobox + toolchain → build host + web + **plugin runner** → write config → build the **HDR gamescope** below → tune sysctl + udev + `vhci-hcd` + `input` group and **register it on SteamOS's atomic-update keep list** (sudo) → install + start `punktfunk-host` / `punktfunk-web` systemd **user** services with linger, plus the **rebuild check** below. |
|
||||
| `update.sh` | Rebuild everything from the current source and restart the services (config + pairings persist). `--pull` does `git pull` first. Also retrofits anything a newer install.sh writes (runner, HDR gamescope, keep-list registration, rebuild check) onto older installs. |
|
||||
| `build-gamescope.sh` | Build gamescope + the `pipewire-hdr` patches (`packaging/gamescope`) in the same distrobox and install it as `~/.local/bin/punktfunk-gamescope`, wiring `PUNKTFUNK_GAMESCOPE_BIN` into `host.env` — what lets Game Mode stream **10-bit BT.2020 PQ (HDR)** instead of 8-bit SDR. Best-effort: a failure warns and the host streams SDR. Content-stamped — a no-op unless `packaging/gamescope/` changed or the binary broke. |
|
||||
| `rebuild-check.sh` | The post-OS-update self-heal (run by `punktfunk-rebuild-check.service` before the host at session start): `ldd`-probes the host binary **and the HDR gamescope** — milliseconds when healthy, a full `update.sh` rebuild only when a SteamOS update actually broke library links. |
|
||||
|
||||
```sh
|
||||
git clone https://git.unom.io/unom/punktfunk ~/punktfunk
|
||||
@@ -74,6 +75,13 @@ default `pf2`), `PUNKTFUNK_MGMT_PORT` (47990), `PUNKTFUNK_WEB_PORT` (47992).
|
||||
- **Plugin runner:** the deb's payload laid out user-scoped (read-only `/usr` can't take the
|
||||
package): wrapper `~/.local/bin/punktfunk-scripting`, pinned `bun` in
|
||||
`~/.local/lib/punktfunk-scripting/`, bundle in `~/.local/share/punktfunk-scripting/`.
|
||||
- **HDR gamescope:** `~/.local/bin/punktfunk-gamescope` (gamescope + the `pipewire-hdr` patches,
|
||||
built in `pf2`, run natively — it does **not** replace the system gamescope; only the sessions
|
||||
the host spawns use it). `host.env` gains `PUNKTFUNK_GAMESCOPE_BIN=` pointing at it — a line
|
||||
`build-gamescope.sh` maintains and removes again if the binary ever stops working, because a
|
||||
stale absolute override would break session spawning, not just HDR. HDR is attempted by
|
||||
default when present; `PUNKTFUNK_GAMESCOPE_HDR=0` in `host.env` forces SDR. Verify with
|
||||
`punktfunk-host hdr-probe`.
|
||||
- **System tuning (sudo):** `/etc/sysctl.d/99-punktfunk-net.conf` (32 MB UDP buffers — the #1
|
||||
high-bitrate lever), `/etc/udev/rules.d/60-punktfunk.rules` (`uinput`/`uhid` access),
|
||||
`/etc/modules-load.d/punktfunk.conf` (`vhci-hcd` for the native Deck pad), `$USER` in the `input`
|
||||
|
||||
Executable
+122
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env bash
|
||||
# punktfunk — build the HDR-capable `punktfunk-gamescope` for a SteamOS host (called by
|
||||
# install.sh and update.sh; safe to run by hand).
|
||||
#
|
||||
# Stock gamescope offers no 10-bit PQ formats on its PipeWire capture node, so every session on
|
||||
# the gamescope backend stays 8-bit SDR. This builds gamescope + punktfunk's `pipewire-hdr`
|
||||
# patches (the ONE canonical recipe, packaging/gamescope/build-punktfunk-gamescope.sh) inside the
|
||||
# same Debian-trixie distrobox the host is built in, installs it as
|
||||
# ~/.local/bin/punktfunk-gamescope, and pins the host to it via PUNKTFUNK_GAMESCOPE_BIN in
|
||||
# host.env. HDR is on by default in the host; with this binary present a 10-bit-capable client
|
||||
# streams true HDR10 from Game Mode.
|
||||
#
|
||||
# BEST-EFFORT BY DESIGN, mirroring the CI packaging paths: streaming works without it (sessions
|
||||
# stay SDR), so a failure here warns loudly and exits 0 — it must never cost the host install or
|
||||
# update it runs inside. The one hard rule is honesty at the seams: host.env points at the binary
|
||||
# ONLY while `--version` proves it runs on SteamOS glass and carries the `+pfhdr` marker; on any
|
||||
# failure both the binary and the host.env line are removed (a stale absolute override would
|
||||
# otherwise break gamescope session spawning entirely, which is worse than SDR).
|
||||
#
|
||||
# The build clones gamescope fresh each run (submodules included). That costs bandwidth, but it
|
||||
# only runs when the packaging/gamescope tree changed (pin bump, patch change — tracked by a
|
||||
# content stamp) or the installed binary stopped working; a no-op re-run is milliseconds.
|
||||
set -euo pipefail
|
||||
|
||||
log() { printf '\033[1;36m==>\033[0m %s\n' "$*"; }
|
||||
ok() { printf '\033[1;32m ok\033[0m %s\n' "$*"; }
|
||||
warn() { printf '\033[1;33m !!\033[0m %s\n' "$*" >&2; }
|
||||
|
||||
SRC="${PUNKTFUNK_SRC:-$HOME/punktfunk}"
|
||||
BOX="${PUNKTFUNK_BOX:-pf2}"
|
||||
GS_BIN="$HOME/.local/bin/punktfunk-gamescope"
|
||||
STAMP="$HOME/.local/share/punktfunk/gamescope.stamp"
|
||||
HOST_ENV="$HOME/.config/punktfunk/host.env"
|
||||
PKGDIR="$SRC/packaging/gamescope"
|
||||
|
||||
# host.env may only name the binary while it verifiably works (see header). sed -i is fine: the
|
||||
# file is ours (install.sh §3) and the line is one this script wrote.
|
||||
unwire() {
|
||||
[ -f "$HOST_ENV" ] || return 0
|
||||
if grep -q '^PUNKTFUNK_GAMESCOPE_BIN=' "$HOST_ENV"; then
|
||||
sed -i '/^# HDR gamescope built by scripts\/steamdeck/d;/^PUNKTFUNK_GAMESCOPE_BIN=/d' "$HOST_ENV"
|
||||
warn "host.env: removed PUNKTFUNK_GAMESCOPE_BIN (no working punktfunk-gamescope) — sessions stream SDR"
|
||||
fi
|
||||
}
|
||||
wire() {
|
||||
[ -f "$HOST_ENV" ] || return 0 # install.sh runs this after §3 wrote host.env; by-hand runs may predate it
|
||||
grep -q '^PUNKTFUNK_GAMESCOPE_BIN=' "$HOST_ENV" && return 0
|
||||
printf '\n# HDR gamescope built by scripts/steamdeck/build-gamescope.sh (10-bit BT.2020 PQ capture;\n# rebuilt by update.sh; PUNKTFUNK_GAMESCOPE_HDR=0 forces SDR).\nPUNKTFUNK_GAMESCOPE_BIN=%s\n' "$GS_BIN" >> "$HOST_ENV"
|
||||
ok "host.env: PUNKTFUNK_GAMESCOPE_BIN=$GS_BIN"
|
||||
}
|
||||
# The on-glass truth: the binary must RUN ON STEAMOS (not merely in the build container — this is
|
||||
# the ABI seam the whole distrobox approach exists for) and carry the +pfhdr banner marker.
|
||||
verifies() { [ -x "$GS_BIN" ] && "$GS_BIN" --version 2>&1 | head -1 | grep -q '+pfhdr'; }
|
||||
|
||||
[ -d "$PKGDIR/patches" ] || { warn "no $PKGDIR — skipping the HDR gamescope build"; exit 0; }
|
||||
|
||||
# Content stamp over the packaging tree: a pin bump or patch edit re-triggers the build; anything
|
||||
# else makes this a fast no-op.
|
||||
want_stamp="$(find "$PKGDIR" -type f | LC_ALL=C sort | xargs sha256sum | sha256sum | cut -d' ' -f1)"
|
||||
if [ -f "$STAMP" ] && [ "$(cat "$STAMP")" = "$want_stamp" ] && verifies; then
|
||||
ok "punktfunk-gamescope up to date ($("$GS_BIN" --version 2>&1 | head -1))"
|
||||
wire
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Building punktfunk-gamescope (HDR 10-bit capture; ~5-10 min, best-effort)"
|
||||
# gamescope's build deps in the box (trixie names, mirroring packaging/gamescope/PKGBUILD — keep
|
||||
# the two lists in step). Provisioned here, not in install.sh's main pass, so a dep problem can
|
||||
# only ever cost this feature. glm/stb come in as meson wraps; wlroots/libliftoff/vkroots/
|
||||
# libdisplay-info are vendored submodules — none of those need packages.
|
||||
if ! distrobox enter "$BOX" -- bash -lc '
|
||||
set -e
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y -qq --no-install-recommends \
|
||||
meson ninja-build cmake glslang-tools \
|
||||
libwayland-dev wayland-protocols libxkbcommon-dev \
|
||||
libx11-dev libxcomposite-dev libxdamage-dev libxext-dev libxmu-dev \
|
||||
libxrender-dev libxres-dev libxtst-dev libxxf86vm-dev libxcursor-dev \
|
||||
libxcb-errors-dev libxcb-icccm4-dev libxcb-ewmh-dev \
|
||||
libxcb1-dev libxcb-composite0-dev libxcb-render0-dev libxcb-xfixes0-dev \
|
||||
libxcb-shm0-dev libxcb-res0-dev libxcb-present-dev libxcb-xinput-dev \
|
||||
libxcb-randr0-dev libxcb-dri3-dev libxcb-shape0-dev \
|
||||
libcap-dev libdrm-dev libinput-dev libudev-dev libseat-dev \
|
||||
libvulkan-dev libglm-dev libpixman-1-dev libeis-dev \
|
||||
libavif-dev libdecor-0-dev hwdata libluajit-5.1-dev \
|
||||
libpipewire-0.3-dev libspa-0.2-dev libsdl2-dev \
|
||||
xwayland liblcms2-dev >/dev/null
|
||||
' ; then
|
||||
warn "could not provision gamescope build deps in '$BOX' — sessions stay SDR (re-run update.sh to retry)"
|
||||
exit 0
|
||||
fi
|
||||
if ! distrobox enter "$BOX" -- bash -lc "
|
||||
set -e
|
||||
bash '$PKGDIR/build-punktfunk-gamescope.sh' --prefix \"\$HOME/.local\" --no-setcap
|
||||
"; then
|
||||
warn "punktfunk-gamescope failed to build — sessions stay SDR (re-run update.sh to retry)"
|
||||
unwire
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if verifies; then
|
||||
# CAP_SYS_NICE is gamescope's scheduling boost — worth it, never required. Filesystem caps
|
||||
# can't be set from the rootless box, so it happens here on the SteamOS side.
|
||||
if sudo -n true 2>/dev/null; then
|
||||
sudo setcap 'CAP_SYS_NICE=eip' "$GS_BIN" 2>/dev/null \
|
||||
&& ok "setcap CAP_SYS_NICE (frame-pacing boost)" \
|
||||
|| warn "could not setcap CAP_SYS_NICE (gamescope still works, pacing slightly worse)"
|
||||
fi
|
||||
mkdir -p "$(dirname "$STAMP")"
|
||||
printf '%s' "$want_stamp" > "$STAMP"
|
||||
wire
|
||||
ok "punktfunk-gamescope ready: $("$GS_BIN" --version 2>&1 | head -1)"
|
||||
else
|
||||
# Built in the box but does not run on SteamOS (an ABI/soname seam) or lacks the marker. A
|
||||
# broken binary must not linger where PATH or host.env could resolve it.
|
||||
warn "built binary failed its on-glass check on SteamOS — removing it; sessions stay SDR"
|
||||
"$GS_BIN" --version 2>&1 | head -3 >&2 || true
|
||||
rm -f "$GS_BIN" "$STAMP"
|
||||
unwire
|
||||
fi
|
||||
exit 0
|
||||
@@ -245,6 +245,14 @@ else
|
||||
[ "$WITH_WEB" = 1 ] && ok "web.env exists (login password unchanged)"
|
||||
fi
|
||||
|
||||
# --- 3b. HDR gamescope (punktfunk-gamescope, best-effort) ------------------
|
||||
# Stock gamescope offers only 8-bit capture, so Game Mode streams SDR. The punktfunk build adds
|
||||
# the 10-bit BT.2020 PQ formats, and the host attempts HDR by default the moment it is present
|
||||
# (PUNKTFUNK_GAMESCOPE_HDR=0 forces SDR). Best-effort by design: on any failure this warns and
|
||||
# the host streams SDR — see build-gamescope.sh, which also wires PUNKTFUNK_GAMESCOPE_BIN into
|
||||
# host.env only while the binary provably runs on SteamOS.
|
||||
PUNKTFUNK_SRC="$SRC" PUNKTFUNK_BOX="$BOX" bash "$SRC/scripts/steamdeck/build-gamescope.sh"
|
||||
|
||||
# --- 4. system tuning (needs sudo: UDP buffers + gamepad udev rule + vhci-hcd + input group) --------
|
||||
log "System tuning (UDP buffers + gamepad rules + vhci-hcd + input group)"
|
||||
# sudo was acquired up front in preflight (SUDO_OK) so this never stalls behind the long build; a
|
||||
|
||||
@@ -18,14 +18,29 @@ export PATH="$HOME/.local/bin:$PATH"
|
||||
SRC="${PUNKTFUNK_SRC:-$HOME/punktfunk}"
|
||||
BIN="$SRC/target-steamos/release/punktfunk-host"
|
||||
|
||||
NEED=0
|
||||
if [ ! -x "$BIN" ]; then
|
||||
echo "punktfunk-host binary missing at $BIN — running a full rebuild" >&2
|
||||
elif ! ldd "$BIN" 2>/dev/null | grep -q "not found"; then
|
||||
exit 0 # every library resolves — nothing to do
|
||||
else
|
||||
NEED=1
|
||||
elif ldd "$BIN" 2>/dev/null | grep -q "not found"; then
|
||||
echo "punktfunk-host no longer loads after a SteamOS update — its missing libraries:" >&2
|
||||
ldd "$BIN" 2>/dev/null | grep "not found" >&2 || true
|
||||
echo "rebuilding against the new OS tree (this takes a few minutes; streaming resumes after)" >&2
|
||||
NEED=1
|
||||
fi
|
||||
|
||||
# The HDR gamescope companion chases OS libraries the same way. Probe it only when host.env pins
|
||||
# it (build-gamescope.sh wires that line only while the binary works) — a break here would not
|
||||
# just lose HDR, it would break gamescope session SPAWNING via the stale absolute override, so it
|
||||
# rebuilds with the same urgency as the host binary.
|
||||
GS_BIN="$(sed -n 's/^PUNKTFUNK_GAMESCOPE_BIN=//p' "$HOME/.config/punktfunk/host.env" 2>/dev/null | head -1)"
|
||||
if [ -n "$GS_BIN" ]; then
|
||||
if [ ! -x "$GS_BIN" ] || ldd "$GS_BIN" 2>/dev/null | grep -q "not found"; then
|
||||
echo "punktfunk-gamescope no longer loads after a SteamOS update — rebuilding it" >&2
|
||||
NEED=1
|
||||
fi
|
||||
fi
|
||||
|
||||
[ "$NEED" = 0 ] && exit 0 # everything resolves — nothing to do (every normal boot)
|
||||
|
||||
exec bash "$SRC/scripts/steamdeck/update.sh"
|
||||
|
||||
@@ -52,6 +52,12 @@ sed 's|^ExecStart=.*|ExecStart=%h/.local/bin/punktfunk-scripting|' \
|
||||
systemctl --user daemon-reload
|
||||
ok "plugin runner rebuilt (opt-in service: systemctl --user enable --now punktfunk-scripting)"
|
||||
|
||||
# HDR gamescope (punktfunk-gamescope): rebuild when the packaging tree changed or the installed
|
||||
# binary stopped working — also RETROFITS it onto older installs that predate it (fast no-op
|
||||
# otherwise). Best-effort; on failure the host streams SDR (see build-gamescope.sh).
|
||||
log "HDR gamescope (punktfunk-gamescope)"
|
||||
PUNKTFUNK_SRC="$SRC" PUNKTFUNK_BOX="$BOX" bash "$SRC/scripts/steamdeck/build-gamescope.sh"
|
||||
|
||||
# Retrofit the post-OS-update rebuild check (install.sh §5) onto older installs: probes the host
|
||||
# binary with ldd at session start and re-runs this script when a SteamOS update broke its links.
|
||||
if [ ! -f "$HOME/.config/systemd/user/punktfunk-rebuild-check.service" ]; then
|
||||
|
||||
Reference in New Issue
Block a user