# OVRAS: features and how they are implemented Analysis of 2026-10-07, based on upstream `master` (`31df8b4`, 5.8.17). Line numbers refer to that state. `qt6` has the same features, so the numbers are close. Paths are relative to `src/`. Which of these features SteamVR now offers itself is in [REQUIREMENTS.md](REQUIREMENTS.md), "Feature classification". ## Architecture - **Dashboard overlay:** - `VR_Init(VRApplication_Overlay)`, app key `steam.overlay.1009850`. - QML UI (1200×800) rendered off-screen via `QQuickRenderControl`. - The texture goes to `SetOverlayTexture`. On `steamframe` the render backends in `render/` do this; before, it was an OpenGL FBO, and on `qt6` QRhi. - **Input:** SteamVR mouse/scroll events are turned into Qt events (`overlaycontroller.cpp`, `mainEventLoop()`). - **Main loop (`OnTimeoutPumpEvents` → `mainEventLoop()`):** - Timer; "vsync" mode once per compositor frame (`GetTimeSinceLastVsync`). - Per tick: 1. input actions 2. VR events 3. poses 4. `eventLoopTick` of the tab controllers (always) 5. `dashboardLoopTick` (only while the dashboard is visible) - **SteamVR Input:** `package_files/action_manifest.json`, 5 action sets: - `motion`: space drag/turn, gravity, snap/smooth turn, height toggle, offsets, auto-turn - `music`: media keys - `misc`: axis locks, chaperone toggle, keyboard shortcuts - `system`: push-to-talk, exclusive input, haptic clicks - `haptic`: vibration, proximity sensor Default bindings exist for Vive, Index, WMR, HP, Touch, generic HMD and Frame controllers. - **Persistence:** - App settings in a `QSettings` INI file (`settings/`). Profiles are stored as arrays (`ISettingsObject`). - SteamVR's own settings go directly through `vr::VRSettings()` (`openvr/ovr_settings_wrapper`). - **Platform:** - Windows: WASAPI / `IPolicyConfig`, `SendInput`. - Linux: PulseAudio, X11 XTest, D-Bus MPRIS. - Elsewhere: dummies. ## Features ### Playspace movement (`tabcontrollers/MoveCenterTabController.cpp`) Almost everything shifts or rotates the **working standing zero pose** through `IVRChaperoneSetup` and shows it live with `ShowWorkingSetPreview()`. Nothing is committed per frame. This mechanism does **not work on the Frame** (inside-out tracking). - **Core, `updateSpace()` (:2497–2721):** - Offsets X/Y/Z and the rotation (centidegrees) are multiplied with the stored basis. - The result goes to `SetWorkingStandingZeroPoseToRawTrackingPose`, in seated mode additionally to the seated pose. - Safety limit: about 40 km. - **Space drag:** - The controller's position delta, multiplied by `dragMult`, is added to the offsets. - Axis locks; the speed is kept for fling. - Comfort mode skips frames. - **Space turn:** the change of the yaw angle via quaternions (`q·conj(q_last)`, `quaternion/quaternion.h`). Rotation pivots on the HMD by default. - **Snap/smooth turn:** fixed angle per press resp. per tick, so smooth turn depends on the frame rate. - **Gravity/momentum:** Euler integration with friction, terminal velocity 50 m/s, landing on the floor, reversible. - **Height toggle:** Y offset plus a new gravity floor. - **Offset profiles, apply/reset:** - "Apply" commits with `CommitWorkingCopy(Live)` (limit 990 m). - `zeroOffsets()` / `updateChaperoneResetData()` also commit at start-up. On the Frame this shifts the play area, hence R7. ### Rotation (`tabcontrollers/RotationTabController.cpp`, via `setRotation`) - **Auto-turn:** - Rotates the space when the user gets near a wall, either as a snap or smoothly. - Direction: from the corner shared with the last wall, otherwise cord untangling (HMD total rotation count). - **Vestibular motion:** rotation as if walking on a circle of radius r (`2·asin(d/2r)`). - **View ratchetting:** amplifies head turns away from the wall. ### Fix floor / recenter (`tabcontrollers/FixFloorTabController.cpp`) - **Fix floor:** - Measures the lower controller over 25 frames. - Subtracts the controller thickness per model and orientation (Vive / Knuckles). - Commits the result as a Y offset; undo is possible. - **Recenter:** `VRChaperone()->ResetZeroPose(...)`. ### Chaperone (`tabcontrollers/ChaperoneTabController.cpp`) - **Settings:** - Visibility, color, fade distance, wall height, center marker, floor bounds and style. - These are keys in `k_pch_CollisionBounds_Section`. - "Disable" sets the fade distance to 0 and remembers the old value. - **Own center marker overlay:** shows the rotation count (cord). - **Dim when crouching:** below a height, the fade distance goes to 0 over 10 s. - **Proximity warnings:** - Distance of HMD and controllers to the walls (`utils/ChaperoneUtils.cpp`: projection onto each wall segment). - Reactions: switch to the beginner style, vibration (own thread, `TriggerHapticVibrationAction`), alarm sound with distance-dependent volume, open the dashboard. - **Chaperone profiles:** - Save the geometry (`GetLiveCollisionBoundsInfo`, zero pose, size) and the settings. - Apply them with `SetWorking…` + `CommitWorkingCopy`. - Autosave profile. ### Video (`tabcontrollers/VideoTabController.cpp`) - **Brightness:** a black overlay right in front of the HMD. Its alpha `(1−x)^(1/3)` compensates for perception. - **Color tint:** - either a colored overlay (`SetOverlayColor`) - or SteamVR's `HmdDisplayColorGainR/G/B` - **SteamVR keys:** supersampling (incl. manual override), motion smoothing, supersample filtering. - **Video profiles.** ### Audio (`tabcontrollers/AudioTabController.cpp`, `audiomanager/`) - **Interface:** `AudioManager` with a Windows, a PulseAudio and a dummy implementation. - **Windows:** - WASAPI/MMDevice, `IAudioEndpointVolume`. - The default device is set via the undocumented `IPolicyConfig`. - External changes come in via `IMMNotificationClient`. - **Linux:** - PulseAudio: `pa_context_set_default_sink/source`; existing source outputs are moved to the new source. - The original state is restored at exit. - **No mirror device.** - **Mirror device and device override:** these are SteamVR keys (`k_pch_audio_*`), not OVRAS's own routing. - **Push-to-talk / -mute:** SteamVR Input action → mic mute + notification overlay on the HMD. - **Mute the mic when the HMD is taken off:** proximity sensor action. - **Audio profiles.** ### SteamVR tab (`tabcontrollers/SteamVRTabController.cpp`) - **Toggles:** each is a SteamVR setting. - performance graph, multiple drivers, do not disturb, no-HMD mode - camera options - system button to all apps, don't fade to grid, controller power auto-launch - **Restart SteamVR:** Windows only, via `.bat`. - **Per-game controller bindings:** - Use SteamVR's web server `localhost:27062`, with a spoofed `Referer`. - Saved as JSON and applied when the scene app changes. - **Receiver/tracker pairing:** WebSocket to the lighthouse web interface (`start_pairing`). - **`openvr/lh_console_util.cpp`** (`lighthouse_console`) is compiled but **not used**. - There is no base-station power management. ### Utilities (`tabcontrollers/UtilitiesTabController.cpp`, `keyboard_input/`, `media_keys/`, `alarm_clock/`) - **Keyboard:** - Buttons (Enter, Alt-Tab, Ctrl-C/V, …) and 3 freely defined shortcuts. - A parser for the syntax `^`=Ctrl, `*`=Alt, `>`=Shift, `#`=Super. - Sent via Windows `SendInput` or X11 XTest. On the Frame (gamescope) the X11 path probably doesn't reach games. - **Media keys:** - Windows: `VK_MEDIA_*`. - Linux: D-Bus to every `org.mpris.MediaPlayer2.*`. - **Tracker battery:** icon overlay on each tracker (`Prop_DeviceBatteryPercentage_Float`), only while the dashboard is visible. - **Alarm clock:** HMD overlay at a set time, shown for 3 s. ### Statistics (`tabcontrollers/StatisticsTabController.cpp`) - **Frame counters:** `VRCompositor()->GetCumulativeStats()` gives presented, dropped, reprojected and timed-out frames. "Reset" only stores an offset. - **Distance traveled:** horizontal HMD movement, every 11th tick, steps ≥ 1 cm. - **Rotation count, maximum controller speed.** ### Settings tab (`tabcontrollers/SettingsTabController.cpp`) Autostart (`SetApplicationAutoLaunch`), native chaperone import, block Oculus SDK, desktop mode, tick rate (vsync / custom tick rate). ## Issues found while reading (not verified) | Place (master) | Issue | |---|---| | `MoveCenterTabController.cpp:1198` | `updateChaperoneResetData()` allocates `new vr::HmdQuad_t[]` on every call and never frees the old array (memory leak) | | `MoveCenterTabController.cpp:2512` | `if ( m_chaperoneHasCommit ) { m_chaperoneHasCommit = true; … }` looks like a slip | | `MoveCenterTabController.cpp:1332` | `leftHandSpaceDrag`: the final `else` overwrites the swap/override choice | | `MoveCenterTabController` | Rotation is wrapped inconsistently (snap/smooth/auto-turn 0–36000 vs. ±18000 elsewhere) | | `VideoTabController.cpp:114` | Sets the sort order of the brightness overlay instead of the color overlay | | `ChaperoneTabController.cpp:903` | Floor bounds are read as Bool but written as Int32 | | `MoveCenterTabController::zeroOffsets` | If `GetCalibrationState() == 200`, init never completes and the commit repeats every tick (with commits enabled) | | `utils/FrameRateUtils.cpp`, `openvr/lh_console_util.cpp` | Compiled but unused | Found on the `qt6` branch and fixed on `steamframe` (see PROGRESS.md): - the build fails on Linux - QRhi Vulkan path: wrong `m_nFormat` (Qt enum instead of `VkFormat`); no layout transition, no SteamVR extensions, no choice of the HMD's GPU - mouse y not flipped on Linux - no `VR_Shutdown` - hard-coded version 5.7.4