767e67ca's per-channel mechanics were correct; they were aimed at the wrong binary. Each one is restored here pointed at punktfunk-encode-worker, and every host-side removal from #136 stays verbatim. All grants remain best-effort — an uncapped worker still encodes, at default priority, so a failed setcap must never fail an install. * Arch: setcap in post_install AND post_upgrade (a replaced binary is a new inode). * RPM: %caps(cap_sys_nice=ep) in %files, never a %post setcap — %caps applies, restores and verifies, and covers Fedora as well as Bazzite via rpm-ostree layering. * Bazzite + Arch sysext: setcap on the staging tree before mksquashfs, which does record security.capability. The assertion is amended, not removed: host EMPTY is still a hard fail, and the worker must carry exactly cap_sys_nice=ep — missing is fine, anything else is not. * deb: setcap in postinst. * NixOS: security.wrappers for the WORKER plus PUNKTFUNK_ENCODE_WORKER in the unit. A file capability cannot live on a store path, and an ambient grant is right here precisely because nothing ever identifies the worker. The host's ExecStart stays on the store path. * Steam Deck: setcap the worker; the .desktop the script writes stays valid this time. Four things the plan's channel table missed: * packaging/arch/build-sysext.sh had no capability handling at all, and a sysext can never run a pacman scriptlet — the SteamOS image would have shipped the lever permanently inert. * scripts/steamdeck/update.sh had none either. It rebuilds both binaries, so a new inode drops the grant, and it is the documented steady-state path: the lever would have died on the first update. It also never healed a Deck already capped by 0.26.0-1. * A capped worker is AT_SECURE, and glibc drops $ORIGIN-expanded RPATH entries for secure binaries unless they normalise into a trusted system dir. Copying the host's rpath under BUNDLE_FFMPEG=1 would have left the capped worker unable to find libavcodec on exactly the channel that bundles it. Absolute DT_RPATH instead. * Nix crane scopes by -p, so the worker would not have been built at all, and it needs its own addDriverRunpath. scripts/ci/assert-cap-matrix.sh mechanizes the lesson from 0.26.0-1 — verify the PACKAGE, never the board. It unpacks the built Arch package, the deb, the rpm and the mounted sysext raw and asserts one matrix: the host carries NOTHING (hard fail), the worker exactly cap_sys_nice=ep. The sysext reader first proves it can round-trip a capability through mksquashfs/unsquashfs at all, so an unreadable artifact fails rather than issuing a blind PASS, and --self-test red-teams the assertions themselves. Red-teaming the leg found a real bug: setcap originally ran BEFORE the assertion, so "the worker arrived carrying something unexpected" was unreachable and a stray %caps would have been silently overwritten. Both sysext scripts now assert, then grant, then assert again.
406 lines
29 KiB
Bash
406 lines
29 KiB
Bash
# Maintainer: unom <packages@unom.io>
|
|
#
|
|
# Arch Linux / SteamOS split package: punktfunk-host (the gaming-rig HOST, NVENC) and
|
|
# punktfunk-client (the native GTK4/libadwaita Linux 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()).
|
|
#
|
|
# Host encode: NVENC on NVIDIA (nvidia-utils), VAAPI on AMD/Intel (mesa) — PUNKTFUNK_ENCODER=auto
|
|
# picks per GPU. The CLIENT decodes via VAAPI (AMD/Intel, incl. the Deck) with a software
|
|
# fallback, so it works everywhere. See README.md.
|
|
pkgbase=punktfunk
|
|
# punktfunk-web (the browser console) and punktfunk-scripting (the plugin/script runner) are OPT-IN:
|
|
# both need `bun` (AUR-only as bun-bin on stock Arch/SteamOS), so a default makepkg builds only
|
|
# host+client with no JS tooling — mirroring the RPM spec's `%bcond_with web` / `%bcond_with
|
|
# scripting` (off by default). Set PF_WITH_WEB=1 / PF_WITH_SCRIPTING=1 to also build them (each
|
|
# appends its pkgname + bun to makedepends below).
|
|
# aarch64 (Arch Linux ARM) is CLIENT-ONLY — see the `arch` note below. Dropping the host from
|
|
# `pkgname` (rather than giving package_punktfunk-host a per-package arch) is what keeps makepkg
|
|
# from entering the host's build/package path at all, and mirrors how PF_WITH_WEB extends this
|
|
# array further down.
|
|
if [ "${CARCH:-x86_64}" = 'aarch64' ]; then
|
|
pkgname=('punktfunk-client')
|
|
else
|
|
pkgname=('punktfunk-host' 'punktfunk-client')
|
|
fi
|
|
# CI (.gitea/workflows/arch.yml) drives the version: stable tags -> X.Y.Z-1, main pushes ->
|
|
# X.Y.Z-0.<run#> in the separate punktfunk-canary repo (mirrors the RPM's 0.ciN release; pkgrel
|
|
# allows only digits+dots, so the run number carries the monotonic ordering).
|
|
pkgver="${PF_PKGVER:-0.7.0}"
|
|
pkgrel="${PF_PKGREL:-1}"
|
|
# The CLIENT builds and runs on aarch64 (Arch Linux ARM); the HOST does not — its encode stack is
|
|
# NVENC/QSV/AMF, all x86. On aarch64 the split below drops punktfunk-host from `pkgname`, so
|
|
# makepkg builds only the client and never tries to compile the host crate.
|
|
arch=('x86_64' 'aarch64')
|
|
url="https://git.unom.io/unom/punktfunk"
|
|
license=('MIT OR Apache-2.0')
|
|
# !lto: makepkg's `lto` option injects -flto=auto into CFLAGS; aws-lc-sys (rustls' crypto)
|
|
# compiles its C with those flags and GCC LTO bitcode objects are unreadable by rust's lld
|
|
# linker -> "undefined symbol: aws_lc_*" at link (reproduced 2026-07-04, Arch + rust 1.90).
|
|
# !debug: skip the -debug split package (debuginfo bloat, not shipped).
|
|
options=('!lto' '!debug')
|
|
|
|
# All build deps for both packages (Arch runtime packages ship their own headers, so these cover
|
|
# build + link). aws-lc/ring need clang+cmake; nasm is for asm. ffmpeg stays because the HOST's
|
|
# encoder links libav* — the CLIENT dropped it in M10 and decodes natively. `ffmpeg` is
|
|
# deliberately UNVERSIONED here: ffmpeg-sys-next auto-detects the installed FFmpeg, so the package
|
|
# builds against whatever the builder ships, and the RUNTIME bound follows automatically from the
|
|
# soname deps makepkg derives from the linked binary (see package_punktfunk-host's depends). Pinning
|
|
# a version at build time would just have to be re-edited on every FFmpeg major. No vulkan-headers:
|
|
# nothing in the workspace compiles against the system Vulkan headers any more (pyrowave-sys builds
|
|
# against its own vendored copy, and both binaries reach Vulkan through ash, which dlopens it).
|
|
makedepends=('rust' 'cargo' 'clang' 'cmake' 'nasm' 'pkgconf' 'git'
|
|
'gtk4' 'libadwaita' 'sdl3' 'ffmpeg' 'pipewire' 'wayland' 'libxkbcommon' 'opus' 'libei')
|
|
|
|
# Opt-in punktfunk-web / punktfunk-scripting: only then is bun (the build tool AND the vendored
|
|
# runtime) required. Adding bun twice is harmless (makepkg dedups makedepends).
|
|
if [ "${PF_WITH_WEB:-0}" = 1 ]; then
|
|
pkgname+=('punktfunk-web')
|
|
makedepends+=('bun') # `bun-bin` from the AUR if bun isn't in your configured repos
|
|
fi
|
|
if [ "${PF_WITH_SCRIPTING:-0}" = 1 ]; then
|
|
pkgname+=('punktfunk-scripting')
|
|
makedepends+=('bun')
|
|
fi
|
|
|
|
# AUR source (a tagged release). For an in-tree CI build, set PF_SRCDIR to the repo root —
|
|
# build() uses it instead AND the fetch is skipped entirely (a canary pkgver has no tag to
|
|
# clone, and CI already has the checkout).
|
|
if [ -z "${PF_SRCDIR:-}" ]; then
|
|
source=("git+https://git.unom.io/unom/punktfunk.git#tag=v${pkgver}")
|
|
sha256sums=('SKIP')
|
|
else
|
|
source=()
|
|
sha256sums=()
|
|
fi
|
|
|
|
_repo() { printf '%s' "${PF_SRCDIR:-$srcdir/punktfunk}"; }
|
|
|
|
build() {
|
|
cd "$(_repo)"
|
|
export RUSTUP_TOOLCHAIN=stable CARGO_TARGET_DIR="$srcdir/target"
|
|
export PUNKTFUNK_BUILD_VERSION="${pkgver}-${pkgrel}" # stamp --version / mgmt /health (build.rs)
|
|
# 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/
|
|
# punktfunk-client-session is the Vulkan/Skia streamer the shell (punktfunk-client) execs for
|
|
# a connect — both binaries must ship or streaming from the desktop client breaks.
|
|
# `--features punktfunk-host/nvenc` compiles in the direct-SDK NVENC path (real RFI + recovery
|
|
# anchor on Linux NVIDIA; design/linux-direct-nvenc.md). AMD/Intel-SAFE: the NVENC/CUDA entry
|
|
# points are dlopen'd at RUNTIME (libloading), never link-imported — `objdump -p` shows the same
|
|
# DT_NEEDED as a plain build (no libcuda/libnvidia-encode), so the binary starts fine driver-less
|
|
# and only touches NVIDIA on a CUDA capture frame (default on NVIDIA; PUNKTFUNK_NVENC_DIRECT=0
|
|
# opts back to libav). AMD/Intel never reach it — the `cuda` gate leaves them on VAAPI.
|
|
# `punktfunk-host/vulkan-encode` is the AMD/Intel twin: a raw VK_KHR_video_encode_h265 backend with
|
|
# real RFI (clean P-frame recovery anchor via DPB reference slots; design/linux-vulkan-video-encode.md).
|
|
# Pure Rust `ash` (no new lib, no link-time deps); default on for HEVC (PUNKTFUNK_VULKAN_ENCODE=0
|
|
# opts back to libav VAAPI), and a failed open falls back to VAAPI so unsupported devices are safe.
|
|
if [ "$CARCH" = 'aarch64' ]; then
|
|
# Client-only: no host crate, so none of the encode features below apply. pf-update still
|
|
# builds — the client package ships its own copy for `--apply-update`.
|
|
cargo build --release --locked -p punktfunk-client-linux -p punktfunk-client-session \
|
|
-p punktfunk-cli -p pf-update
|
|
else
|
|
# `-p punktfunk-encode-worker`: the capability-carrying PyroWave encode worker, shipped next to
|
|
# the host in /usr/bin and setcap'd by punktfunk-host.install. It MUST be its own file — the host
|
|
# can never carry a capability (KWin identification; see the scriptlet) — and it must ship in the
|
|
# SAME package as the host, because host and worker version-check each other over their socket
|
|
# and fall back to the in-process encoder on any mismatch. Co-built here on purpose: v1 accepts
|
|
# that the worker links the same FFmpeg the host does (same package, same sonames, no new break
|
|
# class), so cargo's feature unification across this one invocation is harmless.
|
|
cargo build --release --locked --features punktfunk-host/nvenc,punktfunk-host/vulkan-encode \
|
|
-p punktfunk-host -p punktfunk-encode-worker \
|
|
-p punktfunk-client-linux -p punktfunk-client-session -p punktfunk-cli \
|
|
-p pf-update
|
|
# The status tray in its OWN cargo invocation — load-bearing, not tidiness. Cargo unifies features
|
|
# across everything in one build, so co-building the tray with the host pulls the host's
|
|
# ashpd -> zbus/tokio onto the tray's shared zbus; the tray (ksni async-io + blocking, no tokio
|
|
# runtime by design) then panics at startup ("there is no reactor running, must be called from the
|
|
# context of a Tokio 1.x runtime"). Built alone, its zbus stays on async-io. (Same split the .deb does.)
|
|
cargo build --release --locked -p punktfunk-tray
|
|
fi
|
|
# Management web console (opt-in): the Nitro `bun`-preset .output bundle (Bun.serve TLS),
|
|
# built AND run with bun.
|
|
if [ "${PF_WITH_WEB:-0}" = 1 ]; then
|
|
( cd web && bun install --frozen-lockfile && bun run build )
|
|
fi
|
|
# Plugin/script runner (opt-in): one self-contained JS via `bun build --target=bun` (effect + the
|
|
# SDK inlined; the dynamic plugin import stays a runtime import). bun is also its vendored runtime.
|
|
if [ "${PF_WITH_SCRIPTING:-0}" = 1 ]; then
|
|
( cd sdk && bun install --frozen-lockfile --ignore-scripts && \
|
|
bun build src/runner-cli.ts --target=bun --outfile="$srcdir/runner-cli.js" )
|
|
grep -q 'attempt=' "$srcdir/runner-cli.js" \
|
|
|| { echo "ERROR: runner bundle missing the dynamic plugin import — wrong build" >&2; exit 1; }
|
|
fi
|
|
}
|
|
|
|
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.
|
|
# The host captures the sink monitor through NATIVE PipeWire (audio/linux.rs) — it never
|
|
# opens a Pulse socket itself, so pipewire-pulse is an OPTdepend, not a depend: it exists
|
|
# for the GAMES, which commonly emit through the PulseAudio API. Hard-depending on it made
|
|
# the package uninstallable next to real `pulseaudio`, which serves those games just as well.
|
|
# ⚠ The libav* entries below are SONAME deps, not package names, and they are load-bearing.
|
|
# pacman is the only one of our packaging formats that does NOT derive dependencies from ELF
|
|
# DT_NEEDED (rpm auto-generates `libavcodec.so.62()(64bit)`; dpkg-shlibdeps emits `libavcodec62`;
|
|
# nix pins the closure). So a bare `depends=('ffmpeg')` let `pacman -Syu` walk the host across an
|
|
# FFmpeg soname bump with no warning and no conflict — which is exactly what FFmpeg 8 -> 9
|
|
# (2026-08-08, ffmpeg 2:9.0-5: libavutil .60->.61, libavcodec .62->.63, libavfilter .11->.12,
|
|
# libavdevice .62->.63, libswscale .9->.10) did to every Arch/CachyOS user: the dynamic loader
|
|
# cannot start the binary at all, so it is exit 127 *before* main() in a systemd restart loop,
|
|
# with nothing in the host's own log to explain it (`ldd /usr/bin/punktfunk-host | grep "not found"`
|
|
# is the one-line diagnosis). Arch's ffmpeg declares the matching
|
|
# `provides=(libavcodec.so=63-64 libavutil.so=61-64 ...)`, and makepkg rewrites each bare
|
|
# `libfoo.so` listed here into `libfoo.so=<soname>-<arch>` by reading the built binary's
|
|
# DT_NEEDED — so the bound tracks whatever FFmpeg the builder linked against, with nothing to
|
|
# hand-maintain across the next bump. pacman then REFUSES the ffmpeg upgrade instead of bricking
|
|
# the install, and the canary rebuilt on an ffmpeg-9 image installs cleanly on an ffmpeg-9 box.
|
|
# A hand-written `ffmpeg<2:9` would have to be edited (and would go stale) on every bump; this
|
|
# does not. NOT bundling FFmpeg the way the .deb does (BUNDLE_FFMPEG=1): that exists because
|
|
# Ubuntu 24.04 LTS is frozen on FFmpeg 6.1 and can never satisfy the dep, whereas rolling Arch
|
|
# always ships a current FFmpeg — and vendoring a second copy of a system library is against
|
|
# Arch packaging doctrine (and would not survive AUR review).
|
|
# All seven are listed even though --as-needed currently drops libavformat/libswresample from the
|
|
# link: an unlinked soname is left bare by makepkg and is satisfied by any ffmpeg, so listing it
|
|
# costs nothing, while a future link picking one up gets the version bound automatically.
|
|
depends=('ffmpeg' 'pipewire' 'wireplumber' 'opus' 'libei'
|
|
'mesa' 'libglvnd' 'libxkbcommon' 'wayland'
|
|
'libavcodec.so' 'libavutil.so' 'libavfilter.so' 'libavdevice.so'
|
|
'libavformat.so' 'libswscale.so' 'libswresample.so')
|
|
optdepends=('pipewire-pulse: PulseAudio-API audio from games/apps (real `pulseaudio` also works)'
|
|
'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'
|
|
'punktfunk-gamescope: HDR (10-bit BT.2020 PQ) streaming on the gamescope backend — attempted by default when installed'
|
|
'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'
|
|
'punktfunk-web: browser management console (device pairing + status)'
|
|
'punktfunk-scripting: plugin/script runner for host automation (bun)')
|
|
install=punktfunk-host.install
|
|
# User-editable config: the headless game-mode drop-in (see below) — don't clobber local edits.
|
|
backup=('etc/gamescope-session-plus/sessions.d/steam')
|
|
local R; R="$(_repo)"; local T="$srcdir/target/release"
|
|
|
|
install -Dm0755 "$T/punktfunk-host" "$pkgdir/usr/bin/punktfunk-host"
|
|
# The PyroWave encode worker — a SEPARATE file in the same bindir (the host resolves it as a
|
|
# sibling of /proc/self/exe). punktfunk-host.install setcaps this one, and only this one; the
|
|
# host must stay capability-free or KWin cannot identify it and desktop streaming dies.
|
|
install -Dm0755 "$T/punktfunk-encode-worker" "$pkgdir/usr/bin/punktfunk-encode-worker"
|
|
# /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"
|
|
# Managed gamescope takeover on DM-autologin boxes: root helper + polkit action so the host can
|
|
# stop/restore the display manager for the stream. Arch has no /usr/libexec — install under
|
|
# /usr/lib/punktfunk and rewrite the policy's exec.path annotation to match (the host probes both).
|
|
install -Dm0755 "$R/scripts/pf-dm-helper" "$pkgdir/usr/lib/punktfunk/pf-dm-helper"
|
|
install -Dm0644 "$R/scripts/io.unom.punktfunk.dm-helper.policy" \
|
|
"$pkgdir/usr/share/polkit-1/actions/io.unom.punktfunk.dm-helper.policy"
|
|
sed -i 's#/usr/libexec/punktfunk/pf-dm-helper#/usr/lib/punktfunk/pf-dm-helper#' \
|
|
"$pkgdir/usr/share/polkit-1/actions/io.unom.punktfunk.dm-helper.policy"
|
|
# Web-console-triggered updates (host-update-from-web-console.md §7): root helper + oneshot
|
|
# unit + group-scoped polkit rule. Same no-libexec relocation as pf-dm-helper, with the
|
|
# unit's ExecStart rewritten to match. On pacman the helper additionally requires the
|
|
# explicit PACMAN_FULL_SYSUPGRADE opt-in (partial upgrades are against Arch doctrine).
|
|
install -Dm0755 "$T/pf-update" "$pkgdir/usr/lib/punktfunk/pf-update"
|
|
install -Dm0644 "$R/packaging/linux/punktfunk-update.service" \
|
|
"$pkgdir/usr/lib/systemd/system/punktfunk-update.service"
|
|
sed -i 's#/usr/libexec/punktfunk/pf-update#/usr/lib/punktfunk/pf-update#' \
|
|
"$pkgdir/usr/lib/systemd/system/punktfunk-update.service"
|
|
install -Dm0644 "$R/packaging/linux/49-punktfunk-update.rules" \
|
|
"$pkgdir/usr/share/polkit-1/rules.d/49-punktfunk-update.rules"
|
|
# vhci-hcd autoload — usbip transport for the virtual Steam Deck pad (Steam only adopts USB pads)
|
|
install -Dm0644 "$R/scripts/punktfunk-modules.conf" "$pkgdir/usr/lib/modules-load.d/punktfunk.conf"
|
|
# 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); 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"
|
|
# Optional drop-in for a DESKTOP-LOGIN host: binds the host to graphical-session.target so a
|
|
# Plasma/GNOME restart restarts it instead of leaving it on a dead compositor connection. Shipped
|
|
# under /usr/share (NOT as an active drop-in) because it is wrong for the appliance route — the
|
|
# operator copies it into ~/.config/systemd/user/punktfunk-host.service.d/ when they want it.
|
|
install -Dm0644 "$R/scripts/punktfunk-host-desktop-session.conf" \
|
|
"$pkgdir/usr/share/punktfunk/punktfunk-host-desktop-session.conf"
|
|
# Install-kind + channel marker, read by the host's update-check surface (planning:
|
|
# host-update-from-web-console.md §4.1). A canary build's pkgrel is `0.<zero-padded run>`.
|
|
local _pf_update_channel=stable
|
|
[[ $pkgrel == 0.* ]] && _pf_update_channel=canary
|
|
printf 'pacman %s\n' "$_pf_update_channel" | \
|
|
install -Dm0644 /dev/stdin "$pkgdir/usr/share/punktfunk/install-kind"
|
|
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"
|
|
# KWin Desktop-mode authorization: non-launcher .desktop whose X-KDE-Wayland-Interfaces lets the
|
|
# host bind KWin's restricted zkde_screencast (virtual output) + fake_input globals on an
|
|
# interactive Plasma session. Must ship with the host (KWin caches the per-exe grant on first
|
|
# connect). See the file's header comment.
|
|
install -Dm0644 "$R/packaging/linux/io.unom.Punktfunk.Host.desktop" \
|
|
"$pkgdir/usr/share/applications/io.unom.Punktfunk.Host.desktop"
|
|
# Status tray: per-user SNI icon + XDG autostart entry (self-gating: --autostart exits silently
|
|
# for users who don't run a host) + the hicolor status icons it names.
|
|
install -Dm0755 "$T/punktfunk-tray" "$pkgdir/usr/bin/punktfunk-tray"
|
|
install -Dm0644 "$R/packaging/linux/io.unom.Punktfunk.Tray.desktop" \
|
|
"$pkgdir/etc/xdg/autostart/io.unom.Punktfunk.Tray.desktop"
|
|
local sz png
|
|
for sz in 22x22 48x48; do
|
|
for png in "$R"/packaging/linux/icons/hicolor/$sz/apps/*.png; do
|
|
install -Dm0644 "$png" "$pkgdir/usr/share/icons/hicolor/$sz/apps/$(basename "$png")"
|
|
done
|
|
done
|
|
# 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"
|
|
install -Dm0644 "$R/scripts/headless/kde-authorized" "$pkgdir/usr/share/punktfunk/headless/kde-authorized"
|
|
install -Dm0644 "$R/scripts/headless/punktfunk-sink.conf" "$pkgdir/usr/share/punktfunk/headless/punktfunk-sink.conf"
|
|
install -Dm0644 "$R/scripts/host.env.example" "$pkgdir/usr/share/punktfunk/host.env.example"
|
|
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"
|
|
# Headless GAME-mode fix: gamescope-session-plus drop-in that uses the headless backend when no
|
|
# display is connected (so SteamOS/Bazzite "Switch to Game Mode" works on a display-less streaming
|
|
# host). No-op on display-attached boxes; sourced as /etc/gamescope-session-plus/sessions.d/steam.
|
|
install -Dm0644 "$R/packaging/bazzite/gamescope-headless-session" \
|
|
"$pkgdir/etc/gamescope-session-plus/sessions.d/steam"
|
|
install -Dm0644 "$R/api/openapi.json" "$pkgdir/usr/share/punktfunk/openapi.json"
|
|
# Firewall openers — NOT auto-enabled (an Arch package never touches the admin's running firewall).
|
|
# Stock Arch ships no firewall; CachyOS ships ufw; some spins (EndeavourOS) enable firewalld — so we
|
|
# install BOTH a ufw application profile and firewalld service definitions, and the one for whatever
|
|
# firewall you actually run is a one-liner. See README.md → Firewall.
|
|
# ufw: sudo ufw allow punktfunk-native (or punktfunk-gamestream)
|
|
# firewalld: sudo firewall-cmd --reload && sudo firewall-cmd --permanent --add-service=punktfunk-native && sudo firewall-cmd --reload
|
|
install -Dm0644 "$R/packaging/linux/punktfunk.ufw" \
|
|
"$pkgdir/etc/ufw/applications.d/punktfunk"
|
|
install -Dm0644 "$R/packaging/linux/punktfunk-gamestream.xml" \
|
|
"$pkgdir/usr/lib/firewalld/services/punktfunk-gamestream.xml"
|
|
install -Dm0644 "$R/packaging/linux/punktfunk-native.xml" \
|
|
"$pkgdir/usr/lib/firewalld/services/punktfunk-native.xml"
|
|
# Web console opener (TCP 47992) — only meaningful with the optional punktfunk-web package; opened
|
|
# deliberately (see README.md → Firewall). ufw's equivalent is the punktfunk-web profile above.
|
|
install -Dm0644 "$R/packaging/linux/punktfunk-web.xml" \
|
|
"$pkgdir/usr/lib/firewalld/services/punktfunk-web.xml"
|
|
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 — native GTK4/libadwaita Linux app"
|
|
# The GTK4/libadwaita shell + its Vulkan session streamer: SDL3 gamepads, native decode
|
|
# (Vulkan Video, VAAPI, and openh264 + rav1d in software), PipeWire audio/mic.
|
|
# No ffmpeg since M10: the client links no libav* at all, and its VAAPI rung dlopens libva
|
|
# rather than linking it, so the driver optdepends below are all it needs.
|
|
# vulkan-icd-loader: the session binary loads libvulkan at runtime (ash) for both its decoder
|
|
# and its ash/Skia presenter.
|
|
# NOT pipewire-pulse: the client speaks NATIVE PipeWire (audio.rs drives libpipewire-0.3
|
|
# directly for both playback and the mic uplink) and never opens a Pulse socket, so the
|
|
# compat shim buys it nothing — while `pipewire-pulse` CONFLICTS with `pulseaudio`, which
|
|
# made the package uninstallable for anyone keeping real PulseAudio. Matches the .deb
|
|
# (Recommends) and the RPM (Recommends, "degrade gracefully without it").
|
|
depends=('gtk4' 'libadwaita' 'sdl3' 'pipewire' 'wireplumber'
|
|
'opus' 'libglvnd' 'vulkan-icd-loader')
|
|
optdepends=('libva-mesa-driver: VAAPI hardware decode on AMD (incl. Steam Deck); software fallback otherwise'
|
|
'intel-media-driver: VAAPI hardware decode on Intel'
|
|
'vulkan-radeon: Vulkan Video decode + presenter on AMD'
|
|
'vulkan-nouveau: Vulkan Video decode + presenter on NVIDIA (nvidia-utils for the proprietary ICD)'
|
|
'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"
|
|
# The session streamer the shell execs for a connect (resolved as its sibling in /usr/bin).
|
|
install -Dm0755 "$T/punktfunk-session" "$pkgdir/usr/bin/punktfunk-session"
|
|
# The headless CLI (design/client-architecture-split.md §4).
|
|
install -Dm0755 "$T/punktfunk" "$pkgdir/usr/bin/punktfunk"
|
|
# The app icon the desktop entry (and the About dialog) name.
|
|
install -Dm0644 "$R/packaging/linux/icons/hicolor/scalable/apps/io.unom.Punktfunk.svg" \
|
|
"$pkgdir/usr/share/icons/hicolor/scalable/apps/io.unom.Punktfunk.svg"
|
|
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"
|
|
# One-tap client updates (`punktfunk-client --apply-update`, which is what the Decky plugin
|
|
# runs): the same root helper the host package ships, under the CLIENT's own paths — a
|
|
# client-only box (a Deck) has no host package, and two packages owning one path is a hard
|
|
# pacman conflict. Same no-libexec relocation as the host's copy, with the unit's ExecStart
|
|
# rewritten to match. On pacman the helper additionally requires the explicit
|
|
# PACMAN_FULL_SYSUPGRADE opt-in (partial upgrades are against Arch doctrine).
|
|
install -Dm0755 "$T/pf-update" "$pkgdir/usr/lib/punktfunk/pf-update-client"
|
|
install -Dm0644 "$R/packaging/linux/punktfunk-client-update.service" \
|
|
"$pkgdir/usr/lib/systemd/system/punktfunk-client-update.service"
|
|
sed -i 's#/usr/libexec/punktfunk/pf-update#/usr/lib/punktfunk/pf-update-client#' \
|
|
"$pkgdir/usr/lib/systemd/system/punktfunk-client-update.service"
|
|
install -Dm0644 "$R/packaging/linux/49-punktfunk-client-update.rules" \
|
|
"$pkgdir/usr/share/polkit-1/rules.d/49-punktfunk-client-update.rules"
|
|
# Install-kind + channel marker for the CLIENT, read by `punktfunk-client --check-update`
|
|
# (planning: host-update-from-web-console.md §4.1). Its own directory, matching the RPM (where
|
|
# the host subpackage's %{_datadir}/punktfunk/* glob would otherwise co-own a sibling file) —
|
|
# one path per product across every format, so the detector has one answer to remember.
|
|
local _pf_client_channel=stable
|
|
[[ $pkgrel == 0.* ]] && _pf_client_channel=canary
|
|
printf 'pacman %s\n' "$_pf_client_channel" | \
|
|
install -Dm0644 /dev/stdin "$pkgdir/usr/share/punktfunk-client/install-kind"
|
|
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"
|
|
}
|
|
|
|
package_punktfunk-web() {
|
|
pkgdesc="punktfunk management web console (Nitro SSR on bun, HTTPS/HTTP-1.1 over TLS) — pairing + status in the browser"
|
|
# bun is the runtime (Bun.serve), and it's a native binary we vendor, so this package is
|
|
# arch-specific (not 'any'). Auto-wired to the host's mgmt token + identity cert via the systemd
|
|
# --user units; enable with `systemctl --user enable --now punktfunk-web`. No nodejs/bun dependency.
|
|
local R; R="$(_repo)"
|
|
|
|
# Pre-built bun-preset bundle (from build()) + a PATH-stable launcher (matches the .deb/.rpm).
|
|
install -d "$pkgdir/usr/share/punktfunk-web/.output"
|
|
cp -r "$R/web/.output/server" "$pkgdir/usr/share/punktfunk-web/.output/server"
|
|
cp -r "$R/web/.output/public" "$pkgdir/usr/share/punktfunk-web/.output/public"
|
|
# Vendor the build env's bun into a private dir so it never collides with a
|
|
# system-wide bun on PATH.
|
|
install -Dm0755 "$(command -v bun)" "$pkgdir/usr/lib/punktfunk-web/bun"
|
|
install -d "$pkgdir/usr/bin"
|
|
printf '%s\n' '#!/bin/sh' 'exec /usr/lib/punktfunk-web/bun /usr/share/punktfunk-web/.output/server/index.mjs "$@"' \
|
|
> "$pkgdir/usr/bin/punktfunk-web-server"
|
|
chmod 0755 "$pkgdir/usr/bin/punktfunk-web-server"
|
|
# systemd USER units: the console runs per-user; web-init generates the login password on first start.
|
|
install -Dm0644 "$R/scripts/punktfunk-web.service" "$pkgdir/usr/lib/systemd/user/punktfunk-web.service"
|
|
install -Dm0644 "$R/scripts/punktfunk-web-init.service" "$pkgdir/usr/lib/systemd/user/punktfunk-web-init.service"
|
|
install -Dm0755 "$R/scripts/web-init.sh" "$pkgdir/usr/share/punktfunk-web/web-init.sh"
|
|
install -Dm0644 "$R/web/web.env.example" "$pkgdir/usr/share/punktfunk-web/web.env.example"
|
|
install -Dm0644 "$R/LICENSE-MIT" "$pkgdir/usr/share/licenses/punktfunk-web/LICENSE-MIT"
|
|
install -Dm0644 "$R/LICENSE-APACHE" "$pkgdir/usr/share/licenses/punktfunk-web/LICENSE-APACHE"
|
|
}
|
|
|
|
package_punktfunk-scripting() {
|
|
pkgdesc="punktfunk plugin/script runner (Effect SDK on bun) — supervises host automation scripts + punktfunk-plugin-* packages"
|
|
# bun is the runtime (it import()s the operator's .ts plugins), a vendored native binary, so this
|
|
# package is arch-specific (not 'any'). OPT-IN: the systemd --user unit ships disabled (the runner
|
|
# is inert until you add scripts/plugins). No nodejs/bun dependency.
|
|
local R; R="$(_repo)"
|
|
|
|
# Pre-built self-contained bundle (from build()) + a PATH-stable launcher (matches the .deb/.rpm).
|
|
install -Dm0644 "$srcdir/runner-cli.js" "$pkgdir/usr/share/punktfunk-scripting/runner-cli.js"
|
|
# Vendor the build env's bun into a private dir so it never collides with a system-wide bun.
|
|
install -Dm0755 "$(command -v bun)" "$pkgdir/usr/lib/punktfunk-scripting/bun"
|
|
install -d "$pkgdir/usr/bin"
|
|
printf '%s\n' '#!/bin/sh' 'exec /usr/lib/punktfunk-scripting/bun /usr/share/punktfunk-scripting/runner-cli.js "$@"' \
|
|
> "$pkgdir/usr/bin/punktfunk-scripting"
|
|
chmod 0755 "$pkgdir/usr/bin/punktfunk-scripting"
|
|
# systemd USER unit — installed but NOT auto-enabled (opt-in). Enable once you have automation:
|
|
# systemctl --user enable --now punktfunk-scripting
|
|
install -Dm0644 "$R/scripts/punktfunk-scripting.service" "$pkgdir/usr/lib/systemd/user/punktfunk-scripting.service"
|
|
install -Dm0644 "$R/LICENSE-MIT" "$pkgdir/usr/share/licenses/punktfunk-scripting/LICENSE-MIT"
|
|
install -Dm0644 "$R/LICENSE-APACHE" "$pkgdir/usr/share/licenses/punktfunk-scripting/LICENSE-APACHE"
|
|
}
|