# Steam Frame port: progress **Status (2026-10-07): iteration 1 implemented. Next: test checklist T1–T11 on the Frame (at the end of this file), then assessment. Getting started: [README.md](README.md).** ## Iteration 1 ### Step 1: build fixes, bytecode-only QML (2026-10-07) **Changes:** - **`CMakeLists.txt`:** - `Core5Compat` removed (unused, not on the Frame). - `GuiPrivate` requested explicitly. This is temporary, until step 3 replaces QRhi. - `QT_QMLCACHEGEN_ARGUMENTS --only-bytecode`. - OpenVR library directory per architecture (`linux64` / `linuxarm64` / `win64`), for linking and for the copy next to the binary. - `RPATH $ORIGIN`. - `libXtst` linked. - **`overlaycontroller.cpp`:** `d3d11.h` and `case QRhi::D3D11` only under `_WIN32`. - **Logging:** 8 active easylogging `LOG(INFO)` calls became `qInfo()` (`overlaycontroller.cpp` 6, `ChaperoneTabController.cpp` 1, `MoveCenterTabController.cpp` 1). Commented-out `LOG` lines are left unchanged. - Only changed lines are formatted (`git clang-format`). **Results (x86_64, Arch, Qt 6.11.1, GCC):** - The build succeeds, with 1463 compiler warnings, as in `qt6`. - `objdump -T`: 7 `Qt_6_PRIVATE_API` symbols, all `QRhi…` (step 3). The `QQmlPrivate::AOTCompiledContext` symbols are gone (bytecode-only QML). - `libopenvr_api.so` is resolved from the binary's directory (`ldd`). - Not started: without a running SteamVR the start hangs in OpenVR init (expected; tested on the Frame in step 5). ### Step 2: platform option, R7, tick (2026-10-07) **Changes:** - **Platform option:** - CMake option `OVRAS_STEAM_FRAME` (default OFF) sets the compile definition `OVRAS_STEAM_FRAME=1`. - New `src/utils/platform_capabilities.h` evaluates it as the only place. It holds `k_chaperoneCommitSupported`, `k_defaultVsyncDisabled` and `k_defaultCustomTickRateMs`. - **Tick (R6):** - In "vsync disabled" mode, `m_pumpEventsTimer` runs directly at `customTickRateMs` (`applyTickInterval()`, `Qt::PreciseTimer`); the 1 ms counting is gone. - Changing the setting restarts the timer. - Setting defaults in `settings_controller.h` come from the constants: Frame `true`/11 ms, otherwise unchanged `false`/20 ms. - **R7:** with `!k_chaperoneCommitSupported`: - `updateChaperoneResetData()` and `applyChaperoneResetData()` skip `CommitWorkingCopy`/`RevertWorkingCopy`. Each logs once per function. - The init autosave profile is skipped. **Results:** - Both variants (default and `-DOVRAS_STEAM_FRAME=ON`) build on x86_64. The definition is only set in the Frame variant (`compile_commands.json`). - Not tested at runtime yet (Frame, step 5). **Notes:** - Another commit at start-up remains, but only on opt-in. `SetWidget()` applies a chaperone profile and commits when "auto-apply chaperone" (`autoApplyChaperoneToggle`, default `false`) is on. That's a chaperone-profile feature, so it belongs to R5 (iteration 2). - clang-format reflows comment blocks next to new comments. Separate new comments from existing commented-out code with a blank line, otherwise the diff grows. ### Step 3: rendering through public API, `--graphics-api`, mouse y, shutdown order (2026-10-07) **Changes:** - **`src/render/`** (new): `RenderBackend`, `OpenGLRenderBackend`, `VulkanRenderBackend` from the template (`4a5b090`). - Changes to the template code: `VRDASH_*` → `OVRAS_*` names, origin note in the file header, references to `OverlayController`. - Formatted completely in OVRAS style. - One comment in `VulkanRenderBackend.h` was wrapped by hand, because the automatic wrapping had broken its column layout. - **`OverlayController`:** - QRhi block replaced by `configureWindow` → `renderControl.initialize` → `attachWindow` → `createRenderTarget`. - `renderOverlay()` wraps the frame in `beginFrame`/`finishFrame`. It submits `vrTextureBounds` + `vrTexture` while the context is current. - `SetOverlayFromQRhiTexture`, the QRhi members and `QVulkanInstance` are removed. - Render delay 5 → 16 ms (about 60 fps). - **Shutdown:** `Shutdown()` runs only once. It calls `VR_Shutdown()`, then frees the backend's render target (`releaseRenderTarget`, `detachWindow`). - **Mouse y:** always flipped; the Linux un-flip and its TODO are removed. - **`openvr_init::renderInterop()`:** Vulkan extensions and HMD GPU from SteamVR. - **`main.cpp`:** parses `--graphics-api`, then `createRenderBackend` → `initialize` → `QQuickWindow::setGraphicsApi` → `OverlayController`. - **`setup.*`:** `--graphics-api opengl|vulkan` (default `opengl`). Unknown or not built in → error, exit code `GENERAL_FAILURE`. - **CMake:** - `GuiPrivate` is gone. - The Vulkan backend is built when `QT_FEATURE_vulkan` is set (`OVRAS_HAS_VULKAN`). **Results (x86_64, Qt 6.11.1):** - The build succeeds. The Vulkan backend is built in. - `objdump -T`: only `QVulkanDeviceFunctions::*` / `QVulkanFunctions::*` remain as `Qt_6_PRIVATE_API`. Acceptance criterion R1 (symbols) is met. - Warnings 1463 → 1630. The new code itself has none; the additional ones come from `openvr.h` in the three new translation units. - **Not tested yet:** - running both backends, Vulkan validation, the snapshot test (step 5, with the containers) - mouse direction (Frame) **Risk:** - `MyTextField` opens the SteamVR keyboard on `activeFocus`. The template had to send the offscreen window an explicit `FocusIn`, otherwise `activeFocusItem` stayed null. The `qt6` branch doesn't send it. If text fields don't respond on the Frame, that is the cause (R4). ### Step 4: manifest and installation (2026-10-07) **Changes:** - **`manifest.vrmanifest`:** `binary_path_linux` and `binary_path_linux_arm` → `AdvancedSettings_app`. - **`manifest::handleManifests`:** - `VR_Init(Utility)`; on `VRInitError_Init_NotAvailableToUtilityApps`, `VR_Init(Background)`. - Other errors: message and exit code `OPENVR_INIT_ERROR`, instead of `initializeOpenVR`'s retry loop. - **`main.cpp`:** `--graphics-api` is checked before connecting to OpenVR. A wrong value used to fail only after `initializeOpenVR`'s retry loop. **Results (x86_64, without SteamVR):** - `--force-install-manifest` exits right away with `OPENVR_INIT_ERROR` (−2). - `--graphics-api foo` exits right away with `GENERAL_FAILURE` (−1). - The build tree contains the binary, `libopenvr_api.so`, both manifests, `default_action_manifests/` and `res/`. - Not tested yet: `Background` on the Frame (step 5). ### Step 5: Frame build, package, tests (2026-10-07) **Changes:** - **`build_scripts/steamframe/`** (new, see DESIGN.md section 9): containers, toolchain, build, test, package and deploy scripts. - **`test/render/`** (new): render test for both backends (`OVRAS_BUILD_TESTS=ON`). - **`CMakeLists.txt`:** - option `OVRAS_BUILD_TESTS` - RPATH only `$ORIGIN` (`BUILD_WITH_INSTALL_RPATH`). Before, the cross binary also had the host's absolute link directory in its RUNPATH. - **`.gitignore`:** `dist/`. **Results:** - **Render tests:** - locally (Arch, Qt 6.11, Intel Iris Xe): OpenGL and Vulkan, 4/4 passed each - in the container (Debian trixie, Qt 6.8, same GPU via `/dev/dri`): 2/2 passed, **no Vulkan validation messages** - Proof that the layer was active: `VK_LOADER_DEBUG=layer` shows "Inserted device layer VK_LAYER_KHRONOS_validation". - **Cross build (aarch64, Qt 6.8 Debian):** succeeds once the container has `libxt-dev` (`input_sender_X11.cpp` includes `X11/Intrinsic.h`). - `Qt_6_PRIVATE_API` without `QVulkan*`: 0 → **R1 symbols met**. - Qt version tag: `Qt_6.8` (fits the Frame's 6.8.0). - GLIBC ≤ 2.38 (the Frame has 2.39); GLIBCXX ≤ 3.4.32 (not checked against the Frame). - NEEDED: Qt6 Core/Gui/Qml/Quick/Widgets/Network/DBus/Multimedia/WebSockets, libX11, libXtst, libpulse, libopenvr_api, libstdc++/libc. - **Package** `dist/AdvancedSettings-5.7.4-linux-arm64.tar.gz`: 3.0 MB (9.1 MB unpacked); since the version fix below `…-5.8.17-…`. The version 5.7.4 comes from `qt6`'s `project()` (master is at 5.8.17). **Open (needs the Frame):** - Are the QML modules OVRAS imports present in the Frame's system Qt? QtQuick.Controls (Material), Layouts, Dialogs, Multimedia. - Do libXt/libXtst exist on SteamOS? Is libstdc++ new enough? ### Version from `compile_version_string.txt` (2026-10-07) - **Problem:** `qt6`'s `CMakeLists.txt` had the version hard-coded as 5.7.4. The qmake build read it from `build_scripts/compile_version_string.txt` (`5.8.17-release`). `master` is fully merged into `qt6`, so only the number was stale. Consequence: the version check (comparison with `ver/versioncheck.json`, 5.8.17) would have reported an update. - **Fix:** CMake reads the file. - `APPLICATION_VERSION` = full string, as in qmake. - `project(VERSION)` = numeric part. - **Result:** `APPLICATION_VERSION="5.8.17-release"`, `CMAKE_PROJECT_VERSION=5.8.17`, package `dist/AdvancedSettings-5.8.17-linux-arm64.tar.gz` (3.0 MB). ### Fix: manifest commands without a display (2026-10-07) - **Problem (first deploy to the Frame):** `install.sh` aborted with a core dump in `./AdvancedSettings_app --force-install-manifest`. Log: "could not connect to display", then "no Qt platform plugin could be initialized" (fatal). - **Cause:** `main.cpp` creates the `QApplication` before it handles the manifest options. Qt's default platform plugin (`xcb`) aborts without a display, and over SSH there is none. `install.sh` set `DISPLAY=:0` only for the start, not for registering. `uninstall.sh` (`--force-remove-manifest`) had the same problem. - **Fix (`package-release.sh`, generated scripts only):** both manifest calls run with `QT_QPA_PLATFORM=offscreen`. Registering draws nothing, so it needs no display at all. The normal start keeps `DISPLAY=:0` (`xcb`), because rendering needs a real OpenGL/Vulkan context. No change to the program. - **Result:** package rebuilt (`--no-build`), generated scripts checked with `sh -n`. - **Not tested yet:** the fix itself on the Frame (T1, T11). No local x86 build was at hand for a test without a display. ### Fix: OpenVR library for aarch64 (2026-10-07) - **Problem (second deploy):** with the display fix, `--force-install-manifest` got as far as `VR_Init` and failed there: "Installation path could not be located (110)", and on stderr "Unable to read VR Path Registry from /data/work/openvrpaths.vrpath". `~/.config/openvr/openvrpaths.vrpath` on the Frame is correct (runtime `/opt/steamvr`). - **Cause:** the package shipped `third-party/openvr/lib/linuxarm64/libopenvr_api.so`. It is byte-identical to the OpenVR SDK 2.15.6's `lib/linuxarm64` and looks for the path registry in the fixed path `/data/work/openvrpaths.vrpath`. The SDK's `bin/linuxarm64` library (used by the template, verified on the Frame) and SteamVR's own one in `/opt/steamvr/bin/linuxarm64` don't contain that path. OVRAS's `third-party/openvr` had no `bin/linuxarm64`. - **Fix:** - New file `third-party/openvr/bin/linuxarm64/libopenvr_api.so` from the SDK v2.15.6 (`raw.githubusercontent.com/ValveSoftware/openvr/v2.15.6/bin/linuxarm64/…`, SHA-256 `cc3671d2…47e9`). - `CMakeLists.txt`: `OPENVR_LIB_DIR` = `bin/linuxarm64` for aarch64 only. x86_64 and Windows are unchanged. - Rejected: setting `VR_PATHREG_OVERRIDE` in the scripts. It wouldn't help when SteamVR autostarts OVRAS. - **Result:** cross build and package succeed. The package contains the new library (no `/data/work`), `Qt_6_PRIVATE_API` without `QVulkan*`: 0, RUNPATH `$ORIGIN`. Package 1.6 MB (was 3.0 MB, the old library was 5.4 MB). - **Not tested yet:** on the Frame (T1). ### Finding: app key already registered by Steam (2026-10-07) - **Third try of T1** (by hand on the Frame, with the two fixes above): `VR_Init` works, as `Utility` (vrserver.txt: "New Connect message … (VRApplication_Utility)"; SteamVR did **not** refuse Utility this time, unlike with the template). Then: "Manifest Previously Installed", "Could not find working directory of already installed application: VRApplicationError_PropertyNotSet". - **Cause:** Steam's generated `steamapps.vrmanifest` lists every owned VR app, including OVRAS (`steam.overlay.1009850`, launch via `steam://` URL), although the Steam version is uninstalled. Not a leftover of the uninstallation; it can't be removed permanently. - **Next:** design DESIGN.md section 2a (approved 2026-10-07). - **Implementation (`setup.cpp`, `setup.h`):** as designed: key without working directory → nothing removed, `AddApplicationManifest` directly (error name on refusal); `logApplicationEntry()` after "Manifest reinstalled." logs source, launch type, working directory, binary, URL and auto-launch for the key. - **Result:** cross build and package succeed, `Qt_6_PRIVATE_API` without `QVulkan*`: 0. - **Not tested yet:** on the Frame. The log decides: `AppKeyAlreadyExists` or Steam's entry (`steam://` URL) → own app key needed (option 2); our working directory and binary → enough. **Result on the Frame (2026-10-07, deploy with `ec82b5f`):** SteamVR accepts our manifest (no error) but keeps using Steam's entry for the key: `source: "steam" launch type: "url" working dir: PropertyNotSet binary: PropertyNotSet URL: "steam://launch/1009850/openvroverlay" auto-launch: true`. - **Consequence:** the native build needs its own app key (option 2, DESIGN.md 2a, follow-up). Design pending. - **Side effect:** `--force-install-manifest` switched auto-launch on for Steam's key. At the next SteamVR start, SteamVR may launch `steam://launch/1009850` (the uninstalled Steam version). To be switched off again. - OVRAS itself was started by `install.sh` and keeps running (not yet checked in the dashboard, T2). ### Fix: debug output only with debugSymbolsAndLogs (2026-10-07) - **Problem:** the log filled with `[D] updateAllPulseData done.` about every 11 ms (8399 lines in 6 s). `AudioTabController::eventLoopTick()` reads the microphone's mute state on every tick, and every read calls `updateAllPulseData()` (`AudioManagerPulse_internal.h:312`). - **Cause (regression in `qt6`):** on `master` the message was easylogging's `LOG(DEBUG)`, and the Debug level was disabled unless built with `debugSymbolsAndLogs` (`ENABLE_DEBUG_LOGGING`). `qt6` switched to `qDebug()` and lost that; the constant `debugLoggingEnabled` in `setup.cpp` was left unused, so all 46 `qDebug()` calls were always active. - **Fix (`setup.cpp`, `setUpLogging()`):** without `ENABLE_DEBUG_LOGGING`, `QLoggingCategory::setFilterRules("*.debug=false")`. `QT_LOGGING_RULES="*.debug=true"` takes precedence and turns debug output on again without a rebuild. - **Result:** cross build and package succeed, `Qt_6_PRIVATE_API` without `QVulkan*`: 0. - **Not tested yet:** on the Frame (log without `[D]` lines; with `QT_LOGGING_RULES` with them). - **Open for R6/T10:** each tick makes three synchronous PulseAudio queries (server info, all sinks, all sources) for the mute state. Not changed now. ### Layout cut off on the Frame; diagnosis tools (2026-10-08) - **Observed (headset photo):** the dashboard tab appears, navigation and laser work. The fixed 1200×800 layout overflows: about the bottom quarter is cut off (left: Statistics, Bindings, Settings; right: Microphone half, Push-to-Talk). Navigation icons overlap their text. The right edge is complete. - **Ruled out by experiment on the Frame:** `QT_FONT_DPI=96` (verified in the process environment): no change. `QT_QUICK_CONTROLS_STYLE=Basic` (Qt 5's "Default" style; `qt6` sets no style, so Qt 6 uses Fusion on Linux; only `mainwidget.qml` imports Material): no visible change. `QT_QUICK_CONTROLS_MATERIAL_VARIANT=Dense`: slightly better, still cut off. - **Facts:** Frame X screen 1920×1080, `Xft.dpi: 144`; default font Noto Sans (`fc-match sans-serif`). Template renders 1600×1000. - **Also seen:** after removing the Steam license, Steam's `steamapps.vrmanifest` still lists `steam.overlay.1009850` (not yet regenerated); the debug-output fix works (0 `[D]` lines). - **Diagnosis tools (DESIGN.md section 10):** font/screen metrics in the log, option `--snapshot `, `run.sh` passes further options. - **Result:** cross build and package succeed, `Qt_6_PRIVATE_API` without `QVulkan*`: 0, generated `run.sh` checked with `sh -n`. No new warnings in the changed lines. - **Not tested yet:** on the Frame (log line "Font: …", PNG written). ### Test checklist on the Frame (step 5) Deploy: `build_scripts/steamframe/deploy-to-frame.sh @` (SteamVR running). | # | Check | Criterion | Result | |---|---|---|---| | T1 | `install.sh`: registration works (log: "Manifest reinstalled", via Background) | R2 | first try failed: abort without a display (fixed, see above); retest open | | T2 | Tab appears in the dashboard, UI correct (OpenGL) | R2, R3 | open | | T3 | `./run.sh vulkan`: UI correct | R3 | open | | T4 | Laser right way up, click, scrolling on several pages (both APIs) | R4 | open | | T5 | Text field (e.g. new profile name): SteamVR keyboard opens, input arrives | R4 | open | | T6 | An OVRAS action arrives (e.g. push-to-talk, log/notification) | R4 | open | | T7 | Play area before/after start unchanged; log: "CommitWorkingCopy skipped" | R7 | open | | T8 | Quit app (OVRAS "Exit" and Quit in the SteamVR dashboard): no crash (both APIs) | R2 | open | | T9 | Auto-start after a SteamVR restart / reboot | R2 | open | | T10 | `pidstat -p $(pgrep -f AdvancedSettings_app) 10 6`: dashboard closed / OVRAS visible / other tab visible | R6 | open | | T11 | `uninstall.sh` unregisters it | R2 | open | ### Wrap-up (2026-10-07) - **Remotes:** `origin` = `git@git.matzman.net:SteamFrame/OpenVR-AdvancedSettings-frame.git` (branches `steamframe`, `qt6`, `master`); `upstream` = GitHub OVRAS. - **Handover documentation:** `docs/steamframe/README.md` (entry point), `CLAUDE.md` (working rules, conventions), `docs/steamframe/OVRAS-FEATURES.md` (feature analysis). - **Still open in iteration 1:** phase 4 (tests on the Frame, T1–T11) and phase 5 (assessment).