#!/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