Initial FrameAssistant baseline from the vrdashboard template

Template stripped to the overlay platform (examples, demo UI and Frame
test package removed), renamed to FrameAssistant, and working rules for
the project recorded in CLAUDE.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-05 00:06:58 +02:00
co-authored by Claude Opus 5.5
commit 4a5b09053e
63 changed files with 5193 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env bash
# Builds the app inside a Debian container and stages an install in build/<preset>/stage,
# ready to copy to the Steam Frame.
#
# scripts/container-build.sh cross x86_64 host → aarch64, uses the target's
# system Qt 6.8.x (default way to deploy)
# scripts/container-build.sh native same for the host's own architecture
# (run on the Frame for a native arm64 build)
# scripts/container-build.sh cross-bundle / native-bundle
# self-contained: Qt bundled (fallback if the
# target's Qt doesn't fit)
# scripts/container-build.sh <mode> shell interactive shell in that container
#
# Uses podman if available, else docker (override with CONTAINER_ENGINE).
set -euo pipefail
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
mode="${1:-}"
case "$mode" in
cross) dockerfile=cross-arm64.Dockerfile; preset=cross-arm64 ;;
native) dockerfile=native.Dockerfile; preset=native-systemqt ;;
cross-bundle) dockerfile=cross-arm64.Dockerfile; preset=cross-arm64-bundle ;;
native-bundle) dockerfile=native.Dockerfile; preset=native-bundle ;;
*) echo "usage: $0 cross|native|cross-bundle|native-bundle [shell]" >&2; exit 2 ;;
esac
image="frameassistant-build-${dockerfile%.Dockerfile}"
engine="${CONTAINER_ENGINE:-$(command -v podman || command -v docker)}"
"$engine" build -q -t "$image" -f "$root/docker/$dockerfile" "$root/docker" >/dev/null
run=("$engine" run --rm -v "$root:$root:z" -w "$root")
if [[ "$(basename "$engine")" == docker ]]; then
run+=(--user "$(id -u):$(id -g)")
else
run+=(--userns=keep-id)
fi
[[ -t 0 ]] && run+=(-it)
if [[ "${2:-}" == shell ]]; then
exec "${run[@]}" "$image" bash
fi
"${run[@]}" "$image" bash -c "
set -e
cmake --preset $preset
cmake --build --preset $preset
rm -rf build/$preset/stage
cmake --install build/$preset --prefix build/$preset/stage
"
echo "Staged: $root/build/$preset/stage"
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
# Builds and runs all tests in a Debian container with BOTH render backends, Vulkan with the
# Khronos validation layers enabled. Fails on any validation message.
#
# scripts/container-test.sh GPU via /dev/dri if available, otherwise software
# VK_DRIVER=lvp scripts/container-test.sh force lavapipe (software Vulkan)
set -euo pipefail
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
engine="${CONTAINER_ENGINE:-$(command -v podman || command -v docker)}"
"$engine" build -q -t frameassistant-build-native -f "$root/docker/native.Dockerfile" "$root/docker" >/dev/null
"$engine" build -q -t frameassistant-test -f "$root/docker/test.Dockerfile" "$root/docker" >/dev/null
run=("$engine" run --rm -v "$root:$root:z" -w "$root" -e HOME=/tmp -e "VK_DRIVER=${VK_DRIVER:-}")
if [[ "$(basename "$engine")" == docker ]]; then
run+=(--user "$(id -u):$(id -g)")
else
run+=(--userns=keep-id)
fi
[[ -e /dev/dri/renderD128 ]] && run+=(--device /dev/dri/renderD128)
"${run[@]}" frameassistant-test bash -c '
set -e
cmake -S . -B build/container-test -G Ninja -DCMAKE_BUILD_TYPE=Debug -DVRDASH_VULKAN=ON >/dev/null
cmake --build build/container-test
if [[ -n "$VK_DRIVER" ]]; then export VK_DRIVER_FILES=/usr/share/vulkan/icd.d/${VK_DRIVER}_icd.json; fi
vulkaninfo --summary 2>/dev/null | grep -m1 deviceName || true
Xvfb :99 >/dev/null 2>&1 &
sleep 1
export DISPLAY=:99 VRDASH_VULKAN_VALIDATION=1
ctest --test-dir build/container-test --output-on-failure -V > build/container-test/ctest.log 2>&1 \
|| { tail -40 build/container-test/ctest.log; exit 1; }
grep -E "tests passed|Test +#" build/container-test/ctest.log
messages=$(grep -oE "(VUID|UNASSIGNED)-[A-Za-z0-9_.-]+" build/container-test/ctest.log | sort | uniq -c || true)
if [[ -n "$messages" ]]; then
echo "Vulkan validation messages (see build/container-test/ctest.log):"
echo "$messages"
exit 1
fi
echo "No Vulkan validation messages."
'
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# Copies a release package to the Steam Frame over SSH and installs it there (registers
# it with SteamVR and starts it; an existing installation in the same place is updated).
# SteamVR must be running on the Frame.
#
# scripts/deploy-to-frame.sh <ssh-target> [package-dir] [remote-dir]
# e.g. scripts/deploy-to-frame.sh steamos@frame
#
# package-dir defaults to the newest dist/*-linux-arm64 package (scripts/package-release.sh);
# remote-dir defaults to the package's base name in the remote home directory.
set -euo pipefail
target="${1:?usage: $0 <ssh-target> [package-dir] [remote-dir]}"
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
package="${2:-$(ls -dt "$root"/dist/*-linux-arm64 "$root"/dist/*-linux-arm64-bundled 2>/dev/null | head -1)}"
[[ -n "$package" && -x "$package/install.sh" ]] || {
echo "No release package found (run scripts/package-release.sh first)" >&2; exit 1; }
# Without the version: updates go to the same place and replace the old version.
remote="${3:-$(basename "$package" | sed -E 's/-[0-9][0-9.]*-linux-arm64(-bundled)?$//')}"
rsync -a --delete --exclude logs/ "$package/" "$target:$remote/"
ssh "$target" "\$HOME/$remote/install.sh"
echo "Installed $(basename "$package") to $target:~/$remote"
+95
View File
@@ -0,0 +1,95 @@
#!/usr/bin/env bash
# Builds the dashboard for the Steam Frame (aarch64) and packages it for release:
#
# dist/<executable>-<version>-linux-arm64/ uses SteamOS's Qt (default, ~3 MB)
# dist/<executable>-<version>-linux-arm64-bundled/ Qt bundled (--bundle-qt, ~46 MB)
# (each also as .tar.gz)
#
# The package contains only the dashboard itself plus install.sh,
# uninstall.sh and a README. Installing again over an existing installation updates it.
#
# scripts/package-release.sh [--bundle-qt] [--no-build]
set -euo pipefail
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
build=1 bundle_qt=0
for arg in "$@"; do
case "$arg" in
--no-build) build=0 ;;
--bundle-qt) bundle_qt=1 ;;
*) echo "usage: $0 [--bundle-qt] [--no-build]" >&2; exit 2 ;;
esac
done
if [[ "$(uname -m)" == aarch64 ]]; then mode=native; else mode=cross; fi
if (( bundle_qt )); then
mode+=-bundle
[[ "$mode" == native-bundle ]] && preset=native-bundle || preset=cross-arm64-bundle
else
[[ "$mode" == native ]] && preset=native-systemqt || preset=cross-arm64
fi
(( build )) && "$root/scripts/container-build.sh" "$mode"
cache="$root/build/$preset/CMakeCache.txt"
setting() { sed -n "s/^$1:[A-Z]*=//p" "$cache"; }
exe="$(setting VRDASH_EXECUTABLE_NAME)"
name="$(setting VRDASH_DISPLAY_NAME)"
key="$(setting VRDASH_APP_KEY)"
version="$(setting CMAKE_PROJECT_VERSION)"
project="$(setting CMAKE_PROJECT_NAME)"
stage="$root/build/$preset/stage"
[[ "$(file -b "$stage/bin/$exe")" == *aarch64* ]] || { echo "$stage/bin/$exe is not an aarch64 build" >&2; exit 1; }
package="$exe-$version-linux-arm64"
(( bundle_qt )) && package+=-bundled
out="$root/dist/$package"
rm -rf "$out" "$out.tar.gz"
mkdir -p "$out"
cp -a "$stage/." "$out/"
# Only the dashboard: drop any other executables.
find "$out/bin" -maxdepth 1 -type f -perm -u+x ! -name "$exe" -delete
cp "$root/LICENSE" "$out/"
cat > "$out/install.sh" <<EOF
#!/bin/sh
# Installs or updates $name: registers it with SteamVR (starts automatically with
# SteamVR) and starts it now. SteamVR must be running. Works over SSH.
cd "\$(dirname "\$0")" || exit 1
mkdir -p logs
# An update replaces the files of a running instance: stop it, SteamVR restarts it below.
# (Match the command line: process names are cut to 15 characters.)
pkill -f "/bin/$exe( |\\$)" 2>/dev/null && sleep 1
exec ./bin/$exe --install-manifest --launch --log-file logs/install.log
EOF
cat > "$out/uninstall.sh" <<EOF
#!/bin/sh
# Unregisters $name from SteamVR and stops it. The folder can be deleted afterwards.
cd "\$(dirname "\$0")" || exit 1
pkill -f "/bin/$exe( |\\$)" 2>/dev/null
exec ./bin/$exe --uninstall-manifest --log-file logs/install.log
EOF
chmod +x "$out/install.sh" "$out/uninstall.sh"
{
echo "$name $version (SteamVR dashboard overlay, app key $key)"
echo
if (( bundle_qt )); then
echo "Self-contained build: Qt is included."
else
echo "Uses the system's Qt (SteamOS: Qt 6.8 or newer). If it fails to start after a"
echo "SteamOS update, use the -bundled package instead."
fi
echo
echo "Install / update: ./install.sh (SteamVR must be running)"
echo " SteamVR remembers this folder's location: after moving it,"
echo " run ./install.sh again."
echo "Uninstall: ./uninstall.sh"
echo "Logs: logs/install.log; run ./bin/$exe --help for options"
echo
echo "License: MIT (LICENSE). Third-party notices: share/doc/$project/."
} > "$out/README.txt"
tar -C "$root/dist" -czf "$out.tar.gz" "$package"
echo "Release folder: $out"
echo "Archive: $out.tar.gz ($(du -h "$out.tar.gz" | cut -f1))"