Two things were x86-specific, both now expressed properly instead of
hardcoded:
* PKG_CONFIG_PATH carries the runtime's multiarch directory.
flatpak-builder does NOT shell-expand `env` values, so ${FLATPAK_ARCH}
would be taken literally — a build-options.arch override supplies the
aarch64 string and inherits the rest.
* The prebuilt Skia archive is per-target and pinned by sha256. Two
`type: file` sources discriminated by only-arches now share one
dest-filename, so SKIA_BINARIES_URL stays a single literal path.
Upstream publishes the aarch64 archive under the same skia commit hash
and the same resolved-feature key, so the two stay in lockstep on a
skia-safe bump.
build-flatpak.sh gains ARCH (default: this machine's), passes --arch to
both flatpak-builder and build-bundle, and arch-suffixes the bundle name so
an x86_64 and an aarch64 build can coexist in dist/ instead of the second
silently overwriting the first. CI composes its own published filename, so
nothing downstream changes.
The aarch64 Skia sha256 was verified by downloading the published archive,
not taken from the API. No aarch64 flatpak has been built — flatpak-builder
builds in a sandbox for the target arch, which needs an arm64 machine.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
266 lines
17 KiB
YAML
266 lines
17 KiB
YAML
# Flatpak manifest for the native punktfunk Linux client: the shell (crate
|
|
# punktfunk-client-linux, binary `punktfunk-client`) PLUS the Vulkan session binary (crate
|
|
# punktfunk-client-session, binary `punktfunk-session`) that the shell execs for
|
|
# `--connect`/`--browse` — both must ship or streaming from the Deck breaks (the Decky
|
|
# wrapper launches the shell with those flags and the shell resolves its sibling
|
|
# `punktfunk-session`, see clients/linux/src/spawn.rs). Built into a single-file `.flatpak`
|
|
# bundle and published to Gitea's generic package registry (see .gitea/workflows/flatpak.yml
|
|
# + packaging/flatpak/README.md).
|
|
#
|
|
# Why flatpak for the CLIENT (the host stays an RPM/deb — see packaging/README.md "Why not
|
|
# Flatpak"): on SteamOS the Steam Deck's /usr is read-only and image-based, so a bare
|
|
# `punktfunk-client` binary in ~/.local/bin can't bring its own libadwaita / SDL3 (both
|
|
# MISSING from the SteamOS system) — but flatpak is the Deck's native, update-survivable app
|
|
# path (the user already runs Moonlight + chiaki-ng as flatpaks). Unlike the host, the client
|
|
# is sandbox-friendly: it only needs the GPU render node, the host PipeWire socket, the
|
|
# network, Wayland, hidraw for DualSense, and its config dir — all expressible as finish-args.
|
|
#
|
|
# Runtime: GNOME 50 ships GTK 4.20 and libadwaita 1.8 — both far exceed the crate floors
|
|
# (gtk4 0.11 "v4_16", libadwaita 0.9 "v1_5"). GNOME 50 is built on freedesktop-sdk 25.08, so
|
|
# `org.freedesktop.Sdk.Extension.rust-stable` resolves to //25.08 (rustc 1.96 — the GTK4 dep
|
|
# chain, e.g. pango-sys 0.22, needs >= 1.92, which the older GNOME-48/24.08 rust-stable at 1.89
|
|
# could NOT satisfy). GNOME 50 is also a *supported* runtime (GNOME 48 went EOL in March 2026).
|
|
# libopus and the PipeWire client lib are in the freedesktop base; SDL3 is NOT, so it is built
|
|
# from source as a bundled module.
|
|
#
|
|
# HEVC decode: the base runtime's libavcodec is a stripped build (no encumbered codecs). The
|
|
# freedesktop runtime declares `org.freedesktop.Platform.codecs-extra` as a built-in extension
|
|
# point (directory lib/x86_64-linux-gnu/codecs-extra, add-ld-path lib, auto-downloaded with the
|
|
# runtime), whose full libavcodec.so.61 transparently shadows the base one at runtime. So HEVC
|
|
# (software + VAAPI) works with NO app-side codec extension to declare — we just build against
|
|
# the SDK's linkable libavcodec.so.61 and let the runtime swap in the capable build.
|
|
app-id: io.unom.Punktfunk
|
|
runtime: org.gnome.Platform
|
|
runtime-version: '50'
|
|
sdk: org.gnome.Sdk
|
|
# Build-time SDK extensions:
|
|
# - rust-stable: cargo/rustc 1.96 + the bundled mold linker (/usr/lib/sdk/rust-stable/bin).
|
|
# - llvm20: provides libclang (/usr/lib/sdk/llvm20/lib), which bindgen needs — ffmpeg-sys-next
|
|
# and sdl3-sys generate their FFI bindings via bindgen at build time. The base SDK ships no
|
|
# clang/libclang, so without this the build panics ("Unable to find libclang").
|
|
# Both are added to PATH / LIBCLANG_PATH in build-options below.
|
|
sdk-extensions:
|
|
- org.freedesktop.Sdk.Extension.rust-stable
|
|
- org.freedesktop.Sdk.Extension.llvm20
|
|
command: punktfunk-client
|
|
|
|
cleanup:
|
|
- /include
|
|
- /lib/pkgconfig
|
|
- /lib/cmake
|
|
- /share/aclocal
|
|
- /man
|
|
- /share/man
|
|
- '*.a'
|
|
- '*.la'
|
|
|
|
finish-args:
|
|
# --- display ---
|
|
- --socket=wayland # GTK4 native Wayland window (the client is Wayland-first)
|
|
- --socket=fallback-x11 # Xwayland fallback when no Wayland socket is exposed
|
|
- --share=ipc # required alongside X11 for shared-memory surfaces
|
|
# --- GPU + all input devices ---
|
|
# --device=all (not just --device=dri): covers the GPU render node (VAAPI HEVC decode + GL),
|
|
# evdev joysticks, AND the hidraw CHAR devices SDL3's HIDAPI needs for DualSense touchpad/
|
|
# motion/adaptive-triggers/lightbar. flatpak cannot bind individual /dev/hidrawN via
|
|
# --filesystem (they are char devices — "unsupported type 0o20000"), and there is no granular
|
|
# --device=hidraw; --device=all is what game/emulator flatpaks (RetroArch, Dolphin) use. We
|
|
# self-host via the Gitea generic registry — NOT Flathub — so its --device=all review rule
|
|
# does not apply.
|
|
- --device=all
|
|
- --filesystem=/run/udev:ro # SDL/HIDAPI enumerates devices via udev
|
|
# --- audio: the client speaks the NATIVE PipeWire protocol (audio.rs `pw connect`), NOT the
|
|
# PulseAudio shim — so it needs the real `pipewire-0` socket in the sandbox. With only
|
|
# --socket=pulseaudio the sandbox has just `pulse/native`, no `pipewire-0`, and playback +
|
|
# mic both die with "pw connect (is PipeWire running in this session?)" (observed live on the
|
|
# Deck in Gaming Mode). We bind the native socket via --filesystem=xdg-run/pipewire-0 (NOT
|
|
# --socket=pipewire: this flatpak-builder toolchain rejects it as an "Unknown socket type",
|
|
# and the Deck's flatpak 1.16 override CLI does too — the filesystem bind is the portable
|
|
# form, validated on-Deck to make pipewire-0 appear + the client register its audio node).
|
|
# --socket=pulseaudio stays as a fallback for any pulse-only path. ---
|
|
- --filesystem=xdg-run/pipewire-0
|
|
- --socket=pulseaudio
|
|
# --- network: QUIC control + UDP data plane + mDNS discovery (_punktfunk._udp) ---
|
|
- --share=network
|
|
# --- persistent client identity / pairing store (shared with punktfunk-probe) ---
|
|
- --filesystem=~/.config/punktfunk:create # client-{cert,key}.pem, known-hosts, settings
|
|
# --- HDR under gamescope (Steam Deck Game Mode) ---
|
|
# A flatpak's Vulkan loader can't see the host's gamescope WSI layer, so the SDL3 surface never
|
|
# offers the HDR10 (ST.2084) colorspace and the presenter silently tone-maps PQ->SDR — the
|
|
# Game-Mode HDR indicator stays dark (verified on a Deck OLED: the sandbox loader found NO
|
|
# frog/gamescope layer). The layer ships as the runtime extension
|
|
# `org.freedesktop.Platform.VulkanLayer.gamescope`, which org.gnome.Platform//50 auto-mounts at
|
|
# /usr/lib/extensions/vulkan/gamescope once installed (a one-time, per-Deck step; keep it in the
|
|
# Decky plugin's setup / docs):
|
|
# flatpak install --user -y flathub org.freedesktop.Platform.VulkanLayer.gamescope//25.08
|
|
# THREE things are needed, not two (verified live on a Deck OLED — the env vars alone left
|
|
# hdr10_format=None). (1) VK_ADD_IMPLICIT_LAYER_PATH puts the layer's implicit-layer JSON on the
|
|
# Vulkan loader's search path (the runtime point mounts the files but not onto the path). (2)
|
|
# ENABLE_GAMESCOPE_WSI flips the layer's own `enable_environment` gate. (3) The layer, once
|
|
# loaded, must open a *Wayland* connection to gamescope's private socket ($GAMESCOPE_WAYLAND_DISPLAY
|
|
# = gamescope-0) to negotiate the HDR10 colorspace via the gamescope_swapchain protocol — but the
|
|
# Deck runs games as X11 clients (DISPLAY=:1, no WAYLAND_DISPLAY exported), so --socket=wayland
|
|
# binds nothing and that socket never enters the sandbox. Without it the layer loads, maps, and
|
|
# silently can't reach the compositor → no HDR10 offered → PQ tone-mapped to SDR, badge dark.
|
|
# Binding xdg-run/gamescope-0 is the missing half (chiaki-ng does the same). With all three the
|
|
# surface offers HDR10 and the presenter's existing HDR10 swapchain path engages — no client code
|
|
# change. Harmless off-Deck: the layer no-ops when there's no gamescope socket to bind.
|
|
- --env=VK_ADD_IMPLICIT_LAYER_PATH=/usr/lib/extensions/vulkan/gamescope/share/vulkan/implicit_layer.d
|
|
- --env=ENABLE_GAMESCOPE_WSI=1
|
|
- --filesystem=xdg-run/gamescope-0 # gamescope's private Wayland socket (HDR negotiation)
|
|
|
|
build-options:
|
|
append-path: /usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm20/bin
|
|
# The rust build resolves everything via pkg-config: gtk4/libadwaita/pipewire/opus AND a
|
|
# linkable libavcodec.so.61 from org.gnome.Sdk//50 (the multiarch /usr dir), plus the bundled
|
|
# SDL3's .pc from /app. (At runtime the codecs-extra extension swaps in the HEVC-capable
|
|
# libavcodec — see the header.)
|
|
env:
|
|
PKG_CONFIG_PATH: /app/lib/pkgconfig:/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/lib/pkgconfig
|
|
# bindgen (ffmpeg-sys-next / sdl3-sys) loads libclang from the llvm20 extension.
|
|
LIBCLANG_PATH: /usr/lib/sdk/llvm20/lib
|
|
# mold (shipped in rust-stable) speeds the ~450-crate link on the Deck APU.
|
|
RUSTFLAGS: -C link-arg=-fuse-ld=mold
|
|
# The multiarch directory in PKG_CONFIG_PATH is per-architecture, and flatpak-builder does NOT
|
|
# shell-expand `env` values — so ${FLATPAK_ARCH} would be taken literally. An `arch` override is
|
|
# the supported way to vary it; everything else above is inherited. Only the runtime's own
|
|
# /usr/lib/<triple> changes, so aarch64 differs from x86_64 in exactly this one string.
|
|
arch:
|
|
aarch64:
|
|
env:
|
|
PKG_CONFIG_PATH: /app/lib/pkgconfig:/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/lib/pkgconfig
|
|
|
|
modules:
|
|
# ---------------------------------------------------------------------------------------
|
|
# SDL3 — NOT provided as a linkable libSDL3.so.0 by org.gnome.Platform/freedesktop-sdk
|
|
# 25.08, and there is no SDL3 recipe in flathub/shared-modules. Build it from source.
|
|
# Pinned to 3.4.10 to match the crate exactly: sdl3-sys is `0.6.6+SDL-3.4.10`, i.e. its
|
|
# bindings target SDL 3.4.10 — building an older SDL risks missing symbols at link time.
|
|
# HIDAPI is enabled (DualSense touchpad/motion/triggers/lightbar over hidraw).
|
|
# ---------------------------------------------------------------------------------------
|
|
- name: sdl3
|
|
buildsystem: cmake-ninja
|
|
config-opts:
|
|
- -DCMAKE_BUILD_TYPE=Release
|
|
- -DSDL_SHARED=ON
|
|
- -DSDL_STATIC=OFF
|
|
- -DSDL_HIDAPI=ON # DualSense full fidelity over hidraw
|
|
- -DSDL_TEST_LIBRARY=OFF
|
|
- -DSDL_EXAMPLES=OFF
|
|
sources:
|
|
- type: archive
|
|
url: https://github.com/libsdl-org/SDL/releases/download/release-3.4.10/SDL3-3.4.10.tar.gz
|
|
# `sha256sum SDL3-3.4.10.tar.gz` (verified 2026-06-15). Bump url + sha together.
|
|
sha256: 12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785
|
|
x-checker-data:
|
|
type: anitya
|
|
project-id: 4974
|
|
stable-only: true
|
|
url-template: https://github.com/libsdl-org/SDL/releases/download/release-$version/SDL3-$version.tar.gz
|
|
cleanup:
|
|
- /bin
|
|
- /include
|
|
- /lib/cmake
|
|
- /lib/pkgconfig
|
|
|
|
# ---------------------------------------------------------------------------------------
|
|
# Vulkan-Headers — the SESSION binary's pf-ffvk crate runs bindgen over FFmpeg's
|
|
# libavutil/hwcontext_vulkan.h, which `#include <vulkan/vulkan.h>`. The GNOME SDK is not
|
|
# guaranteed to ship those dev headers, so install them into /app ourselves (headers only,
|
|
# no compile) and point pf-ffvk's bindgen at them via PF_FFVK_VULKAN_INCLUDE below. Kept in
|
|
# cleanup — headers aren't needed at runtime (the Vulkan LOADER comes from the GL runtime).
|
|
# ---------------------------------------------------------------------------------------
|
|
- name: vulkan-headers
|
|
buildsystem: cmake-ninja
|
|
sources:
|
|
- type: archive
|
|
url: https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/vulkan-sdk-1.4.313.0.tar.gz
|
|
# `sha256sum vulkan-sdk-1.4.313.0.tar.gz` (verified 2026-07-07). Bump url + sha together.
|
|
sha256: 20743c99a96c07290f24377360e7a12bdd2c465ba202e0c7ef2ec25d446cf61d
|
|
cleanup:
|
|
- '*'
|
|
|
|
# ---------------------------------------------------------------------------------------
|
|
# The client. cargo-sources.json is the GENERATED offline crate cache:
|
|
# python3 flatpak-cargo-generator.py Cargo.lock -o packaging/flatpak/cargo-sources.json
|
|
# (run from the repo root; the CI step does exactly this). With it present the build is fully
|
|
# offline (CARGO_NET_OFFLINE). For quick LOCAL iteration WITHOUT regenerating it, drop the
|
|
# cargo-sources.json source and pass --build-args=--share=network to flatpak-builder
|
|
# (non-reproducible; cargo fetches from crates.io during the build).
|
|
# ---------------------------------------------------------------------------------------
|
|
- name: punktfunk-client
|
|
buildsystem: simple
|
|
build-options:
|
|
env:
|
|
CARGO_HOME: /run/build/punktfunk-client/cargo
|
|
CARGO_NET_OFFLINE: 'true'
|
|
# The session binary's Skia (pf-console-ui → skia-safe) fetches prebuilt libskia in
|
|
# its build script — dead in the offline sandbox. skia-bindings accepts a file://
|
|
# override (read directly, no curl), so we point it at the archive that
|
|
# flatpak-builder pre-downloaded as a pinned source below. No {tag}/{key}
|
|
# placeholders needed: a template without them is used verbatim.
|
|
SKIA_BINARIES_URL: file:///run/build/punktfunk-client/skia-binaries.tar.gz
|
|
# Point pf-ffvk's bindgen at the Vulkan-Headers installed into /app above (its clang
|
|
# invocation doesn't inherit the SDK's default include search, so pass it explicitly).
|
|
PF_FFVK_VULKAN_INCLUDE: /app/include
|
|
build-commands:
|
|
# Drop every reference to the windows-rs GIT dependency before building. That git
|
|
# source is deliberately NOT vendored into cargo-sources.json — see
|
|
# packaging/flatpak/prune-windows-lock.py, which strips it so flatpak-builder doesn't
|
|
# full-clone the multi-GB windows-rs repo. But `cargo --offline` needs every DECLARED
|
|
# dependency's source just to build the unit graph (cfg(windows)-gated or not), so any
|
|
# manifest still pinning it fails with "can't checkout ... you are in the offline
|
|
# mode". Two declare it: the Windows client (removed from the workspace members) and
|
|
# pf-client-core's D3D11VA backend (entry pruned in place — nothing cfg(windows)
|
|
# compiles here). Both edits touch only the sandbox copies. (No --locked: the lock no
|
|
# longer matches the reduced graph; --offline still pins every crate to the vendored
|
|
# cargo-sources.json, so the build stays reproducible.)
|
|
- sed -i '\#"clients/windows",#d' Cargo.toml
|
|
- python3 packaging/flatpak/prune-windows-toml.py crates/pf-client-core/Cargo.toml
|
|
# One cargo invocation for both binaries: the shell and the Vulkan session binary it
|
|
# execs for --connect/--browse. Building them together keeps skia-bindings' feature
|
|
# unification identical to a workspace build (the pinned skia-binaries archive's
|
|
# `pdf-textlayout-vulkan` key must match the resolved feature set).
|
|
- cargo --offline build --release -p punktfunk-client-linux -p punktfunk-client-session
|
|
- install -Dm0755 target/release/punktfunk-client ${FLATPAK_DEST}/bin/punktfunk-client
|
|
- install -Dm0755 target/release/punktfunk-session ${FLATPAK_DEST}/bin/punktfunk-session
|
|
# Desktop entry (renamed to the app id; Exec is the in-sandbox binary).
|
|
- install -Dm0644 packaging/flatpak/io.unom.Punktfunk.desktop
|
|
${FLATPAK_DEST}/share/applications/io.unom.Punktfunk.desktop
|
|
# AppStream metainfo (required for a well-formed flatpak / Software listings).
|
|
- install -Dm0644 packaging/flatpak/io.unom.Punktfunk.metainfo.xml
|
|
${FLATPAK_DEST}/share/metainfo/io.unom.Punktfunk.metainfo.xml
|
|
# Scalable icon named for the app id (GNOME runtime renders SVG via librsvg).
|
|
- install -Dm0644 packaging/flatpak/io.unom.Punktfunk.svg
|
|
${FLATPAK_DEST}/share/icons/hicolor/scalable/apps/io.unom.Punktfunk.svg
|
|
sources:
|
|
# The repo checkout. For a Flathub/published build, replace with a pinned git source:
|
|
# - type: git
|
|
# url: https://git.unom.io/unom/punktfunk
|
|
# tag: vX.Y.Z
|
|
# commit: <sha>
|
|
# For ON-DECK / CI builds we build the checked-out working tree in place:
|
|
- type: dir
|
|
path: ../..
|
|
# Generated offline crate cache (see the comment block above). Remove for --share=network.
|
|
- cargo-sources.json
|
|
# Prebuilt Skia for skia-bindings 0.87.0 (SKIA_BINARIES_URL above). The key encodes the
|
|
# crate's pinned skia commit + target + resolved features — when bumping skia-safe, build
|
|
# the workspace once locally and take the new name from
|
|
# target/release/build/skia-bindings-*/out/.cache/, then update url + sha256 together.
|
|
# One entry per architecture, selected by `only-arches`; both land on the SAME
|
|
# dest-filename, so SKIA_BINARIES_URL above stays a single literal path. The upstream
|
|
# release publishes the aarch64 archive under the same skia commit hash and the same
|
|
# resolved-feature key, so the two stay in lockstep on a skia-safe bump — take both new
|
|
# names and sha256s together.
|
|
- type: file
|
|
only-arches: [x86_64]
|
|
url: https://github.com/rust-skia/skia-binaries/releases/download/0.87.0/skia-binaries-e551f334ad5cbdf43abf-x86_64-unknown-linux-gnu-pdf-textlayout-vulkan.tar.gz
|
|
sha256: b46e7061e6b9df792025acaf9b8b90180224c7cec63f4c3ce09af7ddddb8abfa
|
|
dest-filename: skia-binaries.tar.gz
|
|
- type: file
|
|
only-arches: [aarch64]
|
|
url: https://github.com/rust-skia/skia-binaries/releases/download/0.87.0/skia-binaries-e551f334ad5cbdf43abf-aarch64-unknown-linux-gnu-pdf-textlayout-vulkan.tar.gz
|
|
sha256: 6994fb993064d7d4fff00bf7c79544ffc881bb5441b6768b31cc088540f4bef7
|
|
dest-filename: skia-binaries.tar.gz
|