6.6 KiB
Steam Frame
How to build, deploy and run dashboards on the Steam Frame, and what was learned on the
device. Facts below were verified on a Frame with SteamOS holo 0.4.3 (system Qt 6.8.0,
Adreno 750 GPU with Mesa Turnip).
Building
Frame builds run in a Debian container (Docker or Podman), either cross-compiled on an x86_64 PC (no emulation) or natively on the Frame:
scripts/container-build.sh cross # on the PC → build/cross-arm64/stage
scripts/container-build.sh native # on the Frame itself (podman/distrobox)
scripts/container-build.sh cross-bundle # fallback variant, Qt bundled
scripts/container-build.sh cross shell # a shell in the build container
System Qt (default). Builds use SteamOS's own Qt; packages are about 3 MB.
- The container builds against Debian's Qt 6.8.2; at runtime the Frame's Qt 6.8.0 is used. Qt keeps binary compatibility within 6.8, as long as the program uses no Qt-internal API.
- QML is compiled to bytecode only (
VRDASH_QML_BYTECODE_ONLY), because Qt's QML-to-C++ compiler output is tied to the exact Qt version. - The binary then needs only the stable Qt API,
qt_version_tag(Qt ≥ 6.8), and Qt's Vulkan wrapper functions. Verified with an Arch container frozen at Qt 6.8.0 and on the Frame. - After a SteamOS update with a different Qt, rebuild, or use the bundled variant.
Bundled Qt (fallback, -bundle modes, about 46 MB).
- Qt, its plugins and QML modules, and their non-system dependencies are copied next to the
program (
cmake/BundleQt.cmake). - The GPU and display stack (GL/EGL, Vulkan loader, Wayland, X11, libffi, glibc) always comes from SteamOS; bundling it would break GPU access.
- The Debian copyright files of all bundled libraries are included, as their licenses require.
Packaging and deploying
scripts/package-release.sh # → dist/<exe>-<version>-linux-arm64/ + .tar.gz
scripts/package-release.sh --bundle-qt # → …-linux-arm64-bundled
scripts/deploy-to-frame.sh steamos@<frame> # rsync the newest package to ~/<exe>, run install.sh
A release folder contains the program, install.sh (install or update: stops a running
instance, registers with SteamVR, starts it), uninstall.sh, README.txt and the licenses.
The app logs to logs/frame-assistant.log in that folder (passed by the manifest), and
needs a settings file first (~/.config/frameassistant/config.json, from
share/frameassistant/config.example.json; see README.md, "Settings").
SteamVR stores the folder's absolute path, so after moving the folder, run install.sh
again.
Without the deploy script: copy the .tar.gz, unpack it on the Frame, run ./install.sh
(SteamVR must be running). Over SSH this works too: SteamVR starts the dashboard itself.
What the Frame requires (all handled by the overlay platform)
| Finding | Consequence |
|---|---|
SteamVR is installed system-wide under /opt/steamvr; its log is ~/.local/share/Steam/logs/vrserver.txt |
Look there when a manifest or start fails |
The manifest needs binary_path_linux_arm; entries with only binary_path_linux are silently skipped ("must specify binary_path") |
The manifest template picks the key from the target architecture |
SteamVR refuses VRApplication_Utility (VRInitError_Init_NotAvailableToUtilityApps) |
Manifest commands connect as VRApplication_Background (needs a running SteamVR, never starts one) |
An executable named vrdashboard is taken for SteamVR's own dashboard (its watchdog then "restarts" it) |
Executable frame-assistant; CMake rejects SteamVR process names |
| OpenGL runs through zink on Vulkan (no native GL driver for the Adreno) | Vulkan is the default: 1.01 ms vs 1.89 ms per frame (--benchmark, 1600×1000) |
| OpenVR must shut down before the graphics device is destroyed | Teardown order in DashboardApplication::runOverlay() (else a crash at exit with Vulkan) |
| Qt needs a display connection even for off-screen rendering; over SSH none is set | SteamVR-launched instances inherit it; over SSH, set DISPLAY=:0 |
| Process names are cut to 15 characters | Scripts find running instances by command line, not by name |
| Floated/pinned dashboard tabs stay visible and operable in games | Rendering follows the overlay's visibility, not the dashboard's |
Libraries on the Frame
Checked 2026-10-05; details per requirement in REQUIREMENTS.md, Appendix C.
- System Qt 6.8.0 under
/usr/libincludes, besides the base modules: DBus, Network, Multimedia (FFmpeg plugin), WebSockets, WebEngine (Core, Quick), WebView, TextToSpeech, Positioning, Sensors, Svg, VirtualKeyboard. TLS plugins:libqopensslbackend.so,libqcertonlybackend.so. - It apparently comes with the KDE Plasma desktop mode (plugins for plasma, kwin, discover).
SteamVR does not use Qt 6: nothing under
/opt/steamvrlinks or shipslibQt6*. So the system Qt is not a SteamVR dependency, which is one more reason to keep the bundled-Qt fallback. - Qt WebEngine on the Frame can't be loaded (checked 2026-10-05):
qt6-webengineandqt6-webchannelare 6.7.0 while the rest of Qt is 6.8.0, and WebEngine was built against FFmpeg 6 (libavcodec.so.60,libavformat.so.60,libavutil.so.58); the Frame has FFmpeg 7.0 (.61/.59) and an old FFmpeg 4 (.58/.56). Onlyqt6-webviewandsignon-uidepend on it. A program linking WebEngine fails to start ("libavcodec.so.60: cannot open shared object file"). Other Qt modules are 6.8.0 (pacman -Q | grep qt6-). Not yet checked: whether Qt Multimedia's FFmpeg plugin has the same problem. - Qt add-on modules must come from the system Qt. Shipping one fails: they use Qt's private API, and QtCore aborts on any version mismatch ("Cannot mix incompatible Qt library").
- Not present: any MQTT library (no
libmosquitto, no Qt MQTT). FrameAssistant ships libmosquitto. - Present: OpenSSL 3.2.1 (
libssl.so.3,libcrypto.so.3), glibc 2.39,libcjson.so.1(1.7.17),libpulse.so.0,libpipewire-0.3.so.0, and the toolswpctl,pactl,nmcli. - Libraries we ship must not need newer symbol versions than the Frame has (e.g.
OPENSSL_3.x,GLIBC_2.x): check withreadelf -V. Debian's libmosquitto needs at mostOPENSSL_3.0.0andGLIBC_2.34.
Notes
- The Frame's package repositories in
/etc/pacman.confare private (URLs marked "DO_NOT_SHARE"). Don't put them into scripts or this repository. - Valve's public Holo Core preview container (
registry.gitlab.steamos.cloud/holo/ holo-core-aarch64-preview/base-devel) ships Qt 6.10, newer than the Frame's 6.8. It is not usable for system-Qt builds.