25 Commits
Author SHA1 Message Date
matzmanandClaude Opus 5.5 5a10cdf7ca Wrap-up documentation after the first tests on the Steam Frame
README: status, new Steam Frame facts (Steam's generated app manifest,
OpenVR library, display and font metrics) and open points. PROGRESS:
partial results of T1, T2 and T4, and what is open next.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 00:21:52 +02:00
matzmanandClaude Opus 5.5 7f14551a6a Record the Frame test of the Fusion style fix
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 00:20:05 +02:00
matzmanandClaude Opus 5.5 ff4ea8f455 Use the Fusion Quick Controls style; drop the Material import
On the Steam Frame, the fixed 1200x800 layout lost about its bottom
quarter. Measured with --snapshot: fonts and DPI are as intended, but
the controls ran in the Material style. The qt6 branch changed
mainwidget.qml's "import QtQuick.Controls" into
"import QtQuick.Controls.Material", which fixes Material for all
controls and even overrides QT_QUICK_CONTROLS_STYLE. Material's insets
and padding make the navigation buttons 65 px tall instead of 45 px
(Fusion) or 49 px (Basic, Qt 5's default style renamed); nothing in the
file uses Material.

mainwidget.qml imports QtQuick.Controls again, and main.cpp sets Fusion
explicitly, so the sizes are the same on every platform instead of
depending on Qt 6's per-platform default. With Basic the last navigation
button is still cut off on the Frame; with Fusion everything fits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 00:14:57 +02:00
matzmanandClaude Opus 5.5 d664f86945 Add layout diagnosis: font metrics in the log, --snapshot option
On the Steam Frame, the fixed 1200x800 QML layout overflows: about the
bottom quarter is cut off and the navigation icons overlap their text.
Forcing 96 DPI and switching the Quick Controls style didn't help, so the
cause has to be measured rather than guessed.

- SetWidget() logs the application font (family, pt, px), what 20 pt
  (MyText) is in px, the screen's DPI and the device pixel ratio.
- --snapshot <file> writes the rendered overlay (RenderBackend::readback)
  as PNG at most once per second while it is visible. A time interval,
  because OVRAS only renders when the scene changes. Written to a
  temporary file and renamed, so readers never see a partial file.
- run.sh passes arguments after the graphics API to the program.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 00:06:09 +02:00
matzmanandClaude Opus 5.5 bbb3d1ba0e Log debug output only in builds with debugSymbolsAndLogs
On master, easylogging's Debug level was disabled unless OVRAS was built
with debugSymbolsAndLogs (ENABLE_DEBUG_LOGGING). The qt6 branch switched
to qDebug() and lost that: debugLoggingEnabled was left unused, and all
debug output reached the log. On the Steam Frame it filled with
"updateAllPulseData done." about every 11 ms, as the audio tab reads the
microphone's mute state on every tick.

setUpLogging() now disables debug output through a logging filter rule.
QT_LOGGING_RULES="*.debug=true" takes precedence and turns it on again
for troubleshooting without a rebuild.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 23:32:00 +02:00
matzmanandClaude Opus 5.5 ec82b5fead Register the manifest when Steam already lists the app key
Steam writes every VR app the user owns into its generated
steamapps.vrmanifest, installed or not. Whoever owns OVRAS on Steam
therefore always has steam.overlay.1009850 registered, as a steam:// URL
without a working directory, and --force-install-manifest failed with
"Could not find working directory of already installed application:
VRApplicationError_PropertyNotSet" (found on the Steam Frame).

Such an entry has no manifest file to remove, so OVRAS now leaves it
alone and adds its own manifest. Whether SteamVR accepts a second
manifest for the key, and which entry it then uses, is not documented:
logApplicationEntry() logs what SteamVR reports for the key after
installing, so a test on the Frame shows it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 23:25:57 +02:00
matzmanandClaude Opus 5.5 317db6831d Use the OpenVR SDK's bin/linuxarm64 library on aarch64
On the Steam Frame, VR_Init failed with "Installation path could not be
located": the SDK's lib/linuxarm64/libopenvr_api.so (2.15.6) reads the VR
path registry from the fixed path /data/work/openvrpaths.vrpath instead
of ~/.config/openvr. The SDK's bin/linuxarm64 library of the same version
uses ~/.config/openvr; the vrdashboard template ships it and was verified
on the Frame. It is added as a new file (third-party/openvr had no
bin/linuxarm64, and "bin" is gitignored, hence force-added), and aarch64
builds link against and ship it. x86_64 and Windows are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 23:13:47 +02:00
matzmanandClaude Opus 5.5 537539ab5e Run the manifest commands without a display
The first deploy to the Steam Frame aborted in install.sh: OVRAS creates
its QApplication before it handles --force-install-manifest, and Qt's
default platform plugin (xcb) aborts when there is no display, as over
SSH. Registering and unregistering draw nothing, so the generated
install.sh and uninstall.sh now run them with QT_QPA_PLATFORM=offscreen.
The normal start keeps DISPLAY=:0, as rendering needs a real context.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 23:08:10 +02:00
Matthias JanetschekandClaude Opus 5.5 7c5a9a7a1e Handover documentation for continuing on another computer
- docs/steamframe/README.md: entry point with status, branches/remotes, setup,
  commands, Steam Frame facts, open points.
- CLAUDE.md: working rules and code conventions (fork-only).
- docs/steamframe/OVRAS-FEATURES.md: OVRAS's features, how they are
  implemented, issues found while reading.
- PROGRESS.md: status and wrap-up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:57:20 +02:00
Matthias JanetschekandClaude Opus 5.5 3ed686dec5 Read the version from build_scripts/compile_version_string.txt
The CMake build had the version hard-coded as 5.7.4, while the qmake build
read it from compile_version_string.txt (5.8.17-release). The stale number
also made the version check report an update. As in the qmake build, the
full string becomes APPLICATION_VERSION and its numeric part the project
version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:41:12 +02:00
Matthias JanetschekandClaude Opus 5.5 bde675f1b8 Steam Frame build, packaging and deployment scripts; render test
- build_scripts/steamframe/: Debian trixie (Qt 6.8) containers for the
  aarch64 cross build, native build and tests; toolchain; scripts to build,
  test (Vulkan validation layers), package (system Qt, ~3 MB) and deploy over
  SSH. Adapted from the vrdashboard template.
- test/render/: render test for the OpenGL and Vulkan backends, driven like
  OverlayController (option OVRAS_BUILD_TESTS).
- RPATH only $ORIGIN, without the build machine's link directories.
- Ignore dist/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:50:57 +02:00
Matthias JanetschekandClaude Opus 5.5 b22c35ae29 Manifest for Linux; connect as Background app where Utility is refused
- manifest.vrmanifest: binary_path_linux and binary_path_linux_arm. The
  Steam Frame's SteamVR reads only binary_path_linux_arm.
- handleManifests(): connect as Utility app first (works without a running
  SteamVR, e.g. from the Windows installer); if SteamVR refuses Utility apps,
  as on the Steam Frame, connect as Background app. Fail right away on other
  errors instead of going through initializeOpenVR()'s retry loop.
- main(): check --graphics-api before connecting to OpenVR, so a wrong value
  fails right away.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:42:16 +02:00
Matthias JanetschekandClaude Opus 5.5 4826686a4d Render through public Qt API: OpenGL and Vulkan backends, --graphics-api
Replace the QRhi rendering in OverlayController (private Qt API) with the
render backends of the vrdashboard template (src/render/), which use public
Qt API only (QQuickRenderTarget::fromOpenGLTexture / fromVulkanImage). The
binary then runs on another Qt 6 patch release, e.g. the Steam Frame's system
Qt.

The Vulkan backend also does what SteamVR requires and the QRhi path lacked:
the required instance/device extensions and the HMD's GPU (via
openvr_init::renderInterop()), VK_FORMAT_R8G8B8A8_UNORM, and the transition to
TRANSFER_SRC_OPTIMAL after each frame.

- New option --graphics-api opengl|vulkan (default opengl, no fallback).
- Shutdown() calls VR_Shutdown before the render target and Qt's device go
  (crash on the Steam Frame with Vulkan otherwise); it runs only once.
- Mouse y is flipped on every platform: SteamVR reports it in GL convention.
- Render delay 5 -> 16 ms (about 60 fps).
- GuiPrivate is no longer needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:41:13 +02:00
Matthias JanetschekandClaude Opus 5.5 423ea57db7 Add a Steam Frame build option; no chaperone commit at start; timer-based tick
- CMake option OVRAS_STEAM_FRAME. It is evaluated only in
  src/utils/platform_capabilities.h, which turns it into named capability
  constants and platform defaults.
- Where the chaperone can't be committed (Steam Frame: it shifts the play
  area), MoveCenterTabController no longer commits the working copy at start-up
  or when applying reset data, and doesn't create the init autosave profile.
  Skips are logged once.
- In "vsync disabled" mode the pump timer now runs directly at the custom tick
  rate instead of firing every 1 ms and counting. The Frame defaults to this
  mode at 11 ms (~90 Hz).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:35:46 +02:00
Matthias JanetschekandClaude Opus 5.5 266a2f9ce9 Fix the Linux build of the qt6 branch, compile QML to bytecode only
- Remove Core5Compat (unused, not part of the Steam Frame's system Qt).
- Request GuiPrivate explicitly (required since Qt 6.10 for QRhi); temporary
  until rendering moves to public API.
- Compile QML to bytecode only: C++ generated from QML uses private Qt API
  that only matches the exact Qt version it was built with.
- Pick the OpenVR library per architecture (linux64, linuxarm64, win64) for
  linking and for the copy next to the binary; RPATH $ORIGIN.
- Link libXtst, needed by input_sender_X11.cpp.
- Include d3d11.h and handle QRhi::D3D11 only on Windows.
- Replace leftover easylogging LOG(INFO) calls with qInfo().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:29:53 +02:00
Matthias JanetschekandClaude Opus 5.5 aac3edf7e3 Steam Frame port: requirements and design of iteration 1
Internal fork based on upstream's qt6 branch (2d4db7e). Goal: OVRAS runs
natively on the Steam Frame (SteamOS, aarch64) with a minimal change set
against qt6 and only public Qt API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:29:53 +02:00
ykeara 2d4db7ef4b merge changes 2026-09-25 15:12:25 -05:00
ykeara 8d661d2b31 merge mainline changes into qt6 2026-09-25 15:06:43 -05:00
ykeara 31df8b476b 5.8.17 (#701)
* fix recenter behavior

* temp fix issues

* increment version to dev

* Fix autosave profile initialization, fix openxr movement up/down, allow using space fix in openxr state

* adjusted logging to reduce spam

* removed unused variable

* added delay to openxr space drag to improve performance and stop ovr api calls

* add logging for debugging issues

* improved log verbage

* fix wrong fn call error

* typemismatch error fix

* add default frame bindings

* openXR space drag skip frames increased 3->5, performance still not great

* remove chaperone universe checking completely, we shouldn't need any features

* fix compiler warnings

* update version string to rel

* fix chaperone moving on recenter, fix log spam

* increment version and back to dev

* clarify log

* change to release

* fix openxr space drag issues

* remove commented code around openxr

* increment version

* change how desktop mode displays from URL to steam setting window

* updated openvr to 2.15.6

* increment version

* moved openxr fix to seated mode

* removed calibration state log calls, as without context they aren't particularly useful

* increment version 5.8.16

* remove uncessary logging

* switch keyboard over to instant input instead of buffered

* Fix Edge Cases around keyboard submit, remove permissive compile options

* increment version
2026-09-09 11:57:54 -05:00
ykeara 9ecdda1572 misc 2026-04-27 10:49:13 -05:00
ykeara 93c0851055 fix dx11 rendering in vr, fix various qrc references 2026-03-27 15:57:21 -05:00
ykeara 1152cfee1f compiling and various updates 2025-10-09 18:45:53 -05:00
ykeara 44a05dfd57 fix cast error, change cmake to c++ 20 for some uses, add headers to cmake for better vs project generation 2024-05-08 18:20:19 -05:00
RTUnrealandykeara 383f153c54 draft: upgrade codebase to qt6 and cmake (#657)
* update qt6 upgrade progress

* add flawed addition for QRenderTarget and link needed libs

* update to better logging format

* move many assets and qml files into qrc

* get rid of deprecation messages

* remove additional deprecation notices

* make Qt6 finally render on desktop with some progress on vr

* initialize QRhi with QQuickRenderControl

* use default values to stop SegFaults

* fix compilation on windows

* add windows files for aiding in running exe

* fix formatting of testing qt.conf

* first attempt on using textures instead of files in AudioTabController

* Fix public/private access issue

* use getter instead of directly accessing class members

* use QImage as a base to set overlay textures

* use native  logging

* add more QImage Overlays

* implement faulty vulkan implementation

---------

Co-authored-by: ykeara <ykeara1@gmail.com>
2024-05-01 15:19:36 -05:00
ykearaandRTUnreal ca0ec3e07d Qt6 (#669)
* update qt6 upgrade progress

* add flawed addition for QRenderTarget and link needed libs

* update to better logging format

* move many assets and qml files into qrc

* get rid of deprecation messages

* remove additional deprecation notices

* make Qt6 finally render on desktop with some progress on vr

* initialize QRhi with QQuickRenderControl

* use default values to stop SegFaults

* fix compilation on windows

* add windows files for aiding in running exe

* fix formatting of testing qt.conf

* first attempt on using textures instead of files in AudioTabController

* Fix public/private access issue

---------

Co-authored-by: RTUnreal <unreal@rtinf.net>
2024-04-03 22:49:24 -05:00
174 changed files with 7823 additions and 10001 deletions
+4 -1
View File
@@ -15,4 +15,7 @@ installer/*.exe
build_scripts/win/__pycache__
build_scripts/win/current_build.bat
AdvancedSettings_resource.rc
.qtc_clangd/
.qtc_clangd/
out/
# Steam Frame release packages (build_scripts/steamframe/package-release.sh)
dist/
+96
View File
@@ -0,0 +1,96 @@
# CLAUDE.md
Guidance for Claude Code (and humans) working in this fork. Fork-only file: not part of
upstream pull requests.
**Start with [docs/steamframe/README.md](docs/steamframe/README.md):** goal, status, setup,
commands, Steam Frame facts. Then [REQUIREMENTS.md](docs/steamframe/REQUIREMENTS.md)
(decisions), [DESIGN.md](docs/steamframe/DESIGN.md) and
[PROGRESS.md](docs/steamframe/PROGRESS.md) (status, Frame test checklist).
## Working rules (agreed with the user, always apply)
- **Proper software engineering:** follow established procedures and processes, not ad-hoc
hacking.
- **Nothing without explicit permission.**
- Propose first, then act only on the user's go-ahead.
- Permission covers only what was asked. No extra changes, renames, rewrites or "while I'm
at it" fixes: suggest those instead.
- **Ask, don't assume.** When a requirement, decision or detail is unclear, ask. Give a
recommendation with the question.
- **Iterations in phases.** Each phase ends with the user's OK (informal is enough):
1. requirements
2. design
3. implementation
4. tests
5. assessment
- Keep iterations small.
- **Document everything** in `docs/steamframe/`: requirements, decisions (with date), design,
progress, test results. Report results faithfully, including what was *not* tested. Keep
the docs in sync with every change.
- **Communication:** the user writes German or English; answer in the user's language. Docs
and code comments are in English.
## Goal and constraints (details: REQUIREMENTS.md)
- OVRAS runs natively on the Steam Frame (SteamOS, aarch64). Windows is out of scope for now.
- **Minimal change set against upstream's `qt6` branch**, because changes are meant to go
upstream later.
- Change existing code only where needed.
- Bring new parts in as self-contained files.
- **Only public Qt API.** No `GuiPrivate`/QRhi, and QML is compiled to bytecode only.
`objdump -T … | grep Qt_6_PRIVATE_API` may only show `QVulkan*`.
- **Frame differences:**
- Switched by the CMake option `OVRAS_STEAM_FRAME`.
- It is evaluated **only** in `src/utils/platform_capabilities.h`, which turns it into
capability constants named by capability (e.g. `k_chaperoneCommitSupported`).
- The code asks for capabilities, never for the platform.
## Code conventions
- **Comments:** every change gets comments that explain it fully (what and why, e.g. which
Frame or SteamVR fact). Keep them direct and concise. Trivial mechanical changes such as
`LOG(INFO)` → `qInfo()` are explained in the commit message instead.
- **Style:**
- OVRAS style (`.clang-format`, since 2018) everywhere, including code taken over from the
template.
- **Format only changed lines:** `git add …; git clang-format; git add …`. Never
reformat whole existing files, because a dozen files already deviate.
- New files are formatted completely (`clang-format -i`).
- Separate new comments from existing commented-out code with a blank line. Otherwise
clang-format reflows the old block and the diff grows.
- **Commits:**
- One commit per step / topic, with a message that explains the why.
- End every commit message with `Co-Authored-By: Claude …`.
- Push to `origin` (the fork); `upstream` is the GitHub OVRAS repository.
- **Template code** (`src/render/`, `build_scripts/steamframe/`) carries a note about its
origin (vrdashboard template, FrameAssistant `4a5b090`).
## Architecture notes (what is easy to break)
- **`main.cpp` order:**
1. parse the command line
2. choose the render backend (`--graphics-api`)
3. `initializeOpenVR(Overlay)`
4. `backend->initialize(openvr_init::renderInterop())`
5. `QQuickWindow::setGraphicsApi()`
6. `OverlayController`
`setGraphicsApi` must come before any `QQuickWindow` exists, and `OverlayController` owns
one.
- **`OverlayController`:**
- Owns `m_renderBackend`, declared *before* `m_renderControl` / `m_window`: Qt's device
comes from the backend.
- `SetWidget()` and `renderOverlay()` follow the call order in `src/render/RenderBackend.h`.
- **Shutdown:**
- OVRAS ends with `exit()`.
- `Shutdown()` (once only) calls `vr::VR_Shutdown()` *before* `releaseRenderTarget()` /
`detachWindow()`.
- **Tick:**
- `m_pumpEventsTimer`: 1 ms polling in "vsync" mode; in "vsync disabled" mode it runs
directly at `customTickRateMs` (`applyTickInterval()`).
- Frame defaults: vsync disabled, 11 ms.
- **Mouse:** SteamVR reports y from the bottom, so it is flipped on every platform.
- **Logging:** OVRAS writes `AdvancedSettings.log` into the working directory.
- **SteamVR Input:** `action_manifest.json` and `default_action_manifests/` (incl.
`…_default_frame.json` for `frame_controller`) lie next to the binary.
+620
View File
@@ -0,0 +1,620 @@
cmake_minimum_required(VERSION 3.20)
# The version comes from build_scripts/compile_version_string.txt, as in the
# qmake build (e.g. "5.8.17-release"): the full string becomes
# APPLICATION_VERSION (shown in the UI, compared by the version check), the
# numeric part before "-" the project version.
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/build_scripts/compile_version_string.txt"
OVRAS_VERSION_STRING LIMIT_COUNT 1)
string(REGEX REPLACE "-.*$" "" OVRAS_VERSION_NUMBER "${OVRAS_VERSION_STRING}")
project(AdvancedSettings VERSION ${OVRAS_VERSION_NUMBER} LANGUAGES CXX)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_STANDARD 20)
#set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
#find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core)
#find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui Multimedia Qml Quick WebSockets Widgets QuickControls2 QuickDialogs2 Svg)
#find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS DBus)
# Core5Compat removed: nothing uses it, and the Steam Frame's system Qt
# doesn't ship it. No private Qt modules: the binary must run on another Qt 6
# patch release, e.g. the Frame's system Qt (docs/steamframe/DESIGN.md).
find_package(Qt6 REQUIRED COMPONENTS Gui Multimedia Qml Quick WebSockets Widgets QuickControls2 QuickDialogs2 Svg Qml)
find_package(Vulkan REQUIRED)
qt_standard_project_setup(REQUIRES 6.8)
#qt_standard_project_setup should cover these
#include(GNUInstallDirs)
#set(CMAKE_AUTOMOC ON)
#set(CMAKE_AUTOUIC ON)
qt6_policy(SET QTP0001 NEW)
qt6_policy(SET QTP0004 NEW)
list(APPEND QML_IMPORT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/src/res/qml")
message(STATUS "QML_IMPORT_PATH ${QML_IMPORT_PATH}")
# ability to just put .qrc files into the source list
#set(CMAKE_AUTORCC ON)
set(SRCS
src/alarm_clock/vr_alarm.cpp
src/keyboard_input/input_parser.cpp
src/keyboard_input/keyboard_input.cpp
src/main.cpp
src/openvr/ivrinput.cpp
src/openvr/lh_console_util.cpp
src/openvr/openvr_init.cpp
src/openvr/ovr_application_wrapper.cpp
src/openvr/ovr_overlay_wrapper.cpp
src/openvr/ovr_settings_wrapper.cpp
src/openvr/ovr_system_wrapper.cpp
src/overlaycontroller.cpp
src/settings/settings.cpp
src/settings/settings_object.cpp
src/tabcontrollers/AudioTabController.cpp
src/tabcontrollers/ChaperoneTabController.cpp
src/tabcontrollers/FixFloorTabController.cpp
src/tabcontrollers/MoveCenterTabController.cpp
src/tabcontrollers/RotationTabController.cpp
src/tabcontrollers/SettingsTabController.cpp
src/tabcontrollers/StatisticsTabController.cpp
src/tabcontrollers/SteamVRTabController.cpp
src/tabcontrollers/UtilitiesTabController.cpp
src/tabcontrollers/VideoTabController.cpp
src/utils/ChaperoneUtils.cpp
src/utils/FrameRateUtils.cpp
src/utils/paths.cpp
src/utils/setup.cpp
src/utils/update_rate.cpp
src/render/RenderBackend.cpp
src/render/OpenGLRenderBackend.cpp
)
set(HEADERS
src/alarm_clock/vr_alarm.h
src/keyboard_input/input_parser.h
src/keyboard_input/keyboard_input.h
#TODO, check if we need this
#src/keyboard_input/keyboard_sender.h
src/openvr/ivrinput.h
src/openvr/ivrinput_action.h
src/openvr/ivrinput_input_source.h
src/openvr/ivrinput_manifest.h
src/openvr/lh_console_util.h
src/openvr/openvr_init.h
src/openvr/ovr_application_wrapper.h
src/openvr/ovr_overlay_wrapper.h
src/openvr/ovr_settings_wrapper.h
src/openvr/ovr_system_wrapper.h
src/overlaycontroller.h
src/settings/settings.h
src/settings/settings_object.h
src/settings/internal/bool_setting_value.h
src/settings/internal/double_setting_value.h
src/settings/internal/setting_value.h
src/settings/internal/settings_controller.h
src/settings/internal/settings_object_data.h
src/settings/internal/specific_setting_value.h
src/tabcontrollers/AudioTabController.h
src/tabcontrollers/ChaperoneTabController.h
src/tabcontrollers/FixFloorTabController.h
src/tabcontrollers/MoveCenterTabController.h
src/tabcontrollers/RotationTabController.h
src/tabcontrollers/SettingsTabController.h
src/tabcontrollers/StatisticsTabController.h
src/tabcontrollers/SteamVRTabController.h
src/tabcontrollers/UtilitiesTabController.h
src/tabcontrollers/VideoTabController.h
src/utils/ChaperoneUtils.h
src/utils/FrameRateUtils.h
src/utils/paths.h
src/utils/setup.h
src/utils/update_rate.h
src/utils/platform_capabilities.h
src/render/RenderBackend.h
src/render/OpenGLRenderBackend.h
src/utils/Matrix.h
src/quaternion/quaternion.h
)
set(QML_FILERC
src/res/qml/utilities_page/keyboard_utils/KeyboardGroupBox.qml
src/res/qml/utilities_page/media_keys/MediaControllerKeys.qml
src/res/qml/utilities_page/media_keys/MediaButton.qml
src/res/qml/utilities_page/misc/MiscBox.qml
src/res/qml/utilities_page/alarm_clock/AlarmGroupBox.qml
src/res/qml/utilities_page/UtilitiesPage.qml
src/res/qml/motion_page/space_drag/SpaceDragGroupBox.qml
src/res/qml/motion_page/height_toggle/HeightToggleGroupBox.qml
src/res/qml/motion_page/gravity/GravityGroupBox.qml
src/res/qml/motion_page/MotionPage.qml
src/res/qml/PlayspacePage.qml
src/res/qml/SettingsPage.qml
src/res/qml/ChaperoneWarningsPage.qml
src/res/qml/chaperone_page/ChaperonePage.qml
src/res/qml/chaperone_page/change_orientation/ChangeOrientationGroupBox.qml
src/res/qml/chaperone_page/chaperone_additional/ChaperoneAdditionalPage.qml
src/res/qml/chaperone_page/chaperone_additional/chaperonemisc/ChaperoneMiscGroupBox.qml
src/res/qml/chaperone_page/chaperone_additional/chaperonetype/ChaperoneTypeGroupBox.qml
src/res/qml/chaperone_page/chaperone_additional/chaperoneboundscolor/ChaperoneBoundsColorGroupBox.qml
src/res/qml/RootPage.qml
src/res/qml/steamvr_page/steamvrmisc/SteamVRMiscGroupBox.qml
src/res/qml/steamvr_page/SteamVRPage.qml
src/res/qml/steamvr_page/camera/CameraGroupBox.qml
src/res/qml/steamvr_page/steamvr_additional/SteamVRTXRXPage.qml
src/res/qml/steamvr_page/steamvrbind/SteamVRBindGroupBox.qml
src/res/qml/audio_page/device_selector/MicDeviceSelector.qml
src/res/qml/audio_page/device_selector/AudioDeviceSelector.qml
src/res/qml/audio_page/device_selector/MirrorDeviceSelector.qml
src/res/qml/audio_page/device_selector/MirrorVolumeSlider.qml
src/res/qml/audio_page/device_selector/MicVolumeSlider.qml
src/res/qml/audio_page/dialog_boxes/AudioDeleteProfileDialog.qml
src/res/qml/audio_page/dialog_boxes/AudioNewProfileDialog.qml
src/res/qml/audio_page/dialog_boxes/AudioMessageDialog.qml
src/res/qml/audio_page/proximity/ProximityToggle.qml
src/res/qml/audio_page/profiles/ProfileButtons.qml
src/res/qml/audio_page/AudioPage.qml
src/res/qml/audio_page/push_to_talk/PttButtons.qml
src/res/qml/FixFloorPage.qml
src/res/qml/common/MySlider.qml
src/res/qml/common/MyResources.qml
src/res/qml/common/MyPushButton.qml
src/res/qml/common/HourComboBox.qml
src/res/qml/common/MyText.qml
src/res/qml/common/MyToggleButton.qml
src/res/qml/common/MinuteSecondComboBox.qml
src/res/qml/common/MyComboBox.qml
src/res/qml/common/MyTextField.qml
src/res/qml/common/TimeAssembly.qml
src/res/qml/common/MyDialogOkPopup.qml
src/res/qml/common/mainwidget.qml
src/res/qml/common/LineSeparator.qml
src/res/qml/common/MyPushButton2.qml
src/res/qml/common/MyStackViewPage.qml
src/res/qml/common/MyRadioButton.qml
src/res/qml/common/FullWidthSliderBox.qml
src/res/qml/common/MyDialogOkCancelPopup.qml
src/res/qml/rotation_page/view_ratchet/ViewRatchetGroupBox.qml
src/res/qml/rotation_page/redirected/RedirectedGroupBox.qml
src/res/qml/rotation_page/redirected/AutoTurnGroupBox.qml
src/res/qml/rotation_page/snap_turn/SnapTurnGroupBox.qml
src/res/qml/rotation_page/space_turn/SpaceTurnGroupBox.qml
src/res/qml/rotation_page/RotationPage.qml
src/res/qml/video_page/brightness/BrightnessGroupBox.qml
src/res/qml/video_page/profiles/VideoProfileGroupBox.qml
src/res/qml/video_page/overlaycolorbright/OverlayMethodGroupBox.qml
src/res/qml/video_page/miscvid/VideoMiscGroupBox.qml
src/res/qml/video_page/supersampling/SuperSamplingGroupBox.qml
src/res/qml/video_page/color/ColorGroupBox.qml
src/res/qml/video_page/VideoPage.qml
src/res/qml/video_page/dialogboxes/VideoNewProfileDialog.qml
src/res/qml/video_page/dialogboxes/VideoMessageDialog.qml
src/res/qml/video_page/dialogboxes/VideoDeleteProfileDialog.qml
src/res/qml/StatisticsPage.qml
)
set(ResourcesRC
src/res/img/alarm/alarm_activated.png
src/res/img/audio/media_keys/outline_play_pause_white_24dp.svg
src/res/img/audio/media_keys/outline_skip_next_white_24dp.svg
src/res/img/audio/media_keys/outline_skip_previous_white_24dp.svg
src/res/img/audio/media_keys/outline_stop_white_24dp.svg
src/res/img/audio/microphone/mic_off.svg
src/res/img/audio/microphone/mic_on.svg
src/res/img/audio/microphone/ptm_notification.png
src/res/img/audio/microphone/ptt_notification.png
src/res/img/audio/microphone/ptt_notification.svg
src/res/img/audio/speaker/speaker_off.svg
src/res/img/audio/speaker/speaker_on.svg
src/res/img/battery/battery_0.png
src/res/img/battery/battery_1.png
src/res/img/battery/battery_2.png
src/res/img/battery/battery_3.png
src/res/img/battery/battery_4.png
src/res/img/battery/battery_5.png
src/res/img/chaperone/centermark.png
src/res/img/chaperone/centermarkl1.png
src/res/img/chaperone/centermarkl2.png
src/res/img/chaperone/centermarkl3.png
src/res/img/chaperone/centermarkr1.png
src/res/img/chaperone/centermarkr2.png
src/res/img/chaperone/centermarkr3.png
src/res/img/common/backarrow.svg
src/res/img/common/check.svg
src/res/img/icons/advicon256px.ico
src/res/img/icons/thumbicon.png
src/res/img/main_menu_icons/audio_tab_icon.svg
src/res/img/main_menu_icons/bindings_tab_icon.svg
src/res/img/main_menu_icons/chaperone_tab_icon.svg
src/res/img/main_menu_icons/motion_tab_icon.svg
src/res/img/main_menu_icons/offsets_tab_icon.svg
src/res/img/main_menu_icons/rotation_tab_icon.svg
src/res/img/main_menu_icons/settings_tab_icon.svg
src/res/img/main_menu_icons/space_fix_tab_icon.svg
src/res/img/main_menu_icons/statistics_tab_icon.svg
src/res/img/main_menu_icons/steamvr_tab_icon.svg
src/res/img/main_menu_icons/utilities_tab_icon.svg
src/res/img/main_menu_icons/video_tab_icon.svg
src/res/img/rotation/autoturn.png
src/res/img/rotation/noautoturn.png
src/res/img/rotation/noautoturn.svg
src/res/img/video/color.png
src/res/img/video/dimmer.png
src/res/sounds/alarm01.wav
src/res/sounds/click.wav
src/res/sounds/focus.wav
)
#set_source_files_properties(src/res/qml/common/MyResources.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
#qt_add_executable(AdvancedSettings WIN32 MACOSX_BUNDLE ${SRCS} ${HEADERS})
qt_add_executable(AdvancedSettings
${SRCS}
${HEADERS}
)
#src/res/resources.qrc
# Compile QML to bytecode only, not to C++. C++ generated from QML calls Qt's
# private API (QQmlPrivate::AOTCompiledContext), which only matches the exact
# Qt version it was built with. Bytecode keeps the binary on public API, so it
# runs on another Qt 6 patch release, e.g. the Steam Frame's system Qt.
# Must be set before qt_add_qml_module() reads it.
set_target_properties(AdvancedSettings PROPERTIES
QT_QMLCACHEGEN_ARGUMENTS --only-bytecode
)
qt_add_qml_module(AdvancedSettings
URI AdvancedSettings
QML_FILES
${QML_FILERC}
RESOURCES
${ResourcesRC}
VERSION ${PROJECT_VERSION}
)
target_include_directories(AdvancedSettings PUBLIC
third-party/nlohmann
third-party/openvr/headers
${Vulkan_INCLUDE_DIR}
)
# Avoid collision between executable name and QML module directory (QTBUG-98568).
if(NOT WIN32 AND NOT APPLE)
set_property(TARGET AdvancedSettings PROPERTY OUTPUT_NAME AdvancedSettings_app)
# libopenvr_api.so is copied next to the executable (see "openvrApiCopy");
# $ORIGIN makes the loader look there, in the build tree and in packages.
# BUILD_WITH_INSTALL_RPATH: only $ORIGIN, without the build machine's
# link directories (the build tree is what gets packaged).
set_target_properties(AdvancedSettings PROPERTIES
BUILD_WITH_INSTALL_RPATH ON
INSTALL_RPATH "$ORIGIN"
)
endif()
target_compile_definitions(AdvancedSettings PRIVATE
APPLICATION_VERSION=\"${OVRAS_VERSION_STRING}\"
)
# Vulkan render backend (src/render/VulkanRenderBackend.*), selected at run
# time with --graphics-api vulkan. Needs a Qt built with Vulkan support; the
# Vulkan headers come from find_package(Vulkan) above. Nothing links against
# libvulkan: Qt loads it at run time.
if (QT_FEATURE_vulkan)
target_sources(AdvancedSettings PRIVATE
src/render/VulkanRenderBackend.cpp
src/render/VulkanRenderBackend.h
)
target_compile_definitions(AdvancedSettings PRIVATE OVRAS_HAS_VULKAN=1)
else ()
target_compile_definitions(AdvancedSettings PRIVATE OVRAS_HAS_VULKAN=0)
endif ()
message(STATUS "Vulkan render backend: ${QT_FEATURE_vulkan}")
# Build for the Steam Frame (SteamOS, aarch64). Switches off what doesn't work
# there and changes some defaults; the details are named capability constants
# in src/utils/platform_capabilities.h, the only place reading this definition.
option(OVRAS_STEAM_FRAME "Build for the Steam Frame" OFF)
if (OVRAS_STEAM_FRAME)
target_compile_definitions(AdvancedSettings PRIVATE OVRAS_STEAM_FRAME=1)
endif ()
#find_library(OPENVR_LIBRARIES
# NAMES
# openvr_api
# PATHS
# ${CMAKE_CURRENT_SOURCE_DIR}/third-party/openvr/bin
# ${CMAKE_CURRENT_SOURCE_DIR}/third-party/openvr/lib
# PATH_SUFFIXES
# linux64
# win64
# NO_DEFAULT_PATH
# NO_CMAKE_FIND_ROOT_PATH)
# Directory of the OpenVR library for the target platform. Linux distinguishes
# x86_64 (linux64) from aarch64 (linuxarm64, e.g. the Steam Frame).
if (WIN32)
set(OPENVR_LIB_DIR ${CMAKE_SOURCE_DIR}/third-party/openvr/lib/win64)
elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|ARM64)$")
# bin/, not lib/: the SDK's lib/linuxarm64/libopenvr_api.so looks for the
# VR path registry in the fixed path /data/work/openvrpaths.vrpath instead
# of ~/.config/openvr, so on the Steam Frame it never finds SteamVR
# ("Installation path could not be located"). bin/linuxarm64 (SDK 2.15.6)
# uses ~/.config/openvr.
set(OPENVR_LIB_DIR ${CMAKE_SOURCE_DIR}/third-party/openvr/bin/linuxarm64)
else ()
set(OPENVR_LIB_DIR ${CMAKE_SOURCE_DIR}/third-party/openvr/lib/linux64)
endif ()
target_link_directories(AdvancedSettings PRIVATE ${OPENVR_LIB_DIR})
target_link_libraries(AdvancedSettings PRIVATE
Qt::Gui
Qt::Multimedia
Qt::Qml
Qt::Quick
Qt::WebSockets
Qt::Widgets
Qt::QuickControls2
Qt::QuickDialogs2
Qt::Svg
Qt::Qml
openvr_api
)
#${OPENVR_LIBRARIES}
#qt6_add_resources(RESOURCES src/res/resources.qrc)
#target_sources(AdvancedSettings PRIVATE ${RESOURCES})
#qt_add_qml_module(AdvancedSettings
# URI AdvancedSettings
# VERSION ${PROJECT_VERSION}
# QML_FILES ${QML_FILES}
#)
set(COPY_DEST_DIR "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>")
set_target_properties(AdvancedSettings PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${COPY_DEST_DIR}"
)
# resCopy
add_custom_command(TARGET AdvancedSettings POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_SOURCE_DIR}/src/res" "${COPY_DEST_DIR}/res"
)
# readmeCopy
add_custom_command(TARGET AdvancedSettings POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_SOURCE_DIR}/Readme.md" "${COPY_DEST_DIR}/Readme.md"
)
# licenseCopy
add_custom_command(TARGET AdvancedSettings POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/LICENSE" "${COPY_DEST_DIR}/LICENSE"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/third-party/openvr/LICENSE-VALVE" "${COPY_DEST_DIR}/LICENSE-VALVE"
#COMMAND ${CMAKE_COMMAND} -E copy_if_different
#"${CMAKE_SOURCE_DIR}/third-party/magic_enum/LICENSE" "${COPY_DEST_DIR}/LICENSE-MAGIC-ENUM"
)
# packageFoldersCopy
add_custom_command(TARGET AdvancedSettings POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_SOURCE_DIR}/src/package_files/default_action_manifests"
"${COPY_DEST_DIR}/default_action_manifests"
)
# packageFilesCopy
add_custom_command(TARGET AdvancedSettings POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/src/package_files/action_manifest.json" "${COPY_DEST_DIR}/action_manifest.json"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/src/package_files/manifest.vrmanifest" "${COPY_DEST_DIR}/manifest.vrmanifest"
)
if (WIN32)
add_custom_command(TARGET AdvancedSettings POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/src/package_files/qt.conf" "${COPY_DEST_DIR}/qt.conf"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/src/package_files/restartvrserver.bat" "${COPY_DEST_DIR}/restartvrserver.bat"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/src/package_files/startdesktopmode.bat" "${COPY_DEST_DIR}/startdesktopmode.bat"
)
endif ()
# openvrApiCopy
if (WIN32)
add_custom_command(TARGET AdvancedSettings POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/third-party/openvr/bin/win64/openvr_api.dll" "${COPY_DEST_DIR}/openvr_api.dll"
)
else ()
add_custom_command(TARGET AdvancedSettings POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${OPENVR_LIB_DIR}/libopenvr_api.so" "${COPY_DEST_DIR}/libopenvr_api.so"
)
endif ()
# opensslCopy
if (WIN32)
add_custom_command(TARGET AdvancedSettings POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/third-party/openssl/libcrypto-1_1-x64.dll" "${COPY_DEST_DIR}/libcrypto-1_1-x64.dll"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/third-party/openssl/libssl-1_1-x64.dll" "${COPY_DEST_DIR}/libssl-1_1-x64.dll"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/third-party/openssl/LICENSE-OPENSSL" "${COPY_DEST_DIR}/LICENSE-OPENSSL"
)
endif ()
target_include_directories(AdvancedSettings PRIVATE
third-party/openvr/headers
third-party/policyconfig
)
if (WIN32)
target_sources(AdvancedSettings PRIVATE
src/keyboard_input/input_sender_win.cpp
src/media_keys/media_keys_win.cpp
src/tabcontrollers/audiomanager/AudioManagerWindows.cpp
src/tabcontrollers/audiomanager/AudioManagerWindows.h
)
target_link_libraries(AdvancedSettings PRIVATE
ole32
user32
)
if (MSVC)
target_compile_definitions(AdvancedSettings PRIVATE
_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
)
target_compile_options(AdvancedSettings PRIVATE
/GL /Gw /W4 /permissive- /w14254 /w14263 /w14265 /w14287 /w14296 /w14311
/w14545 /w14546 /w14547 /w14549 /w14555 /w14640 /w14826 /w14905 /w14906
/w14928 /wd4127 /we4289 /Zc:__cplusplus
)
if (warnings_as_errors)
target_compile_options(AdvancedSettings PRIVATE /WX)
endif ()
endif ()
elseif (UNIX)
if (NOT APPLE AND NOT noX11)
target_sources(AdvancedSettings PRIVATE
src/keyboard_input/input_sender_X11.cpp
)
find_package(X11 REQUIRED)
# input_sender_X11.cpp uses the XTest extension (XTestFakeKeyEvent,
# XTestGrabControl), which lives in its own library.
if (NOT X11_XTest_FOUND)
message(FATAL_ERROR "libXtst (X11 XTest extension) not found")
endif ()
target_link_libraries(AdvancedSettings PRIVATE ${X11_LIBRARIES} ${X11_XTest_LIB})
elseif (NOT APPLE AND noX11)
target_sources(AdvancedSettings PRIVATE
src/keyboard_input/input_sender_dummy.cpp
)
endif ()
if (NOT APPLE AND NOT noDBUS)
target_sources(AdvancedSettings PRIVATE
src/media_keys/media_keys_dbus.cpp
)
find_package(Qt6 OPTIONAL_COMPONENTS DBus)
target_link_libraries(AdvancedSettings PRIVATE Qt6::DBus)
elseif (NOT APPLE AND noDBUS)
target_sources(AdvancedSettings PRIVATE
src/media_keys/media_keys_dummy.cpp
)
endif ()
if (NOT APPLE AND NOT noPulse)
target_sources(AdvancedSettings PRIVATE
src/tabcontrollers/audiomanager/AudioManagerPulse.cpp
src/tabcontrollers/audiomanager/AudioManagerPulse.h
src/tabcontrollers/audiomanager/AudioManagerPulse_internal.h
)
target_link_libraries(AdvancedSettings PRIVATE pulse)
elseif (NOT APPLE AND noPulse)
target_sources(AdvancedSettings PRIVATE
src/tabcontrollers/audiomanager/AudioManagerDummy.cpp
src/tabcontrollers/audiomanager/AudioManagerDummy.h
)
endif ()
if (APPLE)
target_sources(AdvancedSettings PRIVATE
src/keyboard_input/input_sender_dummy.cpp
src/media_keys/media_keys_dummy.cpp
src/tabcontrollers/audiomanager/AudioManagerDummy.cpp
src/tabcontrollers/audiomanager/AudioManagerDummy.h
)
endif ()
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(AdvancedSettings PRIVATE
-Wcast-align
-Wconversion
-Wdouble-promotion
-Wduplicated-branches
-Wduplicated-cond
-Wextra
-Wformat=2
-Wlogical-op
-Wno-sign-conversion
-Wnon-virtual-dtor
-Wnull-dereference
-Wold-style-cast
-Woverloaded-virtual
-Wpedantic
-Wrestrict
-Wshadow
-Wunused
-Wzero-as-null-pointer-constant
#-flto
)
if (warnings_as_errors)
target_compile_options(AdvancedSettings PRIVATE -Werror)
endif ()
endif ()
endif ()
if (debugSymbolsAndLogs)
target_compile_definitions(AdvancedSettings PRIVATE ENABLE_DEBUG_LOGGING)
endif ()
install(TARGETS AdvancedSettings
BUNDLE DESTINATION .
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
#qt_generate_deploy_qml_app_script(
# TARGET AdvancedSettings
# OUTPUT_SCRIPT deploy_script
#)
#install(SCRIPT ${deploy_script})
if (WIN32)
qt_generate_deploy_qml_app_script(
TARGET AdvancedSettings
OUTPUT_SCRIPT deploy_script
MACOS_BUNDLE_POST_BUILD
NO_UNSUPPORTED_PLATFORM_ERROR
DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
install(SCRIPT ${deploy_script})
message(STATUS "Deploy Script ${deploy_script}")
install(SCRIPT ${deploy_script})
# message(STATUS "Path to qml: ${CMAKE_SOURCE_DIR}/src/res/qml")
#This should be re-dundant but for some reason Visual Studio really wants this
#set(WINDEPLOYQT_BUILD_TARGET "$<$<CONFIG:Debug>:--debug>$<$<NOT:$<CONFIG:Debug>>:--release>")
#add_custom_command(TARGET AdvancedSettings POST_BUILD
# COMMAND "${Qt6_DIR}/../../../bin/windeployqt6.exe"
# --dir "${COPY_DEST_DIR}/qtdata"
# --libdir "${COPY_DEST_DIR}"
# --plugindir "${COPY_DEST_DIR}/qtdata/plugins"
# --no-system-d3d-compiler
# --no-opengl-sw
# ${WINDEPLOYQT_BUILD_TARGET}
# --qmldir "${CMAKE_SOURCE_DIR}/src/res/qml"
# "${COPY_DEST_DIR}/AdvancedSettings.exe"
#)
endif ()
# Tests (off by default). test/render checks the render backends
# (docs/steamframe/DESIGN.md, "Tests"); build_scripts/steamframe/
# container-test.sh runs them with the Vulkan validation layers.
option(OVRAS_BUILD_TESTS "Build the tests" OFF)
if (OVRAS_BUILD_TESTS)
enable_testing()
add_subdirectory(test/render)
endif ()
+33
View File
@@ -0,0 +1,33 @@
{
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"variables": [
{
"name": "CMAKE_INSTALL_PREFIX",
"value": "D:/Code/ovras/OpenVR-AdvancedSettings/out/build/x64-Debug/Debug",
"type": "PATH"
}
]
},
{
"name": "x64-Release",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "msvc_x64_x64" ]
}
]
}
-129
View File
@@ -1,129 +0,0 @@
QT += core gui qml quick multimedia widgets websockets
CONFIG += c++1z file_copies optimize_full
DEFINES += ELPP_THREAD_SAFE ELPP_QT_LOGGING ELPP_NO_DEFAULT_LOG_FILE
VERSION = "$$cat($$PWD/build_scripts/compile_version_string.txt)"
DEFINES += APPLICATION_VERSION=\\\"$$VERSION\\\"
# Qmake can't handle non-integers in the version string,
# so we take out anything after the dash.
SPLIT_VERSION_STRING = $$split(VERSION, -)
NUMERICAL_VERSION_NUMBER = $$member(SPLIT_VERSION_STRING, 0, 0)
VERSION = $$NUMERICAL_VERSION_NUMBER
# Keep the literal version number in the description field for debugging.
QMAKE_TARGET_DESCRIPTION = "$$cat($$PWD/build_scripts/compile_version_string.txt)"
lessThan(QT_MAJOR_VERSION, 5): error("requires Qt 5.12 or higher")
lessThan(QT_MINOR_VERSION, 12): error("requires Qt 5.12 or higher")
TARGET = AdvancedSettings
TEMPLATE = app
win32:DESTDIR = bin/win64/AdvancedSettings
unix:DESTDIR = bin/linux/AdvancedSettings
include_dir = $$PWD/build_scripts/qt/
win32-msvc {
include($$include_dir/compilers/msvc.pri)
}
win32-clang-msvc{
include($$include_dir/compilers/clang-msvc.pri)
}
*g++* {
include($$include_dir/compilers/gcc.pri)
}
#Look for anything clang that is not clang-msvc, since it does not
#allow all the same switches as regular clang.
*clang|*clang-g++|*clang-libc++ {
include($$include_dir/compilers/clang.pri)
}
include($$include_dir/sources.pri)
include($$include_dir/resources.pri)
# Copy extra files
COPIES += resCopy readmeCopy licenseCopy packageFoldersCopy openvrApiCopy packageFilesCopy
COPY_DEST_DIR = $$OUT_PWD/$$DESTDIR
resCopy.files = $$files(src/res)
resCopy.path = $$COPY_DEST_DIR
readmeCopy.files = Readme.md
readmeCopy.path = $$COPY_DEST_DIR
licenseCopy.files = LICENSE \
third-party/openvr/LICENSE-VALVE \
third-party/easylogging++/LICENSE-MIT
licenseCopy.path = $$COPY_DEST_DIR
packageFoldersCopy.files = src/package_files/default_action_manifests
packageFoldersCopy.path = $$COPY_DEST_DIR
packageFilesCopy.files = src/package_files/action_manifest.json src/package_files/manifest.vrmanifest
win32:packageFilesCopy.files += src/package_files/qt.conf src/package_files/restartvrserver.bat src/package_files/startdesktopmode.bat
packageFilesCopy.path = $$COPY_DEST_DIR
win32:openvrApiCopy.files = third-party/openvr/bin/win64/openvr_api.dll
unix:openvrApiCopy.files = third-party/openvr/lib/linux64/libopenvr_api.so
openvrApiCopy.path = $$COPY_DEST_DIR
# Deploy openssl when on win32
win32 {
COPIES += opensslCopy
opensslCopy.files = third-party/openssl/libcrypto-1_1-x64.dll
opensslCopy.files += third-party/openssl/libssl-1_1-x64.dll
opensslCopy.files += third-party/openssl/LICENSE-OPENSSL
opensslCopy.path = $$COPY_DEST_DIR
}
# Deploy resources and DLLs to exe dir on Windows
win32 {
WINDEPLOYQT_LOCATION = $$dirname(QMAKE_QMAKE)/windeployqt.exe
CONFIG( debug, debug|release ) {
WINDEPLOYQT_BUILD_TARGET += "--debug"
} else {
WINDEPLOYQT_BUILD_TARGET += "--release"
}
WINDEPLOYQT_OPTIONS = --dir $$COPY_DEST_DIR/qtdata \
--libdir $$COPY_DEST_DIR \
--plugindir $$COPY_DEST_DIR/qtdata/plugins \
--no-system-d3d-compiler \
--no-opengl-sw \
$$WINDEPLOYQT_BUILD_TARGET \
--qmldir $$PWD/src/res/qml \
$$COPY_DEST_DIR/AdvancedSettings.exe
WINDEPLOYQT_FULL_LINE = "$$WINDEPLOYQT_LOCATION $$WINDEPLOYQT_OPTIONS"
# Force windeployqt to run in cmd, because powershell has different syntax
# for running executables.
QMAKE_POST_LINK = cmd /c $$WINDEPLOYQT_FULL_LINE
}
# Add make install support
unix {
isEmpty(PREFIX){
PREFIX = /opt/OpenVR-AdvancedSettings
}
message(PREFIX value is: \'$$PREFIX\'. `make install` will install to this location.)
application.path = $$PREFIX
application.files = $$COPY_DEST_DIR
INSTALLS += application
}
debugSymbolsAndLogs {
message(Debug symbols and logging enabled.)
CONFIG += force_debug_info
DEFINES += ENABLE_DEBUG_LOGGING
}
warnings_as_errors {
message(Warnings as errors enabled.)
}
+1 -1
View File
@@ -1 +1 @@
5.8.13-release
5.8.17-release
@@ -1,16 +0,0 @@
QMAKE_CXXFLAGS += -Wextra
QMAKE_CXXFLAGS += -Wshadow
QMAKE_CXXFLAGS += -Wold-style-cast
QMAKE_CXXFLAGS += -Wnon-virtual-dtor
QMAKE_CXXFLAGS += -Wcast-align -Wunused -Woverloaded-virtual -Wformat=2
QMAKE_CXXFLAGS += -Wdouble-promotion
# Enable Link Time Optimization
QMAKE_CXXFLAGS += -flto
# Clang LTO requires the gold linker instead of ld
QMAKE_LFLAGS += -flto -fuse-ld=gold
-18
View File
@@ -1,18 +0,0 @@
include(common-msvc.pri)
#win32-clang-msvc is unable to correctly build the first time
#with warnings as errors on. There seems to be an issue with the moc_predefs.h
#file not being built correctly when /WX is on. Until the issue is fixed,
#win32-clang-msvc is built without /WX.
#clang-msvc does not enable the c++17 flag with qmake's c++1z flag.
QMAKE_CXXFLAGS += /std:c++17
include(clang-gcc-common-switches.pri)
QMAKE_CXXFLAGS += -Wmost
QMAKE_CXXFLAGS += -Weffc++ -Wmissing-variable-declarations
#clang-msvc spams warnings on -Wconversion and -Wsign-conversion
# from the moc .cpp files including Qt stuff.
-62
View File
@@ -1,62 +0,0 @@
# comments are the value of CLANG_VERSION
CLANG_VERSION = $$system("clang++ --version | grep 'clang version'")
# clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
CLANG_VERSION = $$split(CLANG_VERSION, ' ')
# CLANG_VERSION is now a list, this is shown as spaces in QMAKE
# clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
CLANG_VERSION = $$member(CLANG_VERSION, 2)
# 6.0.0-1ubuntu2
CLANG_VERSION = $$split(CLANG_VERSION, '.')
# 6 0 0-1ubuntu2
CLANG_VERSION = $$member(CLANG_VERSION, 0)
# 6
greaterThan(CLANG_VERSION, 4) {
message('clang++' version is above 4. Using regular clang++.)
QMAKE_CXX = clang++
QMAKE_LINK = clang++
}
else {
message('clang++' version is not above 4. Attempting to use highest specific version.)
system("clang++-5 --version") {
QMAKE_CXX = clang++-5
QMAKE_LINK = clang++-5
message('clang++-5' found.)
}
system("clang++-6 --version") {
QMAKE_CXX = clang++-6
QMAKE_LINK = clang++-6
message('clang++-6' found.)
}
system("clang++-7 --version") {
QMAKE_CXX = clang++-7
QMAKE_LINK = clang++-7
message('clang++-7' found.)
}
system("clang++-8 --version") {
QMAKE_CXX = clang++-8
QMAKE_LINK = clang++-8
message('clang++-8' found.)
}
}
system("$$QMAKE_CXX --version")
include(clang-gcc-common-switches.pri)
QMAKE_CXXFLAGS += -Wmost
QMAKE_CXXFLAGS += -Wpedantic
warnings_as_errors {
QMAKE_CXXFLAGS += -Werror
}
#All includes from the third-party directory will not warn.
QMAKE_CXXFLAGS += --system-header-prefix=third-party
QMAKE_CXXFLAGS += --system-header-prefix=qt
# Sign conversion warns on auto generated Qt MOC files.
QMAKE_CXXFLAGS += -Weffc++ -Wconversion -Wno-sign-conversion
QMAKE_CXXFLAGS += -Wextra-semi -Wzero-as-null-pointer-constant -Wmissing-variable-declarations
@@ -1,49 +0,0 @@
#Removing -W3 from both FLAGS is necessary, otherwise compiler will give
#D9025: overriding '/W4' with '/W3'
QMAKE_CFLAGS_WARN_ON -= -W3
QMAKE_CXXFLAGS_WARN_ON -= -W3
#C4127 is a warning in qvarlengtharray.h that can not be turned off
#on the current version of MSVC.
QMAKE_CXXFLAGS += /W4 /wd4127
# The codecvt header being used in AudioManagerWindows.cpp is deprecated by the standard,
# but no suitable replacement has been standardized yet. It is possible to use the Windows
# specific MultiByteToWideChar() and WideCharToMultiByte() from <Windows.h>
DEFINES += _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
# Enables Whole Program Optimization
QMAKE_CXXFLAGS += /Gw
# 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
QMAKE_CXXFLAGS += /w14254
# 'function': member function does not override any base class virtual member function
QMAKE_CXXFLAGS += /w14263
# 'classname': class has virtual functions, but destructor is not virtual instances of this class may not be destructed correctly
QMAKE_CXXFLAGS += /w14265
# 'operator': unsigned/negative constant mismatch
QMAKE_CXXFLAGS += /w14287
# nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside the for-loop scope
QMAKE_CXXFLAGS += /we4289
# 'operator': expression is always 'boolean_value'
QMAKE_CXXFLAGS += /w14296
# 'variable': pointer truncation from 'type1' to 'type2'
QMAKE_CXXFLAGS += /w14311
# expression before comma evaluates to a function which is missing an argument list
QMAKE_CXXFLAGS += /w14545
# function call before comma missing argument list
QMAKE_CXXFLAGS += /w14546
# 'operator': operator before comma has no effect; expected operator with side-effect
QMAKE_CXXFLAGS += /w14547
# 'operator': operator before comma has no effect; did you intend 'operator'?
QMAKE_CXXFLAGS += /w14549
# expression has no effect; expected expression with side-effect
QMAKE_CXXFLAGS += /w14555
# Enable warning on thread un-safe static member initialization
QMAKE_CXXFLAGS += /w14640
# Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior.
QMAKE_CXXFLAGS += /w14826
# wide string literal cast to 'LPSTR'
QMAKE_CXXFLAGS += /w14905
# string literal cast to 'LPWSTR'
QMAKE_CXXFLAGS += /w14906
# illegal copy-initialization; more than one user-defined conversion has been implicitly applied
QMAKE_CXXFLAGS += /w14928
-44
View File
@@ -1,44 +0,0 @@
GCC_VERSION = $$system("g++ -dumpversion")
greaterThan(GCC_VERSION, 6) {
message('g++' version is above 6. Using regular g++.)
}
else {
message('g++' version is not above 6. Attempting to use highest specific version.)
system(g++-7 --version) {
message('g++-7' found.)
QMAKE_CXX = g++-7
QMAKE_LINK = g++-7
}
system(g++-8 --version) {
message('g++-8' found.)
QMAKE_CXX = g++-8
QMAKE_LINK = g++-8
}
system(g++-9 --version) {
message('g++-9' found.)
QMAKE_CXX = g++-9
QMAKE_LINK = g++-9
}
}
system("$$QMAKE_CXX --version")
include(clang-gcc-common-switches.pri)
QMAKE_CXXFLAGS += -Wpedantic
warnings_as_errors {
QMAKE_CXXFLAGS += -Werror
}
# GCC only switches
QMAKE_CXXFLAGS += -Wduplicated-branches -Wduplicated-cond -Wlogical-op -Wrestrict -Wnull-dereference
# Sign conversion warns on auto generated Qt MOC files.
QMAKE_CXXFLAGS += -Wconversion -Wno-sign-conversion
QMAKE_CXXFLAGS += -Wzero-as-null-pointer-constant
# Qmake will append this even though higher optimization levels are used.
QMAKE_LFLAGS_RELEASE -= -Wl,-O1
QMAKE_LFLAGS += -Wl,-O3 -O3
+1 -1
View File
@@ -9,4 +9,4 @@ warnings_as_errors {
QMAKE_CXXFLAGS += /GL
# Force standards-conformance
QMAKE_CXXFLAGS += /permissive-
# QMAKE_CXXFLAGS += /permissive-
-69
View File
@@ -1,69 +0,0 @@
project_dir = $$PWD/../../
win32:LIBS += -L"$$project_dir/third-party/openvr/lib/win64" -luser32 -lole32
unix:LIBS += -L"$$project_dir/third-party/openvr/lib/linux64"
LIBS += -lopenvr_api
RESOURCES += src/res/resources.qrc
DISTFILES += \
src/res/sounds/alarm01.wav \
src/res/sounds/click.wav \
src/res/sounds/focus.wav \
src/res/img/audio/microphone/ptt_notification.png \
src/res/img/audio/microphone/ptt_notification.svg \
src/res/img/battery/* \
src/res/img/icons/* \
src/res/img/alarm/* \
src/res/img/video/* \
src/res/img/chaperone/* \
src/res/qml/qmldir \
src/res/qml/audio_page/* \
src/res/qml/ChaperonePage.qml \
src/res/qml/ChaperoneWarningsPage.qml \
src/res/qml/chaperone_page/* \
src/res/qml/chaperone_page/change_orientation/* \
src/res/qml/chaperone_page/chaperone_additional/* \
src/res/qml/chaperone_page/chaperone_additional/chaperoneboundscolor/* \
src/res/qml/chaperone_page/chaperone_additional/chaperonetype/* \
src/res/qml/chaperone_page/chaperone_additional/chaperonemisc/* \
src/res/qml/FixFloorPage.qml \
src/res/qml/PlayspacePage.qml \
src/res/qml/motion_page/* \
src/res/qml/motion_page/gravity/* \
src/res/qml/motion_page/height_toggle/* \
src/res/qml/motion_page/space_drag/* \
src/res/qml/RootPage.qml \
src/res/qml/SettingsPage.qml \
src/res/qml/StatisticsPage.qml \
src/res/qml/steamvr_page/SteamVRPage.qml \
src/res/qml/steamvr_page/steamvr_additional/* \
src/res/qml/steamvr_page/camera/* \
src/res/qml/steamvr_page/steamvrmisc/* \
src/res/qml/steamvr_page/steamvrbind/* \
src/res/qml/utilities_page/* \
src/res/qml/utilities_page/media_keys/* \
src/res/qml/utilities_page/keyboard_utils/* \
src/res/qml/utilities_page/alarm_clock/* \
src/res/qml/utilities_page/misc/* \
src/res/qml/audio_page/dialog_boxes/* \
src/res/qml/audio_page/device_selector/* \
src/res/qml/audio_page/proximity/* \
src/res/qml/audio_page/push_to_talk/* \
src/res/qml/audio_page/profiles/* \
src/res/qml/video_page/* \
src/res/qml/video_page/brightness/* \
src/res/qml/video_page/color/* \
src/res/qml/video_page/supersampling/* \
src/res/qml/video_page/miscvid/* \
src/res/qml/video_page/profiles/* \
src/res/qml/video_page/dialogboxes/* \
src/res/qml/video_page/overlaycolorbright/* \
src/res/qml/rotation_page/* \
src/res/qml/rotation_page/redirected/* \
src/res/qml/rotation_page/snap_turn/* \
src/res/qml/rotation_page/space_turn/* \
src/res/qml/rotation_page/view_ratchet/* \
src/res/qml/common/*
win32:RC_ICONS += src/res/img/icons/advicon256px.ico
-157
View File
@@ -1,157 +0,0 @@
SOURCES += src/main.cpp\
src/overlaycontroller.cpp \
src/tabcontrollers/AudioTabController.cpp \
src/tabcontrollers/ChaperoneTabController.cpp \
src/tabcontrollers/FixFloorTabController.cpp \
src/tabcontrollers/MoveCenterTabController.cpp \
src/tabcontrollers/SettingsTabController.cpp \
src/tabcontrollers/StatisticsTabController.cpp \
src/tabcontrollers/SteamVRTabController.cpp \
src/tabcontrollers/UtilitiesTabController.cpp \
src/tabcontrollers/VideoTabController.cpp \
src/tabcontrollers/RotationTabController.cpp\
src/utils/ChaperoneUtils.cpp \
src/openvr/openvr_init.cpp \
src/openvr/ivrinput.cpp \
src/openvr/ovr_settings_wrapper.cpp \
src/openvr/ovr_overlay_wrapper.cpp \
src/openvr/ovr_system_wrapper.cpp \
src/openvr/lh_console_util.cpp \
src/openvr/ovr_application_wrapper.cpp \
src/utils/setup.cpp \
src/utils/paths.cpp \
src/utils/FrameRateUtils.cpp \
src/keyboard_input/keyboard_input.cpp \
src/keyboard_input/input_parser.cpp \
src/settings/settings.cpp \
src/settings/settings_object.cpp \
src/alarm_clock/vr_alarm.cpp \
src/utils/update_rate.cpp \
HEADERS += src/overlaycontroller.h \
src/tabcontrollers/AudioTabController.h \
src/tabcontrollers/ChaperoneTabController.h \
src/tabcontrollers/FixFloorTabController.h \
src/tabcontrollers/MoveCenterTabController.h \
src/tabcontrollers/SettingsTabController.h \
src/tabcontrollers/StatisticsTabController.h \
src/tabcontrollers/SteamVRTabController.h \
src/tabcontrollers/UtilitiesTabController.h \
src/tabcontrollers/VideoTabController.h \
src/tabcontrollers/audiomanager/AudioManager.h \
src/tabcontrollers/RotationTabController.h\
src/keyboard_input/keyboard_input.h \
src/media_keys/media_keys.h \
src/utils/Matrix.h \
src/utils/ChaperoneUtils.h \
src/quaternion/quaternion.h \
src/openvr/openvr_init.h \
src/openvr/ivrinput_action.h \
src/openvr/ivrinput_manifest.h \
src/openvr/ivrinput_action_set.h \
src/openvr/ivrinput_input_source.h \
src/openvr/ivrinput.h \
src/openvr/ovr_settings_wrapper.h \
src/openvr/ovr_overlay_wrapper.h \
src/openvr/ovr_system_wrapper.h \
src/openvr/ovr_application_wrapper.h \
src/openvr/lh_console_util.h \
src/utils/setup.h \
src/utils/paths.h \
src/utils/FrameRateUtils.h \
src/keyboard_input/input_parser.h \
src/keyboard_input/input_sender.h \
src/settings/settings.h \
src/settings/internal/setting_value.h \
src/settings/internal/settings_internal.h \
src/settings/internal/settings_controller.h \
src/settings/internal/specific_setting_value.h \
src/settings/settings_object.h \
src/settings/internal/settings_object_data.h \
src/alarm_clock/vr_alarm.h \
src/settings/internal/settings_object_data.h \
src/settings/internal/settings_object_data.h \
src/utils/update_rate.h \
win32 {
SOURCES += src/tabcontrollers/audiomanager/AudioManagerWindows.cpp \
src/keyboard_input/input_sender_win.cpp \
src/media_keys/media_keys_win.cpp
HEADERS += src/tabcontrollers/audiomanager/AudioManagerWindows.h
}
unix:!macx {
!noX11 {
message(X11 features enabled.)
SOURCES += src/keyboard_input/input_sender_X11.cpp
CONFIG += x11
LIBS += -lXtst
}
else {
message(X11 features disabled.)
SOURCES += src/keyboard_input/input_sender_dummy.cpp
}
!noDBUS {
message(DBUS features enabled.)
SOURCES += src/media_keys/media_keys_dbus.cpp
QT += dbus
}
else {
message(DBUS features disabled.)
SOURCES += src/media_keys/media_keys_dummy.cpp
}
!noPulse {
message(PulseAudio features enabled.)
SOURCES += src/tabcontrollers/audiomanager/AudioManagerPulse.cpp
HEADERS += src/tabcontrollers/audiomanager/AudioManagerPulse.h \
src/tabcontrollers/audiomanager/AudioManagerPulse_internal.h
LIBS += -lpulse
}
else {
message(PulseAudio features disabled.)
SOURCES += src/tabcontrollers/audiomanager/AudioManagerDummy.cpp
HEADERS += src/tabcontrollers/audiomanager/AudioManagerDummy.h
}
}
macx {
SOURCES += src/keyboard_input/input_sender_dummy.cpp \
src/media_keys/media_keys_dummy.cpp \
src/tabcontrollers/audiomanager/AudioManagerDummy.cpp
HEADERS += src/tabcontrollers/audiomanager/AudioManagerDummy.h
}
win32-msvc {
INCLUDEPATH += third-party/openvr/headers \
third-party/easylogging++ \
third-party/policyconfig
}
win32-clang-msvc {
QMAKE_CXXFLAGS += /imsvc "$$PWD/../../third-party/openvr/headers"
QMAKE_CXXFLAGS += /imsvc "$$PWD/../../third-party/easylogging++"
QMAKE_CXXFLAGS += /imsvc "$$PWD/../../third-party/policyconfig"
}
# Anything g++ or clang
# In order to suppress warnings in third party headers
*clang|*clang-g++|*clang-libc++|*g++* {
#Force Linux to look into the current dir for the OpenVR lib
QMAKE_LFLAGS += '-Wl,-rpath,\'\$$ORIGIN\''
QMAKE_CXXFLAGS += -isystem $$[QT_INSTALL_HEADERS]
QMAKE_CXXFLAGS += -isystem $$[QT_INSTALL_HEADERS]/QtCore
QMAKE_CXXFLAGS += -isystem $$[QT_INSTALL_HEADERS]/QtGui
QMAKE_CXXFLAGS += -isystem $$PWD/../../third-party/openvr/headers
QMAKE_CXXFLAGS += -isystem $$PWD/../../third-party/easylogging++
}
# easylogging++ used to be a header only lib. Now requires easylogging++.cc
SOURCES += third-party/easylogging++/easylogging++.cc
@@ -0,0 +1,27 @@
# CMake toolchain: cross-compile for aarch64 Linux (Steam Frame) inside
# cross-arm64.Dockerfile (Debian multiarch). Used by container-build.sh.
# Adapted from the vrdashboard template.
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
# Debian multiarch: target libraries live in /usr/lib/aarch64-linux-gnu;
# headers are shared.
set(CMAKE_LIBRARY_ARCHITECTURE aarch64-linux-gnu)
set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH)
set(ENV{PKG_CONFIG_LIBDIR} /usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig)
# Qt 6 cross builds need a host Qt for its code generators (moc, rcc,
# qmlcachegen, ...).
set(QT_HOST_PATH /usr CACHE PATH "Host Qt prefix")
set(QT_HOST_PATH_CMAKE_DIR /usr/lib/x86_64-linux-gnu/cmake CACHE PATH "Host Qt CMake dir")
set(CMAKE_PREFIX_PATH /usr/lib/aarch64-linux-gnu/cmake)
set(CMAKE_OBJDUMP aarch64-linux-gnu-objdump)
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
# Builds OVRAS for the Steam Frame inside a Debian container (Qt 6.8, the
# series of the Frame's system Qt). Result: build/steamframe-<mode>/Release/,
# packaged by package-release.sh. Adapted from the vrdashboard template.
#
# build_scripts/steamframe/container-build.sh cross x86_64 host -> aarch64
# build_scripts/steamframe/container-build.sh native for the host's own
# architecture (on the Frame: native arm64)
# build_scripts/steamframe/container-build.sh <mode> shell
# interactive shell in that container
#
# Uses podman if available, else docker (override with CONTAINER_ENGINE).
set -euo pipefail
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
root="$(cd "$here/../.." && pwd)"
mode="${1:-}"
case "$mode" in
cross) dockerfile=cross-arm64.Dockerfile
toolchain="-DCMAKE_TOOLCHAIN_FILE=build_scripts/steamframe/aarch64-linux-gnu.cmake" ;;
native) dockerfile=native.Dockerfile; toolchain="" ;;
*) echo "usage: $0 cross|native [shell]" >&2; exit 2 ;;
esac
image="ovras-steamframe-${dockerfile%.Dockerfile}"
engine="${CONTAINER_ENGINE:-$(command -v podman || command -v docker)}"
build="build/steamframe-$mode"
"$engine" build -q -t "$image" -f "$here/$dockerfile" "$here" >/dev/null
# Run as the calling user, so the build tree isn't owned by root.
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
# OVRAS_STEAM_FRAME: Frame capabilities and defaults
# (src/utils/platform_capabilities.h).
"${run[@]}" "$image" bash -c "
set -e
cmake -S . -B $build -G Ninja -DCMAKE_BUILD_TYPE=Release \
-DOVRAS_STEAM_FRAME=ON $toolchain
cmake --build $build
"
echo "Built: $root/$build/Release"
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env bash
# Builds and runs the tests (OVRAS_BUILD_TESTS) in a Debian container with
# both render backends, Vulkan with the Khronos validation layers. Fails on any
# validation message. Adapted from the vrdashboard template.
#
# build_scripts/steamframe/container-test.sh GPU via /dev/dri if
# available, else software
# VK_DRIVER=lvp build_scripts/steamframe/container-test.sh
# force lavapipe (software)
set -euo pipefail
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
root="$(cd "$here/../.." && pwd)"
engine="${CONTAINER_ENGINE:-$(command -v podman || command -v docker)}"
"$engine" build -q -t ovras-steamframe-native -f "$here/native.Dockerfile" "$here" >/dev/null
"$engine" build -q -t ovras-steamframe-test -f "$here/test.Dockerfile" "$here" >/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[@]}" ovras-steamframe-test bash -c '
set -e
build=build/steamframe-test
cmake -S . -B $build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DOVRAS_BUILD_TESTS=ON >/dev/null
cmake --build $build --target tst_render
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 OVRAS_VULKAN_VALIDATION=1
ctest --test-dir $build --output-on-failure -V > $build/ctest.log 2>&1 \
|| { tail -40 $build/ctest.log; exit 1; }
grep -E "tests passed|Test +#|Totals" $build/ctest.log
# A skipped test (no GPU context) must not count as passed.
if grep -q "SKIP" $build/ctest.log; then
echo "Tests were skipped (see $build/ctest.log)"; exit 1
fi
messages=$(grep -oE "(VUID|UNASSIGNED)-[A-Za-z0-9_.-]+" $build/ctest.log | sort | uniq -c || true)
if [[ -n "$messages" ]]; then
echo "Vulkan validation messages (see $build/ctest.log):"
echo "$messages"
exit 1
fi
echo "No Vulkan validation messages."
'
@@ -0,0 +1,20 @@
# Cross-compilation environment for the Steam Frame (aarch64), from an x86_64
# host via Debian multiarch: the compiler and Qt's code generators (moc, rcc,
# qmlcachegen) run natively as amd64 builds, the target libraries are the
# arm64 builds. Debian trixie ships Qt 6.8, the series of the Frame's system
# Qt (SteamOS holo 0.4.3: 6.8.0). Adapted from the vrdashboard template.
FROM debian:trixie
ENV LANG=C.UTF-8
RUN dpkg --add-architecture arm64 \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates cmake ninja-build pkgconf file \
g++-aarch64-linux-gnu binutils-aarch64-linux-gnu \
qt6-base-dev qt6-base-dev-tools qt6-declarative-dev qt6-declarative-dev-tools \
qt6-base-dev:arm64 qt6-declarative-dev:arm64 qt6-multimedia-dev:arm64 \
qt6-websockets-dev:arm64 qt6-svg-dev:arm64 \
libgl-dev:arm64 libegl-dev:arm64 libvulkan-dev:arm64 libxkbcommon-dev:arm64 \
libx11-dev:arm64 libxt-dev:arm64 libxtst-dev:arm64 libpulse-dev:arm64 \
&& rm -rf /var/lib/apt/lists/*
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# Copies the newest package (package-release.sh) to the Steam Frame over SSH
# and runs its install.sh there (registers with SteamVR and starts it; an
# existing installation in the same place is updated). SteamVR must be running
# on the Frame. Adapted from the vrdashboard template.
#
# build_scripts/steamframe/deploy-to-frame.sh <ssh-target> [package-dir] [remote-dir]
# e.g. build_scripts/steamframe/deploy-to-frame.sh steamos@frame
#
# remote-dir defaults to "AdvancedSettings" 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/AdvancedSettings-*-linux-arm64 2>/dev/null | head -1)}"
[[ -n "$package" && -x "$package/install.sh" ]] || {
echo "No package found (run build_scripts/steamframe/package-release.sh first)" >&2; exit 1; }
# Without the version: updates go to the same place and replace the old one.
remote="${3:-AdvancedSettings}"
# Keep the log of earlier runs on the Frame.
rsync -a --delete --exclude AdvancedSettings.log "$package/" "$target:$remote/"
ssh "$target" "\$HOME/$remote/install.sh"
echo "Installed $(basename "$package") to $target:~/$remote"
@@ -0,0 +1,19 @@
# Native build environment (any architecture). On x86_64 it is the base of the
# test container (test.Dockerfile); on the Steam Frame itself (aarch64, e.g.
# with podman) it builds the same package as the cross build. Debian trixie
# ships Qt 6.8, the series of the Frame's system Qt. Adapted from the
# vrdashboard template.
FROM debian:trixie
ENV LANG=C.UTF-8
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates cmake ninja-build pkgconf file g++ \
qt6-base-dev qt6-base-dev-tools qt6-declarative-dev qt6-declarative-dev-tools \
qt6-multimedia-dev qt6-websockets-dev qt6-svg-dev \
libgl-dev libegl-dev libvulkan-dev libxkbcommon-dev \
libx11-dev libxt-dev libxtst-dev libpulse-dev \
qml6-module-qtquick qml6-module-qtqml-workerscript qml6-module-qtquick-window \
qt6-qpa-plugins \
&& rm -rf /var/lib/apt/lists/*
+101
View File
@@ -0,0 +1,101 @@
#!/usr/bin/env bash
# Builds OVRAS for the Steam Frame (aarch64) and packages it:
# dist/AdvancedSettings-<version>-linux-arm64/ (+ .tar.gz)
# The package uses SteamOS's own Qt (no Qt bundled). It contains the program
# folder as OVRAS expects it (binary, libopenvr_api.so, manifests, res/) plus
# install.sh, uninstall.sh, run.sh and README.txt. Adapted from the
# vrdashboard template.
#
# build_scripts/steamframe/package-release.sh [--no-build]
set -euo pipefail
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
root="$(cd "$here/../.." && pwd)"
build=1
for arg in "$@"; do
case "$arg" in
--no-build) build=0 ;;
*) echo "usage: $0 [--no-build]" >&2; exit 2 ;;
esac
done
# On the Frame itself build natively, elsewhere cross-compile.
if [[ "$(uname -m)" == aarch64 ]]; then mode=native; else mode=cross; fi
(( build )) && "$here/container-build.sh" "$mode"
exe=AdvancedSettings_app
bin="$root/build/steamframe-$mode/Release"
version="$(sed -n 's/^CMAKE_PROJECT_VERSION:[A-Z]*=//p' "$root/build/steamframe-$mode/CMakeCache.txt")"
[[ "$(file -b "$bin/$exe")" == *aarch64* ]] || { echo "$bin/$exe is not an aarch64 build" >&2; exit 1; }
package="AdvancedSettings-$version-linux-arm64"
out="$root/dist/$package"
rm -rf "$out" "$out.tar.gz"
mkdir -p "$out"
cp -a "$bin/." "$out/"
# The scripts start the program from the package folder: OVRAS writes its log
# (AdvancedSettings.log) to the working directory. Process names are cut to
# 15 characters, so running instances are found by command line.
cat > "$out/install.sh" <<EOF
#!/bin/sh
# Installs or updates OVR Advanced Settings: registers it with SteamVR (starts
# automatically with SteamVR from now on) and starts it now. SteamVR must be
# running. Works over SSH.
cd "\$(dirname "\$0")" || exit 1
# An update replaces the files of a running instance: stop it first.
pkill -f "/$exe( |\\\$)" 2>/dev/null && sleep 1
# OVRAS creates its QApplication before handling the manifest options, and
# Qt's default platform plugin (xcb) aborts without a display, e.g. over SSH.
# Registering draws nothing, so use the display-less "offscreen" plugin.
QT_QPA_PLATFORM=offscreen ./$exe --force-install-manifest || exit 1
# Start now. Over SSH there is no display; Qt needs one even for off-screen
# rendering, so use the Frame's (:0).
DISPLAY="\${DISPLAY:-:0}" nohup ./$exe >/dev/null 2>&1 &
echo "Installed and started."
EOF
cat > "$out/uninstall.sh" <<EOF
#!/bin/sh
# Unregisters OVR Advanced Settings from SteamVR and stops it. The folder can
# be deleted afterwards.
cd "\$(dirname "\$0")" || exit 1
pkill -f "/$exe( |\\\$)" 2>/dev/null
# No display needed for unregistering (see install.sh).
exec env QT_QPA_PLATFORM=offscreen ./$exe --force-remove-manifest
EOF
cat > "$out/run.sh" <<EOF
#!/bin/sh
# Restarts OVR Advanced Settings with the given graphics API, for testing.
# Further arguments go to the program:
# ./run.sh [opengl|vulkan] [options] (default: opengl)
# e.g. ./run.sh opengl --snapshot /tmp/ovras.png
cd "\$(dirname "\$0")" || exit 1
api="\${1:-opengl}"
[ \$# -gt 0 ] && shift
pkill -f "/$exe( |\\\$)" 2>/dev/null && sleep 1
DISPLAY="\${DISPLAY:-:0}" nohup ./$exe --graphics-api "\$api" "\$@" >/dev/null 2>&1 &
echo "Started with \$api; log: \$(pwd)/AdvancedSettings.log"
EOF
chmod +x "$out/install.sh" "$out/uninstall.sh" "$out/run.sh"
cat > "$out/README.txt" <<EOF
OVR Advanced Settings $version for the Steam Frame (SteamOS, aarch64)
Uses the system's Qt (SteamOS: Qt 6.8 or newer).
Install / update: ./install.sh (SteamVR must be running)
SteamVR remembers this folder's location: after moving it,
run ./install.sh again.
Uninstall: ./uninstall.sh
Test a graphics API: ./run.sh opengl | ./run.sh vulkan
Log: AdvancedSettings.log in the working directory (this folder
when started by the scripts)
License: GPL-3.0 (LICENSE); OpenVR: LICENSE-VALVE.
EOF
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))"
+10
View File
@@ -0,0 +1,10 @@
# Test environment on top of native.Dockerfile: Mesa Vulkan/GL drivers (incl.
# the lavapipe software rasterizer), the Khronos validation layers and Xvfb.
# See container-test.sh. Adapted from the vrdashboard template.
FROM ovras-steamframe-native
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
mesa-vulkan-drivers vulkan-validationlayers vulkan-tools libgl1-mesa-dri \
xvfb xauth \
&& rm -rf /var/lib/apt/lists/*
+5
View File
@@ -0,0 +1,5 @@
if ($null -ne $env:_SAVED_PATH) {
$env:PATH=$env:_SAVED_PATH;
}
$env:_SAVED_PATH=$env:PATH;
$env:PATH+=";@NATIVE_QT_ROOT@\bin;@NATIVE_PROJECT_SOURCE_DIR@\third-party\openvr\bin\win64";
+9
View File
@@ -0,0 +1,9 @@
export _SAVED_PATH="${_SAVED_PATH:-}"
if [ ! -z "$_SAVED_PATH" ]; then
export PATH="$_SAVED_PATH"
fi
export _SAVED_PATH="$PATH"
export PATH="$PATH:$(echo "@NATIVE_QT_ROOT@"|sed -E 's/(\w):/\/\L\1/')/bin:$(echo "@PROJECT_SOURCE_DIR@"|sed -E 's/(\w):/\/\L\1/')/third-party/openvr/bin/win64"
echo $_SAVED_PATH
echo $PATH
+2
View File
@@ -0,0 +1,2 @@
[Paths]
Plugins=@QT_ROOT@\\plugins
+8
View File
@@ -0,0 +1,8 @@
set(CPACK_PACKAGE_NAME "${PROJECT_NAME}")
set(CPACK_PACKAGE_VENDOR "OVRAS Team")
# NSIS installer config
set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/src/res/img/icons/advicon256px.ico")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
set(CPACK_NSIS_PACKAGE_NAME "${PROJECT_NAME} ${CMAKE_VERSION}")
set(CPACK_NSIS_MODIFY_PATH OFF)
+1
View File
@@ -0,0 +1 @@
build/compile_commands.json
+346
View File
@@ -0,0 +1,346 @@
# Steam Frame port: design
Design of iteration 1 (R1–R4, R6, R7 in [REQUIREMENTS.md](REQUIREMENTS.md)).
Status: **approved 2026-10-07**.
Principle: **minimal change set against `qt6`**. Existing OVRAS code is changed only where
the Frame or the "public API only" rule requires it. New parts come in as self-contained
files. Every change gets comments that explain what it does and why.
## Starting point
`qt6` (`2d4db7e`) doesn't build on Linux. Checked in a scratch copy with Qt 6.11; with the
fixes from section 1 it builds:
| Error | Place |
|---|---|
| `Qt::GuiPrivate` not found (Qt ≥ 6.10 requires it in `find_package`) | `CMakeLists.txt` |
| `d3d11.h` and `QRhi::D3D11` used on Linux too | `overlaycontroller.cpp` |
| 8 × easylogging `LOG(...)` left over | `overlaycontroller.cpp` (6), `ChaperoneTabController.cpp`, `MoveCenterTabController.cpp` |
| No link path to `libopenvr_api.so` on Linux | `CMakeLists.txt` |
| `libXtst` not linked (`XTestFakeKeyEvent`) | `CMakeLists.txt` |
Private Qt symbols in the binary built that way (`objdump -T`, `Qt_6_PRIVATE_API`):
- `QQmlPrivate::AOTCompiledContext` (2), from compiling QML to C++.
- 7 × `QRhi…`, from the rendering in `OverlayController`.
## Changes at a glance
| # | Area | Files | Kind |
|---|---|---|---|
| 1 | Build fixes, bytecode-only QML, OpenVR per architecture | `CMakeLists.txt`, 3 × `.cpp` | Change |
| 2 | Manifest for Linux; installation tries `Utility`, then `Background` | `manifest.vrmanifest`, `setup.cpp` | Change |
| 3 | Rendering through the template's backends instead of QRhi | `src/render/*` (new), `overlaycontroller.*`, `main.cpp` | New + change |
| 4 | Command-line option `--graphics-api` | `setup.*`, `main.cpp` | Change |
| 5 | Mouse y the right way up | `overlaycontroller.cpp` | Change (−4 lines) |
| 6 | Tick: the timer runs at the tick interval instead of counting 1 ms ticks | `overlaycontroller.cpp` | Change |
| 7 | Platform option `OVRAS_STEAM_FRAME` with capability constants | `CMakeLists.txt`, `src/utils/platform_capabilities.h` (new) | New |
| 8 | No play-area commit at start (R7) | `MoveCenterTabController.cpp` | Change |
| 9 | Frame build, packaging, deployment | `build_scripts/steamframe/*` (new) | New |
## 1. Build (R1)
- **The five errors in the table above:**
- `GuiPrivate` is not needed after section 3, so it is removed rather than added.
- `d3d11.h` goes along with QRhi (section 3).
- `LOG(INFO|WARNING|ERROR)` becomes `qInfo()` / `qWarning()` / `qCritical()`.
- OpenVR link path and `Xtst` are added.
- **QML as bytecode only:** `qt_add_qml_module(... )` gets the bytecode-only flag
(`QT_QMLCACHEGEN_ARGUMENTS --only-bytecode`, as the template's
`VRDASH_QML_BYTECODE_ONLY`). This removes `QQmlPrivate::AOTCompiledContext`. Code compiled
from QML to C++ only fits the exact Qt version it was built with.
- **Qt modules:**
- `Core5Compat` is removed: it is unused, and it isn't on the Frame.
- `GuiPrivate` is removed (section 3).
- `Widgets`, `QuickDialogs2`, `Svg`, `Multimedia` and `WebSockets` stay. They are present
on the Frame (SteamOS 0.4.3, system Qt 6.8.0).
- **OpenVR:** the repository's `third-party/openvr`. The library comes from `lib/linux64` or
`lib/linuxarm64` depending on `CMAKE_SYSTEM_PROCESSOR`. It is copied next to the binary,
and the RPATH is `$ORIGIN`.
- **Check:** `objdump -T AdvancedSettings_app | grep Qt_6_PRIVATE_API` may only show `QVulkan*`.
Qt's Vulkan wrappers carry that tag but are generated from the Vulkan specification.
## 2. Manifest (R2)
- **`manifest.vrmanifest`:** gets `binary_path_linux` and `binary_path_linux_arm`, both set
to `AdvancedSettings_app`.
- On Linux, `qt6` names the binary that way (QTBUG-98568).
- The Frame only reads `binary_path_linux_arm`; entries without it are skipped.
- **`manifest::handleManifests` (`setup.cpp:255`):**
1. First `VR_Init(Utility)`, as before.
2. On `VRInitError_Init_NotAvailableToUtilityApps`, `VR_Init(Background)`.
- Same behavior on every platform.
- `Background` needs a running SteamVR and doesn't start it. `Utility` stays first, so
that Windows' installer still works without SteamVR running.
- It calls `VR_Init` directly, without `initializeOpenVR`'s retry loop. That loop waits
and retries on every error, including the one we expect here.
### 2a. App key already registered by Steam (approved 2026-10-07)
**Finding (Frame test T1):** Steam writes `~/.local/share/Steam/config/steamapps.vrmanifest`
(`"source": "steam"`) with every VR app the user **owns**, installed or not (Frame: 154
entries, 25 installed). It is regenerated by Steam. Whoever owns OVRAS on Steam therefore
always has `steam.overlay.1009850` registered, as `launch_type: url`
(`steam://launch/1009850/openvroverlay`), `is_hidden`. Such an entry has no working
directory. `reinstallApplicationManifest()` treats that as an error ("Could not find working
directory of already installed application: VRApplicationError_PropertyNotSet") and stops.
**Open question:** does SteamVR accept a second manifest for the same key, and which entry
wins? `AddApplicationManifest` may return `VRApplicationError_AppKeyAlreadyExists`. If
Steam's entry wins, auto-start and the dashboard would launch the Steam (Proton) version.
**Change (`setup.cpp`, platform-neutral, also useful upstream):**
- `reinstallApplicationManifest()`: if the key is installed but has no working directory
(`VRApplicationError_PropertyNotSet`), there is no manifest file to remove. Log it with
the entry's source and don't remove anything. Then call `AddApplicationManifest` directly
(`installApplicationManifest()` skips installed keys) and fail with the error's name if it
is refused.
- New helper `logApplicationEntry()`, called at the end of `--force-install-manifest`: logs
what SteamVR now reports for the key (source, launch type, working directory, binary path,
URL, auto-launch). This answers the open question on the Frame.
- Unchanged: the path for a regular earlier installation (with working directory), and
`installApplicationManifest()` on normal starts.
**Follow-up depending on the result:** if SteamVR refuses our manifest or Steam's entry
wins, the native build needs its own app key (option 2, separate design: the key is also in
the manifest, 7 default action manifests and `SteamVRTabController.h`).
## 3. Rendering through public API (R1, R3)
**From the template** (`4a5b090`, verified on the Frame), adopted largely unchanged:
- `RenderBackend.h/.cpp`: the interface, `RenderInterop`, the factory
- `OpenGLRenderBackend.*`
- `VulkanRenderBackend.*`
They depend only on `openvr.h` and public Qt API:
- textures via `QQuickRenderTarget::fromOpenGLTexture` / `fromVulkanImage`
- Qt's device via `QSGRendererInterface::getResource`
The Vulkan backend already handles what `qt6`'s Vulkan path lacks:
- the instance/device extensions SteamVR requires
- the HMD's GPU (`GetOutputDevice`)
- the correct `VkFormat`
- the layout transition to `TRANSFER_SRC_OPTIMAL` after each frame
**Changes in OVRAS:**
- **`main.cpp`**, in this order:
1. parse the command line
2. `initializeOpenVR(Overlay)`
3. `createRenderBackend(api)` and `initialize(renderInterop)`
4. `QQuickWindow::setGraphicsApi(backend->graphicsApi())`
5. construct `OverlayController(…, backend)`
`setGraphicsApi` must come before any `QQuickWindow` exists. `OverlayController::m_window`
is a member, so the API is fixed before the controller is constructed.
- **`openvr_init`:** gets `renderInterop()`. Its three functions query
`GetVulkanInstanceExtensionsRequired`, `GetVulkanDeviceExtensionsRequired` and
`GetOutputDevice`; the code is copied from the template's `VrRuntime::renderInterop()`.
- **`OverlayController::SetWidget`:** the QRhi block (`newTexture`, `newTextureRenderTarget`,
`QVulkanInstance` creation) is replaced by:
1. `backend->configureWindow(&m_window)`
2. `m_renderControl.initialize()`
3. `backend->attachWindow(&m_window)`
4. `m_window.setRenderTarget(backend->createRenderTarget(size))`
- **`OverlayController::renderOverlay`:** the frame is wrapped in
`backend->beginFrame()` / `finishFrame()`. Submission is
`SetOverlayTexture(backend->vrTexture())` plus
`SetOverlayTextureBounds(backend->vrTextureBounds())`, while `makeCurrent()` is active (the
OpenGL submission requires it). `SetOverlayFromQRhiTexture` and the QRhi members are
removed.
- **Frame-rate cap:** the render delay timer goes from 5 to 16 ms. A frame starts at most
16 ms after a change request, which is about 60 fps.
- **Shutdown:** `exitApp()` ends with `exit()`, so destructors don't run. Before that it calls
`vr::VR_Shutdown()`.
- Why: OpenVR has to shut down while the graphics device still exists. Otherwise its client
frees objects on a dead `VkDevice`, which crashed on the Frame with Vulkan.
- Today OVRAS never calls `VR_Shutdown` in overlay mode.
## 4. Command-line option `--graphics-api` (R3)
- **Syntax:** `--graphics-api opengl|vulkan`; the default is `opengl`.
- It goes into `CommandLineOptions` and `returnCommandLineParser` (`setup.*`), next to the
existing options.
- **Unknown value, or Vulkan not compiled in:** error message, exit code `GENERAL_FAILURE`.
- **No automatic fallback.** OpenGL is the default anyway, and an explicit choice shouldn't
be changed silently (as in the template).
- **Vulkan in the build:** compiled in when the Vulkan headers and a Qt with Vulkan support
are present. The cross container has both.
## 5. Mouse y (R4)
- `getMousePositionForEvent` already flips `y` everywhere, then flips it back on Linux in
`#ifdef __linux__`. The Linux block goes, so `y` is flipped on every platform.
- Why: SteamVR delivers mouse coordinates in GL convention (y from the bottom), regardless of
platform and graphics API. This was verified with the template on the Frame for OpenGL and
Vulkan.
- The "`TODO temp mouse is negative??`" comment is replaced by this explanation.
## 6. Tick (R6)
- **Today:** `m_pumpEventsTimer` fires every 1 ms.
- With "vsync" on, it checks for a new compositor frame.
- With "Disable App Vsync", it counts 1 ms ticks up to `customTickRateMs`.
- **Change:**
- With "Disable App Vsync", the timer runs directly at `customTickRateMs`, as a
`Qt::PreciseTimer`. The 1 ms counting is dropped. Same behavior, about 1/11 of the
wake-ups at 11 ms.
- `setVsyncDisabled` / `setCustomTickRateMs` restart the timer with the new interval.
- The vsync mode stays unchanged.
- **Frame defaults** through the platform option (section 7): `vsyncDisabled = true` and
`customTickRateMs = 11`, about 90 Hz as in the template's event polling. Tick-counted
intervals in OVRAS (designed for ~90 fps) keep their meaning.
- Upstream defaults stay `false` / `20`.
- Both can still be changed in the OVRAS settings.
- **Measurement:** `pidstat -p <pid> 10 6` on the Frame, in three states: dashboard closed,
OVRAS tab visible, another tab visible.
## 7. Platform option and capability constants
- **CMake option `OVRAS_STEAM_FRAME`:** default `OFF`, `ON` in the Frame build. It sets the
compile definition `OVRAS_STEAM_FRAME=1`.
- **New `src/utils/platform_capabilities.h`:** the only place that evaluates the definition.
- Everything else asks for named constants and never for the platform.
- Each constant is commented with why it differs on the Frame.
- Iteration 1:
```cpp
namespace platform
{
// IVRChaperoneSetup::CommitWorkingCopy changes the live play area. On the
// Steam Frame (inside-out tracking) this shifts the boundary.
constexpr bool k_chaperoneCommitSupported = !OVRAS_STEAM_FRAME;
// Fixed-interval tick instead of 1 ms vsync polling (fewer wake-ups).
constexpr bool k_defaultVsyncDisabled = OVRAS_STEAM_FRAME;
constexpr int k_defaultCustomTickRateMs = OVRAS_STEAM_FRAME ? 11 : 20;
} // namespace platform
```
- The setting defaults in `settings_controller.h` use the two default constants.
## 8. No play-area commit at start (R7)
**Cause** (`MoveCenterTabController.cpp`):
- `zeroOffsets()` → `updateChaperoneResetData()` → `CommitWorkingCopy(Live)` (`:1185`).
- At init it creates an autosave profile (`:1109`), but only when
`GetCalibrationState() != 200`.
- Otherwise `m_pendingZeroOffsets` stays set, and the commit repeats on every tick.
**Change:**
- With `!platform::k_chaperoneCommitSupported`:
- `updateChaperoneResetData()` still reads the working copy, but skips
`CommitWorkingCopy` / `RevertWorkingCopy`.
- The same applies to `applyChaperoneResetData()`.
- The init autosave profile is skipped.
- Each skip is logged once.
- What the playspace features do when the user operates them is R5 (iteration 2).
## 9. Frame build, packaging, deployment (R1, R2)
New files under `build_scripts/steamframe/`, adapted from the template. They change no
existing code:
| File | Purpose |
|---|---|
| `cross-arm64.Dockerfile` | Debian trixie (Qt 6.8) with arm64 multiarch libraries, cross compiler |
| `native.Dockerfile` | The same for the host's own architecture (on the Frame: native build); base of the test container |
| `test.Dockerfile` | Mesa (incl. lavapipe), Vulkan validation layers, Xvfb |
| `aarch64-linux-gnu.cmake` | Cross-compile toolchain |
| `container-build.sh cross\|native` | Builds with `OVRAS_STEAM_FRAME=ON` → `build/steamframe-<mode>/Release/` |
| `container-test.sh` | Render tests with both APIs, Vulkan with validation layers |
| `package-release.sh` | Build output + `install.sh` / `uninstall.sh` / `run.sh` / `README.txt` → `dist/AdvancedSettings-<version>-linux-arm64(.tar.gz)` |
| `deploy-to-frame.sh` | Copies the package to the Frame over SSH (`rsync`) and runs `install.sh` there |
- **Scripts in the package:**
- `install.sh` stops a running instance, registers it (`--force-install-manifest`, auto-start)
and starts it with `DISPLAY=:0`. Over SSH there is no display, and Qt needs one.
- `run.sh [opengl|vulkan]` restarts it with a given graphics API.
- The scripts start the program in the package folder, because OVRAS writes
`AdvancedSettings.log` into the working directory.
- Packages use the **system Qt**: no Qt is bundled, because of section 3 the binary uses no
private API.
- RPATH is only `$ORIGIN` (`BUILD_WITH_INSTALL_RPATH`): the build tree gets packaged.
## 10. Layout diagnosis: font log and `--snapshot` (approved 2026-10-08)
**Why:** on the Frame the fixed 1200×800 layout overflows: the bottom quarter is cut off
(left: Statistics, Bindings, Settings; right: Microphone, Push-to-Talk), and the navigation
icons overlap their text. `QT_FONT_DPI=96`, `QT_QUICK_CONTROLS_STYLE=Basic` and the Material
"Dense" variant didn't fix it. Font metrics (Noto Sans instead of Segoe UI) are the main
suspect; measuring needs the unwarped rendered image.
**Changes (platform-neutral):**
- `OverlayController::SetWidget()`: logs once the application font (family, pt, px), what
20 pt (`MyText`) is in px, the screen's logical/physical DPI and the window's device pixel
ratio.
- Option `--snapshot <file>` (`setup.*`, `main.cpp`, `overlaycontroller.*`): while the
overlay is visible and rendered, `renderOverlay()` writes `RenderBackend::readback()` as
PNG to `<file>`, at most once per second (OVRAS only renders when the scene changes, so a
frame count could never trigger on a still page). Written to `<file>.tmp` and renamed.
`main.cpp` passes it through a setter, not a new constructor parameter, to keep the diff
small. Without the option nothing changes.
- `run.sh`: arguments after the graphics API go to the program
(`./run.sh opengl --snapshot /tmp/ovras.png`).
## 11. Quick Controls style: Fusion, no Material import (approved 2026-10-08)
**Finding (measured with `--snapshot`, DESIGN.md section 10):** the font is as intended (20 pt
= 27 px, 96 DPI, device pixel ratio 1). The controls run in the Material style: `qt6`
(`93c0851`) changed `mainwidget.qml`'s `import QtQuick.Controls` into
`import QtQuick.Controls.Material`. That import fixes Material for all controls and even
overrides `QT_QUICK_CONTROLS_STYLE`. Material's insets and padding make the navigation
buttons 65 px tall (75 px pitch), so the fixed 1200×800 root page loses about its bottom
quarter. Nothing in the file uses Material.
| Style (root page, navigation buttons) | Button | Pitch | Result |
|---|---|---|---|
| Material (`qt6` as is) | 65 px | 75 px | 9 of 12 buttons visible |
| Basic (Qt 5's "Default" renamed) | 49 px | 59 px | last button cut off (18 of 49 px) |
| Fusion (Qt 6's Linux default) | 45 px | 55 px | everything fits |
**Changes:**
- `mainwidget.qml`: `import QtQuick.Controls` again, as before `93c0851`, with a comment.
- `main.cpp`: `QQuickStyle::setStyle("Fusion")` after `QQuickWindow::setGraphicsApi()`,
before any QML is loaded. Explicit, so the look and sizes are the same on every platform
(without it Qt 6 picks Windows' own style there). Links `Qt6QuickControls2` directly
(already a dependency; present on the Frame: `/usr/lib/libQt6QuickControls2.so.6.8.0`).
- Upstream note: with Material, the layout overflows on Windows too.
## Tests
- **x86_64 locally:** the build succeeds; `objdump` check (R1).
- **Render test `test/render/tst_render`** (`OVRAS_BUILD_TESTS=ON`):
- Drives the backends and `QQuickRenderControl` in the same order as `OverlayController`.
- Checks the texture description for SteamVR (type, Vulkan format) and the content and
orientation via readback.
- One CTest entry per API.
- `container-test.sh` runs it with the Vulkan validation layers. It fails on validation
messages and on skipped tests.
- **OVRAS's keyboard parser test** (`test/keyboard_input`, qmake) is not included in CMake
in iteration 1 (not run).
- **On the Frame:** the checklist in `PROGRESS.md` per acceptance criterion R1–R4, R6, R7,
with both graphics APIs.
## Implementation order
1. Build fixes and bytecode-only QML. Linux x86_64 builds with QRhi (baseline).
2. Platform option, R7, tick.
3. Rendering via the template's backends, `--graphics-api`, mouse y, shutdown order.
`objdump` check.
4. Manifest and installation.
5. Frame build scripts, package, deployment, tests on the Frame (OpenGL, then Vulkan).
## Decisions (formerly open)
- **D1 – Windows render path: (c).** Windows stays open until the upstream evaluation. The
Windows build is broken on `steamframe` until then (2026-10-07).
- **D2 – Code style: OVRAS style**, including for the template files in `src/render/`
(2026-10-07).
- The repository's `.clang-format` dates from 2018-12-27 (one fix the day after). The whole
code base was reformatted once on 2018-12-28 (`51057c1`). The style hasn't changed since;
`qt6` uses the same `.clang-format`.
- The format check in CI is disabled (`.travis.yml`). Under clang-format 22, 12 of 86 files
in `src/` already deviate today (e.g. `overlaycontroller.cpp` with 138 places, `master`
likewise).
- **Hence: only lines we change get formatted (`git clang-format`), never whole existing
files.** Otherwise the diff would contain unrelated formatting changes. New files are
formatted completely.
+197
View File
@@ -0,0 +1,197 @@
# 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
+362
View File
@@ -0,0 +1,362 @@
# 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 <file>`, `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).
### Fix: Quick Controls style Fusion (2026-10-08)
- **Measurements on the Frame** (snapshots of the root page, OpenGL):
- Log: `Font: "Noto Sans" 9 pt = 12 px (requested "Sans Serif" 9 pt); 20 pt = 27 px;
screen "gamescope" logical DPI 96 physical DPI 63.972 device pixel ratio 1`. Fonts, DPI
and scaling are as intended.
- The PNG itself is cut off: OVRAS renders the content too large; SteamVR doesn't crop.
- Navigation buttons: as `qt6` is, 65 px (pitch 75) = Material. With
`QT_QUICK_CONTROLS_STYLE=Basic` (verified in the process environment): identical, the
import overrides it. Test build without the Material import (not committed): 45 px
(pitch 55) = Fusion, **everything fits**. Same build with Basic: 49 px (pitch 59), the
last button is cut off.
- **Fix (DESIGN.md section 11):** `mainwidget.qml` imports `QtQuick.Controls` again;
`main.cpp` sets `QQuickStyle::setStyle("Fusion")`.
- **Result:** cross build and package succeed, `Qt_6_PRIVATE_API` without `QVulkan*`: 0.
- **Frame test of the committed build (`ff4ea8f`, 2026-10-08, OpenGL):** root page fits
completely (12 navigation buttons 45 px, pitch 55, Microphone, Push-to-Talk, version
visible), same as the test build; "Chaperone Settings" fits completely too. No `QT_`
variables set, so Fusion comes from the code. Log: no new errors.
- **Not tested yet:** the other pages, Vulkan.
- **Seen, not changed:** on "Chaperone" and "Space Fix" the icon still overlaps the text
slightly (`MyPushButton`: icon as wide as the button is high, text only indented by
spaces).
### Test checklist on the Frame (step 5)
Deploy: `build_scripts/steamframe/deploy-to-frame.sh <user>@<frame>` (SteamVR running).
| # | Check | Criterion | Result |
|---|---|---|---|
| T1 | `install.sh`: registration works (log: "Manifest reinstalled", via Background) | R2 | partly: "Manifest reinstalled" after three fixes (display, OpenVR library, Steam's entry), but SteamVR keeps Steam's entry for the key (see "Finding: app key …") |
| T2 | Tab appears in the dashboard, UI correct (OpenGL) | R2, R3 | partly: tab appears; root page and "Chaperone Settings" correct after the style fix; other pages not checked |
| T3 | `./run.sh vulkan`: UI correct | R3 | open |
| T4 | Laser right way up, click, scrolling on several pages (both APIs) | R4 | partly: laser and navigation work (OpenGL, user's observation); scrolling and Vulkan not checked |
| 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).
### Wrap-up (2026-10-08)
- **First tests on the Frame (2026-10-07/08):** six fixes, each its own commit:
`537539a` manifest commands without a display, `317db68` OpenVR library
`bin/linuxarm64`, `ec82b5f` registration when Steam lists the key, `bbb3d1b` no debug
output by default, `d664f86` layout diagnosis (font log, `--snapshot`), `ff4ea8f` Fusion
style instead of Material.
- **State on the Frame:** package of `ff4ea8f` deployed, running with
`--snapshot /tmp/ovras.png` (OpenGL).
- **Open, in this order:**
1. Page-by-page layout check with `--snapshot` (SteamVR, Offsets, Motion, Rotation,
Space Fix, Audio, Video, Utilities, Statistics, Settings, Chaperone sub-pages). Known:
icon slightly overlaps the text on "Chaperone" and "Space Fix".
2. Rest of T1–T11, especially T3 (Vulkan), T5 (keyboard), T8 (quit), T9 (auto-start),
T10 (CPU), T11 (uninstall).
3. App key: Steam's entry still wins (see README "Known open points"); auto-launch is
switched on for Steam's key (`~/.config/openvr/config/vrappconfig/steam.overlay.1009850.vrappconfig`,
created by our `install.sh`). Decide on an own app key (option 2).
4. R6: three synchronous PulseAudio queries per tick for the microphone mute state.
- **Not in the repository:** the headset photo `frame-headset-2026-10-07T22-00-14-104Z.png`
in the working tree (untracked).
+190
View File
@@ -0,0 +1,190 @@
# OVRAS for the Steam Frame: start here
This fork ports **OpenVR Advanced Settings (OVRAS)** so that it runs **natively on the Steam
Frame** (SteamOS, aarch64, standalone) as a SteamVR dashboard tab. This file is the entry
point for continuing the work on any computer.
| Document | Content |
|---|---|
| [REQUIREMENTS.md](REQUIREMENTS.md) | Goal, decisions (with dates), requirements per iteration, feature classification |
| [DESIGN.md](DESIGN.md) | Design of iteration 1 (approved): what changes where, and why |
| [PROGRESS.md](PROGRESS.md) | What was done per step, test results, **test checklist for the Frame** |
| [OVRAS-FEATURES.md](OVRAS-FEATURES.md) | OVRAS's features and how they are implemented; issues found while reading |
| [`/CLAUDE.md`](../../CLAUDE.md) | Working rules and conventions (for people and for Claude Code) |
## Status (2026-10-08)
- **Iteration 1 ("native start on the Frame"): implemented; testing on the Frame has
started** (phase 4). OVRAS starts natively on the Frame, the dashboard tab appears, laser
and navigation work, the root page and "Chaperone Settings" fit (OpenGL). Five problems
found on the first deployment were fixed (details in [PROGRESS.md](PROGRESS.md)):
manifest commands without a display, wrong aarch64 OpenVR library, app key already
registered by Steam, debug log flood, layout cut off (Material style).
- Tested without a headset:
- Linux x86_64 build.
- aarch64 cross build: no private Qt API except `QVulkan*`, Qt 6.8, GLIBC ≤ 2.38.
- Render tests with OpenGL and Vulkan; Vulkan with validation layers and no messages.
- Release package (1.6 MB).
- **Next step:** continue on the Frame: page-by-page layout check with `--snapshot`, then the
rest of the test checklist T1–T11 ([PROGRESS.md](PROGRESS.md), "Wrap-up (2026-10-08)"
lists what is open), then assessment.
- **After that:** iteration 2. R5 hides the features that are native in SteamVR or don't
work on the Frame. Then the remaining features are checked one by one.
## Background in brief
- **Proton:** The Windows build of OVRAS runs on the Frame through Proton. Its laser y-axis is
mirrored there, which makes it unusable, and it is inefficient (x86 emulation + Wine).
- **Basis:** the fork is based on upstream's **`qt6` branch**, the maintainer's Qt 6.8 / CMake
port, still in progress. `master` is fully merged into `qt6`.
- **Goal: a minimal change set against `qt6`**, so changes can later go upstream as pull
requests.
- **Template:** the platform knowledge comes from the author's **vrdashboard template**,
verified on the Frame. Its baseline is commit `4a5b090` in
`git@git.matzman.net:SteamFrame/FrameAssistant.git`; its `docs/STEAM-FRAME.md` and
`CLAUDE.md` describe the Frame. From it come the render backends (`src/render/`) and the
build/package scripts (`build_scripts/steamframe/`).
- **Hard rule: only public Qt API.** The binary must run on the Frame's system Qt (6.8.0).
QRhi (private) was therefore replaced and QML is compiled to bytecode only.
## Repository, branches, remotes
| Branch | Meaning |
|---|---|
| `steamframe` | **The work branch.** Based on `qt6` (`2d4db7e`). |
| `qt6` | Mirror of upstream's `qt6` at `2d4db7e` (reference / diff base) |
| `master` | Mirror of upstream's `master` at `31df8b4` (5.8.17) |
Commits on `steamframe` (one per step; use for later pull requests):
```
aac3edf Steam Frame port: requirements and design of iteration 1
266a2f9 Fix the Linux build of the qt6 branch, compile QML to bytecode only
423ea57 Add a Steam Frame build option; no chaperone commit at start; timer-based tick
4826686 Render through public Qt API: OpenGL and Vulkan backends, --graphics-api
b22c35a Manifest for Linux; connect as Background app where Utility is refused
bde675f Steam Frame build, packaging and deployment scripts; render test
3ed686d Read the version from build_scripts/compile_version_string.txt
```
Then the handover documentation (this file, `CLAUDE.md`, `OVRAS-FEATURES.md`).
Fork-only files that don't belong in an upstream pull request: `docs/steamframe/`,
`CLAUDE.md`.
## Setting up on another computer
```sh
git clone -b steamframe git@git.matzman.net:SteamFrame/OpenVR-AdvancedSettings-frame.git
cd OpenVR-AdvancedSettings-frame
git remote add upstream https://github.com/OpenVR-Advanced-Settings/OpenVR-AdvancedSettings.git
git fetch upstream # to see whether upstream's qt6 has moved on
```
**Requirements:**
- **For the Frame build and the tests:** Docker or Podman (containers based on Debian
trixie; they bring Qt 6.8 and everything else).
- **For a local Linux build (optional):**
- CMake ≥ 3.20, Ninja, GCC/Clang (C++20).
- Qt ≥ 6.8 with Quick, QuickControls2, QuickDialogs2, Multimedia, WebSockets, Svg, DBus;
`Test` for the tests.
- Vulkan headers.
- libpulse, libX11, libXt, libXtst.
- Tested on Arch with Qt 6.11.
- **For deployment:** SSH access to the Frame, `rsync`.
## Commands
```sh
# Local build (x86_64), optionally with tests
cmake -S . -B build/x86 -G Ninja -DCMAKE_BUILD_TYPE=Debug -DOVRAS_BUILD_TESTS=ON
cmake --build build/x86
ctest --test-dir build/x86 # render tests: OpenGL and Vulkan
# -DOVRAS_STEAM_FRAME=ON: Frame capabilities/defaults (src/utils/platform_capabilities.h)
# Frame (aarch64) build in the container → build/steamframe-cross/Release/
build_scripts/steamframe/container-build.sh cross
build_scripts/steamframe/container-build.sh cross shell # shell in the container
# Tests in the container, Vulkan with validation layers (must print "No Vulkan validation messages")
build_scripts/steamframe/container-test.sh
VK_DRIVER=lvp build_scripts/steamframe/container-test.sh # software Vulkan
# Package → dist/AdvancedSettings-<version>-linux-arm64(.tar.gz)
build_scripts/steamframe/package-release.sh # builds first; --no-build packages only
# Copy to the Frame and install (SteamVR must be running there)
build_scripts/steamframe/deploy-to-frame.sh <user>@<frame>
```
**Check for private Qt API** (R1). Only `QVulkan*` may appear:
```sh
docker run --rm -v "$PWD:$PWD" -w "$PWD" ovras-steamframe-cross-arm64 \
aarch64-linux-gnu-objdump -T build/steamframe-cross/Release/AdvancedSettings_app \
| grep Qt_6_PRIVATE_API | grep -v QVulkan
```
**On the Frame** (in `~/AdvancedSettings/`):
| Command | Effect |
|---|---|
| `./install.sh` | Registers with SteamVR (auto-start) and starts |
| `./run.sh opengl\|vulkan [options]` | Restarts with that graphics API; further options go to the program |
| `./uninstall.sh` | Unregisters |
| `AdvancedSettings.log` | Log (in the working directory, i.e. this folder) |
Program options (new): `--graphics-api opengl|vulkan` (default `opengl`); `--snapshot <file>`
(diagnosis: writes the rendered overlay as PNG at most once per second while visible). Existing ones
include `--force-install-manifest` and `--force-remove-manifest`; see `--help`.
## Steam Frame facts the code relies on
Verified with the template on SteamOS holo 0.4.3; details in FrameAssistant's
`docs/STEAM-FRAME.md`.
- **Runtime:**
- The system Qt is 6.8.0, with Multimedia, WebSockets, DBus, Svg and Widgets.
- libpulse is present.
- GLIBC is 2.39.
- **Manifest and SteamVR:**
- The manifest needs `binary_path_linux_arm`; entries without it are skipped silently.
- SteamVR refuses `VRApplication_Utility`. Manifest commands therefore fall back to
`Background`, which needs a running SteamVR. (2026-10-07: with OVRAS, SteamVR accepted
`Utility`; the fallback stays.)
- Steam's generated `~/.local/share/Steam/config/steamapps.vrmanifest` lists every VR app
the user owns, installed or not, with a `steam://` URL. For a key listed there, SteamVR
keeps using Steam's entry even after our manifest is added (seen 2026-10-07).
- Use the OpenVR SDK's `bin/linuxarm64/libopenvr_api.so`; the SDK's `lib/linuxarm64` one
looks for the path registry in `/data/work` and never finds SteamVR.
- `VR_Shutdown` must run before the graphics device is destroyed (crash with Vulkan
otherwise).
- **Rendering and input:**
- OpenGL runs through zink (no native GL driver); Vulkan is about 1.9× faster per frame.
- SteamVR reports overlay mouse coordinates with y from the bottom.
- **Processes and display:**
- Over SSH there is no display: set `DISPLAY=:0` (the scripts do). Commands that draw
nothing run with `QT_QPA_PLATFORM=offscreen`.
- X screen 1920×1080 with `Xft.dpi: 144`, yet Qt reports logical DPI 96 and device pixel
ratio 1 for OVRAS; default font Noto Sans (20 pt = 27 px).
- Process names are cut to 15 characters: find instances by command line.
- An executable must not be named like a SteamVR process (e.g. `vrdashboard`).
- **Private package repositories:** the Frame's `/etc/pacman.conf` points to private Valve
repositories marked "DO_NOT_SHARE". Never put those URLs into the repository.
## Known open points and risks
- **SteamVR keyboard:** text fields may not get focus. The template had to send the offscreen
window an explicit `FocusIn`; `qt6` doesn't (T5).
- **App key taken by Steam's entry:** whoever owns OVRAS on Steam has
`steam.overlay.1009850` in Steam's `steamapps.vrmanifest`, and SteamVR keeps using that
entry (auto-start would launch the Steam version). On the author's Frame it is still
listed after removing the license from the account. Possible fix: an own app key for the
native build (option 2, DESIGN.md section 2a; needs its own design).
- **Unverified on the Frame:** QML modules Dialogs and Multimedia (Controls, Layouts and
the binary's libraries load, since OVRAS runs).
- **Remaining play-area commit:** "auto-apply chaperone" (opt-in, default off) still commits
at start-up; this belongs to R5.
- **Windows:** the Windows build is broken on `steamframe`. There is no D3D11 render path
anymore (decision D1: evaluated later, together with upstream).
- **Version:** OVRAS's version comes from `build_scripts/compile_version_string.txt`.
+77
View File
@@ -0,0 +1,77 @@
# Steam Frame port: requirements
Goal: OVRAS runs natively on the Steam Frame (SteamOS, aarch64) as a SteamVR dashboard tab.
- The work happens on the internal branch `steamframe`, based on upstream's `qt6` branch
(commit `2d4db7e`).
- Changes may later go back upstream as pull requests. That decision is made once
something runs.
- **Hence: the change set against `qt6` stays minimal.**
## Background
- **Proton:** The Windows build runs on the Frame through Proton. Its laser y-axis is
mirrored, which makes it unusable. Running x86 code under emulation plus Wine is also
inefficient.
- **State of `qt6`:** Upstream's `qt6` branch (Qt 6.8, CMake) doesn't build on Linux yet.
Its rendering uses QRhi, which is private Qt API (`Qt_6_PRIVATE_API`).
- **Template:** The author's vrdashboard template (FrameAssistant commit `4a5b090`) is
verified on the Frame. It serves as a source of knowledge and of individual,
self-contained parts: render backends, build container, packaging scripts. It does not
replace OVRAS's platform layer.
## Decisions
| Date | Decision |
|---|---|
| 2026-10-07 | Internal fork, Frame only. Windows and upstream pull requests are evaluated once something runs. |
| 2026-10-07 | Minimal change set against `qt6`, rather than rebuilding OVRAS on the template. |
| 2026-10-07 | Only public Qt API: rendering without QRhi, QML compiled to bytecode only. |
| 2026-10-07 | Manifest installation tries `Utility` first, then `Background`, on every platform. |
| 2026-10-07 | Vulkan is an additional render path, selected by a command-line option. OpenGL stays the default. |
| 2026-10-07 | Frame differences are switched by one build option. In the code they are capability constants, named by capability. |
| 2026-10-07 | OVRAS's code style everywhere, including template code that comes in. Only changed lines get formatted. |
| 2026-10-07 | Windows (render path, build) stays open until the upstream evaluation. |
| 2026-10-07 | Every code change gets comments that explain it fully, but directly and concisely. |
| 2026-10-07 | The app key stays `steam.overlay.1009850`; the Proton build gets uninstalled. |
## Iteration 1: native start on the Frame
| # | Requirement | Acceptance criterion |
|---|---|---|
| R1 | Linux-only build: cross-build for aarch64 against the Frame's system Qt 6.8, plus an x86_64 build for development. Only public Qt API. | The container build succeeds. The package contains no Qt. `objdump -T` shows no `Qt_6_PRIVATE_API` symbols except `QVulkan*`. |
| R2 | Runs natively on the Frame as a dashboard tab, with install, auto-start and uninstall. | After `deploy-to-frame.sh` the tab appears and starts by itself after a reboot. Quitting doesn't crash. |
| R3 | Rendering: OpenGL by default, Vulkan as an additional path via a command-line option. Render only on change, capped at 60 fps. | Both paths show every page correctly. With Vulkan, `container-test.sh` reports no validation messages. |
| R4 | Input: laser the right way up, scrolling, text fields via OVRAS's existing SteamVR keyboard handling. OVRAS's SteamVR Input actions work. | Every page can be operated. An OVRAS action (e.g. push-to-talk) arrives. |
| R6 | Throttled tick: a fixed 11 ms timer (as in the template) instead of 1 ms polling. | Idle CPU load is measured and documented. There is no comparison with the Proton build. |
| R7 | OVRAS doesn't change the play area at start-up. Today `MoveCenterTabController::zeroOffsets()` commits the working copy as the live chaperone (`CommitWorkingCopy(Live)` in `updateChaperoneResetData()`) and creates a chaperone autosave profile. On the Frame this shifts the play area at start. | After starting OVRAS on the Frame, the play area is where it was before. The log shows no `CommitWorkingCopy`. |
R5 (hiding features) was moved to iteration 2; the number stays reserved.
**Not in iteration 1:**
- hiding or disabling features
- checking features one by one
- replacements for the playspace features
- the template's D-pad navigation
- Windows
- upstream pull requests
## Iteration 2 (planned)
| # | Requirement | Acceptance criterion |
|---|---|---|
| R5 | Hide features per the feature classification below. Features that don't work make no `IVRChaperoneSetup` zero-pose calls. | The affected pages/groups are hidden; no chaperone-setup calls appear in the log. |
Then check features one by one and re-enable them.
## Feature classification (basis for R5)
Native in SteamVR: according to SteamVR's release notes up to 2.17 / 2.18 beta and Valve's
Steam Frame guide (researched 2026-10-07).
| Group | Features | Plan |
|---|---|---|
| Native in SteamVR | Wall height, fade distance, style, center marker, brightness, supersampling, motion smoothing, supersample filtering, audio devices and mirror, performance graph, do not disturb, pairing, tracker battery, recenter, native chaperone import; also Oculus SDK block and restart SteamVR (Windows only), multiple drivers | Hide |
| Only in OVRAS, useful on the Frame | Proximity warnings, dim when crouching, push-to-talk/-mute, mic mute when the HMD is taken off, audio and video profiles, color tint, media keys (MPRIS), alarm clock, statistics, per-game bindings | Keep; check one by one |
| Doesn't work, needs a replacement | Space drag/turn, gravity, height toggle, offsets, fix floor, chaperone profiles (geometry), all rotation features | Disable for now |
| Questionable | Keyboard input; cord untangling and rotation counter (the Frame is wireless) | Disable for now |
+22 -15
View File
@@ -1,8 +1,12 @@
#include <easylogging++.h>
#include "../tabcontrollers/UtilitiesTabController.h"
#include "vr_alarm.h"
#include "../openvr/ovr_overlay_wrapper.h"
#include "../utils/update_rate.h"
#include <thread>
#include <QtLogging>
#include <QtDebug>
#include <QImage>
namespace alarm_clock
{
@@ -20,8 +24,10 @@ VrAlarm::VrAlarm()
ovr_overlay_wrapper::hideOverlay( m_alarmOverlayHandle );
const auto filename = "/res/img/alarm/alarm_activated.png";
ovr_overlay_wrapper::setOverlayFromFile( m_alarmOverlayHandle, filename );
// Only need to set it once, no need to keep it in memory
QImage alarmIcon( ":qt/qml/AdvancedSettings/src/res/img/alarm/alarm_activated.png" );
ovr_overlay_wrapper::setOverlayFromQImage( m_alarmOverlayHandle,
alarmIcon );
ovr_overlay_wrapper::setOverlayWidthInMeters( m_alarmOverlayHandle, 0.3f );
@@ -76,13 +82,14 @@ void VrAlarm::checkAlarmStatus()
&& timeToAlarmActivation > 0;
if ( activateAlarm )
{
LOG( INFO ) << "Alarm fired at " << m_alarm.hour() << ":"
<< m_alarm.minute() << ":" << m_alarm.second() << ".";
qInfo() << "Alarm fired at " << m_alarm.hour() << ":"
<< m_alarm.minute() << ":" << m_alarm.second() << ".";
showAlarmNotification();
setAlarmEnabled( false );
std::thread t(
[]( vr::VROverlayHandle_t overlayHandle ) {
[]( vr::VROverlayHandle_t overlayHandle )
{
// This will freeze the overlay for 3 seconds
std::this_thread::sleep_for( std::chrono::seconds( 3 ) );
ovr_overlay_wrapper::hideOverlay( overlayHandle );
@@ -102,9 +109,9 @@ void VrAlarm::setAlarmTime( const int hour, const int minute, const int second )
{
if ( !QTime::isValid( hour, minute, second ) )
{
LOG( ERROR ) << "setAlarmTime passed invalid time values: '" << hour
<< "' hours, '" << minute << "' minutes, '" << second
<< "' seconds.";
qCritical() << "setAlarmTime passed invalid time values: '" << hour
<< "' hours, '" << minute << "' minutes, '" << second
<< "' seconds.";
}
setSetting( IntSetting::UTILITY_alarmHour, hour );
@@ -119,20 +126,20 @@ void VrAlarm::modAlarmTime( const int hour, const int minute, const int second )
{
if ( hour > 22 || hour < -22 )
{
LOG( ERROR ) << "modAlarmTime called with incorrect hour value: '"
<< hour << "'. Values not applied.";
qCritical() << "modAlarmTime called with incorrect hour value: '"
<< hour << "'. Values not applied.";
return;
}
if ( minute > 58 || minute < -58 )
{
LOG( ERROR ) << "modAlarmTime called with incorrect minute value: '"
<< minute << "'. Values not applied.";
qCritical() << "modAlarmTime called with incorrect minute value: '"
<< minute << "'. Values not applied.";
return;
}
if ( second > 58 || minute < -58 )
{
LOG( ERROR ) << "modAlarmTime called with incorrect second value: '"
<< second << "'. Values not applied.";
qCritical() << "modAlarmTime called with incorrect second value: '"
<< second << "'. Values not applied.";
return;
}
+6 -3
View File
@@ -1,5 +1,8 @@
#include "input_parser.h"
#include <QtLogging>
#include <QtDebug>
std::optional<Token> getModifier( const char& character ) noexcept
{
switch ( character )
@@ -50,7 +53,7 @@ std::optional<Token> getFunctionNumber( const char& character ) noexcept
return Token::KEY_F9;
default:
LOG( INFO ) << "Unknown function key number: " << character;
qInfo() << "Unknown function key number: " << character;
return std::nullopt;
}
}
@@ -80,7 +83,7 @@ std::optional<Token> getFunctionNumberExtended( const char& character ) noexcept
case '9':
return Token::KEY_F19;
default:
LOG( INFO ) << "Unknown function key number: " << character;
qInfo() << "Unknown function key number: " << character;
return std::nullopt;
}
}
@@ -210,7 +213,7 @@ std::vector<Token>
if ( !isupper( *ch ) || isspace( *ch ) )
{
LOG( INFO ) << "Unknown character found in sequence: " << *ch;
qInfo() << "Unknown character found in sequence: " << *ch;
continue;
}
// Everything below is upper case
-1
View File
@@ -3,7 +3,6 @@
#include <vector>
#include <cctype>
#include <optional>
#include <easylogging++.h>
enum class Token
{
-1
View File
@@ -1,6 +1,5 @@
#pragma once
#include "input_parser.h"
#include <easylogging++.h>
enum class KeyStatus
{
+4 -2
View File
@@ -2,6 +2,8 @@
#include <vector>
#include <Windows.h>
#include <cctype>
#include <QtLogging>
#include <QtDebug>
WORD convertToVirtualKeycode( const Token token )
{
@@ -183,13 +185,13 @@ void sendKeyboardInputRaw( std::vector<INPUT> inputs )
0,
nullptr ) )
{
LOG( ERROR )
qCritical()
<< "Error calling SendInput(): Could not get error message ("
<< errCode << ")";
}
else
{
LOG( ERROR ) << "Error calling SendInput(): " << err;
qCritical() << "Error calling SendInput(): " << err;
}
}
}
+84 -40
View File
@@ -1,6 +1,13 @@
#include "utils/setup.h"
#include "settings/settings.h"
#include "openvr/ovr_settings_wrapper.h"
#include "render/RenderBackend.h"
#include <QQuickStyle>
#include <QtLogging>
#include <QtDebug>
#include <memory>
#ifdef _WIN64
# include <windows.h>
extern "C" __declspec( dllexport ) DWORD NvOptimusEnablement = 0x00000001;
@@ -8,16 +15,13 @@ extern "C" __declspec( dllexport ) DWORD AmdPowerXpressRequestHighPerformance
= 0x00000001;
#endif
INITIALIZE_EASYLOGGINGPP
int main( int argc, char* argv[] )
{
setUpLogging();
LOG( INFO ) << "Settings File: "
<< settings::initializeAndGetSettingsPath();
qInfo() << "Settings File: " << settings::initializeAndGetSettingsPath();
LOG( INFO ) << settings::getSettingsAndValues();
qInfo() << settings::getSettingsAndValues();
QCoreApplication::setAttribute( Qt::AA_Use96Dpi );
QCoreApplication::setAttribute( Qt::AA_UseDesktopOpenGL );
@@ -30,8 +34,6 @@ int main( int argc, char* argv[] )
mainEventLoop.setApplicationVersion(
application_strings::applicationVersionString );
qInstallMessageHandler( mainQtMessageHandler );
const auto commandLineArgs
= argument::returnCommandLineParser( mainEventLoop );
@@ -44,41 +46,81 @@ int main( int argc, char* argv[] )
commandLineArgs.forceRemoveManifest );
}
// Render backend (render/RenderBackend.h). Chosen before connecting to
// OpenVR, so a wrong --graphics-api fails right away.
GraphicsApi graphicsApi = GraphicsApi::OpenGL;
if ( !parseGraphicsApi( commandLineArgs.graphicsApi, &graphicsApi ) )
{
qCritical() << "Unknown graphics API:" << commandLineArgs.graphicsApi
<< "(expected opengl or vulkan)";
return ReturnErrorCode::GENERAL_FAILURE;
}
std::unique_ptr<RenderBackend> renderBackend
= createRenderBackend( graphicsApi );
if ( !renderBackend )
{
qCritical() << "Graphics API" << commandLineArgs.graphicsApi
<< "is not compiled into this build";
return ReturnErrorCode::GENERAL_FAILURE;
}
openvr_init::initializeOpenVR(
openvr_init::OpenVrInitializationType::Overlay );
// Initialized after OpenVR, because the Vulkan backend needs SteamVR's
// device requirements (openvr_init::renderInterop()), and before
// OverlayController, because QQuickWindow::setGraphicsApi() must run
// before any QQuickWindow exists (OverlayController owns one).
QString renderBackendError;
if ( !renderBackend->initialize( openvr_init::renderInterop(),
&renderBackendError ) )
{
qCritical() << renderBackend->name()
<< "initialization failed:" << renderBackendError;
return ReturnErrorCode::GENERAL_FAILURE;
}
QQuickWindow::setGraphicsApi( renderBackend->graphicsApi() );
// The QML layout has a fixed size (1200x800) and was made for Qt 5's
// default style. Without an explicit style, Qt 6 picks one per platform
// (Linux: Fusion, Windows: Windows), so the sizes would differ by
// platform. Fusion was measured on the Steam Frame: the root page fits
// (navigation buttons 45 px; Basic, Qt 5's style renamed: 49 px, the last
// button is cut off; Material: 65 px, a quarter is cut off). Must be set
// before any QML is loaded.
QQuickStyle::setStyle( "Fusion" );
try
{
QQmlEngine qmlEngine;
advsettings::OverlayController controller( commandLineArgs.desktopMode,
commandLineArgs.forceNoSound,
qmlEngine );
constexpr auto widgetPath = "res/qml/common/mainwidget.qml";
const auto path = paths::binaryDirectoryFindFile( widgetPath );
if ( !path.has_value() )
// Just error checking to ensure QRC works properly
qDebug() << "list of qmldirs";
QDirIterator it( ":", QDirIterator::Subdirectories );
while ( it.hasNext() )
{
LOG( ERROR ) << "Unable to find file '" << widgetPath << "'.";
throw std::runtime_error(
"Unable to find critical file. See log for more information." );
qDebug() << it.next();
}
const auto url
= QUrl::fromLocalFile( QString::fromStdString( ( *path ) ) );
QQmlComponent component( &qmlEngine, url );
std::unique_ptr<advsettings::OverlayController> controller(
new advsettings::OverlayController( commandLineArgs.desktopMode,
commandLineArgs.forceNoSound,
qmlEngine,
std::move( renderBackend ) ) );
controller->setSnapshotPath( commandLineArgs.snapshotPath );
//const auto url
// = QUrl( ":/qt/qml/AdvancedSettings/src/res/qml/common/mainwidget.qml" );
QQmlComponent component( &qmlEngine, ":/qt/qml/AdvancedSettings/src/res/qml/common/mainwidget.qml" );
auto errors = component.errors();
for ( auto& e : errors )
{
LOG( ERROR ) << "QML Error: " << e.toString().toStdString()
<< std::endl;
qCritical() << "QML Error: "
<< e.toString().toStdString(); //<< std::endl;
}
auto quickObj = component.create();
controller.SetWidget( qobject_cast<QQuickItem*>( quickObj ),
application_strings::applicationDisplayName,
application_strings::applicationKey );
controller->SetWidget( qobject_cast<QQuickItem*>( quickObj ),
application_strings::applicationDisplayName,
application_strings::applicationKey );
// Attempts to install the application manifest on all "regular" starts.
if ( !commandLineArgs.forceNoManifest )
@@ -99,7 +141,7 @@ int main( int argc, char* argv[] )
}
catch ( std::exception& e )
{
LOG( ERROR ) << e.what();
qCritical() << e.what();
}
}
@@ -107,17 +149,19 @@ int main( int argc, char* argv[] )
|| settings::getSetting(
settings::BoolSetting::APPLICATION_desktopModeToggle ) )
{
auto m_pWindow = new QQuickWindow();
qobject_cast<QQuickItem*>( quickObj )
->setParentItem( m_pWindow->contentItem() );
m_pWindow->setGeometry(
0,
0,
static_cast<int>(
qobject_cast<QQuickItem*>( quickObj )->width() ),
static_cast<int>(
qobject_cast<QQuickItem*>( quickObj )->height() ) );
m_pWindow->show();
//TODO TEMP
//qDebug() << "Desktop MODE!";
//auto m_pWindow = new QQuickWindow();
//qobject_cast<QQuickItem*>( quickObj )
// ->setParentItem( m_pWindow->contentItem() );
//m_pWindow->setGeometry(
// 0,
// 0,
// static_cast<int>(
// qobject_cast<QQuickItem*>( quickObj )->width() ),
// static_cast<int>(
// qobject_cast<QQuickItem*>( quickObj )->height() ) );
//m_pWindow->show();
}
if ( commandLineArgs.resetSettings )
{
@@ -129,7 +173,7 @@ int main( int argc, char* argv[] )
}
catch ( const std::exception& e )
{
LOG( FATAL ) << e.what();
qFatal() << e.what();
return ReturnErrorCode::GENERAL_FAILURE;
}
}
-1
View File
@@ -1,5 +1,4 @@
#pragma once
#include <easylogging++.h>
#include <vector>
#include <QString>
+5 -3
View File
@@ -3,6 +3,8 @@
#include <QtCore/QDebug>
#include <QtCore/QStringList>
#include <QtDBus/QtDBus>
#include <QtLogging>
#include <QtDebug>
namespace keyboardinput
{
@@ -10,7 +12,7 @@ void callMethodForAllMediaPlayerAvailableServices( const QString& method )
{
if ( !QDBusConnection::sessionBus().isConnected() )
{
LOG( ERROR ) << "Media Keys: Unable to connect to DBUS session bus.";
qCritical() << "Media Keys: Unable to connect to DBUS session bus.";
return;
}
@@ -18,9 +20,9 @@ void callMethodForAllMediaPlayerAvailableServices( const QString& method )
= QDBusConnection::sessionBus().interface()->registeredServiceNames();
if ( !names.isValid() )
{
LOG( ERROR )
qCritical()
<< "Media Keys: Error getting DBUS registered service names:";
LOG( ERROR ) << names.error().message();
qCritical() << names.error().message();
}
foreach ( QString name, names.value() )
+4 -2
View File
@@ -1,6 +1,8 @@
#include "media_keys.h"
#include "src/keyboard_input/input_sender.h"
#include <Windows.h>
#include <QtLogging>
#include <QtDebug>
namespace keyboardinput
{
@@ -45,13 +47,13 @@ void sendKeyboardInputRaw( std::vector<INPUT> inputs )
0,
nullptr ) )
{
LOG( ERROR )
qCritical()
<< "Error calling SendInput(): Could not get error message ("
<< errCode << ")";
}
else
{
LOG( ERROR ) << "Error calling SendInput(): " << err;
qCritical() << "Error calling SendInput(): " << err;
}
}
}
+7 -6
View File
@@ -3,7 +3,8 @@
#include <openvr.h>
#include <iostream>
#include <array>
#include <easylogging++.h>
#include <QtLogging>
#include <QtDebug>
namespace input
{
@@ -30,8 +31,8 @@ vr::InputDigitalActionData_t getDigitalActionData( DigitalAction& action )
if ( error != vr::EVRInputError::VRInputError_None )
{
LOG( ERROR ) << "Error getting IVRInput Digital Action Data for handle "
<< action.name() << ". SteamVR Error: " << error;
qCritical() << "Error getting IVRInput Digital Action Data for handle "
<< action.name() << ". SteamVR Error: " << error;
}
return handleData;
@@ -60,8 +61,8 @@ vr::InputAnalogActionData_t getAnalogActionData( AnalogAction& action )
if ( error != vr::EVRInputError::VRInputError_None )
{
LOG( ERROR ) << "Error getting IVRInput Digital Action Data for handle "
<< action.name() << ". SteamVR Error: " << error;
qCritical() << "Error getting IVRInput Digital Action Data for handle "
<< action.name() << ". SteamVR Error: " << error;
}
return handleData;
@@ -454,7 +455,7 @@ void SteamIVRInput::UpdateStates()
if ( error != vr::EVRInputError::VRInputError_None )
{
LOG( ERROR )
qCritical()
<< "Error during IVRInput action state update. OpenVR Error: "
<< error;
}
+4 -3
View File
@@ -1,6 +1,7 @@
#pragma once
#include <openvr.h>
#include <easylogging++.h>
#include <QtLogging>
#include <QtDebug>
namespace input
{
@@ -30,8 +31,8 @@ public:
if ( error != vr::EVRInputError::VRInputError_None )
{
LOG( ERROR ) << "Error getting handle for '" << actionName
<< "'. OpenVR Error: " << error;
qCritical() << "Error getting handle for '" << actionName
<< "'. OpenVR Error: " << error;
}
}
/*!
+4 -3
View File
@@ -1,6 +1,7 @@
#pragma once
#include <openvr.h>
#include <easylogging++.h>
#include <QtLogging>
#include <QtDebug>
namespace input
{
@@ -16,8 +17,8 @@ public:
auto error = vr::VRInput()->GetActionSetHandle( setName, &handle );
if ( error != vr::EVRInputError::VRInputError_None )
{
LOG( ERROR ) << "Error getting handle for '" << setName
<< "'. OpenVR Error: " << error;
qCritical() << "Error getting handle for '" << setName
<< "'. OpenVR Error: " << error;
}
m_activeActionSet.ulActionSet = handle;
+4 -4
View File
@@ -1,6 +1,7 @@
#pragma once
#include <openvr.h>
#include <easylogging++.h>
#include <QtLogging>
#include <QtDebug>
namespace input
{
@@ -17,9 +18,8 @@ public:
if ( error != vr::EVRInputError::VRInputError_None )
{
LOG( ERROR ) << "Error getting input handle for '"
<< inputSourceName
<< "'. OpenVR Input Error: " << error;
qCritical() << "Error getting input handle for '" << inputSourceName
<< "'. OpenVR Input Error: " << error;
}
}
/*!
+6 -5
View File
@@ -1,6 +1,7 @@
#pragma once
#include <openvr.h>
#include <easylogging++.h>
#include <QtLogging>
#include <QtDebug>
#include "../utils/paths.h"
namespace input
@@ -19,8 +20,8 @@ public:
if ( !actionManifestPath.has_value() )
{
LOG( ERROR ) << "Could not find action manifest. Action manifests "
"not initialized.";
qCritical() << "Could not find action manifest. Action manifests "
"not initialized.";
return;
}
@@ -29,8 +30,8 @@ public:
auto error = vr::VRInput()->SetActionManifestPath( validManifestPath );
if ( error != vr::EVRInputError::VRInputError_None )
{
LOG( ERROR ) << "Error setting action manifest path: "
<< validManifestPath << ". OpenVR Error: " << error;
qCritical() << "Error setting action manifest path: "
<< validManifestPath << ". OpenVR Error: " << error;
}
}
+12 -9
View File
@@ -1,4 +1,7 @@
#include "lh_console_util.h"
#include <QtLogging>
#include <QtDebug>
#include <QRegularExpression>
namespace lh_con_util
{
@@ -6,12 +9,12 @@ bool LHCUtil::FindAll()
{
if ( !FindAllRX() )
{
LOG( ERROR ) << "Find All Recievers Failed";
qCritical() << "Find All Recievers Failed";
return false;
}
if ( !FindAllTX() )
{
LOG( ERROR ) << "Find All Transmitters Failed";
qCritical() << "Find All Transmitters Failed";
return false;
}
return true;
@@ -26,7 +29,7 @@ bool LHCUtil::FindAllRX()
QString output = QString( lhProcess->readAllStandardOutput() );
QStringList outputLines
= output.split( QRegExp( "[\r\n]" ), QString::SkipEmptyParts );
= output.split( QRegularExpression( "[\r\n]" ), Qt::SkipEmptyParts );
bool RecieverStart = false;
for ( auto OutputLine : outputLines )
@@ -44,7 +47,7 @@ bool LHCUtil::FindAllRX()
}
OutputLine.simplified().remove( " " );
OutputLine.remove( 0, 1 );
// LOG( WARNING ) << OutputLine << std::endl;
// qWarning() << OutputLine << std::endl;
RXTX_Pairs_.push_back( RXTX_Pair{ OutputLine, " ", true, false } );
}
}
@@ -53,21 +56,21 @@ bool LHCUtil::FindAllRX()
bool LHCUtil::FindConnectedTX( QString RXSerial )
{
LOG( WARNING ) << RXSerial;
qWarning() << RXSerial;
QProcess* lhProcess = new QProcess();
lhProcess->start( path_, QStringList() << "/serial" << RXSerial << "exit" );
lhProcess->waitForFinished();
QString output = QString( lhProcess->readAllStandardOutput() );
QStringList outputLines
= output.split( QRegExp( "[\r\n]" ), QString::SkipEmptyParts );
= output.split( QRegularExpression( "[\r\n]" ), Qt::SkipEmptyParts );
// std::vector<std::string> OutputLines
// = exec( ( "\"" + path_ + "\" /serial " + RXSerial + " exit" ).c_str()
// );
bool lhConFound = false;
for ( auto outputLine : outputLines )
{
// LOG( WARNING ) << outputLine;
// qWarning() << outputLine;
if ( outputLine.contains( "Connected to receiver" ) )
{
auto splitList = outputLine.split( ":" );
@@ -84,7 +87,7 @@ bool LHCUtil::FindConnectedTX( QString RXSerial )
rxtx.TX_Serial = TXSerial;
rxtx.Is_Paired = true;
rxtx.Is_Init = true;
LOG( WARNING )
qWarning()
<< "tx serial stored: " << TXSerial.toStdString();
return true;
}
@@ -102,7 +105,7 @@ bool LHCUtil::FindConnectedTX( QString RXSerial )
rxtx.TX_Serial = "";
rxtx.Is_Paired = false;
rxtx.Is_Init = true;
LOG( WARNING ) << "tx oops";
qWarning() << "tx oops";
return true;
}
}
-1
View File
@@ -12,7 +12,6 @@
#include <QString>
#include <QStringList>
#include <QRegularExpression>
#include <easylogging++.h>
namespace lh_con_util
{
+91 -18
View File
@@ -1,9 +1,12 @@
#include <string>
#include <thread>
#include <openvr.h>
#include <QMessageBox>
#include "openvr_init.h"
#include <easylogging++.h>
#include <QtLogging>
#include <QtDebug>
#include "../openvr/ivrinput_manifest.h"
#include <QByteArray>
namespace openvr_init
{
@@ -29,17 +32,17 @@ bool initializeProperly( const OpenVrInitializationType initType )
// In particular in some setups these errors are thrown while
// nothing is wrong with their setup presumably this is some sort of
// race condition
LOG( WARNING ) << "HMD not Found During Startup";
LOG( WARNING ) << "steamvr error: "
+ std::string(
vr::VR_GetVRInitErrorAsEnglishDescription(
initError ) );
qWarning() << "HMD not Found During Startup";
qWarning() << "steamvr error: "
+ std::string(
vr::VR_GetVRInitErrorAsEnglishDescription(
initError ) );
return false;
}
LOG( ERROR ) << "Failed to initialize OpenVR: "
+ std::string(
vr::VR_GetVRInitErrorAsEnglishDescription(
initError ) );
qCritical() << "Failed to initialize OpenVR: "
+ std::string(
vr::VR_GetVRInitErrorAsEnglishDescription(
initError ) );
// Going to stop Exiting App, This may lead to crashes if OpenVR
// actually fails to start, HOWEVER based on the HMD errors we are
// probably pre-maturely killing ourselves from some sort of OpenVR race
@@ -48,7 +51,7 @@ bool initializeProperly( const OpenVrInitializationType initType )
return false;
}
LOG( INFO ) << "OpenVR initialized successfully.";
qInfo() << "OpenVR initialized successfully.";
return true;
}
@@ -65,9 +68,9 @@ void initializeOpenVR( const OpenVrInitializationType initType, int count )
// as of 5.8.1 Based on some information from valve, if ANY interface
// fails to load we should have issues. we will re-initialize a few
// times if that is the case and hope that fixes things
LOG( WARNING ) << "OpenVR version is invalid: Interface version "
<< interfaceAndVersion << " not found. attempt number: "
<< std::to_string( trynumber );
qWarning() << "OpenVR version is invalid: Interface version "
<< interfaceAndVersion << " not found. attempt number: "
<< std::to_string( trynumber );
};
// Check whether OpenVR is too outdated
@@ -121,17 +124,87 @@ void initializeOpenVR( const OpenVrInitializationType initType, int count )
}
if ( !success && count < 3 )
{
LOG( WARNING ) << "An error occured on initialization of "
"openvr/steamvr attempting again "
<< std::to_string( count + 1 ) << " of 3 Attempts";
qWarning() << "An error occured on initialization of "
"openvr/steamvr attempting again "
<< std::to_string( count + 1 ) << " of 3 Attempts";
std::this_thread::sleep_for( std::chrono::seconds( 5 ) );
// 5.8.1 Unknown if VR shutdown needs to be callled if vr init fails
initializeOpenVR( initType, ( count + 1 ) );
}
if ( count >= 3 )
{
LOG( ERROR ) << "initialization errors persist proceeding anyways";
qCritical() << "initialization errors persist proceeding anyways";
}
}
// Calls an OpenVR "Get...ExtensionsRequired" function and splits its
// space-separated result. `query( buffer, size )` returns the size needed,
// including the terminating zero.
template <typename Query>
static QByteArrayList queryExtensionList( Query query )
{
const uint32_t size = query( nullptr, 0 );
if ( size == 0 )
{
return {};
}
QByteArray buffer( static_cast<int>( size ), '\0' );
query( buffer.data(), size );
QByteArrayList result;
for ( const QByteArray& ext :
buffer.left( buffer.indexOf( '\0' ) ).split( ' ' ) )
{
if ( !ext.isEmpty() )
{
result.append( ext );
}
}
return result;
}
// Same as the template's VrRuntime::renderInterop(). The functions are called
// by the Vulkan backend while it creates its instance and device.
RenderInterop renderInterop()
{
RenderInterop interop;
interop.vulkanInstanceExtensions = []
{
vr::IVRCompositor* compositor = vr::VRCompositor();
if ( !compositor )
{
qWarning() << "IVRCompositor unavailable: cannot query required "
"Vulkan instance extensions";
return QByteArrayList();
}
return queryExtensionList(
[&]( char* buffer, uint32_t size )
{
return compositor->GetVulkanInstanceExtensionsRequired( buffer,
size );
} );
};
interop.vulkanDeviceExtensions = []( VkPhysicalDevice_T* device )
{
vr::IVRCompositor* compositor = vr::VRCompositor();
if ( !compositor )
{
return QByteArrayList();
}
return queryExtensionList(
[&]( char* buffer, uint32_t size )
{
return compositor->GetVulkanDeviceExtensionsRequired(
device, buffer, size );
} );
};
interop.vulkanOutputDevice = []( VkInstance_T* instance )
{
uint64_t device = 0;
vr::VRSystem()->GetOutputDevice(
&device, vr::TextureType_Vulkan, instance );
return reinterpret_cast<VkPhysicalDevice_T*>( device );
};
return interop;
}
} // namespace openvr_init
+7
View File
@@ -1,5 +1,7 @@
#pragma once
#include "../render/RenderBackend.h"
namespace openvr_init
{
enum class OpenVrInitializationType
@@ -10,4 +12,9 @@ enum class OpenVrInitializationType
void initializeOpenVR( const OpenVrInitializationType initType, int count = 0 );
// What SteamVR requires from the render backend's graphics device: the
// Vulkan instance/device extensions and the GPU driving the HMD (Vulkan only;
// OpenGL ignores it). Requires an initialized OpenVR connection.
RenderInterop renderInterop();
} // namespace openvr_init
+9 -8
View File
@@ -1,4 +1,6 @@
#include "ovr_application_wrapper.h"
#include <QtLogging>
#include <QtDebug>
constexpr uint32_t bufferLen = 4096;
namespace ovr_application_wrapper
{
@@ -12,9 +14,8 @@ std::string getSceneAppID()
std::string keys = key;
if ( error != vr::VRApplicationError_None )
{
LOG( WARNING ) << "Could not Get App ID by PID: "
+ std::to_string( error );
LOG( WARNING ) << "Attempting to find through Enumeration";
qWarning() << "Could not Get App ID by PID: " + std::to_string( error );
qWarning() << "Attempting to find through Enumeration";
auto appCount = vr::VRApplications()->GetApplicationCount();
const uint32_t bufferMax = vr::k_unMaxApplicationKeyLength;
char cStringOut[bufferMax];
@@ -25,17 +26,17 @@ std::string getSceneAppID()
std::string appID( cStringOut );
if ( appID.find( "steam.app" ) != std::string::npos )
{
LOG( WARNING ) << "Found App ID through Enumeration (only "
"steam apps caught): "
<< appID;
qWarning() << "Found App ID through Enumeration (only "
"steam apps caught): "
<< appID;
if ( error2 == vr::VRApplicationError_None )
{
return appID;
}
LOG( WARNING ) << "Error getting app id via Enum: " << error2;
qWarning() << "Error getting app id via Enum: " << error2;
}
}
LOG( ERROR ) << "Unable to Find App ID by PID or by Enumeration";
qCritical() << "Unable to Find App ID by PID or by Enumeration";
return "error";
}
return keys;
-1
View File
@@ -3,7 +3,6 @@
#include <openvr.h>
#include <iostream>
#include <string>
#include <easylogging++.h>
#include <utility>
#include <map>
+54 -34
View File
@@ -1,4 +1,7 @@
#include "ovr_overlay_wrapper.h"
#include <QtLogging>
#include <QtDebug>
#include <QImage>
namespace ovr_overlay_wrapper
{
@@ -11,10 +14,9 @@ OverlayError createOverlay( const std::string overlayKey,
overlayKey.c_str(), overlayName.c_str(), overlayHandle );
if ( oError != vr::VROverlayError_None )
{
LOG( ERROR ) << "Could not create overlay: " << overlayName
<< "because "
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
<< " " << customErrorMsg;
qCritical() << "Could not create overlay: " << overlayName << "because "
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
<< " " << customErrorMsg;
return OverlayError::UndefinedError;
}
return OverlayError::NoError;
@@ -30,10 +32,10 @@ OverlayError setOverlayColor( vr::VROverlayHandle_t overlayHandle,
= vr::VROverlay()->SetOverlayColor( overlayHandle, red, green, blue );
if ( oError != vr::VROverlayError_None )
{
LOG( ERROR ) << "Error setting Overlay Color For "
<< getOverlayKey( overlayHandle ) << " with error: "
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
<< " " << customErrorMsg;
qCritical() << "Error setting Overlay Color For "
<< getOverlayKey( overlayHandle ) << " with error: "
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
<< " " << customErrorMsg;
return OverlayError::UndefinedError;
}
return OverlayError::NoError;
@@ -47,10 +49,10 @@ OverlayError setOverlayAlpha( vr::VROverlayHandle_t overlayHandle,
= vr::VROverlay()->SetOverlayAlpha( overlayHandle, alpha );
if ( oError != vr::VROverlayError_None )
{
LOG( ERROR ) << "Error setting Overlay Alpha For "
<< getOverlayKey( overlayHandle ) << " with error: "
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
<< " " << customErrorMsg;
qCritical() << "Error setting Overlay Alpha For "
<< getOverlayKey( overlayHandle ) << " with error: "
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
<< " " << customErrorMsg;
return OverlayError::UndefinedError;
}
return OverlayError::NoError;
@@ -64,10 +66,10 @@ OverlayError setOverlayWidthInMeters( vr::VROverlayHandle_t overlayHandle,
overlayHandle, widthInMeters );
if ( oError != vr::VROverlayError_None )
{
LOG( ERROR ) << "Error setting overlay width For "
<< getOverlayKey( overlayHandle ) << " with error: "
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
<< " " << customErrorMsg;
qCritical() << "Error setting overlay width For "
<< getOverlayKey( overlayHandle ) << " with error: "
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
<< " " << customErrorMsg;
return OverlayError::UndefinedError;
}
return OverlayError::NoError;
@@ -81,34 +83,52 @@ OverlayError setOverlayFromFile( vr::VROverlayHandle_t overlayHandle,
const auto overlayPath = paths::binaryDirectoryFindFile( fileName );
if ( !overlayPath.has_value() )
{
LOG( ERROR ) << "File not Found: " << fileName << customErrorMsg;
qCritical() << "File not Found: " << fileName << customErrorMsg;
return OverlayError::UndefinedError;
}
vr::VROverlayError oError = vr::VROverlay()->SetOverlayFromFile(
overlayHandle, overlayPath->c_str() );
if ( oError != vr::VROverlayError_None )
{
LOG( ERROR ) << "Error setting Overlay from file For "
<< getOverlayKey( overlayHandle ) << " with error: "
<< vr::VROverlay()->GetOverlayErrorNameFromEnum(
oError )
<< " " << customErrorMsg;
qCritical() << "Error setting Overlay from file For "
<< getOverlayKey( overlayHandle ) << " with error: "
<< vr::VROverlay()->GetOverlayErrorNameFromEnum(
oError )
<< " " << customErrorMsg;
return OverlayError::UndefinedError;
}
return OverlayError::NoError;
}
}
OverlayError setOverlayFromQImage( vr::VROverlayHandle_t overlayHandle,
QImage& image,
std::string customErrorMsg )
{
image.convertTo( QImage::Format_RGBA8888 );
vr::VROverlayError oError = vr::VROverlay()->SetOverlayRaw(
overlayHandle, image.bits(), image.width(), image.height(), 4 );
if ( oError != vr::VROverlayError_None )
{
qCritical() << "Error setting Overlay from" << image << "For"
<< getOverlayKey( overlayHandle ) << "with error:"
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
<< customErrorMsg;
return OverlayError::UndefinedError;
}
return OverlayError::NoError;
}
OverlayError showOverlay( vr::VROverlayHandle_t overlayHandle,
std::string customErrorMsg )
{
vr::VROverlayError oError = vr::VROverlay()->ShowOverlay( overlayHandle );
if ( oError != vr::VROverlayError_None )
{
LOG( ERROR ) << "Error showing overlay For "
<< getOverlayKey( overlayHandle ) << " with error: "
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
<< " " << customErrorMsg;
qCritical() << "Error showing overlay For "
<< getOverlayKey( overlayHandle ) << " with error: "
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
<< " " << customErrorMsg;
return OverlayError::UndefinedError;
}
return OverlayError::NoError;
@@ -120,10 +140,10 @@ OverlayError hideOverlay( vr::VROverlayHandle_t overlayHandle,
vr::VROverlayError oError = vr::VROverlay()->HideOverlay( overlayHandle );
if ( oError != vr::VROverlayError_None )
{
LOG( ERROR ) << "Error hiding overlay For "
<< getOverlayKey( overlayHandle ) << " with error: "
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
<< " " << customErrorMsg;
qCritical() << "Error hiding overlay For "
<< getOverlayKey( overlayHandle ) << " with error: "
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
<< " " << customErrorMsg;
return OverlayError::UndefinedError;
}
return OverlayError::NoError;
@@ -139,10 +159,10 @@ OverlayError setOverlayTransformAbsolute(
overlayHandle, trackingOrigin, trackingOriginToOverlayTransform );
if ( oError != vr::VROverlayError_None )
{
LOG( ERROR ) << "Error setting Overlay Position For "
<< getOverlayKey( overlayHandle ) << " with error: "
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
<< " " << customErrorMsg;
qCritical() << "Error setting Overlay Position For "
<< getOverlayKey( overlayHandle ) << " with error: "
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
<< " " << customErrorMsg;
return OverlayError::UndefinedError;
}
return OverlayError::NoError;
+4 -1
View File
@@ -3,9 +3,9 @@
#include <openvr.h>
#include <iostream>
#include <string>
#include <easylogging++.h>
#include <utility>
#include <string>
#include <QImage>
#include "../utils/paths.h"
/* Wrapper For OpenVR's IVR settings class, allows us to do our error logging
@@ -43,6 +43,9 @@ OverlayError setOverlayWidthInMeters( vr::VROverlayHandle_t overlayHandle,
OverlayError setOverlayFromFile( vr::VROverlayHandle_t overlayHandle,
std::string fileName,
std::string customErrorMsg = "" );
OverlayError setOverlayFromQImage( vr::VROverlayHandle_t overlayHandle,
QImage& image,
std::string customErrorMsg = "" );
OverlayError showOverlay( vr::VROverlayHandle_t overlayHandle,
std::string customErrorMsg = "" );
OverlayError hideOverlay( vr::VROverlayHandle_t overlayHandle,
+5 -3
View File
@@ -1,4 +1,6 @@
#include "ovr_settings_wrapper.h"
#include <QtLogging>
#include <QtDebug>
namespace ovr_settings_wrapper
{
@@ -19,9 +21,9 @@ SettingsError handleErrors( std::string settingsKey,
{
return SettingsError::UndefinedError;
}
LOG( ERROR ) << "Could not access \"" << settingsKey << "\" setting: "
<< vr::VRSettings()->GetSettingsErrorNameFromEnum( error )
<< " " << customErrorMsg;
qCritical() << "Could not access \"" << settingsKey << "\" setting: "
<< vr::VRSettings()->GetSettingsErrorNameFromEnum( error )
<< " " << customErrorMsg;
if ( error
== vr::EVRSettingsError::VRSettingsError_UnsetSettingHasNoDefault )
{
-1
View File
@@ -3,7 +3,6 @@
#include <openvr.h>
#include <iostream>
#include <string>
#include <easylogging++.h>
#include <utility>
/* Wrapper For OpenVR's IVR settings class, allows us to do our error logging
+15 -12
View File
@@ -1,4 +1,7 @@
#include "ovr_system_wrapper.h"
#include <QtLogging>
#include <QtDebug>
#include <vector>
namespace ovr_system_wrapper
{
@@ -8,11 +11,11 @@ SystemError handleTrackedPropertyErrors( vr::TrackedDeviceProperty tdp,
{
if ( error != vr::TrackedProp_Success )
{
LOG( ERROR ) << "Could not get Property with error \""
<< vr::VRSystem()->GetPropErrorNameFromEnum( error )
<< "\" property: "
<< "todo"
<< " " << customErrorMsg;
qCritical() << "Could not get Property with error \""
<< vr::VRSystem()->GetPropErrorNameFromEnum( error )
<< "\" property: "
<< "todo"
<< " " << customErrorMsg;
if ( tdp == vr::Prop_TrackedDeviceProperty_Max )
{
// Just for Error OPPS todo
@@ -295,7 +298,7 @@ std::string getControllerName()
}
if ( !isLeftCon && !isRightCon && hasHMD )
{
LOG( WARNING ) << "WARNING: No Controllers assuming based on HMD";
qWarning() << "WARNING: No Controllers assuming based on HMD";
auto hmdName
= getStringTrackedProperty( hmdIdx, vr::Prop_ControllerType_String )
.second;
@@ -309,27 +312,27 @@ std::string getControllerName()
}
if ( hmdName == "vive_pro" )
{
LOG( WARNING )
qWarning()
<< "Vive Pro Detected, Assuming Knuckles this may be wrong";
return "knuckles";
}
if ( hmdName == "holographic_hmd" )
{
LOG( WARNING ) << "WMR detected, Assuming HP CONTROLLERS";
qWarning() << "WMR detected, Assuming HP CONTROLLERS";
return "hpmotioncontroller";
}
if ( hmdName == "rift" )
{
return "oculus_touch";
}
LOG( WARNING ) << "Headset Not recognized Assuming touch controllers "
+ hmdName;
qWarning() << "Headset Not recognized Assuming touch controllers "
+ hmdName;
return "oculus_touch";
}
if ( right != left )
{
LOG( WARNING ) << "WARNING: Left and Right Controllers are Different, "
"Prioritizing right";
qWarning() << "WARNING: Left and Right Controllers are Different, "
"Prioritizing right";
}
if ( rightIdx == 999 )
{
+1 -1
View File
@@ -3,9 +3,9 @@
#include <openvr.h>
#include <iostream>
#include <string>
#include <easylogging++.h>
#include <utility>
#include <map>
#include <vector>
/* Wrapper For OpenVR's IVR settings class, allows us to do our error logging
* while also minimizing code
+222 -176
View File
@@ -11,17 +11,27 @@
#include <QtWidgets/QGraphicsSceneMouseEvent>
#include <QtWidgets/QApplication>
#include <QtWidgets/QGraphicsEllipseItem>
#include <QQuickGraphicsConfiguration>
#include <QOpenGLExtraFunctions>
#include <QCursor>
#include <QProcess>
#include <QMessageBox>
#include <QFile>
#include <QFontInfo>
#include <QScreen>
#include <QtLogging>
#include <QtDebug>
#include <iostream>
#include <cmath>
#include <openvr.h>
#include <easylogging++.h>
#include "openvr/ovr_overlay_wrapper.h"
#include "utils/Matrix.h"
#include "keyboard_input/input_sender.h"
#include "settings/settings.h"
#include <qapplication.h>
#include <qlogging.h>
#include <qnamespace.h>
#include <stdexcept>
// application namespace
namespace advsettings
@@ -40,10 +50,13 @@ int verifyCustomTickRate( const int tickRate )
return tickRate;
}
OverlayController::OverlayController( bool desktopMode,
bool noSound,
QQmlEngine& qmlEngine )
: QObject(), m_desktopMode( desktopMode ), m_noSound( noSound ),
OverlayController::OverlayController(
bool desktopMode,
bool noSound,
QQmlEngine& qmlEngine,
std::unique_ptr<RenderBackend> renderBackend )
: QObject(), m_renderBackend( std::move( renderBackend ) ),
m_desktopMode( desktopMode ), m_noSound( noSound ),
m_verifiedCustomTickRateMs( verifyCustomTickRate( settings::getSetting(
settings::IntSetting::APPLICATION_customTickRateMs ) ) ),
m_actions(), m_alarm()
@@ -60,61 +73,23 @@ OverlayController::OverlayController( bool desktopMode,
// Throw Error If over 16k characters in path string
if ( !pathIsGood )
{
LOG( ERROR ) << "Error Finding VR Runtime Path, Attempting Recovery: ";
qCritical() << "Error Finding VR Runtime Path, Attempting Recovery: ";
uint32_t maxLengthRe = requiredLength;
LOG( INFO ) << "Open VR reporting Required path length of: "
<< maxLengthRe;
qInfo() << "Open VR reporting Required path length of: " << maxLengthRe;
}
m_runtimePathUrl = QUrl::fromLocalFile( tempRuntimePath );
LOG( INFO ) << "VR Runtime Path: " << m_runtimePathUrl.toLocalFile();
qInfo() << "VR Runtime Path: " << m_runtimePathUrl.toLocalFile();
const double initVol = soundVolume();
constexpr auto clickSoundURL = "res/sounds/click.wav";
const auto activationSoundFile
= paths::binaryDirectoryFindFile( clickSoundURL );
m_activationSoundEffect.setSource( QUrl( ":/qt/qml/AdvancedSettings/src/res/sounds/click.wav" ) );
m_activationSoundEffect.setVolume( initVol );
if ( activationSoundFile.has_value() )
{
m_activationSoundEffect.setSource( QUrl::fromLocalFile(
QString::fromStdString( ( *activationSoundFile ) ) ) );
m_activationSoundEffect.setVolume( initVol );
}
else
{
LOG( ERROR ) << "Could not find activation sound file "
<< clickSoundURL;
}
constexpr auto focusChangedSoundURL = "res/sounds/focus.wav";
const auto focusChangedSoundFile
= paths::binaryDirectoryFindFile( focusChangedSoundURL );
m_focusChangedSoundEffect.setSource( QUrl( ":/qt/qml/AdvancedSettings/src/res/sounds/focus.wav" ) );
m_focusChangedSoundEffect.setVolume( initVol );
if ( focusChangedSoundFile.has_value() )
{
m_focusChangedSoundEffect.setSource( QUrl::fromLocalFile(
QString::fromStdString( ( *focusChangedSoundFile ) ) ) );
m_focusChangedSoundEffect.setVolume( initVol );
}
else
{
LOG( ERROR ) << "Could not find focus Changed sound file "
<< focusChangedSoundURL;
}
constexpr auto alarmFileName = "res/sounds/alarm01.wav";
const auto alarm01SoundFile
= paths::binaryDirectoryFindFile( alarmFileName );
if ( alarm01SoundFile.has_value() )
{
m_alarm01SoundEffect.setSource( QUrl::fromLocalFile(
QString::fromStdString( ( *alarm01SoundFile ) ) ) );
m_alarm01SoundEffect.setVolume( 1.0 );
}
else
{
LOG( ERROR ) << "Could not find alarm01 sound file " << alarmFileName;
}
m_alarm01SoundEffect.setSource( QUrl( ":/qt/qml/AdvancedSettings/src/res/sounds/alarm01.wav" ) );
m_alarm01SoundEffect.setVolume( 1.0 );
// If we have desktop mode flag ignore waht toggle says otherwise we use
// toggle
@@ -122,28 +97,7 @@ OverlayController::OverlayController( bool desktopMode,
{
m_desktopMode = desktopModeToggle();
}
QSurfaceFormat format;
// Qt's QOpenGLPaintDevice is not compatible with OpenGL versions >= 3.0
// NVIDIA does not care, but unfortunately AMD does
// Are subtle changes to the semantics of OpenGL functions actually covered
// by the compatibility profile, and this is an AMD bug?
format.setVersion( 2, 1 );
// format.setProfile( QSurfaceFormat::CompatibilityProfile );
format.setDepthBufferSize( 16 );
format.setStencilBufferSize( 8 );
format.setSamples( 16 );
m_openGLContext.setFormat( format );
if ( !m_openGLContext.create() )
{
throw std::runtime_error( "Could not create OpenGL context" );
}
// create an offscreen surface to attach the context and FBO to
m_offscreenSurface.setFormat( m_openGLContext.format() );
m_offscreenSurface.create();
m_openGLContext.makeCurrent( &m_offscreenSurface );
m_desktopMode = false;
if ( !vr::VROverlay() )
{
@@ -344,11 +298,11 @@ OverlayController::OverlayController( bool desktopMode,
}
else
{
LOG( INFO ) << "Version Check: Feature disabled. Not checking version.";
qInfo() << "Version Check: Feature disabled. Not checking version.";
}
LOG( INFO ) << "OPENSSL VERSION: "
<< QSslSocket::sslLibraryBuildVersionString();
qInfo() << "OPENSSL VERSION: "
<< QSslSocket::sslLibraryBuildVersionString();
}
OverlayController::~OverlayController()
@@ -373,13 +327,21 @@ void OverlayController::exitApp()
Shutdown();
QApplication::exit();
LOG( INFO ) << "All systems exited.";
qInfo() << "All systems exited.";
exit( EXIT_SUCCESS );
// Does not fallthrough
}
// Stops ticking and rendering, disconnects from SteamVR and frees the render
// target. Called by exitApp() and by the destructor; runs only once.
void OverlayController::Shutdown()
{
if ( m_shutdownDone )
{
return;
}
m_shutdownDone = true;
disconnect( &m_pumpEventsTimer,
SIGNAL( timeout() ),
this,
@@ -403,7 +365,21 @@ void OverlayController::Shutdown()
m_pRenderTimer->stop();
m_pRenderTimer.reset();
}
m_pFbo.reset();
// Order: OpenVR shuts down while the graphics device still exists, then
// the render target goes. OpenVR's client holds objects on the device of
// the submitted textures; destroying the device first crashed on the
// Steam Frame with Vulkan (openvr.h: call VR_Shutdown before destroying
// these resources). OVRAS ends with exit(), so this is the only place
// where VR_Shutdown runs in overlay mode.
vr::VR_Shutdown();
if ( m_renderBackend && m_renderBackend->makeCurrent() )
{
// Frees what the backend created on Qt's device/context, before Qt
// destroys it together with m_renderControl.
m_renderBackend->releaseRenderTarget();
m_renderBackend->detachWindow();
}
}
void OverlayController::SetWidget( QQuickItem* quickItem,
@@ -412,6 +388,7 @@ void OverlayController::SetWidget( QQuickItem* quickItem,
{
if ( !m_desktopMode )
{
qDebug() << "not desktop mode";
vr::VROverlayError overlayError
= vr::VROverlay()->CreateDashboardOverlay(
key.c_str(),
@@ -439,47 +416,62 @@ void OverlayController::SetWidget( QQuickItem* quickItem,
vr::VROverlayFlags_SendVRSmoothScrollEvents,
true );
constexpr auto thumbiconFilename = "res/img/icons/thumbicon.png";
const auto thumbIconPath
= paths::binaryDirectoryFindFile( thumbiconFilename );
if ( thumbIconPath.has_value() )
{
vr::VROverlay()->SetOverlayFromFile( m_ulOverlayThumbnailHandle,
thumbIconPath->c_str() );
}
else
{
LOG( ERROR ) << "Could not find thumbnail icon \""
<< thumbiconFilename << "\"";
QImage thumbiconImg( ":qt/qml/AdvancedSettings/src/res/img/icons/thumbicon.png");
ovr_overlay_wrapper::setOverlayFromQImage(
m_ulOverlayThumbnailHandle, thumbiconImg );
}
// Too many render calls in too short time overwhelm Qt and an
// assertion gets thrown. Therefore we use an timer to delay render
// calls
m_pRenderTimer.reset( new QTimer() );
m_pRenderTimer->moveToThread( qApp->thread() );
m_pRenderTimer->setSingleShot( true );
m_pRenderTimer->setInterval( 5 );
// 16 ms: a frame starts at most 16 ms after a change, about 60 fps.
m_pRenderTimer->setInterval( 16 );
connect( m_pRenderTimer.get(),
SIGNAL( timeout() ),
this,
SLOT( renderOverlay() ) );
QOpenGLFramebufferObjectFormat fboFormat;
fboFormat.setAttachment(
QOpenGLFramebufferObject::CombinedDepthStencil );
fboFormat.setTextureTarget( GL_TEXTURE_2D );
m_pFbo.reset( new QOpenGLFramebufferObject(
static_cast<int>( quickItem->width() ),
static_cast<int>( quickItem->height() ),
fboFormat ) );
// Render target through the render backend (public Qt API only).
// Call order: see render/RenderBackend.h.
m_renderBackend->configureWindow( &m_window );
if ( !m_renderBackend->makeCurrent() )
throw std::runtime_error( "render backend: makeCurrent failed" );
if ( !m_renderControl.initialize() )
throw std::runtime_error( "could not initialize m_renderControl" );
QString renderError;
if ( !m_renderBackend->attachWindow( &m_window, &renderError ) )
throw std::runtime_error( renderError.toStdString() );
qInfo() << "Rendering with" << m_renderBackend->name() << "on"
<< m_renderBackend->deviceName();
m_window.setRenderTarget( m_pFbo.get() );
const QQuickRenderTarget renderTarget
= m_renderBackend->createRenderTarget( quickItem->size().toSize() );
if ( renderTarget.isNull() )
throw std::runtime_error( "could not create render target" );
m_window.setRenderTarget( renderTarget );
quickItem->setParentItem( m_window.contentItem() );
m_window.setGeometry( 0,
0,
static_cast<int>( quickItem->width() ),
static_cast<int>( quickItem->height() ) );
m_renderControl.initialize( &m_openGLContext );
// Font and screen metrics that decide how big the fixed-size
// (1200x800) QML layout comes out, e.g. on the Steam Frame (Noto Sans,
// Xft.dpi 144) compared to Windows (Segoe UI, 96 DPI).
const QFont font = QGuiApplication::font();
const QFontInfo fontInfo( font );
qInfo() << "Font:" << fontInfo.family() << fontInfo.pointSizeF()
<< "pt =" << fontInfo.pixelSize() << "px (requested"
<< font.family() << font.pointSizeF() << "pt); 20 pt ="
<< QFontInfo( QFont( font.family(), 20 ) ).pixelSize()
<< "px; screen" << m_window.screen()->name() << "logical DPI"
<< m_window.screen()->logicalDotsPerInch() << "physical DPI"
<< m_window.screen()->physicalDotsPerInch()
<< "device pixel ratio" << m_window.effectiveDevicePixelRatio();
vr::HmdVector2_t vecWindowSize
= { static_cast<float>( quickItem->width() ),
@@ -502,15 +494,13 @@ void OverlayController::SetWidget( QQuickItem* quickItem,
this,
SLOT( OnTimeoutPumpEvents() ) );
// Every 1ms we check if the current frame has advanced (for vsync)
m_pumpEventsTimer.setInterval( 1 );
applyTickInterval();
m_pumpEventsTimer.start();
m_steamVRTabController.initStage2( this );
m_chaperoneTabController.initStage2( this );
m_fixFloorTabController.initStage2( this );
m_audioTabController.initStage2();
m_audioTabController.initStage2( this );
m_statisticsTabController.initStage2( this );
m_settingsTabController.initStage2( this );
m_utilitiesTabController.initStage2( this );
@@ -526,7 +516,7 @@ void OverlayController::SetWidget( QQuickItem* quickItem,
autoApplyChaperoneName() );
if ( chapindex.first )
{
LOG( INFO ) << "Auto Applying Chaperone";
qInfo() << "Auto Applying Chaperone";
m_chaperoneTabController.applyChaperoneProfile( chapindex.second );
// This should be the way to stop room-setup from starting... as it
// sends steamvr a signal that it is completed
@@ -534,7 +524,7 @@ void OverlayController::SetWidget( QQuickItem* quickItem,
vr::EChaperoneConfigFile_Live );
return;
}
LOG( WARNING ) << "Profile Not Found for Auto Apply Chaperone!";
qWarning() << "Profile Not Found for Auto Apply Chaperone!";
}
auto indexList = ovr_system_wrapper::getAllConnectedDevices();
@@ -546,21 +536,25 @@ void OverlayController::SetWidget( QQuickItem* quickItem,
auto ctrlRole = info.second;
if ( vr::TrackedDeviceClass_HMD == ctrlRole )
{
LOG(INFO) << "HMD is: " << ovr_system_wrapper::getDeviceName(device);
qInfo() << "HMD is: "
<< ovr_system_wrapper::getDeviceName( device );
continue;
}
if ( vr::TrackedControllerRole_LeftHand == ctrlRole )
{
LOG(INFO) << "Controller (L) is: " << ovr_system_wrapper::getDeviceName(device);
qInfo() << "Controller (L) is: "
<< ovr_system_wrapper::getDeviceName( device );
continue;
}
if ( vr::TrackedControllerRole_RightHand == ctrlRole )
{
LOG(INFO) << "Controller (R) is: " << ovr_system_wrapper::getDeviceName(device);
qInfo() << "Controller (R) is: "
<< ovr_system_wrapper::getDeviceName( device );
continue;
}
}
LOG(INFO)<< "Initilizaion is complete--------------------------------------------------";
qInfo() << "Initilizaion is "
"complete--------------------------------------------------";
}
void OverlayController::OnRenderRequest()
@@ -581,29 +575,41 @@ void OverlayController::renderOverlay()
&& !vr::VROverlay()->IsOverlayVisible(
m_ulOverlayThumbnailHandle ) ) )
return;
// OpenGL: the context must be current while rendering and while
// handing the texture to SteamVR.
if ( !m_renderBackend->makeCurrent() )
return;
// The backends put the texture into the state Qt expects
// (beginFrame) and the one SteamVR expects (finishFrame).
m_renderBackend->beginFrame();
m_renderControl.polishItems();
m_renderControl.beginFrame();
m_renderControl.sync();
m_renderControl.render();
m_renderControl.endFrame();
m_renderBackend->finishFrame();
GLuint unTexture = m_pFbo->texture();
if ( unTexture != 0 )
// --snapshot: writes the rendered overlay to a PNG at most once per
// second while it is visible, so the layout can be checked without a
// headset photo. Written to a temporary file and renamed, so readers
// never see a partial file.
if ( !m_snapshotPath.isEmpty()
&& ( !m_snapshotTimer.isValid()
|| m_snapshotTimer.elapsed() >= 1000 ) )
{
#if defined _WIN64 || defined _LP64
// To avoid any compiler warning because of cast to a larger
// pointer type (warning C4312 on VC)
vr::Texture_t texture = { reinterpret_cast<void*>(
static_cast<uint64_t>( unTexture ) ),
vr::TextureType_OpenGL,
vr::ColorSpace_Auto };
#else
vr::Texture_t texture = { reinterpret_cast<void*>( unTexture ),
vr::TextureType_OpenGL,
vr::ColorSpace_Auto };
#endif
vr::VROverlay()->SetOverlayTexture( m_ulOverlayHandle, &texture );
m_snapshotTimer.start();
const QString tmp = m_snapshotPath + ".tmp";
if ( m_renderBackend->readback().save( tmp, "PNG" ) )
{
QFile::remove( m_snapshotPath );
QFile::rename( tmp, m_snapshotPath );
}
}
m_openGLContext.functions()->glFlush(); // We need to flush otherwise
// the texture may be empty.*/
vr::VRTextureBounds_t bounds = m_renderBackend->vrTextureBounds();
vr::VROverlay()->SetOverlayTextureBounds( m_ulOverlayHandle, &bounds );
vr::Texture_t texture = m_renderBackend->vrTexture();
vr::VROverlay()->SetOverlayTexture( m_ulOverlayHandle, &texture );
}
}
@@ -621,14 +627,13 @@ bool OverlayController::pollNextEvent( vr::VROverlayHandle_t ulOverlayHandle,
}
}
QPoint OverlayController::getMousePositionForEvent( vr::VREvent_Mouse_t mouse )
QPointF OverlayController::getMousePositionForEvent( vr::VREvent_Mouse_t mouse )
{
float y = mouse.y;
#ifdef __linux__
float h = static_cast<float>( m_window.height() );
y = h - y;
#endif
return QPoint( static_cast<int>( mouse.x ), static_cast<int>( y ) );
// SteamVR reports mouse coordinates in GL convention (y counted from the
// bottom) on every platform and for every graphics API (verified on the
// Steam Frame with OpenGL and Vulkan); Qt counts y from the top.
float y = static_cast<float>( m_window.height() ) - mouse.y;
return { mouse.x, y };
}
void OverlayController::processMediaKeyBindings()
@@ -943,6 +948,8 @@ void OverlayController::setVsyncDisabled( bool value, bool notify )
{
settings::setSetting( settings::BoolSetting::APPLICATION_vsyncDisabled,
value );
// The timer interval depends on the mode, see applyTickInterval().
applyTickInterval();
if ( notify )
{
emit vsyncDisabledChanged( value );
@@ -1013,7 +1020,7 @@ QString OverlayController::versionCheckText() const
void OverlayController::setVersionCheckText( QString value, bool notify )
{
m_versionCheckText = value;
LOG( INFO ) << "m_versionCheckText = " << m_versionCheckText;
qInfo() << "m_versionCheckText = " << m_versionCheckText;
if ( notify )
{
emit versionCheckTextChanged( m_versionCheckText );
@@ -1053,6 +1060,8 @@ void OverlayController::setCustomTickRateMs( int value, bool notify )
settings::setSetting( settings::IntSetting::APPLICATION_customTickRateMs,
verifiedTickRate );
m_verifiedCustomTickRateMs = verifiedTickRate;
// In "vsync disabled" mode the timer runs at this interval.
applyTickInterval();
if ( notify )
{
@@ -1060,6 +1069,19 @@ void OverlayController::setCustomTickRateMs( int value, bool notify )
}
}
// Sets the interval of m_pumpEventsTimer for the current tick mode:
// - vsync: every 1 ms, OnTimeoutPumpEvents() checks whether the compositor
// started a new frame.
// - vsync disabled: the timer fires directly at the custom tick rate, so the
// process wakes up once per tick instead of every 1 ms.
// QTimer::setInterval() also restarts a running timer with the new interval.
void OverlayController::applyTickInterval()
{
m_pumpEventsTimer.setTimerType( Qt::PreciseTimer );
m_pumpEventsTimer.setInterval( vsyncDisabled() ? customTickRateMs() : 1 );
}
// Called by m_pumpEventsTimer, see applyTickInterval().
// vsync implementation:
// this function triggers every 1ms
// this function should remain lightweight and only check if it's time to
@@ -1068,17 +1090,10 @@ void OverlayController::OnTimeoutPumpEvents()
{
if ( vsyncDisabled() )
{
// check if it's time for a custom tick rate tick
if ( m_customTickRateCounter > customTickRateMs() )
{
mainEventLoop();
m_customTickRateCounter = 0;
updateRate.incrementCounter();
}
else
{
m_customTickRateCounter++;
}
// The timer already runs at the custom tick rate: every call is a
// tick.
mainEventLoop();
updateRate.incrementCounter();
}
// vsync is enabled
@@ -1134,7 +1149,7 @@ void OverlayController::mainEventLoop()
{
case vr::VREvent_MouseMove:
{
QPoint ptNewMouse = getMousePositionForEvent( vrEvent.data.mouse );
QPointF ptNewMouse = getMousePositionForEvent( vrEvent.data.mouse );
if ( ptNewMouse != m_ptLastMouse )
{
QMouseEvent mouseEvent( QEvent::MouseMove,
@@ -1142,7 +1157,7 @@ void OverlayController::mainEventLoop()
m_window.mapToGlobal( ptNewMouse ),
Qt::NoButton,
m_lastMouseButtons,
nullptr );
Qt::NoModifier );
m_ptLastMouse = ptNewMouse;
QCoreApplication::sendEvent( &m_window, &mouseEvent );
OnRenderRequest();
@@ -1152,7 +1167,7 @@ void OverlayController::mainEventLoop()
case vr::VREvent_MouseButtonDown:
{
QPoint ptNewMouse = getMousePositionForEvent( vrEvent.data.mouse );
QPointF ptNewMouse = getMousePositionForEvent( vrEvent.data.mouse );
Qt::MouseButton button
= vrEvent.data.mouse.button == vr::VRMouseButton_Right
? Qt::RightButton
@@ -1163,14 +1178,14 @@ void OverlayController::mainEventLoop()
m_window.mapToGlobal( ptNewMouse ),
button,
m_lastMouseButtons,
nullptr );
Qt::NoModifier );
QCoreApplication::sendEvent( &m_window, &mouseEvent );
}
break;
case vr::VREvent_MouseButtonUp:
{
QPoint ptNewMouse = getMousePositionForEvent( vrEvent.data.mouse );
QPointF ptNewMouse = getMousePositionForEvent( vrEvent.data.mouse );
Qt::MouseButton button
= vrEvent.data.mouse.button == vr::VRMouseButton_Right
? Qt::RightButton
@@ -1181,7 +1196,7 @@ void OverlayController::mainEventLoop()
m_window.mapToGlobal( ptNewMouse ),
button,
m_lastMouseButtons,
nullptr );
Qt::NoModifier );
QCoreApplication::sendEvent( &m_window, &mouseEvent );
}
break;
@@ -1197,10 +1212,10 @@ void OverlayController::mainEventLoop()
* ( 360.0f * 8.0f ) ),
static_cast<int>( vrEvent.data.scroll.ydelta
* ( 360.0f * 8.0f ) ) ),
0,
Qt::Vertical,
m_lastMouseButtons,
nullptr );
Qt::KeyboardModifiers::fromInt( 0 ),
Qt::ScrollPhase::NoScrollPhase,
false );
QCoreApplication::sendEvent( &m_window, &wheelEvent );
}
break;
@@ -1213,7 +1228,7 @@ void OverlayController::mainEventLoop()
case vr::VREvent_Quit:
{
LOG( INFO ) << "Received quit request.";
qInfo() << "Received quit request.";
vr::VRSystem()->AcknowledgeQuit_Exiting(); // Let us buy some
// time just in case
@@ -1225,27 +1240,49 @@ void OverlayController::mainEventLoop()
case vr::VREvent_DashboardActivated:
{
LOG( DEBUG ) << "Dashboard activated";
qDebug() << "Dashboard activated";
m_dashboardVisible = true;
}
break;
case vr::VREvent_DashboardDeactivated:
{
LOG( DEBUG ) << "Dashboard deactivated";
qDebug() << "Dashboard deactivated";
m_dashboardVisible = false;
settings::saveChangedSettings();
if(0UL != m_lastTextUID){
submitLastTextField(m_lastTextUID);
m_lastTextUID = 0;
}
}
break;
case vr::VREvent_KeyboardDone:
case vr::VREvent_HideKeyboard:
{
char keyboardBuffer[1024];
vr::VROverlay()->GetKeyboardText( keyboardBuffer, 1024 );
if(0UL != m_lastTextUID){
submitLastTextField(m_lastTextUID);
m_lastTextUID = 0;
}
}
break;
case vr::VREvent_KeyboardCharInput:
{
char keyboardBuffer[64];
vr::VROverlay()->GetKeyboardText( keyboardBuffer, 64 );
emit keyBoardInputSignal( QString( keyboardBuffer ),
static_cast<unsigned long>(
vrEvent.data.keyboard.uUserValue ) );
m_lastTextUID = static_cast<unsigned long>(vrEvent.data.keyboard.uUserValue);
}
// case vr::VREvent_KeyboardDone:
// {
// char keyboardBuffer[1024];
// vr::VROverlay()->GetKeyboardText( keyboardBuffer, 1024 );
// emit keyBoardInputSignal( QString( keyboardBuffer ),
// static_cast<unsigned long>(
// vrEvent.data.keyboard.uUserValue ) );
// }
break;
case vr::VREvent_SeatedZeroPoseReset:
@@ -1390,7 +1427,7 @@ void OverlayController::RotateUniverseCenter(
// }
vr::HmdMatrix34_t curPos;
auto calState = vr::VRChaperone()->GetCalibrationState();
LOG( INFO ) << "Calibration State on Rotate is: " << calState;
qInfo() << "Calibration State on Rotate is: " << calState;
if ( universe == vr::TrackingUniverseStanding )
{
vr::VRChaperoneSetup()->GetWorkingStandingZeroPoseToRawTrackingPose(
@@ -1405,7 +1442,8 @@ void OverlayController::RotateUniverseCenter(
{
//TODO 8/3/26, will see if this causes issues
//should exclusively happen with openxr
LOG(INFO) << "rotating on undefined tracking universe probably openxr";
qInfo()
<< "rotating on undefined tracking universe probably openxr";
vr::VRChaperoneSetup()->GetLiveSeatedZeroPoseToRawTrackingPose(&curPos);
}
@@ -1572,11 +1610,20 @@ const vr::VROverlayHandle_t& OverlayController::overlayThumbnailHandle()
void OverlayController::showKeyboard( QString existingText,
unsigned long userValue )
{
// vr::VROverlay()->ShowKeyboardForOverlay(
// m_ulOverlayHandle,
// vr::k_EGamepadTextInputModeNormal,
// vr::k_EGamepadTextInputLineModeSingleLine,
// 0,
// "Advanced Settings Overlay",
// 1024,
// existingText.toStdString().c_str(),
// userValue );
vr::VROverlay()->ShowKeyboardForOverlay(
m_ulOverlayHandle,
vr::k_EGamepadTextInputModeNormal,
vr::k_EGamepadTextInputLineModeSingleLine,
0,
vr::KeyboardFlag_Modal+vr::KeyboardFlag_Minimal+vr::KeyboardFlag_HideDoneKey,
"Advanced Settings Overlay",
1024,
existingText.toStdString().c_str(),
@@ -1678,7 +1725,7 @@ void OverlayController::OnNetworkReply( QNetworkReply* reply )
if ( reply->error() == QNetworkReply::NoError )
{
QByteArray replyByteData = reply->readAll();
LOG( INFO ) << "Version Check: Recieved Data: " << replyByteData;
qInfo() << "Version Check: Recieved Data: " << replyByteData;
QJsonParseError parseError;
m_remoteVersionJsonDocument
= QJsonDocument::fromJson( replyByteData, &parseError );
@@ -1721,10 +1768,9 @@ void OverlayController::OnNetworkReply( QNetworkReply* reply )
+ QString::number( m_remoteVersionPatch )
+ ") available." );
}
LOG( INFO )
<< "Version Check: Newer version (" << m_remoteVersionMajor
<< "." << m_remoteVersionMinor << "."
<< m_remoteVersionPatch << ") available.";
qInfo() << "Version Check: Newer version ("
<< m_remoteVersionMajor << "." << m_remoteVersionMinor
<< "." << m_remoteVersionPatch << ") available.";
}
else
{
@@ -1733,22 +1779,22 @@ void OverlayController::OnNetworkReply( QNetworkReply* reply )
setVersionCheckText( m_optionalMessage );
}
setNewVersionDetected( false );
LOG( INFO ) << "Version Check: Installed version is latest "
"release.";
qInfo() << "Version Check: Installed version is latest "
"release.";
}
}
else
{
LOG( ERROR )
qCritical()
<< "Version Check: Error parsing json. QJsonParseError = "
<< parseError.error;
}
}
else
{
LOG( ERROR ) << "Version Check: Error connecting to network. "
"QNetworkReply::NetworkError = "
<< reply->error();
qCritical() << "Version Check: Error connecting to network. "
"QNetworkReply::NetworkError = "
<< reply->error();
}
reply->deleteLater();
}
+43 -9
View File
@@ -1,10 +1,10 @@
#pragma once
#include <algorithm>
#include <openvr.h>
#include <QtCore/QtCore>
// because of incompatibilities with QtOpenGL and GLEW we need to cherry pick
// includes
#include <QVector2D>
#include <QMatrix4x4>
#include <QVector>
#include <QVector2D>
@@ -22,8 +22,11 @@
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QQuickRenderTarget>
#include <QElapsedTimer>
#include <memory>
#include <easylogging++.h>
#include <optional>
#include "render/RenderBackend.h"
#include "openvr/openvr_init.h"
@@ -106,18 +109,34 @@ private:
vr::VROverlayHandle_t m_ulOverlayThumbnailHandle
= vr::k_ulOverlayHandleInvalid;
// Renders the QML scene into a texture for SteamVR (OpenGL or Vulkan),
// using public Qt API only. Declared before m_renderControl and m_window:
// Qt's device/context is created from the backend's context/instance, so
// the backend must be destroyed after them.
std::unique_ptr<RenderBackend> m_renderBackend;
QQuickRenderControl m_renderControl;
QQuickWindow m_window{ &m_renderControl };
std::optional<vr::ETextureType> m_cached_vr_texture_type;
// --snapshot (see renderOverlay()): target file and time of the last one.
QString m_snapshotPath;
QElapsedTimer m_snapshotTimer;
// Set by Shutdown(), which must run only once (see there).
bool m_shutdownDone = false;
/*
std::unique_ptr<QOpenGLFramebufferObject> m_pFbo;
QQuickRenderTarget m_pRenderTarget;
QOpenGLContext m_openGLContext;
QOffscreenSurface m_offscreenSurface;
*/
QTimer m_pumpEventsTimer;
std::unique_ptr<QTimer> m_pRenderTimer;
bool m_dashboardVisible = false;
QPoint m_ptLastMouse;
Qt::MouseButtons m_lastMouseButtons = nullptr;
QPointF m_ptLastMouse;
Qt::MouseButtons m_lastMouseButtons;
bool m_desktopMode;
bool m_noSound;
@@ -131,6 +150,7 @@ private:
QString m_updateMessage = "";
QString m_optionalMessage = "";
QString m_versionCheckText = "";
unsigned long m_lastTextUID = 0;
QUrl m_runtimePathUrl;
@@ -143,7 +163,6 @@ private:
uint64_t m_currentFrame = 0;
uint64_t m_lastFrame = 0;
int m_vsyncTooLateCounter = 0;
int m_customTickRateCounter = 0;
int m_verifiedCustomTickRateMs = 0;
input::SteamIVRInput m_actions;
@@ -154,8 +173,9 @@ private:
QJsonDocument m_remoteVersionJsonDocument = QJsonDocument();
QJsonObject m_remoteVersionJsonObject;
public: // I know it's an ugly hack to make them public to enable external
// access, but I am too lazy to implement getters.
public:
// I know it's an ugly hack to make them public to enable external
// access, but I am too lazy to implement getters.
SteamVRTabController m_steamVRTabController;
ChaperoneTabController m_chaperoneTabController;
MoveCenterTabController m_moveCenterTabController;
@@ -169,7 +189,7 @@ public: // I know it's an ugly hack to make them public to enable external
bool m_incomingReset = false;
private:
QPoint getMousePositionForEvent( vr::VREvent_Mouse_t mouse );
QPointF getMousePositionForEvent( vr::VREvent_Mouse_t mouse );
void processInputBindings();
void processMediaKeyBindings();
void processMotionBindings();
@@ -179,14 +199,27 @@ private:
void processRotationBindings();
void processExclusiveInputBinding();
void applyTickInterval();
bool m_exclusiveState = false;
bool m_keyPressOneState = false;
bool m_keyPressTwoState = false;
public:
OverlayController( bool desktopMode, bool noSound, QQmlEngine& qmlEngine );
// renderBackend: initialized, and its graphics API already set with
// QQuickWindow::setGraphicsApi() (must happen before m_window exists).
OverlayController( bool desktopMode,
bool noSound,
QQmlEngine& qmlEngine,
std::unique_ptr<RenderBackend> renderBackend );
virtual ~OverlayController();
// Diagnosis (--snapshot): see renderOverlay(). Empty: off.
void setSnapshotPath( const QString& path )
{
m_snapshotPath = path;
}
void Shutdown();
Q_INVOKABLE void exitApp();
Q_INVOKABLE void setAutoChapProfileName( int index );
@@ -293,6 +326,7 @@ signals:
void autoApplyChaperoneEnabledChanged( bool value );
void soundVolumeChanged( double value );
void desktopModeToggleChanged( bool value );
void submitLastTextField(unsigned long value = 0);
};
} // namespace advsettings
+2
View File
@@ -4,6 +4,8 @@
"app_key": "steam.overlay.1009850",
"launch_type": "binary",
"binary_path_windows": "AdvancedSettings.exe",
"binary_path_linux": "AdvancedSettings_app",
"binary_path_linux_arm": "AdvancedSettings_app",
"is_dashboard_overlay": true,
"strings": {
+180
View File
@@ -0,0 +1,180 @@
// Adapted from the vrdashboard template (FrameAssistant commit 4a5b090),
// verified on the Steam Frame. See docs/steamframe/DESIGN.md, section 3.
#include "OpenGLRenderBackend.h"
#include <QOpenGLFunctions>
#include <QQuickGraphicsDevice>
#include <QQuickWindow>
#include <QSurfaceFormat>
#include <cstdint>
OpenGLRenderBackend::~OpenGLRenderBackend()
{
releaseRenderTarget();
}
bool OpenGLRenderBackend::initialize( const RenderInterop&, QString* error )
{
QSurfaceFormat format = QSurfaceFormat::defaultFormat();
format.setDepthBufferSize( 24 );
format.setStencilBufferSize( 8 );
m_context = std::make_unique<QOpenGLContext>();
m_context->setFormat( format );
if ( !m_context->create() )
{
if ( error )
*error = QStringLiteral( "Failed to create OpenGL context" );
return false;
}
m_surface = std::make_unique<QOffscreenSurface>();
m_surface->setFormat( m_context->format() );
m_surface->create();
if ( !m_surface->isValid() )
{
if ( error )
*error = QStringLiteral( "Failed to create offscreen surface" );
return false;
}
return true;
}
void OpenGLRenderBackend::configureWindow( QQuickWindow* window )
{
window->setGraphicsDevice(
QQuickGraphicsDevice::fromOpenGLContext( m_context.get() ) );
}
bool OpenGLRenderBackend::attachWindow( QQuickWindow*, QString* error )
{
if ( !makeCurrent() )
{
*error = QStringLiteral( "makeCurrent failed" );
return false;
}
QOpenGLFunctions* f = m_context->functions();
m_deviceName = QStringLiteral( "%1 (%2)" )
.arg( QString::fromLatin1( reinterpret_cast<const char*>(
f->glGetString( GL_RENDERER ) ) ),
QString::fromLatin1( reinterpret_cast<const char*>(
f->glGetString( GL_VERSION ) ) ) );
return true;
}
bool OpenGLRenderBackend::makeCurrent()
{
return m_context && m_context->makeCurrent( m_surface.get() );
}
void OpenGLRenderBackend::doneCurrent()
{
if ( m_context )
m_context->doneCurrent();
}
QQuickRenderTarget OpenGLRenderBackend::createRenderTarget( QSize size )
{
releaseRenderTarget();
if ( !makeCurrent() )
return {};
QOpenGLFunctions* f = m_context->functions();
GLuint texture = 0;
f->glGenTextures( 1, &texture );
f->glBindTexture( GL_TEXTURE_2D, texture );
f->glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
f->glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
f->glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
f->glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
f->glTexImage2D( GL_TEXTURE_2D,
0,
GL_RGBA8,
size.width(),
size.height(),
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
nullptr );
f->glBindTexture( GL_TEXTURE_2D, 0 );
m_texture = texture;
m_size = size;
return QQuickRenderTarget::fromOpenGLTexture( m_texture, size );
}
void OpenGLRenderBackend::releaseRenderTarget()
{
if ( m_texture && makeCurrent() )
{
GLuint texture = m_texture;
m_context->functions()->glDeleteTextures( 1, &texture );
}
m_texture = 0;
m_size = {};
}
void OpenGLRenderBackend::finishFrame()
{
// SteamVR reads the texture from another process; make sure all
// rendering commands have been submitted before the handle is passed
// on.
m_context->functions()->glFlush();
}
void OpenGLRenderBackend::waitForGpu()
{
if ( makeCurrent() )
m_context->functions()->glFinish();
}
vr::Texture_t OpenGLRenderBackend::vrTexture()
{
// For TextureType_OpenGL the handle *is* the GL texture name, cast to a
// pointer.
return vr::Texture_t{ reinterpret_cast<void*>(
static_cast<uintptr_t>( m_texture ) ),
vr::TextureType_OpenGL,
vr::ColorSpace_Auto };
}
vr::VRTextureBounds_t OpenGLRenderBackend::vrTextureBounds() const
{
// Qt Quick renders into GL textures bottom-up (GL convention) and SteamVR
// treats TextureType_OpenGL accordingly: no flip needed (verified on the
// Steam Frame).
return vr::VRTextureBounds_t{ 0.f, 0.f, 1.f, 1.f };
}
QImage OpenGLRenderBackend::readback()
{
if ( !m_texture || !makeCurrent() )
return {};
QOpenGLFunctions* f = m_context->functions();
GLuint fbo = 0;
f->glGenFramebuffers( 1, &fbo );
f->glBindFramebuffer( GL_FRAMEBUFFER, fbo );
f->glFramebufferTexture2D(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture, 0 );
QImage image( m_size, QImage::Format_RGBA8888_Premultiplied );
f->glReadPixels( 0,
0,
m_size.width(),
m_size.height(),
GL_RGBA,
GL_UNSIGNED_BYTE,
image.bits() );
f->glBindFramebuffer( GL_FRAMEBUFFER,
m_context->defaultFramebufferObject() );
f->glDeleteFramebuffers( 1, &fbo );
// GL rows are bottom-up.
#if QT_VERSION >= QT_VERSION_CHECK( 6, 9, 0 )
return image.flipped( Qt::Vertical );
#else
return image.mirrored( false, true );
#endif
}
+57
View File
@@ -0,0 +1,57 @@
// Adapted from the vrdashboard template (FrameAssistant commit 4a5b090),
// verified on the Steam Frame. See docs/steamframe/DESIGN.md, section 3.
#pragma once
#include <QOffscreenSurface>
#include <QOpenGLContext>
#include "RenderBackend.h"
class OpenGLRenderBackend final : public RenderBackend
{
public:
OpenGLRenderBackend() = default;
~OpenGLRenderBackend() override;
QString name() const override
{
return QStringLiteral( "OpenGL" );
}
QSGRendererInterface::GraphicsApi graphicsApi() const override
{
return QSGRendererInterface::OpenGL;
}
bool initialize( const RenderInterop& interop, QString* error ) override;
void configureWindow( QQuickWindow* window ) override;
bool attachWindow( QQuickWindow* window, QString* error ) override;
void detachWindow() override
{
releaseRenderTarget();
}
QString deviceName() const override
{
return m_deviceName;
}
bool makeCurrent() override;
void doneCurrent() override;
QQuickRenderTarget createRenderTarget( QSize size ) override;
void releaseRenderTarget() override;
void beginFrame() override {}
void finishFrame() override;
void waitForGpu() override;
vr::Texture_t vrTexture() override;
vr::VRTextureBounds_t vrTextureBounds() const override;
QImage readback() override;
private:
std::unique_ptr<QOpenGLContext> m_context;
std::unique_ptr<QOffscreenSurface> m_surface;
unsigned int m_texture = 0; // GLuint
QSize m_size;
QString m_deviceName;
};
+38
View File
@@ -0,0 +1,38 @@
// Adapted from the vrdashboard template (FrameAssistant commit 4a5b090),
// verified on the Steam Frame. See docs/steamframe/DESIGN.md, section 3.
#include "RenderBackend.h"
#include "OpenGLRenderBackend.h"
#if OVRAS_HAS_VULKAN
# include "VulkanRenderBackend.h"
#endif
std::unique_ptr<RenderBackend> createRenderBackend( GraphicsApi api )
{
switch ( api )
{
case GraphicsApi::OpenGL:
return std::make_unique<OpenGLRenderBackend>();
case GraphicsApi::Vulkan:
#if OVRAS_HAS_VULKAN
return std::make_unique<VulkanRenderBackend>();
#else
return nullptr; // built without Vulkan support
#endif
}
return nullptr;
}
bool parseGraphicsApi( const QString& name, GraphicsApi* api )
{
const QString lower = name.toLower();
if ( lower == QLatin1String( "opengl" ) || lower == QLatin1String( "gl" ) )
*api = GraphicsApi::OpenGL;
else if ( lower == QLatin1String( "vulkan" )
|| lower == QLatin1String( "vk" ) )
*api = GraphicsApi::Vulkan;
else
return false;
return true;
}
+116
View File
@@ -0,0 +1,116 @@
// Adapted from the vrdashboard template (FrameAssistant commit 4a5b090),
// verified on the Steam Frame. See docs/steamframe/DESIGN.md, section 3.
#pragma once
#include <openvr.h>
#include <QByteArrayList>
#include <QImage>
#include <QQuickRenderTarget>
#include <QSGRendererInterface>
#include <QSize>
#include <QString>
#include <functional>
#include <memory>
class QQuickWindow;
// Requirements the VR runtime imposes on the graphics device. All optional:
// without them (snapshot mode, tests) backends pick defaults.
struct RenderInterop
{
std::function<QByteArrayList()> vulkanInstanceExtensions;
std::function<QByteArrayList( VkPhysicalDevice_T* )> vulkanDeviceExtensions;
// The physical device driving the HMD, for a given VkInstance.
std::function<VkPhysicalDevice_T*( VkInstance_T* )> vulkanOutputDevice;
};
// Abstraction over the graphics API used to render the Qt Quick scene into a
// texture that SteamVR can consume. Everything API-specific (context/device
// setup, texture allocation, layout transitions, handing the texture to OpenVR,
// CPU readback) lives behind this interface; OverlayController and the
// VR/input code stay API-agnostic.
//
// Call order (driven by OverlayController):
// initialize → configureWindow → [QQuickRenderControl::initialize] →
// attachWindow → createRenderTarget → per frame: beginFrame … [Qt renders] …
// finishFrame → vrTexture → releaseRenderTarget → detachWindow → [Qt destroys
// its device/context]
class RenderBackend
{
public:
virtual ~RenderBackend() = default;
virtual QString name() const = 0;
// Must be applied via QQuickWindow::setGraphicsApi() before any
// QQuickWindow exists.
virtual QSGRendererInterface::GraphicsApi graphicsApi() const = 0;
// Creates API-level objects (context / instance). Requires a
// QGuiApplication.
virtual bool initialize( const RenderInterop& interop, QString* error ) = 0;
// Tells Qt Quick which device/context/instance to use. Before
// QQuickRenderControl::initialize().
virtual void configureWindow( QQuickWindow* window ) = 0;
// Picks up objects Qt created during QQuickRenderControl::initialize()
// (e.g. VkDevice).
virtual bool attachWindow( QQuickWindow* window, QString* error ) = 0;
// Releases everything created on Qt's device; called before Qt destroys
// it.
virtual void detachWindow() = 0;
// Human-readable GPU/driver description (valid after attachWindow).
virtual QString deviceName() const = 0;
// Bind the API context to the current thread (no-op for APIs without
// one).
virtual bool makeCurrent() = 0;
virtual void doneCurrent() = 0;
// (Re)allocates the shared color texture; returns the target for
// QQuickWindow::setRenderTarget.
virtual QQuickRenderTarget createRenderTarget( QSize size ) = 0;
// Frees the texture. Called before Qt's device/context goes away.
virtual void releaseRenderTarget() = 0;
// Before Qt renders a frame / after QQuickRenderControl::endFrame().
// Backends put the texture into the state Qt resp. SteamVR expects
// (layouts, flushes).
virtual void beginFrame() = 0;
virtual void finishFrame() = 0;
// Blocks until the GPU has finished all submitted work (benchmarks:
// makes the asynchronous OpenGL path comparable to the synchronous
// Vulkan one).
virtual void waitForGpu() = 0;
// Texture description for IVROverlay::SetOverlayTexture. Only valid
// while the render target exists; must be submitted with the context
// current (see makeCurrent()).
virtual vr::Texture_t vrTexture() = 0;
// UV bounds for IVROverlay::SetOverlayTextureBounds (compensates
// API-specific origin).
virtual vr::VRTextureBounds_t vrTextureBounds() const = 0;
// Reads the last finished frame back to the CPU (snapshots, tests).
// Slow.
virtual QImage readback() = 0;
};
enum class GraphicsApi
{
OpenGL,
Vulkan
};
// Returns nullptr if the API was not compiled in (Vulkan is optional, see
// OVRAS_HAS_VULKAN).
std::unique_ptr<RenderBackend> createRenderBackend( GraphicsApi api );
// "opengl" / "vulkan" (case-insensitive). Returns false for unknown names.
bool parseGraphicsApi( const QString& name, GraphicsApi* api );
+461
View File
@@ -0,0 +1,461 @@
// Adapted from the vrdashboard template (FrameAssistant commit 4a5b090),
// verified on the Steam Frame. See docs/steamframe/DESIGN.md, section 3.
#include "VulkanRenderBackend.h"
#include <QQuickGraphicsConfiguration>
#include <QQuickGraphicsDevice>
#include <QQuickWindow>
#include <QVersionNumber>
#include <QVulkanDeviceFunctions>
#include <QVulkanFunctions>
#include <cstring>
namespace
{
constexpr VkFormat kFormat = VK_FORMAT_R8G8B8A8_UNORM;
}
VulkanRenderBackend::~VulkanRenderBackend()
{
// The VkDevice belongs to Qt Quick; everything we created on it must
// already be gone.
Q_ASSERT( m_commandPool == VK_NULL_HANDLE && m_image == VK_NULL_HANDLE );
}
void VulkanRenderBackend::detachWindow()
{
releaseRenderTarget(); // waits for the device to be idle
if ( m_df )
{
for ( Submission* submission : { &m_restore, &m_handoff } )
{
if ( submission->fence != VK_NULL_HANDLE )
m_df->vkDestroyFence( m_device, submission->fence, nullptr );
*submission = {};
}
// Destroying the pool also frees its command buffers.
if ( m_commandPool != VK_NULL_HANDLE )
m_df->vkDestroyCommandPool( m_device, m_commandPool, nullptr );
}
m_commandPool = VK_NULL_HANDLE;
m_df = nullptr;
m_device = VK_NULL_HANDLE;
m_queue = VK_NULL_HANDLE;
}
bool VulkanRenderBackend::initialize( const RenderInterop& interop,
QString* error )
{
QByteArrayList instanceExtensions
= QQuickGraphicsConfiguration::preferredInstanceExtensions();
if ( interop.vulkanInstanceExtensions )
{
for ( const QByteArray& ext : interop.vulkanInstanceExtensions() )
{
if ( !instanceExtensions.contains( ext ) )
instanceExtensions.append( ext );
}
}
m_instance.setExtensions( instanceExtensions );
// QVulkanInstance defaults to Vulkan 1.0; request what the loader
// supports (capped at 1.3) so Qt can rely on core 1.1+ features instead
// of half-enabled extensions.
const QVersionNumber supported = m_instance.supportedApiVersion();
m_instance.setApiVersion( qMin( supported, QVersionNumber( 1, 3 ) ) );
if ( qEnvironmentVariableIntValue( "OVRAS_VULKAN_VALIDATION" ) )
m_instance.setLayers( { "VK_LAYER_KHRONOS_validation" } );
if ( !m_instance.create() )
{
*error
= QStringLiteral( "Failed to create Vulkan instance (VkResult %1)" )
.arg( m_instance.errorCode() );
return false;
}
// Use the GPU SteamVR renders the HMD with; otherwise Qt picks one.
if ( interop.vulkanOutputDevice )
m_physicalDevice
= interop.vulkanOutputDevice( m_instance.vkInstance() );
if ( m_physicalDevice != VK_NULL_HANDLE && interop.vulkanDeviceExtensions )
m_deviceExtensions = interop.vulkanDeviceExtensions( m_physicalDevice );
return true;
}
void VulkanRenderBackend::configureWindow( QQuickWindow* window )
{
window->setVulkanInstance( &m_instance );
if ( m_physicalDevice != VK_NULL_HANDLE )
window->setGraphicsDevice(
QQuickGraphicsDevice::fromPhysicalDevice( m_physicalDevice ) );
QQuickGraphicsConfiguration config = window->graphicsConfiguration();
config.setDeviceExtensions( m_deviceExtensions );
window->setGraphicsConfiguration( config );
}
bool VulkanRenderBackend::attachWindow( QQuickWindow* window, QString* error )
{
QSGRendererInterface* rif = window->rendererInterface();
auto resource = [&]( QSGRendererInterface::Resource r )
{ return rif->getResource( window, r ); };
auto* device = static_cast<VkDevice*>(
resource( QSGRendererInterface::DeviceResource ) );
auto* physicalDevice = static_cast<VkPhysicalDevice*>(
resource( QSGRendererInterface::PhysicalDeviceResource ) );
auto* queue = static_cast<VkQueue*>(
resource( QSGRendererInterface::CommandQueueResource ) );
auto* family = static_cast<uint32_t*>(
resource( QSGRendererInterface::GraphicsQueueFamilyIndexResource ) );
if ( !device || !physicalDevice || !queue || !family )
{
*error = QStringLiteral(
"Qt Quick did not expose its Vulkan device objects" );
return false;
}
m_device = *device;
m_physicalDevice = *physicalDevice;
m_queue = *queue;
m_queueFamilyIndex = *family;
m_df = m_instance.deviceFunctions( m_device );
VkPhysicalDeviceProperties props{};
m_instance.functions()->vkGetPhysicalDeviceProperties( m_physicalDevice,
&props );
m_deviceName = QStringLiteral( "%1 (Vulkan %2.%3.%4)" )
.arg( QString::fromUtf8( props.deviceName ) )
.arg( VK_API_VERSION_MAJOR( props.apiVersion ) )
.arg( VK_API_VERSION_MINOR( props.apiVersion ) )
.arg( VK_API_VERSION_PATCH( props.apiVersion ) );
VkCommandPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
poolInfo.queueFamilyIndex = m_queueFamilyIndex;
if ( m_df->vkCreateCommandPool(
m_device, &poolInfo, nullptr, &m_commandPool )
!= VK_SUCCESS )
{
*error = QStringLiteral( "vkCreateCommandPool failed" );
return false;
}
for ( Submission* submission : { &m_restore, &m_handoff } )
{
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = m_commandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = 1;
VkFenceCreateInfo fenceInfo{};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
if ( m_df->vkAllocateCommandBuffers(
m_device, &allocInfo, &submission->commandBuffer )
!= VK_SUCCESS
|| m_df->vkCreateFence(
m_device, &fenceInfo, nullptr, &submission->fence )
!= VK_SUCCESS )
{
*error
= QStringLiteral( "Allocating command buffers/fences failed" );
return false;
}
}
return true;
}
bool VulkanRenderBackend::findMemoryType( uint32_t typeBits,
VkMemoryPropertyFlags properties,
uint32_t* index ) const
{
VkPhysicalDeviceMemoryProperties memProps{};
m_instance.functions()->vkGetPhysicalDeviceMemoryProperties(
m_physicalDevice, &memProps );
for ( uint32_t i = 0; i < memProps.memoryTypeCount; ++i )
{
if ( ( typeBits & ( 1u << i ) )
&& ( memProps.memoryTypes[i].propertyFlags & properties )
== properties )
{
*index = i;
return true;
}
}
return false;
}
QQuickRenderTarget VulkanRenderBackend::createRenderTarget( QSize size )
{
releaseRenderTarget();
if ( !m_df )
return {};
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.format = kFormat;
imageInfo.extent
= { uint32_t( size.width() ), uint32_t( size.height() ), 1 };
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
// COLOR_ATTACHMENT for Qt; TRANSFER_SRC | SAMPLED are required by
// OpenVR.
imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT
| VK_IMAGE_USAGE_SAMPLED_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
if ( m_df->vkCreateImage( m_device, &imageInfo, nullptr, &m_image )
!= VK_SUCCESS )
return {};
VkMemoryRequirements req{};
m_df->vkGetImageMemoryRequirements( m_device, m_image, &req );
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = req.size;
if ( !findMemoryType( req.memoryTypeBits,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&allocInfo.memoryTypeIndex )
|| m_df->vkAllocateMemory(
m_device, &allocInfo, nullptr, &m_imageMemory )
!= VK_SUCCESS
|| m_df->vkBindImageMemory( m_device, m_image, m_imageMemory, 0 )
!= VK_SUCCESS )
{
releaseRenderTarget();
return {};
}
m_size = size;
m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
// Qt transitions UNDEFINED -> COLOR_ATTACHMENT_OPTIMAL in the first
// frame.
return QQuickRenderTarget::fromVulkanImage(
m_image, VK_IMAGE_LAYOUT_UNDEFINED, kFormat, size );
}
void VulkanRenderBackend::releaseRenderTarget()
{
if ( !m_df )
return;
m_df->vkDeviceWaitIdle( m_device );
if ( m_image != VK_NULL_HANDLE )
m_df->vkDestroyImage( m_device, m_image, nullptr );
if ( m_imageMemory != VK_NULL_HANDLE )
m_df->vkFreeMemory( m_device, m_imageMemory, nullptr );
m_image = VK_NULL_HANDLE;
m_imageMemory = VK_NULL_HANDLE;
m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
m_size = {};
}
bool VulkanRenderBackend::waitFor( Submission& submission )
{
if ( !submission.pending )
return true;
submission.pending = false;
const bool ok = m_df->vkWaitForFences(
m_device, 1, &submission.fence, VK_TRUE, UINT64_MAX )
== VK_SUCCESS;
m_df->vkResetFences( m_device, 1, &submission.fence );
return ok;
}
bool VulkanRenderBackend::submit(
Submission& submission,
const std::function<void( VkCommandBuffer )>& record,
bool wait )
{
if ( !waitFor( submission ) ) // the command buffer may still be in use
return false;
m_df->vkResetCommandBuffer( submission.commandBuffer, 0 );
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
m_df->vkBeginCommandBuffer( submission.commandBuffer, &beginInfo );
record( submission.commandBuffer );
m_df->vkEndCommandBuffer( submission.commandBuffer );
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &submission.commandBuffer;
if ( m_df->vkQueueSubmit( m_queue, 1, &submitInfo, submission.fence )
!= VK_SUCCESS )
return false;
submission.pending = true;
return wait ? waitFor( submission ) : true;
}
void VulkanRenderBackend::transitionImage( Submission& submission,
VkImageLayout from,
VkImageLayout to,
bool wait )
{
// Only two transitions exist: rendered -> hand-off and back.
const bool toTransfer = to == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
const VkAccessFlags colorWrite = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
const VkAccessFlags transferRead = VK_ACCESS_TRANSFER_READ_BIT;
const VkPipelineStageFlags colorStage
= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
const VkPipelineStageFlags transferStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.srcAccessMask = toTransfer ? colorWrite : transferRead;
barrier.dstAccessMask = toTransfer ? transferRead : colorWrite;
barrier.oldLayout = from;
barrier.newLayout = to;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = m_image;
barrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
const VkPipelineStageFlags srcStage
= toTransfer ? colorStage : transferStage;
const VkPipelineStageFlags dstStage
= toTransfer ? transferStage : colorStage;
submit(
submission,
[&]( VkCommandBuffer cb )
{
m_df->vkCmdPipelineBarrier( cb,
srcStage,
dstStage,
0,
0,
nullptr,
0,
nullptr,
1,
&barrier );
},
wait );
m_layout = to;
}
void VulkanRenderBackend::beginFrame()
{
// Hand the image back to Qt in the layout its tracking assumes (after
// the first frame). No CPU wait needed: Qt's frame is submitted to the
// same queue afterwards.
if ( m_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL )
transitionImage( m_restore,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
false );
}
void VulkanRenderBackend::finishFrame()
{
// Qt leaves the render target in COLOR_ATTACHMENT_OPTIMAL; OpenVR wants
// TRANSFER_SRC_OPTIMAL. Wait, so the texture is complete and in the
// right layout when SteamVR gets it.
transitionImage( m_handoff,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
true );
}
void VulkanRenderBackend::waitForGpu()
{
if ( m_df )
m_df->vkQueueWaitIdle( m_queue );
}
vr::Texture_t VulkanRenderBackend::vrTexture()
{
m_vrTextureData.m_nImage = reinterpret_cast<uint64_t>( m_image );
m_vrTextureData.m_pDevice = m_device;
m_vrTextureData.m_pPhysicalDevice = m_physicalDevice;
m_vrTextureData.m_pInstance = m_instance.vkInstance();
m_vrTextureData.m_pQueue = m_queue;
m_vrTextureData.m_nQueueFamilyIndex = m_queueFamilyIndex;
m_vrTextureData.m_nWidth = uint32_t( m_size.width() );
m_vrTextureData.m_nHeight = uint32_t( m_size.height() );
m_vrTextureData.m_nFormat = uint32_t( kFormat );
m_vrTextureData.m_nSampleCount = 1;
// For TextureType_Vulkan the handle points to a VRVulkanTextureData_t.
return vr::Texture_t{ &m_vrTextureData,
vr::TextureType_Vulkan,
vr::ColorSpace_Auto };
}
vr::VRTextureBounds_t VulkanRenderBackend::vrTextureBounds() const
{
// Vulkan images are top-down, like D3D — SteamVR's native convention
// (verified on the Steam Frame).
return vr::VRTextureBounds_t{ 0.f, 0.f, 1.f, 1.f };
}
QImage VulkanRenderBackend::readback()
{
if ( m_image == VK_NULL_HANDLE
|| m_layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL )
return {}; // no finished frame yet
const VkDeviceSize byteSize
= VkDeviceSize( m_size.width() ) * m_size.height() * 4;
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = byteSize;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VkBuffer buffer = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
if ( m_df->vkCreateBuffer( m_device, &bufferInfo, nullptr, &buffer )
!= VK_SUCCESS )
return {};
QImage image;
VkMemoryRequirements req{};
m_df->vkGetBufferMemoryRequirements( m_device, buffer, &req );
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = req.size;
if ( findMemoryType( req.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&allocInfo.memoryTypeIndex )
&& m_df->vkAllocateMemory( m_device, &allocInfo, nullptr, &memory )
== VK_SUCCESS
&& m_df->vkBindBufferMemory( m_device, buffer, memory, 0 )
== VK_SUCCESS )
{
const bool copied = submit(
m_handoff,
[&]( VkCommandBuffer cb )
{
VkBufferImageCopy region{};
region.imageSubresource
= { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
region.imageExtent = { uint32_t( m_size.width() ),
uint32_t( m_size.height() ),
1 };
m_df->vkCmdCopyImageToBuffer(
cb,
m_image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
buffer,
1,
&region );
},
true );
void* data = nullptr;
if ( copied
&& m_df->vkMapMemory( m_device, memory, 0, byteSize, 0, &data )
== VK_SUCCESS )
{
image = QImage( m_size, QImage::Format_RGBA8888_Premultiplied );
std::memcpy( image.bits(), data, size_t( byteSize ) );
m_df->vkUnmapMemory( m_device, memory );
}
}
m_df->vkDestroyBuffer( m_device, buffer, nullptr );
if ( memory != VK_NULL_HANDLE )
m_df->vkFreeMemory( m_device, memory, nullptr );
return image;
}
+109
View File
@@ -0,0 +1,109 @@
// Adapted from the vrdashboard template (FrameAssistant commit 4a5b090),
// verified on the Steam Frame. See docs/steamframe/DESIGN.md, section 3.
#pragma once
#include <QVulkanInstance>
#include "RenderBackend.h"
// Vulkan implementation. Qt Quick creates the VkDevice (on the physical device
// SteamVR reports for the HMD, with the device extensions SteamVR requires);
// this backend owns the color image Qt renders into and handles the layout
// dance SteamVR needs:
//
// Qt renders -> image in COLOR_ATTACHMENT_OPTIMAL
// finishFrame() -> transition to TRANSFER_SRC_OPTIMAL (required by
// OpenVR); the CPU waits for it, the texture is
// handed over right after
// SetOverlayTexture -> SteamVR copies from the image using our queue
// beginFrame() (next) -> transition back to COLOR_ATTACHMENT_OPTIMAL, the
// layout Qt's resource tracking expects; no CPU
// wait: queue order puts it before Qt's frame
//
// Everything runs on the GUI thread; QQuickRenderControl::endFrame() waits for
// the GPU (offscreen frames are synchronous).
class VulkanRenderBackend final : public RenderBackend
{
public:
VulkanRenderBackend() = default;
~VulkanRenderBackend() override;
QString name() const override
{
return QStringLiteral( "Vulkan" );
}
QSGRendererInterface::GraphicsApi graphicsApi() const override
{
return QSGRendererInterface::Vulkan;
}
bool initialize( const RenderInterop& interop, QString* error ) override;
void configureWindow( QQuickWindow* window ) override;
bool attachWindow( QQuickWindow* window, QString* error ) override;
void detachWindow() override;
QString deviceName() const override
{
return m_deviceName;
}
bool makeCurrent() override
{
return true;
}
void doneCurrent() override {}
QQuickRenderTarget createRenderTarget( QSize size ) override;
void releaseRenderTarget() override;
void beginFrame() override;
void finishFrame() override;
void waitForGpu() override;
vr::Texture_t vrTexture() override;
vr::VRTextureBounds_t vrTextureBounds() const override;
QImage readback() override;
private:
bool findMemoryType( uint32_t typeBits,
VkMemoryPropertyFlags properties,
uint32_t* index ) const;
// A command buffer with its fence, for small submissions of our own.
struct Submission
{
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
VkFence fence = VK_NULL_HANDLE;
bool pending = false;
};
// Records `record` into the submission's command buffer (after its
// previous use has finished) and submits it; with `wait`, blocks until
// the GPU has executed it.
bool submit( Submission& submission,
const std::function<void( VkCommandBuffer )>& record,
bool wait );
bool waitFor( Submission& submission );
void transitionImage( Submission& submission,
VkImageLayout from,
VkImageLayout to,
bool wait );
QVulkanInstance m_instance;
QByteArrayList m_deviceExtensions;
VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
VkDevice m_device = VK_NULL_HANDLE;
VkQueue m_queue = VK_NULL_HANDLE;
uint32_t m_queueFamilyIndex = 0;
QVulkanDeviceFunctions* m_df = nullptr;
VkCommandPool m_commandPool = VK_NULL_HANDLE;
Submission m_restore; // beginFrame(): async
Submission m_handoff; // finishFrame() and readback(): synchronous
VkImage m_image = VK_NULL_HANDLE;
VkDeviceMemory m_imageMemory = VK_NULL_HANDLE;
// Actual layout outside of Qt's frame
VkImageLayout m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
QSize m_size;
vr::VRVulkanTextureData_t m_vrTextureData{};
QString m_deviceName;
};
+10 -10
View File
@@ -1,7 +1,7 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.2
import QtQuick.Dialogs
import ovras.advsettings 1.0
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
import "common"
@@ -415,43 +415,43 @@ MyStackViewPage {
Connections {
target: ChaperoneTabController
onChaperoneSwitchToBeginnerEnabledChanged: {
function onChaperoneSwitchToBeginnerEnabledChanged() {
switchBeginnerToggle.checked = ChaperoneTabController.chaperoneSwitchToBeginnerEnabled
}
onChaperoneSwitchToBeginnerDistanceChanged: {
function onChaperoneSwitchToBeginnerDistanceChanged() {
var d = ChaperoneTabController.chaperoneSwitchToBeginnerDistance.toFixed(2)
if (d <= switchBeginnerDistanceSlider.to && Math.abs(switchBeginnerDistanceSlider.value - d) > 0.0008) {
switchBeginnerDistanceSlider.value = d
}
switchBeginnerDistanceText.text = d
}
onChaperoneHapticFeedbackEnabledChanged: {
function onChaperoneHapticFeedbackEnabledChanged() {
hapticFeedbackToggle.checked = ChaperoneTabController.chaperoneHapticFeedbackEnabled
}
onChaperoneHapticFeedbackDistanceChanged: {
function onChaperoneHapticFeedbackDistanceChanged() {
var d = ChaperoneTabController.chaperoneHapticFeedbackDistance.toFixed(2)
if (d <= hapticFeedbackDistanceSlider.to && Math.abs(hapticFeedbackDistanceSlider.value - d) > 0.0008) {
hapticFeedbackDistanceSlider.value = d
}
hapticFeedbackDistanceText.text = d
}
onChaperoneAlarmSoundEnabledChanged: {
function onChaperoneAlarmSoundEnabledChanged() {
audioWarningToggle.checked = ChaperoneTabController.chaperoneAlarmSoundEnabled
}
onChaperoneAlarmSoundLoopingChanged: {
function onChaperoneAlarmSoundLoopingChanged() {
audioWarningLoopingToggle.checked = ChaperoneTabController.chaperoneAlarmSoundLooping
}
onChaperoneAlarmSoundDistanceChanged: {
function onChaperoneAlarmSoundDistanceChanged() {
var d = ChaperoneTabController.chaperoneAlarmSoundDistance.toFixed(2)
if (d <= audioWarningDistanceSlider.to && Math.abs(audioWarningDistanceSlider.value - d) > 0.0008) {
audioWarningDistanceSlider.value = d
}
audioWarningDistanceText.text = d
}
onChaperoneShowDashboardEnabledChanged: {
function onChaperoneShowDashboardEnabledChanged() {
openDashboardToggle.checked = ChaperoneTabController.chaperoneShowDashboardEnabled
}
onChaperoneShowDashboardDistanceChanged: {
function onChaperoneShowDashboardDistanceChanged() {
var d = ChaperoneTabController.chaperoneShowDashboardDistance.toFixed(2)
if (d <= openDashboardDistanceSlider.to && Math.abs(openDashboardDistanceSlider.value - d) > 0.0008) {
openDashboardDistanceSlider.value = d
+4 -4
View File
@@ -140,7 +140,7 @@ MyStackViewPage {
Connections {
target: FixFloorTabController
onStatusMessageSignal: {
function onStatusMessageSignal() {
if (statusMessageTimer.running) {
statusMessageTimer.stop()
}
@@ -148,15 +148,15 @@ MyStackViewPage {
statusMessageTimer.interval = FixFloorTabController.currentStatusMessageTimeout() * 1000
statusMessageTimer.start()
}
onMeasureStartSignal: {
function onMeasureStartSignal() {
fixButton.enabled = false
undoFixButton.enabled = false
}
onMeasureEndSignal: {
function onMeasureEndSignal() {
fixButton.enabled = true
// undoFixButton.enabled = FixFloorTabController.canUndo
}
onCanUndoChanged: {
function onCanUndoChanged() {
//undoFixButton.enabled = FixFloorTabController.canUndo
// revert below to this -^
undoFixButton.enabled = false
+10 -10
View File
@@ -524,33 +524,33 @@ MyStackViewPage {
Connections {
target: MoveCenterTabController
onOffsetXChanged: {
function onOffsetXChanged() {
spaceMoveXText.text = MoveCenterTabController.offsetX.toFixed(2)
}
onOffsetYChanged: {
function onOffsetYChanged() {
spaceMoveYText.text = MoveCenterTabController.offsetY.toFixed(2)
}
onOffsetZChanged: {
function onOffsetZChanged() {
spaceMoveZText.text = MoveCenterTabController.offsetZ.toFixed(2)
}
onRotationChanged: {
function onRotationChanged() {
spaceRotationSlider.value = ( MoveCenterTabController.rotation / 100 )
}
onTempRotationChanged: {
function onTempRotationChanged() {
var intTempRotation = MoveCenterTabController.tempRotation
spaceRotationSlider.value = ( intTempRotation / 100 )
spaceRotationText.text = ( intTempRotation / 100 ) + "°"
}
onLockXToggleChanged: {
function onLockXToggleChanged() {
lockXToggle.checked = MoveCenterTabController.lockXToggle
}
onLockYToggleChanged: {
function onLockYToggleChanged() {
lockYToggle.checked = MoveCenterTabController.lockYToggle
}
onLockZToggleChanged: {
function onLockZToggleChanged() {
lockZToggle.checked = MoveCenterTabController.lockZToggle
}
onTrackingUniverseChanged: {
function onTrackingUniverseChanged() {
if (MoveCenterTabController.trackingUniverse === 0) {
spaceModeText.text = "Sitting"
spaceSeatedRecenter.visible = true
@@ -560,7 +560,7 @@ MyStackViewPage {
spaceModeText.text = "Unknown(" + MoveCenterTabController.trackingUniverse + ")"
}
}
onOffsetProfilesUpdated: {
function onOffsetProfilesUpdated() {
reloadOffsetProfiles()
}
}
+38 -39
View File
@@ -1,7 +1,7 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import ovras.advsettings 1.0
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import ovras.advsettings
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
import "common"
import "utilities_page"
@@ -28,107 +28,107 @@ MyStackViewPage {
spacing: 10
MyPushButton {
id: steamVRButton
iconPath: "qrc:/main_menu_icons/steamvr_tab_icon"
iconPath: "qrc:/qt/qml/AdvancedSettings/src/res/img/main_menu_icons/steamvr_tab_icon.svg"
activationSoundEnabled: false
text: " SteamVR"
Layout.fillWidth: true
onClicked: {
MyResources.playFocusChangedSound()
OverlayController.playFocusChangedSound()
mainView.push(steamVRPage)
}
}
MyPushButton {
id: chaperoneButton
iconPath: "qrc:/main_menu_icons/chaperone_tab_icon"
iconPath: "qrc:/qt/qml/AdvancedSettings/src/res/img/main_menu_icons/chaperone_tab_icon.svg"
activationSoundEnabled: false
text: " Chaperone"
Layout.fillWidth: true
onClicked: {
MyResources.playFocusChangedSound()
OverlayController.playFocusChangedSound()
mainView.push(chaperonePage)
}
}
MyPushButton {
id: playSpaceButton
iconPath: "qrc:/main_menu_icons/offsets_tab_icon"
iconPath: "qrc:/qt/qml/AdvancedSettings/src/res/img/main_menu_icons/offsets_tab_icon.svg"
activationSoundEnabled: false
text: " Offsets"
Layout.fillWidth: true
onClicked: {
MyResources.playFocusChangedSound()
OverlayController.playFocusChangedSound()
mainView.push(playspacePage)
}
}
MyPushButton {
id: motionButton
iconPath: "qrc:/main_menu_icons/motion_tab_icon"
iconPath: "qrc:/qt/qml/AdvancedSettings/src/res/img/main_menu_icons/motion_tab_icon.svg"
activationSoundEnabled: false
text: " Motion"
Layout.fillWidth: true
onClicked: {
MyResources.playFocusChangedSound()
OverlayController.playFocusChangedSound()
mainView.push(motionPage)
}
}
MyPushButton {
id: rotationButton
iconPath: "qrc:/main_menu_icons/rotation_tab_icon"
iconPath: "qrc:/qt/qml/AdvancedSettings/src/res/img/main_menu_icons/rotation_tab_icon.svg"
activationSoundEnabled: false
text: " Rotation"
Layout.fillWidth: true
onClicked: {
MyResources.playFocusChangedSound()
OverlayController.playFocusChangedSound()
mainView.push(rotationPage)
}
}
MyPushButton {
id: fixFloorButton
iconPath: "qrc:/main_menu_icons/space_fix_tab_icon"
iconPath: "qrc:/qt/qml/AdvancedSettings/src/res/img/main_menu_icons/space_fix_tab_icon.svg"
activationSoundEnabled: false
text: " Space Fix"
Layout.fillWidth: true
onClicked: {
MyResources.playFocusChangedSound()
OverlayController.playFocusChangedSound()
mainView.push(fixFloorPage)
}
}
MyPushButton {
id: audioButton
iconPath: "qrc:/main_menu_icons/audio_tab_icon"
iconPath: "qrc:/qt/qml/AdvancedSettings/src/res/img/main_menu_icons/audio_tab_icon.svg"
activationSoundEnabled: false
text: " Audio"
Layout.fillWidth: true
onClicked: {
MyResources.playFocusChangedSound()
OverlayController.playFocusChangedSound()
mainView.push(audioPage)
}
}
MyPushButton {
id: videoButton
iconPath: "qrc:/main_menu_icons/video_tab_icon"
iconPath: "qrc:/qt/qml/AdvancedSettings/src/res/img/main_menu_icons/video_tab_icon.svg"
activationSoundEnabled: false
text: " Video"
Layout.fillWidth: true
onClicked: {
MyResources.playFocusChangedSound()
OverlayController.playFocusChangedSound()
mainView.push(videoPage)
}
}
MyPushButton {
id: utilitiesButton
iconPath: "qrc:/main_menu_icons/utilities_tab_icon"
iconPath: "qrc:/qt/qml/AdvancedSettings/src/res/img/main_menu_icons/utilities_tab_icon.svg"
activationSoundEnabled: false
text: " Utilities"
Layout.fillWidth: true
onClicked: {
MyResources.playFocusChangedSound()
OverlayController.playFocusChangedSound()
mainView.push(utilitiesPage)
}
}
@@ -136,12 +136,12 @@ MyStackViewPage {
MyPushButton {
id: statisticsButton
iconPath: "qrc:/main_menu_icons/statistics_tab_icon"
iconPath: "qrc:/qt/qml/AdvancedSettings/src/res/img/main_menu_icons/statistics_tab_icon.svg"
activationSoundEnabled: false
text: " Statistics"
Layout.fillWidth: true
onClicked: {
MyResources.playFocusChangedSound()
OverlayController.playFocusChangedSound()
mainView.push(statisticsPage)
}
}
@@ -153,7 +153,7 @@ MyStackViewPage {
MyPushButton {
id: bindingsButton
iconPath: "qrc:/main_menu_icons/bindings_tab_icon"
iconPath: "qrc:/qt/qml/AdvancedSettings/src/res/img/main_menu_icons/bindings_tab_icon.svg"
activationSoundEnabled: false
text: " Bindings"
Layout.fillWidth: true
@@ -164,12 +164,12 @@ MyStackViewPage {
MyPushButton {
id: settingsButton
iconPath: "qrc:/main_menu_icons/settings_tab_icon"
iconPath: "qrc:/qt/qml/AdvancedSettings/src/res/img/main_menu_icons/settings_tab_icon.svg"
activationSoundEnabled: false
text: " Settings"
Layout.fillWidth: true
onClicked: {
MyResources.playFocusChangedSound()
OverlayController.playFocusChangedSound()
mainView.push(settingsPage)
}
}
@@ -379,7 +379,7 @@ MyStackViewPage {
Layout.leftMargin: 12
checkable: true
contentItem: Image {
source: parent.checked ? "qrc:/microphone/off" : "qrc:/microphone/on"
source: parent.checked ? "qrc:/qt/qml/AdvancedSettings/src/res/img/audio/microphone/mic_off.svg" : "qrc:/qt/qml/AdvancedSettings/src/res/img/audio/microphone/mic_on.svg"
sourceSize.width: 32
sourceSize.height: 32
anchors.fill: parent
@@ -470,10 +470,9 @@ MyStackViewPage {
summaryMicMuteToggle.checked = AudioTabController.micMuted
summaryPttToggle.checked = AudioTabController.pttEnabled
}
Connections {
target: OverlayController
onNewVersionDetectedChanged: {
target: OverlayController
function onNewVersionDetectedChanged() {
if (OverlayController.newVersionDetected)
{
summaryVersionCheckTextRect.color = "#ff0000"
@@ -481,10 +480,10 @@ MyStackViewPage {
summaryVersionCheckTextRect.color = "#2a2e35"
}
}
onVersionCheckTextChanged: {
function onVersionCheckTextChanged() {
summaryVersionCheckText.text = OverlayController.versionCheckText
}
onDisableVersionCheckChanged: {
function onDisableVersionCheckChanged() {
if (OverlayController.disableVersionCheck)
{
summaryVersionCheckText.visible = false
@@ -503,34 +502,34 @@ MyStackViewPage {
Connections {
target: ChaperoneTabController
onChaperoneProfilesUpdated: {
function onChaperoneProfilesUpdated() {
reloadChaperoneProfiles()
}
}
Connections {
target: VideoTabController
onVideoProfilesUpdated: {
function onVideoProfilesUpdated() {
reloadVideoProfiles()
}
}
Connections {
target: AudioTabController
onMicVolumeChanged: {
function onMicVolumeChanged() {
summaryMicVolumeSlider.value = AudioTabController.micVolume
}
onMicMutedChanged: {
function onMicMutedChanged() {
summaryMicMuteToggle.checked = AudioTabController.micMuted
}
onPttEnabledChanged: {
function onPttEnabledChanged() {
summaryPttToggle.checked = AudioTabController.pttEnabled
}
}
Connections {
target: MoveCenterTabController
onTrackingUniverseChanged: {
function onTrackingUniverseChanged() {
if (MoveCenterTabController.trackingUniverse === 0) {
summaryPlaySpaceModeText.text = "Sitting"
} else if (MoveCenterTabController.trackingUniverse === 1) {
+15 -15
View File
@@ -338,66 +338,66 @@ MyStackViewPage {
Connections {
target: SettingsTabController
onAutoStartEnabledChanged: {
function onAutoStartEnabledChanged() {
settingsAutoStartToggle.checked = SettingsTabController.autoStartEnabled
}
onNativeChaperoneToggleChanged:{
function onNativeChaperoneToggleChanged(){
nativeChaperoneToggleButton.checked = SettingsTabController.nativeChaperoneToggle
}
onOculusSdkToggleChanged:{
function onOculusSdkToggleChanged(){
oculusSdkToggleButton.checked = SettingsTabController.oculusSdkToggle
}
}
Connections {
target: MoveCenterTabController
onUniverseCenteredRotationChanged: {
function onUniverseCenteredRotationChanged() {
universeCenteredRotationToggle.checked = MoveCenterTabController.universeCenteredRotation
}
}
Connections {
target: OverlayController
onVsyncDisabledChanged: {
function onVsyncDisabledChanged() {
vsyncDisabledToggle.checked = OverlayController.vsyncDisabled
customTickRateText.visible = vsyncDisabledToggle.checked
customTickRateLabel.visible = vsyncDisabledToggle.checked
customTickRateMsLabel.visible = vsyncDisabledToggle.checked
}
onCrashRecoveryDisabledChanged: {
function onCrashRecoveryDisabledChanged() {
disableCrashRecoveryToggle.checked = !OverlayController.crashRecoveryDisabled
}
onCustomTickRateMsChanged: {
function onCustomTickRateMsChanged() {
customTickRateText.text = OverlayController.customTickRateMs
}
onEnableDebugChanged: {
function onEnableDebugChanged() {
debugStateRow.visible = OverlayController.enableDebug
}
onDebugStateChanged: {
function onDebugStateChanged() {
debugStateText.text = OverlayController.debugState
}
onDisableVersionCheckChanged: {
function onDisableVersionCheckChanged() {
disableVersionCheckToggle.checked = OverlayController.disableVersionCheck
}
onExclusiveInputEnabledChanged:{
function onExclusiveInputEnabledChanged(){
exclusiveInputToggleButton.checked = OverlayController.exclusiveInputEnabled
}
onAutoApplyChaperoneEnabledChanged: {
function onAutoApplyChaperoneEnabledChanged() {
autoApplyChaperoneToggleButton.checked = OverlayController.autoApplyChaperoneEnabled
}
onSoundVolumeChanged:{
function onSoundVolumeChanged(){
volumeSlider.value = OverlayController.soundVolume
}
onDesktopModeToggleChanged:{
function onDesktopModeToggleChanged(){
desktopModeToggleButton.checked = OverlayController.desktopModeToggle
}
}
Connections{
target: ChaperoneTabController
onChaperoneProfilesUpdated: {
function onChaperoneProfilesUpdated() {
reloadChaperoneProfiles()
}
}
@@ -44,14 +44,14 @@ RowLayout {
Connections {
target: AudioTabController
onPlaybackDeviceIndexChanged: {
function onPlaybackDeviceIndexChanged() {
setShownAudioDevice(index)
}
onPlaybackDeviceListChanged: {
function onPlaybackDeviceListChanged() {
audioPlaybackNameCombo.devices = getAudioDeviceList()
setShownAudioDevice(AudioTabController.playbackDeviceIndex)
}
onPlaybackOverrideChanged: {
function onPlaybackOverrideChanged() {
playbackOverrideToggle.value = AudioTabController.playbackOverride
}
}
@@ -63,14 +63,14 @@ RowLayout {
}
Connections {
target: AudioTabController
onMicDeviceIndexChanged: {
function onMicDeviceIndexChanged() {
setShownAudioDevice(index)
}
onRecordingDeviceListChanged: {
function onRecordingDeviceListChanged() {
devices = getAudioDeviceList()
setShownAudioDevice(AudioTabController.micDeviceIndex)
}
onRecordingOverrideChanged: {
function onRecordingOverrideChanged() {
recordingOverrideToggle.value = AudioTabController.recordingOverride
}
}
@@ -61,7 +61,7 @@ ColumnLayout {
Layout.leftMargin: 12
checkable: true
contentItem: Image {
source: parent.checked ? "qrc:/microphone/off" : "qrc:/microphone/on"
source: parent.checked ? "qrc:/qt/qml/AdvancedSettings/src/res/img/audio/microphone/mic_on.svg" : "qrc:/qt/qml/AdvancedSettings/src/res/img/audio/microphone/mic_off.svg"
sourceSize.width: 32
sourceSize.height: 32
anchors.fill: parent
@@ -89,7 +89,7 @@ ColumnLayout {
}
Connections {
target: AudioTabController
onMicDeviceIndexChanged: {
function onMicDeviceIndexChanged() {
if (index < 0) {
audioMicVolumeMinusButton.enabled = false
audioMicVolumeSlider.enabled = false
@@ -102,16 +102,16 @@ ColumnLayout {
audioMicMuteToggle.enabled = true
}
}
onMicVolumeChanged: {
function onMicVolumeChanged() {
audioMicVolumeSlider.value = AudioTabController.micVolume
}
onMicMutedChanged: {
function onMicMutedChanged() {
audioMicMuteToggle.checked = AudioTabController.micMuted
}
onPlaybackDeviceListChanged: {
function onPlaybackDeviceListChanged() {
}
onRecordingDeviceListChanged: {
function onRecordingDeviceListChanged() {
if (AudioTabController.micDeviceIndex < 0) {
audioMicVolumeMinusButton.enabled = false
audioMicVolumeSlider.enabled = false
@@ -41,10 +41,10 @@ RowLayout {
}
Connections {
target: AudioTabController
onMirrorDeviceIndexChanged: {
function onMirrorDeviceIndexChanged() {
setShownAudioDevice(index)
}
onPlaybackDeviceListChanged: {
function onPlaybackDeviceListChanged() {
audioMirrorNameCombo.devices = getAudioDeviceList()
setShownAudioDevice(AudioTabController.mirrorDeviceIndex)
}
@@ -62,7 +62,7 @@ ColumnLayout {
Layout.leftMargin: 12
checkable: true
contentItem: Image {
source: parent.checked ? "qrc:/speaker/off" : "qrc:/speaker/on"
source: parent.checked ? "qrc:/qt/qml/AdvancedSettings/src/res/img/audio/speaker/speaker_off.svg" : "qrc:/qt/qml/AdvancedSettings/src/res/img/audio/speaker/speaker_on.svg"
sourceSize.width: 32
sourceSize.height: 32
anchors.fill: parent
@@ -89,7 +89,7 @@ ColumnLayout {
}
Connections {
target: AudioTabController
onMirrorDeviceIndexChanged: {
function onMirrorDeviceIndexChanged() {
if (index < 0) {
audioMirrorNameCombo.deviceIndex = 0
audioMirrorVolumeMinusButton.enabled = false
@@ -104,13 +104,13 @@ ColumnLayout {
audioMirrorNameCombo.deviceIndex = index + 1
}
}
onMirrorVolumeChanged: {
function onMirrorVolumeChanged() {
audioMirrorVolumeSlider.value = AudioTabController.mirrorVolume
}
onMirrorMutedChanged: {
function onMirrorMutedChanged() {
audioMirrorMuteToggle.checked = AudioTabController.mirrorMuted
}
onPlaybackDeviceListChanged: {
function onPlaybackDeviceListChanged() {
if (AudioTabController.mirrorDeviceIndex < 0) {
audioMirrorVolumeMinusButton.enabled = false
audioMirrorVolumeSlider.enabled = false
@@ -98,13 +98,13 @@ ColumnLayout {
}
Connections {
target: AudioTabController
onAudioProfilesUpdated: {
function onAudioProfilesUpdated() {
reloadAudioProfiles()
}
onAudioProfileAdded: {
function onAudioProfileAdded() {
audioProfileComboBox.currentIndex = AudioTabController.getAudioProfileCount()
}
onDefaultProfileDisplay: {
function onDefaultProfileDisplay() {
audioProfileComboBox.currentIndex = AudioTabController.getDefaultAudioProfileIndex(
) + 1
}
@@ -21,7 +21,7 @@ MyToggleButton {
}
Connections {
target: AudioTabController
onMicProximitySensorCanMuteChanged: {
function onMicProximitySensorCanMuteChanged() {
audioMuteProximitySensorToggle.checked = AudioTabController.micProximitySensorCanMute
}
}
@@ -39,13 +39,13 @@ ColumnLayout {
}
Connections {
target: AudioTabController
onPttEnabledChanged: {
function onPttEnabledChanged() {
audioPttEnabledToggle.checked = AudioTabController.pttEnabled
}
onMicReversePttChanged: {
function onMicReversePttChanged() {
audioPttReverseToggle.checked = AudioTabController.micReversePtt
}
onPttShowNotificationChanged: {
function onPttShowNotificationChanged() {
audioPttShowNotificationToggle.checked = AudioTabController.pttShowNotification
}
}
+11 -11
View File
@@ -1,7 +1,7 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.2
import QtQuick.Dialogs
import ovras.advsettings 1.0
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
import "../common"
@@ -494,7 +494,7 @@ MyStackViewPage {
text: "Proximity Warning Settings"
Layout.preferredWidth: 350
onClicked: {
MyResources.playFocusChangedSound()
OverlayController.playFocusChangedSound()
mainView.push(chaperoneWarningsPage)
}
}
@@ -506,7 +506,7 @@ MyStackViewPage {
Layout.preferredWidth: 400
onClicked: {
MyResources.playFocusChangedSound()
OverlayController.playFocusChangedSound()
mainView.push(chaperoneAdditionalPage)
}
}
@@ -586,26 +586,26 @@ MyStackViewPage {
Connections {
target: ChaperoneTabController
onBoundsVisibilityChanged: {
function onBoundsVisibilityChanged() {
if (Math.abs(chaperoneVisibilitySlider.value - ChaperoneTabController.boundsVisibility) > 0.008) {
chaperoneVisibilitySlider.value = ChaperoneTabController.boundsVisibility
}
}
onFadeDistanceChanged: {
function onFadeDistanceChanged() {
var d = ChaperoneTabController.fadeDistance.toFixed(1)
if (d <= chaperoneFadeDistanceSlider.to && Math.abs(chaperoneFadeDistanceSlider.value - d) > 0.008) {
chaperoneFadeDistanceSlider.value = d
}
chaperoneFadeDistanceText.text = d
}
onHeightChanged: {
function onHeightChanged() {
var h = ChaperoneTabController.height.toFixed(2)
if (h <= chaperoneHeightSlider.to && Math.abs(chaperoneHeightSlider.value - h) > 0.008) {
chaperoneHeightSlider.value = h
}
chaperoneHeightText.text = h
}
onChaperoneDimHeightChanged: {
function onChaperoneDimHeightChanged() {
var dim = ChaperoneTabController.chaperoneDimHeight
if(dim > 0.0){
chaperoneDisableChaperone.enabled = false;
@@ -625,16 +625,16 @@ MyStackViewPage {
chaperoneFadeDistanceText.enabled = true;
}
}
onCenterMarkerNewChanged: {
function onCenterMarkerNewChanged() {
chaperoneCenterMarkerToggle.checked = ChaperoneTabController.centerMarkerNew
}
onPlaySpaceMarkerChanged: {
function onPlaySpaceMarkerChanged() {
chaperonePlaySpaceToggle.checked = ChaperoneTabController.playSpaceMarker
}
onForceBoundsChanged: {
function onForceBoundsChanged() {
chaperoneForceBoundsToggle.checked = ChaperoneTabController.forceBounds
}
onChaperoneProfilesUpdated: {
function onChaperoneProfilesUpdated() {
reloadChaperoneProfiles()
}
}
@@ -1,7 +1,7 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.2
import QtQuick.Dialogs
import ovras.advsettings 1.0
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
import "../../common"
@@ -113,7 +113,7 @@ MyStackViewPage {
Connections {
target: ChaperoneTabController
onChaperoneDimHeightChanged: {
function onChaperoneDimHeightChanged() {
var d = ChaperoneTabController.chaperoneDimHeight.toFixed(2)
if (d <= dimHeightSlider.to && Math.abs(dimHeightSlider.value - d) > 0.0008) {
dimHeightSlider.value = d
@@ -180,17 +180,17 @@ GroupBox {
Connections {
target: ChaperoneTabController
onChaperoneColorRChanged:{
function onChaperoneColorRChanged(){
var redValue = ChaperoneTabController.chaperoneColorR
colorRedText.text = redValue
chaperoneColorRedSlider.value = redValue
}
onChaperoneColorGChanged:{
function onChaperoneColorGChanged(){
var greenValue = ChaperoneTabController.chaperoneColorG
colorGreenText.text = greenValue
chaperoneColorGreenSlider.value = greenValue
}
onChaperoneColorBChanged:{
function onChaperoneColorBChanged(){
var blueValue = ChaperoneTabController.chaperoneColorB
colorBlueText.text = blueValue
chaperoneColorBlueSlider.value = blueValue
@@ -70,10 +70,10 @@ GroupBox {
Connections {
target: ChaperoneTabController
onChaperoneFloorToggleChanged:{
function onChaperoneFloorToggleChanged(){
chaperoneFloorToggleBtn.checked = ChaperoneTabController.chaperoneFloorToggle
}
onCenterMarkerChanged:{
function onCenterMarkerChanged(){
legacyCenterMarkerbtn.checked = ChaperoneTabController.centerMarker
}
}
@@ -40,7 +40,7 @@ GroupBox {
Layout.preferredWidth: 200
onCheckedChanged: {
if(checked){
MyResources.playActivationSound()
OverlayController.playActivationSound()
ChaperoneTabController.setCollisionBoundStyle(0)
}
}
@@ -52,7 +52,7 @@ GroupBox {
Layout.preferredWidth: 200
onCheckedChanged: {
if(checked){
MyResources.playActivationSound()
OverlayController.playActivationSound()
ChaperoneTabController.setCollisionBoundStyle(1)
}
}
@@ -64,7 +64,7 @@ GroupBox {
Layout.preferredWidth: 200
onCheckedChanged: {
if(checked){
MyResources.playActivationSound()
OverlayController.playActivationSound()
ChaperoneTabController.setCollisionBoundStyle(2)
}
}
@@ -76,7 +76,7 @@ GroupBox {
Layout.preferredWidth: 200
onCheckedChanged: {
if(checked){
MyResources.playActivationSound()
OverlayController.playActivationSound()
ChaperoneTabController.setCollisionBoundStyle(3)
}
}
@@ -88,7 +88,7 @@ GroupBox {
Layout.preferredWidth: 200
onCheckedChanged: {
if(checked){
MyResources.playActivationSound()
OverlayController.playActivationSound()
ChaperoneTabController.setCollisionBoundStyle(4)
}
}
@@ -112,7 +112,7 @@ GroupBox {
Connections {
target: ChaperoneTabController
onCollisionBoundStyleChanged:{
function onCollisionBoundStyleChanged(){
var collisiontype = ChaperoneTabController.collisionBoundStyle
switch(collisiontype){
case 0: denseRadio.checked = true; break;
@@ -124,4 +124,7 @@ GroupBox {
}
}
Connections {
target: OverlayController
}
}
+5 -1
View File
@@ -1,5 +1,6 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import ovras.advsettings
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
@@ -63,11 +64,14 @@ ComboBox {
onActivated: {
if (activeFocus) {
MyResources.playActivationSound()
OverlayController.playActivationSound()
}
}
Component.onCompleted: {
popup.background.color = "#2a2e35"
}
Connections {
target: OverlayController
}
}
@@ -1,6 +1,7 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import ovras.advsettings
Popup {
id: myDialogPopup
+1
View File
@@ -1,6 +1,7 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import ovras.advsettings
Popup {
id: myDialogPopup
+5 -1
View File
@@ -1,5 +1,6 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import ovras.advsettings
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
Button {
@@ -35,7 +36,10 @@ Button {
onClicked: {
if (activationSoundEnabled) {
MyResources.playActivationSound()
OverlayController.playActivationSound()
}
}
Connections {
target: OverlayController
}
}
+5 -1
View File
@@ -1,5 +1,6 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import ovras.advsettings
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
Button {
@@ -24,6 +25,9 @@ Button {
}
onClicked: {
MyResources.playActivationSound()
OverlayController.playActivationSound()
}
Connections {
target: OverlayController
}
}
+5 -1
View File
@@ -1,5 +1,6 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import ovras.advsettings
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
RadioButton {
@@ -19,6 +20,9 @@ RadioButton {
}
onClicked: {
MyResources.playActivationSound()
OverlayController.playActivationSound()
}
Connections {
target: OverlayController
}
}
+3
View File
@@ -10,4 +10,7 @@ QtObject {
function playFocusChangedSound() {
OverlayController.playFocusChangedSound()
}
Connections {
target: OverlayController
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import ovras.advsettings
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
@@ -48,7 +49,7 @@ Slider {
onValueChanged: {
if (activeFocus) {
//MyResources.playActivationSound()
//OverlayController.playActivationSound()
}
}
}
+7 -3
View File
@@ -1,8 +1,9 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.2
import ovras.advsettings
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
// Needed for MyResources in the default folder.
// Needed for OverlayController in the default folder.
import ".."
Rectangle {
@@ -26,7 +27,7 @@ Rectangle {
enabled: headerShowBackButton
opacity: headerShowBackButton ? 1.0 : 0.0
contentItem: Image {
source: "qrc:/common/backarrow"
source: "qrc:/qt/qml/AdvancedSettings/src/res/img/common/backarrow.svg"
sourceSize.width: 50
sourceSize.height: 50
anchors.fill: parent
@@ -45,7 +46,7 @@ Rectangle {
}
}
onClicked: {
MyResources.playFocusChangedSound()
OverlayController.playFocusChangedSound()
stackView.pop()
}
}
@@ -90,4 +91,7 @@ Rectangle {
content.Layout.rightMargin = 40
content.Layout.bottomMargin = 40
}
Connections {
target: OverlayController
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import ovras.advsettings
Text {
color: "#d9dbe0"
+48 -5
View File
@@ -5,6 +5,7 @@ import ovras.advsettings 1.0
TextField {
property int keyBoardUID: 0
property string savedText: ""
property bool active: false
id: myTextField
color: "#d9dbe0"
text: ""
@@ -18,19 +19,25 @@ TextField {
}
onClicked: {
myTextField.forceActiveFocus()
active = true;
}
}
onActiveFocusChanged: {
if (activeFocus) {
active = true;
if (!OverlayController.desktopMode) {
OverlayController.showKeyboard(text, keyBoardUID)
} else {
savedText = text
}
//savedText = text
}
//When box loses focus apply changes
else{
myTextField.onInputEvent(text)
active = false;
}
}
onEditingFinished: {
if (OverlayController.desktopMode && savedText !== text) {
if(OverlayController.desktopMode){
myTextField.onInputEvent(text)
}
}
@@ -39,11 +46,47 @@ TextField {
}
Connections {
target: OverlayController
onSubmitLastTextField:{
//value here is UID
if(value == 0){
active = false;
return;
}
if(value == keyBoardUID && active == true){
myTextField.onInputEvent(text)
active = false;
}
}
onKeyBoardInputSignal: {
if (userValue == keyBoardUID) {
if (myTextField.text !== input) {
myTextField.onInputEvent(input)
if(input == '\b'){
myTextField.text = text.slice(0,-1)
return
}
else if(input == '\e'){
myTextField.text = text.slice(0,cursorPosition)+text.slice(cursorposition+1)
return;
}
//Execute input
else if(input == '\n'){
if(!OverlayController.desktopMode){
//myTextField.onInputEvent(input)
myTextField.focus = false;
}
return;
}
else if(input=='\r'){
return;
}
else{
myTextField.text = text + input;
}
//if (myTextField.text !== input) {
// myTextField.onInputEvent(input)
//}
}
}
}
+7 -3
View File
@@ -1,7 +1,8 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import ovras.advsettings
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
// Needed for MyResources in the default folder.
// Needed for OverlayControllers in the default folder.
import ".."
CheckBox {
@@ -31,7 +32,7 @@ CheckBox {
height: 28
x: (parent.width - width) / 2
y: (parent.height - height) / 2
source: "qrc:/common/box_checkmark"
source: "qrc:/qt/qml/AdvancedSettings/src/res/img/common/check.svg"
sourceSize.width: width
sourceSize.height: height
visible: parent.parent.checked
@@ -60,6 +61,9 @@ CheckBox {
}
onClicked: {
MyResources.playActivationSound()
OverlayController.playActivationSound()
}
Connections {
target: OverlayController
}
}
+8 -3
View File
@@ -1,6 +1,10 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
import QtQuick
// Not QtQuick.Controls.Material: importing a style module fixes that style
// for all controls and overrides the one set in main.cpp. Material's
// controls are too tall for the fixed-size layout (cut off on the Steam
// Frame).
import QtQuick.Controls
import QtQuick.Layouts
import ".."
import "../utilities_page"
import "../audio_page"
@@ -11,6 +15,7 @@ import "../steamvr_page"
import "../rotation_page"
import "../chaperone_page/chaperone_additional"
import "../steamvr_page/steamvr_additional"
import ovras.advsettings
Rectangle {
id: root
@@ -218,19 +218,19 @@ GroupBox {
Connections {
target: MoveCenterTabController
onGravityActiveChanged: {
function onGravityActiveChanged() {
gravityToggleButton.checked = MoveCenterTabController.gravityActive
}
onGravityStrengthChanged: {
function onGravityStrengthChanged() {
gravityStrengthText.text = MoveCenterTabController.gravityStrength.toFixed(2)
}
onMomentumSaveChanged: {
function onMomentumSaveChanged() {
momentumToggleButton.checked = MoveCenterTabController.momentumSave
}
onFlingStrengthChanged: {
function onFlingStrengthChanged() {
flingStrengthText.text = MoveCenterTabController.flingStrength.toFixed(2)
}
onFrictionPercentChanged: {
function onFrictionPercentChanged() {
frictionPercentText.text = MoveCenterTabController.frictionPercent + "%"
}
}
@@ -88,10 +88,10 @@ GroupBox {
Connections {
target: MoveCenterTabController
onHeightToggleChanged: {
function onHeightToggleChanged() {
heightToggleButton.checked = MoveCenterTabController.heightToggle
}
onHeightToggleOffsetChanged: {
function onHeightToggleOffsetChanged() {
heightToggleOffsetText.text = MoveCenterTabController.heightToggleOffset.toFixed(2)
}
}

Some files were not shown because too many files have changed in this diff Show More