diff --git a/packaging/arch/PKGBUILD b/packaging/arch/PKGBUILD index 00b0ce1..7346e28 100644 --- a/packaging/arch/PKGBUILD +++ b/packaging/arch/PKGBUILD @@ -1,87 +1,75 @@ # Maintainer: unom # -# Arch Linux / SteamOS package for the punktfunk streaming HOST. Mirrors the artifact -# set of packaging/rpm/punktfunk.spec and packaging/debian/build-deb.sh exactly. +# Arch Linux / SteamOS split package: punktfunk-host (the gaming-rig HOST, NVENC) and +# punktfunk-client (the GTK4 couch/Deck CLIENT). Mirrors the rpm subpackages +# (packaging/rpm/punktfunk.spec) and the two deb build scripts. On a Steam Deck you want +# `punktfunk-client` (it's what the Decky plugin launches); on a gaming rig, `punktfunk-host`. # # Two build modes: # - AUR / standalone: makepkg in this dir (fetches the git tag below). # - In-tree / CI: PF_SRCDIR=$(git rev-parse --show-toplevel) makepkg --holdver # (builds the working tree instead of the tagged source — see build()). # -# IMPORTANT: encode is NVENC-only (crates/punktfunk-host/src/encode/linux.rs). This package is -# functional on NVIDIA hosts (nvidia-utils + Arch's NVENC-enabled ffmpeg). On an AMD Steam Deck -# it installs but CANNOT encode until a VAAPI (hevc_vaapi) backend exists — see packaging/arch/README.md. -pkgname=punktfunk-host +# IMPORTANT: host encode is NVENC-only (crates/punktfunk-host/src/encode/linux.rs) — functional on +# NVIDIA hosts; an AMD Deck-as-HOST needs a VAAPI backend first. The CLIENT decodes via VAAPI +# (AMD/Intel, incl. the Deck) with a software fallback, so it works everywhere. See README.md. +pkgbase=punktfunk +pkgname=('punktfunk-host' 'punktfunk-client') pkgver=0.0.1 pkgrel=1 -pkgdesc="Low-latency desktop/game streaming host (Moonlight-compatible + punktfunk/1)" arch=('x86_64') url="https://git.unom.io/unom/punktfunk" license=('MIT OR Apache-2.0') -# Runtime libs the binary links or drives. NVENC + GPU EGL/CUDA come from the NVIDIA driver -# (nvidia-utils) — kept an optdepend, never a hard dep, exactly as the RPM (__requires_exclude -# libcuda) and deb (shlibdeps grep-filter) do: the driver is installed out of band. -depends=( - 'ffmpeg' # libavcodec/format/util — Arch's ffmpeg ships hevc_nvenc/av1_nvenc - 'pipewire' # capture + audio transport - 'pipewire-pulse' # desktop audio bridge - 'wireplumber' # PipeWire session manager - 'opus' # GameStream/punktfunk Opus audio - 'libei' # input injection (RemoteDesktop portal / EIS) - 'mesa' # libgbm (+ EGL on non-NVIDIA) - 'libglvnd' # libGL/libEGL loader - 'libxkbcommon' # virtual keyboard keymap - 'wayland' # wayland-client (vdisplay backends) -) -optdepends=( - 'nvidia-utils: NVENC hardware encode + GPU EGL/CUDA zero-copy (REQUIRED to encode on NVIDIA)' - 'gamescope: per-session nested compositor backend (no desktop login needed) — needs >=3.16.22' - 'kwin: stream a KDE Plasma desktop (kwin VirtualDisplay backend)' - 'mutter: stream a GNOME desktop (Mutter RecordVirtual backend)' - 'sway: stream a wlroots desktop (Sway VirtualDisplay backend)' - 'xdg-desktop-portal-kde: portal for the headless KDE session helper' - 'xdg-desktop-portal-wlr: portal for the headless Sway session helper' -) -makedepends=('rust' 'cargo' 'clang' 'cmake' 'nasm' 'pkgconf' 'git') +# All build deps for both crates (Arch runtime packages ship their own headers, so these cover +# build + link). aws-lc/ring need clang+cmake; nasm is for asm. +makedepends=('rust' 'cargo' 'clang' 'cmake' 'nasm' 'pkgconf' 'git' + 'gtk4' 'libadwaita' 'sdl3' 'ffmpeg' 'pipewire' 'wayland' 'libxkbcommon' 'opus' 'libei') # AUR source (a tagged release). For an in-tree CI build, set PF_SRCDIR to the repo root and # build() uses it instead; see the README. source=("git+https://git.unom.io/unom/punktfunk.git#tag=v${pkgver}") sha256sums=('SKIP') -install="${pkgname}.install" _repo() { printf '%s' "${PF_SRCDIR:-$srcdir/punktfunk}"; } build() { cd "$(_repo)" export RUSTUP_TOOLCHAIN=stable CARGO_TARGET_DIR="$srcdir/target" - # The zero-copy FFI link-needs libcuda at build time; nvidia-utils provides it on an NVIDIA - # builder. On a GPU-less builder, symlink the CUDA stub into the link path first (same caveat - # the RPM documents): ln -s "$(find / -name libcuda.so -path '*stubs*' | head -1)" /usr/lib/ - cargo build --release --locked -p punktfunk-host + # The host's zero-copy FFI link-needs libcuda at build time; nvidia-utils provides it on an + # NVIDIA builder. On a GPU-less builder symlink the CUDA stub into the link path first (same + # caveat the RPM documents): ln -s "$(find / -name libcuda.so -path '*stubs*'|head -1)" /usr/lib/ + cargo build --release --locked -p punktfunk-host -p punktfunk-client-linux } -package() { +package_punktfunk-host() { + pkgdesc="Low-latency desktop/game streaming HOST (Moonlight-compatible + punktfunk/1)" + # NVENC + GPU EGL/CUDA come from the NVIDIA driver (nvidia-utils) — kept an optdepend, never a + # hard dep, exactly as the RPM (__requires_exclude libcuda) and deb (shlibdeps filter) do. + depends=('ffmpeg' 'pipewire' 'pipewire-pulse' 'wireplumber' 'opus' 'libei' + 'mesa' 'libglvnd' 'libxkbcommon' 'wayland') + optdepends=('nvidia-utils: NVENC hardware encode + GPU EGL/CUDA zero-copy (REQUIRED to encode on NVIDIA)' + 'gamescope: per-session nested compositor backend (no desktop login needed) — needs >=3.16.22' + 'kwin: stream a KDE Plasma desktop (kwin VirtualDisplay backend)' + 'mutter: stream a GNOME desktop (Mutter RecordVirtual backend)' + 'sway: stream a wlroots desktop (Sway VirtualDisplay backend)' + 'xdg-desktop-portal-kde: portal for the headless KDE session helper' + 'xdg-desktop-portal-wlr: portal for the headless Sway session helper') + install=punktfunk-host.install local R; R="$(_repo)"; local T="$srcdir/target/release" install -Dm0755 "$T/punktfunk-host" "$pkgdir/usr/bin/punktfunk-host" - # /dev/uinput + /dev/uhid -> input group (virtual gamepads + DualSense UHID) install -Dm0644 "$R/scripts/60-punktfunk.rules" "$pkgdir/usr/lib/udev/rules.d/60-punktfunk.rules" # 32 MB UDP socket buffers (send-side headroom at high bitrate) install -Dm0644 "$R/scripts/99-punktfunk-net.conf" "$pkgdir/usr/lib/sysctl.d/99-punktfunk-net.conf" - - # systemd USER units (the host runs in the graphical session, not as root); the source units - # point at the dev tree — repoint ExecStart at the installed paths (mirrors rpm/deb). + # systemd USER units (the host runs in the graphical session, not as root); repoint ExecStart. install -Dm0644 "$R/scripts/punktfunk-host.service" "$pkgdir/usr/lib/systemd/user/punktfunk-host.service" sed -i 's#%h/punktfunk/target/release/punktfunk-host#/usr/bin/punktfunk-host#' \ "$pkgdir/usr/lib/systemd/user/punktfunk-host.service" - install -Dm0644 "$R/scripts/punktfunk-kde-session.service" "$pkgdir/usr/lib/systemd/user/punktfunk-kde-session.service" 2>/dev/null || true - [ -f "$pkgdir/usr/lib/systemd/user/punktfunk-kde-session.service" ] && \ - sed -i 's#%h/punktfunk/scripts/headless/run-headless-kde.sh#/usr/share/punktfunk/headless/run-headless-kde.sh#' \ - "$pkgdir/usr/lib/systemd/user/punktfunk-kde-session.service" - + install -Dm0644 "$R/scripts/punktfunk-kde-session.service" "$pkgdir/usr/lib/systemd/user/punktfunk-kde-session.service" + sed -i 's#%h/punktfunk/scripts/headless/run-headless-kde.sh#/usr/share/punktfunk/headless/run-headless-kde.sh#' \ + "$pkgdir/usr/lib/systemd/user/punktfunk-kde-session.service" # headless session helpers + env templates + OpenAPI doc install -Dm0755 "$R/scripts/headless/run-headless-kde.sh" "$pkgdir/usr/share/punktfunk/headless/run-headless-kde.sh" install -Dm0755 "$R/scripts/headless/run-headless-sway.sh" "$pkgdir/usr/share/punktfunk/headless/run-headless-sway.sh" @@ -91,8 +79,31 @@ package() { install -Dm0644 "$R/packaging/bazzite/host.env" "$pkgdir/usr/share/punktfunk/host.env.bazzite" install -Dm0644 "$R/packaging/kde/host.env" "$pkgdir/usr/share/punktfunk/host.env.kde" install -Dm0644 "$R/docs/api/openapi.json" "$pkgdir/usr/share/punktfunk/openapi.json" - - install -Dm0644 "$R/LICENSE-MIT" "$pkgdir/usr/share/licenses/$pkgname/LICENSE-MIT" - install -Dm0644 "$R/LICENSE-APACHE" "$pkgdir/usr/share/licenses/$pkgname/LICENSE-APACHE" - install -Dm0644 "$R/README.md" "$pkgdir/usr/share/doc/$pkgname/README.md" + install -Dm0644 "$R/LICENSE-MIT" "$pkgdir/usr/share/licenses/punktfunk-host/LICENSE-MIT" + install -Dm0644 "$R/LICENSE-APACHE" "$pkgdir/usr/share/licenses/punktfunk-host/LICENSE-APACHE" + install -Dm0644 "$R/README.md" "$pkgdir/usr/share/doc/punktfunk-host/README.md" +} + +package_punktfunk-client() { + pkgdesc="Low-latency desktop/game streaming CLIENT (GTK4) — the couch/Deck side" + # The GTK4/libadwaita client: SDL3 gamepads, FFmpeg (VAAPI) decode, PipeWire audio/mic. + depends=('gtk4' 'libadwaita' 'sdl3' 'ffmpeg' 'pipewire' 'wireplumber' 'pipewire-pulse' + 'opus' 'libglvnd') + optdepends=('libva-mesa-driver: VAAPI hardware decode on AMD (incl. Steam Deck); software fallback otherwise' + 'intel-media-driver: VAAPI hardware decode on Intel' + 'gamescope: run the client fullscreen in a Gaming-Mode session') + install=punktfunk-client.install + local R; R="$(_repo)"; local T="$srcdir/target/release" + + install -Dm0755 "$T/punktfunk-client" "$pkgdir/usr/bin/punktfunk-client" + install -Dm0644 "$R/packaging/linux/io.unom.Punktfunk.desktop" \ + "$pkgdir/usr/share/applications/io.unom.Punktfunk.desktop" + # DualSense hidraw access (full pad fidelity through SDL's HIDAPI driver). + install -Dm0644 "$R/scripts/70-punktfunk-client.rules" \ + "$pkgdir/usr/lib/udev/rules.d/70-punktfunk-client.rules" + # 32 MB UDP recv buffer (so high-bitrate streams don't overflow the kernel socket buffer). + install -Dm0644 "$R/scripts/99-punktfunk-client-net.conf" \ + "$pkgdir/usr/lib/sysctl.d/99-punktfunk-client-net.conf" + install -Dm0644 "$R/LICENSE-MIT" "$pkgdir/usr/share/licenses/punktfunk-client/LICENSE-MIT" + install -Dm0644 "$R/LICENSE-APACHE" "$pkgdir/usr/share/licenses/punktfunk-client/LICENSE-APACHE" } diff --git a/packaging/arch/README.md b/packaging/arch/README.md index c6eb9d3..ca38000 100644 --- a/packaging/arch/README.md +++ b/packaging/arch/README.md @@ -1,15 +1,18 @@ # punktfunk on Arch Linux / SteamOS -Packaging for the punktfunk streaming **host** on Arch and Arch-derived immutable distros -(SteamOS 3, etc.). Mirrors the artifact set of `packaging/rpm/punktfunk.spec` and -`packaging/debian/build-deb.sh`. +Packaging for punktfunk on Arch and Arch-derived immutable distros (SteamOS 3, etc.). The +`PKGBUILD` is a **split package** producing both **`punktfunk-host`** (the gaming-rig host) and +**`punktfunk-client`** (the GTK4 couch/Deck client) — mirrors the rpm subpackages +(`packaging/rpm/punktfunk.spec`) and the two deb build scripts. On a **Steam Deck you want +`punktfunk-client`** (it's what the [Decky plugin](../../clients/decky/) launches); on a gaming +rig, `punktfunk-host`. -> ⚠️ **Encode is NVENC-only today.** `crates/punktfunk-host/src/encode/linux.rs` implements -> `hevc_nvenc`/`av1_nvenc`/`h264_nvenc` and a CUDA zero-copy path — there is **no VAAPI backend**. -> So this package is functional on **Arch + NVIDIA** (the realistic target). On an **AMD Steam -> Deck it installs but cannot encode** until a `hevc_vaapi`/`av1_vaapi` encoder is added in -> `src/encode/` — a code change, not a packaging one. (`bazzite-deck-nvidia`, i.e. SteamOS-style -> images running on NVIDIA hardware, work fine — they're NVIDIA.) +> ⚠️ **Host encode is NVENC-only today.** `crates/punktfunk-host/src/encode/linux.rs` implements +> `hevc_nvenc`/`av1_nvenc`/`h264_nvenc` + a CUDA zero-copy path — there is **no VAAPI encoder**. So +> `punktfunk-host` works on **Arch + NVIDIA** (incl. `bazzite-deck-nvidia`); an **AMD Deck-as-host** +> can't encode until a `hevc_vaapi` backend is added (a code change, not packaging). The **client +> is unaffected** — `punktfunk-client` decodes via **VAAPI on AMD/Intel** (the Deck) with a software +> fallback, so streaming *to* a Deck works today. ## Arch Linux (mutable) @@ -64,7 +67,28 @@ systemctl --user enable --now punktfunk-host # the user unit is now under /u The udev rule, sysctl, and systemd **user** unit all live under `/usr/lib`, so the merged sysext exposes them. `systemd-sysext refresh` re-merges after a reboot. +## Steam Deck — the client (what the Decky plugin launches) + +To stream *to* a Deck, you install **`punktfunk-client`** there — same sysext mechanism, but +wrapping the client package instead. The split `makepkg` produces both `.pkg.tar.zst` files; on the +Deck use the client one: +```sh +cd packaging/arch && PF_SRCDIR="$(git rev-parse --show-toplevel)" makepkg -f --holdver +bash build-sysext.sh punktfunk-client-*.pkg.tar.zst # → punktfunk-client.raw +# on the Deck: +sudo cp punktfunk-client.raw /var/lib/extensions/ +sudo systemctl enable --now systemd-sysext +sudo pacman -S --needed libva-mesa-driver # VAAPI hw decode on the Deck's AMD APU +``` +Now `punktfunk-client` is on `PATH`, so the **[Decky plugin](../../clients/decky/)** finds and +launches it (`punktfunk-client --connect host:port`) — gamescope composites its video like a game. +The client needs no `/dev/uinput` or compositor-spawning rights (it captures input and decodes), +so it's a much lighter sysext than the host. + ## Files -- `PKGBUILD` — the package recipe (builds the working tree via `PF_SRCDIR`, or a git tag for AUR). -- `punktfunk-host.install` — pacman scriptlet (udev reload + sysctl + first-run hint), mirrors RPM `%post`. -- `build-sysext.sh` — wraps a built `.pkg.tar.zst` into a `systemd-sysext` `.raw` for SteamOS. +- `PKGBUILD` — split package: `punktfunk-host` + `punktfunk-client` (builds the working tree via + `PF_SRCDIR`, or a git tag for AUR). +- `punktfunk-host.install` / `punktfunk-client.install` — pacman scriptlets (udev reload + sysctl + + first-run hint), mirror the RPM `%post` / deb postinst. +- `build-sysext.sh` — wraps either built `.pkg.tar.zst` into a `systemd-sysext` `.raw` for SteamOS + (derives the name from the package, so it works for host or client). diff --git a/packaging/arch/build-sysext.sh b/packaging/arch/build-sysext.sh index 5219fb8..37884ac 100755 --- a/packaging/arch/build-sysext.sh +++ b/packaging/arch/build-sysext.sh @@ -1,16 +1,19 @@ #!/usr/bin/env bash -# Wrap a built punktfunk-host pacman package into a systemd-sysext image — the update-survivable -# way to add the host to an immutable Arch-derived distro (SteamOS 3): the .raw overlays /usr -# read-only from the writable /var/lib/extensions/, so it persists across A/B OS updates with no -# `steamos-readonly disable`. Needs `bsdtar`/`tar`, `squashfs-tools` (mksquashfs). +# Wrap a built punktfunk pacman package into a systemd-sysext image — the update-survivable way to +# add it to an immutable Arch-derived distro (SteamOS 3): the .raw overlays /usr read-only from the +# writable /var/lib/extensions/, so it persists across A/B OS updates with no `steamos-readonly +# disable`. Works for either split package — on a Steam Deck you'd wrap the CLIENT. Needs +# `bsdtar`/`tar`, `squashfs-tools` (mksquashfs). # -# Usage: bash build-sysext.sh -# Output: punktfunk-host.raw +# Usage: bash build-sysext.sh +# Output: .raw (e.g. punktfunk-client.raw) set -euo pipefail -PKG="${1:?usage: build-sysext.sh }" +PKG="${1:?usage: build-sysext.sh }" [ -f "$PKG" ] || { echo "no such package: $PKG" >&2; exit 1; } -NAME=punktfunk-host +# Derive the package name from the file (pkgname is everything before the -). +NAME="$(basename "$PKG" | sed -E 's/-[0-9].*//')" +[ -n "$NAME" ] || { echo "could not derive package name from $PKG" >&2; exit 1; } STAGE="$(mktemp -d)" trap 'rm -rf "$STAGE"' EXIT @@ -36,4 +39,8 @@ rm -f "$OUT" mksquashfs "$STAGE" "$OUT" -all-root -noappend -quiet echo "built $OUT" echo " install: sudo cp $OUT /var/lib/extensions/ && sudo systemctl enable --now systemd-sysext" -echo " then: systemctl --user enable --now $NAME" +if [ "$NAME" = "punktfunk-host" ]; then + echo " then: systemctl --user enable --now punktfunk-host" +else + echo " then: run 'punktfunk-client' (or let the Decky plugin launch it)" +fi diff --git a/packaging/arch/punktfunk-client.install b/packaging/arch/punktfunk-client.install new file mode 100644 index 0000000..31446f0 --- /dev/null +++ b/packaging/arch/punktfunk-client.install @@ -0,0 +1,19 @@ +# pacman scriptlet for the GTK4 client — mirrors build-client-deb.sh's postinst. +post_install() { + udevadm control --reload-rules 2>/dev/null || true + udevadm trigger --subsystem-match=hidraw 2>/dev/null || true + update-desktop-database /usr/share/applications 2>/dev/null || true + # Apply the 32 MB UDP recv-buffer tuning now (also auto-applied at boot by systemd-sysctl). + sysctl -p /usr/lib/sysctl.d/99-punktfunk-client-net.conf >/dev/null 2>&1 || true + cat <<'MSG' +punktfunk-client installed. + punktfunk-client # GUI: discover hosts on the LAN + connect + punktfunk-client --connect HOST[:PORT] # direct / scripted (this is what the Decky plugin runs) +On a Steam Deck / AMD box, install 'libva-mesa-driver' for hardware (VAAPI) decode. +MSG +} + +post_upgrade() { + udevadm control --reload-rules 2>/dev/null || true + sysctl -p /usr/lib/sysctl.d/99-punktfunk-client-net.conf >/dev/null 2>&1 || true +}