Compare commits
24
Commits
master
..
steamframe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a10cdf7ca | ||
|
|
7f14551a6a | ||
|
|
ff4ea8f455 | ||
|
|
d664f86945 | ||
|
|
bbb3d1ba0e | ||
|
|
ec82b5fead | ||
|
|
317db6831d | ||
|
|
537539ab5e | ||
|
|
7c5a9a7a1e | ||
|
|
3ed686dec5 | ||
|
|
bde675f1b8 | ||
|
|
b22c35ae29 | ||
|
|
4826686a4d | ||
|
|
423ea57db7 | ||
|
|
266a2f9ce9 | ||
|
|
aac3edf7e3 | ||
|
|
2d4db7ef4b | ||
|
|
8d661d2b31 | ||
|
|
9ecdda1572 | ||
|
|
93c0851055 | ||
|
|
1152cfee1f | ||
|
|
44a05dfd57 | ||
|
|
383f153c54 | ||
|
|
ca0ec3e07d |
@@ -16,3 +16,6 @@ build_scripts/win/__pycache__
|
|||||||
build_scripts/win/current_build.bat
|
build_scripts/win/current_build.bat
|
||||||
AdvancedSettings_resource.rc
|
AdvancedSettings_resource.rc
|
||||||
.qtc_clangd/
|
.qtc_clangd/
|
||||||
|
out/
|
||||||
|
# Steam Frame release packages (build_scripts/steamframe/package-release.sh)
|
||||||
|
dist/
|
||||||
|
|||||||
@@ -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
@@ -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 ()
|
||||||
@@ -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" ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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,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
|
|
||||||
@@ -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.
|
|
||||||
@@ -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
|
|
||||||
@@ -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,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
|
|
||||||
@@ -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)
|
||||||
Executable
+51
@@ -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"
|
||||||
Executable
+50
@@ -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/*
|
||||||
Executable
+24
@@ -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/*
|
||||||
Executable
+101
@@ -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))"
|
||||||
@@ -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/*
|
||||||
@@ -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";
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[Paths]
|
||||||
|
Plugins=@QT_ROOT@\\plugins
|
||||||
@@ -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)
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
build/compile_commands.json
|
||||||
@@ -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.
|
||||||
@@ -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
|
||||||
@@ -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).
|
||||||
@@ -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`.
|
||||||
@@ -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 |
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
#include <easylogging++.h>
|
|
||||||
#include "../tabcontrollers/UtilitiesTabController.h"
|
#include "../tabcontrollers/UtilitiesTabController.h"
|
||||||
#include "vr_alarm.h"
|
#include "vr_alarm.h"
|
||||||
#include "../openvr/ovr_overlay_wrapper.h"
|
#include "../openvr/ovr_overlay_wrapper.h"
|
||||||
#include "../utils/update_rate.h"
|
#include "../utils/update_rate.h"
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
namespace alarm_clock
|
namespace alarm_clock
|
||||||
{
|
{
|
||||||
@@ -20,8 +24,10 @@ VrAlarm::VrAlarm()
|
|||||||
|
|
||||||
ovr_overlay_wrapper::hideOverlay( m_alarmOverlayHandle );
|
ovr_overlay_wrapper::hideOverlay( m_alarmOverlayHandle );
|
||||||
|
|
||||||
const auto filename = "/res/img/alarm/alarm_activated.png";
|
// Only need to set it once, no need to keep it in memory
|
||||||
ovr_overlay_wrapper::setOverlayFromFile( m_alarmOverlayHandle, filename );
|
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 );
|
ovr_overlay_wrapper::setOverlayWidthInMeters( m_alarmOverlayHandle, 0.3f );
|
||||||
|
|
||||||
@@ -76,13 +82,14 @@ void VrAlarm::checkAlarmStatus()
|
|||||||
&& timeToAlarmActivation > 0;
|
&& timeToAlarmActivation > 0;
|
||||||
if ( activateAlarm )
|
if ( activateAlarm )
|
||||||
{
|
{
|
||||||
LOG( INFO ) << "Alarm fired at " << m_alarm.hour() << ":"
|
qInfo() << "Alarm fired at " << m_alarm.hour() << ":"
|
||||||
<< m_alarm.minute() << ":" << m_alarm.second() << ".";
|
<< m_alarm.minute() << ":" << m_alarm.second() << ".";
|
||||||
showAlarmNotification();
|
showAlarmNotification();
|
||||||
setAlarmEnabled( false );
|
setAlarmEnabled( false );
|
||||||
|
|
||||||
std::thread t(
|
std::thread t(
|
||||||
[]( vr::VROverlayHandle_t overlayHandle ) {
|
[]( vr::VROverlayHandle_t overlayHandle )
|
||||||
|
{
|
||||||
// This will freeze the overlay for 3 seconds
|
// This will freeze the overlay for 3 seconds
|
||||||
std::this_thread::sleep_for( std::chrono::seconds( 3 ) );
|
std::this_thread::sleep_for( std::chrono::seconds( 3 ) );
|
||||||
ovr_overlay_wrapper::hideOverlay( overlayHandle );
|
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 ) )
|
if ( !QTime::isValid( hour, minute, second ) )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "setAlarmTime passed invalid time values: '" << hour
|
qCritical() << "setAlarmTime passed invalid time values: '" << hour
|
||||||
<< "' hours, '" << minute << "' minutes, '" << second
|
<< "' hours, '" << minute << "' minutes, '" << second
|
||||||
<< "' seconds.";
|
<< "' seconds.";
|
||||||
}
|
}
|
||||||
|
|
||||||
setSetting( IntSetting::UTILITY_alarmHour, hour );
|
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 )
|
if ( hour > 22 || hour < -22 )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "modAlarmTime called with incorrect hour value: '"
|
qCritical() << "modAlarmTime called with incorrect hour value: '"
|
||||||
<< hour << "'. Values not applied.";
|
<< hour << "'. Values not applied.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( minute > 58 || minute < -58 )
|
if ( minute > 58 || minute < -58 )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "modAlarmTime called with incorrect minute value: '"
|
qCritical() << "modAlarmTime called with incorrect minute value: '"
|
||||||
<< minute << "'. Values not applied.";
|
<< minute << "'. Values not applied.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( second > 58 || minute < -58 )
|
if ( second > 58 || minute < -58 )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "modAlarmTime called with incorrect second value: '"
|
qCritical() << "modAlarmTime called with incorrect second value: '"
|
||||||
<< second << "'. Values not applied.";
|
<< second << "'. Values not applied.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#include "input_parser.h"
|
#include "input_parser.h"
|
||||||
|
|
||||||
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
std::optional<Token> getModifier( const char& character ) noexcept
|
std::optional<Token> getModifier( const char& character ) noexcept
|
||||||
{
|
{
|
||||||
switch ( character )
|
switch ( character )
|
||||||
@@ -50,7 +53,7 @@ std::optional<Token> getFunctionNumber( const char& character ) noexcept
|
|||||||
return Token::KEY_F9;
|
return Token::KEY_F9;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LOG( INFO ) << "Unknown function key number: " << character;
|
qInfo() << "Unknown function key number: " << character;
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,7 +83,7 @@ std::optional<Token> getFunctionNumberExtended( const char& character ) noexcept
|
|||||||
case '9':
|
case '9':
|
||||||
return Token::KEY_F19;
|
return Token::KEY_F19;
|
||||||
default:
|
default:
|
||||||
LOG( INFO ) << "Unknown function key number: " << character;
|
qInfo() << "Unknown function key number: " << character;
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,7 +213,7 @@ std::vector<Token>
|
|||||||
|
|
||||||
if ( !isupper( *ch ) || isspace( *ch ) )
|
if ( !isupper( *ch ) || isspace( *ch ) )
|
||||||
{
|
{
|
||||||
LOG( INFO ) << "Unknown character found in sequence: " << *ch;
|
qInfo() << "Unknown character found in sequence: " << *ch;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Everything below is upper case
|
// Everything below is upper case
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <easylogging++.h>
|
|
||||||
|
|
||||||
enum class Token
|
enum class Token
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "input_parser.h"
|
#include "input_parser.h"
|
||||||
#include <easylogging++.h>
|
|
||||||
|
|
||||||
enum class KeyStatus
|
enum class KeyStatus
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
WORD convertToVirtualKeycode( const Token token )
|
WORD convertToVirtualKeycode( const Token token )
|
||||||
{
|
{
|
||||||
@@ -183,13 +185,13 @@ void sendKeyboardInputRaw( std::vector<INPUT> inputs )
|
|||||||
0,
|
0,
|
||||||
nullptr ) )
|
nullptr ) )
|
||||||
{
|
{
|
||||||
LOG( ERROR )
|
qCritical()
|
||||||
<< "Error calling SendInput(): Could not get error message ("
|
<< "Error calling SendInput(): Could not get error message ("
|
||||||
<< errCode << ")";
|
<< errCode << ")";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Error calling SendInput(): " << err;
|
qCritical() << "Error calling SendInput(): " << err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+84
-40
@@ -1,6 +1,13 @@
|
|||||||
#include "utils/setup.h"
|
#include "utils/setup.h"
|
||||||
#include "settings/settings.h"
|
#include "settings/settings.h"
|
||||||
#include "openvr/ovr_settings_wrapper.h"
|
#include "openvr/ovr_settings_wrapper.h"
|
||||||
|
#include "render/RenderBackend.h"
|
||||||
|
|
||||||
|
#include <QQuickStyle>
|
||||||
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#ifdef _WIN64
|
#ifdef _WIN64
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
extern "C" __declspec( dllexport ) DWORD NvOptimusEnablement = 0x00000001;
|
extern "C" __declspec( dllexport ) DWORD NvOptimusEnablement = 0x00000001;
|
||||||
@@ -8,16 +15,13 @@ extern "C" __declspec( dllexport ) DWORD AmdPowerXpressRequestHighPerformance
|
|||||||
= 0x00000001;
|
= 0x00000001;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
INITIALIZE_EASYLOGGINGPP
|
|
||||||
|
|
||||||
int main( int argc, char* argv[] )
|
int main( int argc, char* argv[] )
|
||||||
{
|
{
|
||||||
setUpLogging();
|
setUpLogging();
|
||||||
|
|
||||||
LOG( INFO ) << "Settings File: "
|
qInfo() << "Settings File: " << settings::initializeAndGetSettingsPath();
|
||||||
<< settings::initializeAndGetSettingsPath();
|
|
||||||
|
|
||||||
LOG( INFO ) << settings::getSettingsAndValues();
|
qInfo() << settings::getSettingsAndValues();
|
||||||
|
|
||||||
QCoreApplication::setAttribute( Qt::AA_Use96Dpi );
|
QCoreApplication::setAttribute( Qt::AA_Use96Dpi );
|
||||||
QCoreApplication::setAttribute( Qt::AA_UseDesktopOpenGL );
|
QCoreApplication::setAttribute( Qt::AA_UseDesktopOpenGL );
|
||||||
@@ -30,8 +34,6 @@ int main( int argc, char* argv[] )
|
|||||||
mainEventLoop.setApplicationVersion(
|
mainEventLoop.setApplicationVersion(
|
||||||
application_strings::applicationVersionString );
|
application_strings::applicationVersionString );
|
||||||
|
|
||||||
qInstallMessageHandler( mainQtMessageHandler );
|
|
||||||
|
|
||||||
const auto commandLineArgs
|
const auto commandLineArgs
|
||||||
= argument::returnCommandLineParser( mainEventLoop );
|
= argument::returnCommandLineParser( mainEventLoop );
|
||||||
|
|
||||||
@@ -44,41 +46,81 @@ int main( int argc, char* argv[] )
|
|||||||
commandLineArgs.forceRemoveManifest );
|
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::initializeOpenVR(
|
||||||
openvr_init::OpenVrInitializationType::Overlay );
|
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
|
try
|
||||||
{
|
{
|
||||||
QQmlEngine qmlEngine;
|
QQmlEngine qmlEngine;
|
||||||
|
|
||||||
advsettings::OverlayController controller( commandLineArgs.desktopMode,
|
// Just error checking to ensure QRC works properly
|
||||||
commandLineArgs.forceNoSound,
|
qDebug() << "list of qmldirs";
|
||||||
qmlEngine );
|
QDirIterator it( ":", QDirIterator::Subdirectories );
|
||||||
|
while ( it.hasNext() )
|
||||||
constexpr auto widgetPath = "res/qml/common/mainwidget.qml";
|
|
||||||
const auto path = paths::binaryDirectoryFindFile( widgetPath );
|
|
||||||
|
|
||||||
if ( !path.has_value() )
|
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Unable to find file '" << widgetPath << "'.";
|
qDebug() << it.next();
|
||||||
throw std::runtime_error(
|
|
||||||
"Unable to find critical file. See log for more information." );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto url
|
std::unique_ptr<advsettings::OverlayController> controller(
|
||||||
= QUrl::fromLocalFile( QString::fromStdString( ( *path ) ) );
|
new advsettings::OverlayController( commandLineArgs.desktopMode,
|
||||||
|
commandLineArgs.forceNoSound,
|
||||||
QQmlComponent component( &qmlEngine, url );
|
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();
|
auto errors = component.errors();
|
||||||
for ( auto& e : errors )
|
for ( auto& e : errors )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "QML Error: " << e.toString().toStdString()
|
qCritical() << "QML Error: "
|
||||||
<< std::endl;
|
<< e.toString().toStdString(); //<< std::endl;
|
||||||
}
|
}
|
||||||
auto quickObj = component.create();
|
auto quickObj = component.create();
|
||||||
controller.SetWidget( qobject_cast<QQuickItem*>( quickObj ),
|
controller->SetWidget( qobject_cast<QQuickItem*>( quickObj ),
|
||||||
application_strings::applicationDisplayName,
|
application_strings::applicationDisplayName,
|
||||||
application_strings::applicationKey );
|
application_strings::applicationKey );
|
||||||
|
|
||||||
// Attempts to install the application manifest on all "regular" starts.
|
// Attempts to install the application manifest on all "regular" starts.
|
||||||
if ( !commandLineArgs.forceNoManifest )
|
if ( !commandLineArgs.forceNoManifest )
|
||||||
@@ -99,7 +141,7 @@ int main( int argc, char* argv[] )
|
|||||||
}
|
}
|
||||||
catch ( std::exception& e )
|
catch ( std::exception& e )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << e.what();
|
qCritical() << e.what();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,17 +149,19 @@ int main( int argc, char* argv[] )
|
|||||||
|| settings::getSetting(
|
|| settings::getSetting(
|
||||||
settings::BoolSetting::APPLICATION_desktopModeToggle ) )
|
settings::BoolSetting::APPLICATION_desktopModeToggle ) )
|
||||||
{
|
{
|
||||||
auto m_pWindow = new QQuickWindow();
|
//TODO TEMP
|
||||||
qobject_cast<QQuickItem*>( quickObj )
|
//qDebug() << "Desktop MODE!";
|
||||||
->setParentItem( m_pWindow->contentItem() );
|
//auto m_pWindow = new QQuickWindow();
|
||||||
m_pWindow->setGeometry(
|
//qobject_cast<QQuickItem*>( quickObj )
|
||||||
0,
|
// ->setParentItem( m_pWindow->contentItem() );
|
||||||
0,
|
//m_pWindow->setGeometry(
|
||||||
static_cast<int>(
|
// 0,
|
||||||
qobject_cast<QQuickItem*>( quickObj )->width() ),
|
// 0,
|
||||||
static_cast<int>(
|
// static_cast<int>(
|
||||||
qobject_cast<QQuickItem*>( quickObj )->height() ) );
|
// qobject_cast<QQuickItem*>( quickObj )->width() ),
|
||||||
m_pWindow->show();
|
// static_cast<int>(
|
||||||
|
// qobject_cast<QQuickItem*>( quickObj )->height() ) );
|
||||||
|
//m_pWindow->show();
|
||||||
}
|
}
|
||||||
if ( commandLineArgs.resetSettings )
|
if ( commandLineArgs.resetSettings )
|
||||||
{
|
{
|
||||||
@@ -129,7 +173,7 @@ int main( int argc, char* argv[] )
|
|||||||
}
|
}
|
||||||
catch ( const std::exception& e )
|
catch ( const std::exception& e )
|
||||||
{
|
{
|
||||||
LOG( FATAL ) << e.what();
|
qFatal() << e.what();
|
||||||
return ReturnErrorCode::GENERAL_FAILURE;
|
return ReturnErrorCode::GENERAL_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <easylogging++.h>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
#include <QtDBus/QtDBus>
|
#include <QtDBus/QtDBus>
|
||||||
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
namespace keyboardinput
|
namespace keyboardinput
|
||||||
{
|
{
|
||||||
@@ -10,7 +12,7 @@ void callMethodForAllMediaPlayerAvailableServices( const QString& method )
|
|||||||
{
|
{
|
||||||
if ( !QDBusConnection::sessionBus().isConnected() )
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,9 +20,9 @@ void callMethodForAllMediaPlayerAvailableServices( const QString& method )
|
|||||||
= QDBusConnection::sessionBus().interface()->registeredServiceNames();
|
= QDBusConnection::sessionBus().interface()->registeredServiceNames();
|
||||||
if ( !names.isValid() )
|
if ( !names.isValid() )
|
||||||
{
|
{
|
||||||
LOG( ERROR )
|
qCritical()
|
||||||
<< "Media Keys: Error getting DBUS registered service names:";
|
<< "Media Keys: Error getting DBUS registered service names:";
|
||||||
LOG( ERROR ) << names.error().message();
|
qCritical() << names.error().message();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ( QString name, names.value() )
|
foreach ( QString name, names.value() )
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "media_keys.h"
|
#include "media_keys.h"
|
||||||
#include "src/keyboard_input/input_sender.h"
|
#include "src/keyboard_input/input_sender.h"
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
namespace keyboardinput
|
namespace keyboardinput
|
||||||
{
|
{
|
||||||
@@ -45,13 +47,13 @@ void sendKeyboardInputRaw( std::vector<INPUT> inputs )
|
|||||||
0,
|
0,
|
||||||
nullptr ) )
|
nullptr ) )
|
||||||
{
|
{
|
||||||
LOG( ERROR )
|
qCritical()
|
||||||
<< "Error calling SendInput(): Could not get error message ("
|
<< "Error calling SendInput(): Could not get error message ("
|
||||||
<< errCode << ")";
|
<< errCode << ")";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Error calling SendInput(): " << err;
|
qCritical() << "Error calling SendInput(): " << err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
#include <openvr.h>
|
#include <openvr.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <easylogging++.h>
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
namespace input
|
namespace input
|
||||||
{
|
{
|
||||||
@@ -30,8 +31,8 @@ vr::InputDigitalActionData_t getDigitalActionData( DigitalAction& action )
|
|||||||
|
|
||||||
if ( error != vr::EVRInputError::VRInputError_None )
|
if ( error != vr::EVRInputError::VRInputError_None )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Error getting IVRInput Digital Action Data for handle "
|
qCritical() << "Error getting IVRInput Digital Action Data for handle "
|
||||||
<< action.name() << ". SteamVR Error: " << error;
|
<< action.name() << ". SteamVR Error: " << error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleData;
|
return handleData;
|
||||||
@@ -60,8 +61,8 @@ vr::InputAnalogActionData_t getAnalogActionData( AnalogAction& action )
|
|||||||
|
|
||||||
if ( error != vr::EVRInputError::VRInputError_None )
|
if ( error != vr::EVRInputError::VRInputError_None )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Error getting IVRInput Digital Action Data for handle "
|
qCritical() << "Error getting IVRInput Digital Action Data for handle "
|
||||||
<< action.name() << ". SteamVR Error: " << error;
|
<< action.name() << ". SteamVR Error: " << error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleData;
|
return handleData;
|
||||||
@@ -454,7 +455,7 @@ void SteamIVRInput::UpdateStates()
|
|||||||
|
|
||||||
if ( error != vr::EVRInputError::VRInputError_None )
|
if ( error != vr::EVRInputError::VRInputError_None )
|
||||||
{
|
{
|
||||||
LOG( ERROR )
|
qCritical()
|
||||||
<< "Error during IVRInput action state update. OpenVR Error: "
|
<< "Error during IVRInput action state update. OpenVR Error: "
|
||||||
<< error;
|
<< error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <openvr.h>
|
#include <openvr.h>
|
||||||
#include <easylogging++.h>
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
namespace input
|
namespace input
|
||||||
{
|
{
|
||||||
@@ -30,8 +31,8 @@ public:
|
|||||||
|
|
||||||
if ( error != vr::EVRInputError::VRInputError_None )
|
if ( error != vr::EVRInputError::VRInputError_None )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Error getting handle for '" << actionName
|
qCritical() << "Error getting handle for '" << actionName
|
||||||
<< "'. OpenVR Error: " << error;
|
<< "'. OpenVR Error: " << error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <openvr.h>
|
#include <openvr.h>
|
||||||
#include <easylogging++.h>
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
namespace input
|
namespace input
|
||||||
{
|
{
|
||||||
@@ -16,8 +17,8 @@ public:
|
|||||||
auto error = vr::VRInput()->GetActionSetHandle( setName, &handle );
|
auto error = vr::VRInput()->GetActionSetHandle( setName, &handle );
|
||||||
if ( error != vr::EVRInputError::VRInputError_None )
|
if ( error != vr::EVRInputError::VRInputError_None )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Error getting handle for '" << setName
|
qCritical() << "Error getting handle for '" << setName
|
||||||
<< "'. OpenVR Error: " << error;
|
<< "'. OpenVR Error: " << error;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_activeActionSet.ulActionSet = handle;
|
m_activeActionSet.ulActionSet = handle;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <openvr.h>
|
#include <openvr.h>
|
||||||
#include <easylogging++.h>
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
namespace input
|
namespace input
|
||||||
{
|
{
|
||||||
@@ -17,9 +18,8 @@ public:
|
|||||||
|
|
||||||
if ( error != vr::EVRInputError::VRInputError_None )
|
if ( error != vr::EVRInputError::VRInputError_None )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Error getting input handle for '"
|
qCritical() << "Error getting input handle for '" << inputSourceName
|
||||||
<< inputSourceName
|
<< "'. OpenVR Input Error: " << error;
|
||||||
<< "'. OpenVR Input Error: " << error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <openvr.h>
|
#include <openvr.h>
|
||||||
#include <easylogging++.h>
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
#include "../utils/paths.h"
|
#include "../utils/paths.h"
|
||||||
|
|
||||||
namespace input
|
namespace input
|
||||||
@@ -19,8 +20,8 @@ public:
|
|||||||
|
|
||||||
if ( !actionManifestPath.has_value() )
|
if ( !actionManifestPath.has_value() )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Could not find action manifest. Action manifests "
|
qCritical() << "Could not find action manifest. Action manifests "
|
||||||
"not initialized.";
|
"not initialized.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,8 +30,8 @@ public:
|
|||||||
auto error = vr::VRInput()->SetActionManifestPath( validManifestPath );
|
auto error = vr::VRInput()->SetActionManifestPath( validManifestPath );
|
||||||
if ( error != vr::EVRInputError::VRInputError_None )
|
if ( error != vr::EVRInputError::VRInputError_None )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Error setting action manifest path: "
|
qCritical() << "Error setting action manifest path: "
|
||||||
<< validManifestPath << ". OpenVR Error: " << error;
|
<< validManifestPath << ". OpenVR Error: " << error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
#include "lh_console_util.h"
|
#include "lh_console_util.h"
|
||||||
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
namespace lh_con_util
|
namespace lh_con_util
|
||||||
{
|
{
|
||||||
@@ -6,12 +9,12 @@ bool LHCUtil::FindAll()
|
|||||||
{
|
{
|
||||||
if ( !FindAllRX() )
|
if ( !FindAllRX() )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Find All Recievers Failed";
|
qCritical() << "Find All Recievers Failed";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ( !FindAllTX() )
|
if ( !FindAllTX() )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Find All Transmitters Failed";
|
qCritical() << "Find All Transmitters Failed";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -26,7 +29,7 @@ bool LHCUtil::FindAllRX()
|
|||||||
|
|
||||||
QString output = QString( lhProcess->readAllStandardOutput() );
|
QString output = QString( lhProcess->readAllStandardOutput() );
|
||||||
QStringList outputLines
|
QStringList outputLines
|
||||||
= output.split( QRegExp( "[\r\n]" ), QString::SkipEmptyParts );
|
= output.split( QRegularExpression( "[\r\n]" ), Qt::SkipEmptyParts );
|
||||||
bool RecieverStart = false;
|
bool RecieverStart = false;
|
||||||
|
|
||||||
for ( auto OutputLine : outputLines )
|
for ( auto OutputLine : outputLines )
|
||||||
@@ -44,7 +47,7 @@ bool LHCUtil::FindAllRX()
|
|||||||
}
|
}
|
||||||
OutputLine.simplified().remove( " " );
|
OutputLine.simplified().remove( " " );
|
||||||
OutputLine.remove( 0, 1 );
|
OutputLine.remove( 0, 1 );
|
||||||
// LOG( WARNING ) << OutputLine << std::endl;
|
// qWarning() << OutputLine << std::endl;
|
||||||
RXTX_Pairs_.push_back( RXTX_Pair{ OutputLine, " ", true, false } );
|
RXTX_Pairs_.push_back( RXTX_Pair{ OutputLine, " ", true, false } );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,21 +56,21 @@ bool LHCUtil::FindAllRX()
|
|||||||
|
|
||||||
bool LHCUtil::FindConnectedTX( QString RXSerial )
|
bool LHCUtil::FindConnectedTX( QString RXSerial )
|
||||||
{
|
{
|
||||||
LOG( WARNING ) << RXSerial;
|
qWarning() << RXSerial;
|
||||||
QProcess* lhProcess = new QProcess();
|
QProcess* lhProcess = new QProcess();
|
||||||
lhProcess->start( path_, QStringList() << "/serial" << RXSerial << "exit" );
|
lhProcess->start( path_, QStringList() << "/serial" << RXSerial << "exit" );
|
||||||
lhProcess->waitForFinished();
|
lhProcess->waitForFinished();
|
||||||
|
|
||||||
QString output = QString( lhProcess->readAllStandardOutput() );
|
QString output = QString( lhProcess->readAllStandardOutput() );
|
||||||
QStringList outputLines
|
QStringList outputLines
|
||||||
= output.split( QRegExp( "[\r\n]" ), QString::SkipEmptyParts );
|
= output.split( QRegularExpression( "[\r\n]" ), Qt::SkipEmptyParts );
|
||||||
// std::vector<std::string> OutputLines
|
// std::vector<std::string> OutputLines
|
||||||
// = exec( ( "\"" + path_ + "\" /serial " + RXSerial + " exit" ).c_str()
|
// = exec( ( "\"" + path_ + "\" /serial " + RXSerial + " exit" ).c_str()
|
||||||
// );
|
// );
|
||||||
bool lhConFound = false;
|
bool lhConFound = false;
|
||||||
for ( auto outputLine : outputLines )
|
for ( auto outputLine : outputLines )
|
||||||
{
|
{
|
||||||
// LOG( WARNING ) << outputLine;
|
// qWarning() << outputLine;
|
||||||
if ( outputLine.contains( "Connected to receiver" ) )
|
if ( outputLine.contains( "Connected to receiver" ) )
|
||||||
{
|
{
|
||||||
auto splitList = outputLine.split( ":" );
|
auto splitList = outputLine.split( ":" );
|
||||||
@@ -84,7 +87,7 @@ bool LHCUtil::FindConnectedTX( QString RXSerial )
|
|||||||
rxtx.TX_Serial = TXSerial;
|
rxtx.TX_Serial = TXSerial;
|
||||||
rxtx.Is_Paired = true;
|
rxtx.Is_Paired = true;
|
||||||
rxtx.Is_Init = true;
|
rxtx.Is_Init = true;
|
||||||
LOG( WARNING )
|
qWarning()
|
||||||
<< "tx serial stored: " << TXSerial.toStdString();
|
<< "tx serial stored: " << TXSerial.toStdString();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -102,7 +105,7 @@ bool LHCUtil::FindConnectedTX( QString RXSerial )
|
|||||||
rxtx.TX_Serial = "";
|
rxtx.TX_Serial = "";
|
||||||
rxtx.Is_Paired = false;
|
rxtx.Is_Paired = false;
|
||||||
rxtx.Is_Init = true;
|
rxtx.Is_Init = true;
|
||||||
LOG( WARNING ) << "tx oops";
|
qWarning() << "tx oops";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <easylogging++.h>
|
|
||||||
|
|
||||||
namespace lh_con_util
|
namespace lh_con_util
|
||||||
{
|
{
|
||||||
|
|||||||
+91
-18
@@ -1,9 +1,12 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
#include <openvr.h>
|
#include <openvr.h>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include "openvr_init.h"
|
#include "openvr_init.h"
|
||||||
#include <easylogging++.h>
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
#include "../openvr/ivrinput_manifest.h"
|
#include "../openvr/ivrinput_manifest.h"
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
namespace openvr_init
|
namespace openvr_init
|
||||||
{
|
{
|
||||||
@@ -29,17 +32,17 @@ bool initializeProperly( const OpenVrInitializationType initType )
|
|||||||
// In particular in some setups these errors are thrown while
|
// In particular in some setups these errors are thrown while
|
||||||
// nothing is wrong with their setup presumably this is some sort of
|
// nothing is wrong with their setup presumably this is some sort of
|
||||||
// race condition
|
// race condition
|
||||||
LOG( WARNING ) << "HMD not Found During Startup";
|
qWarning() << "HMD not Found During Startup";
|
||||||
LOG( WARNING ) << "steamvr error: "
|
qWarning() << "steamvr error: "
|
||||||
+ std::string(
|
+ std::string(
|
||||||
vr::VR_GetVRInitErrorAsEnglishDescription(
|
vr::VR_GetVRInitErrorAsEnglishDescription(
|
||||||
initError ) );
|
initError ) );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LOG( ERROR ) << "Failed to initialize OpenVR: "
|
qCritical() << "Failed to initialize OpenVR: "
|
||||||
+ std::string(
|
+ std::string(
|
||||||
vr::VR_GetVRInitErrorAsEnglishDescription(
|
vr::VR_GetVRInitErrorAsEnglishDescription(
|
||||||
initError ) );
|
initError ) );
|
||||||
// Going to stop Exiting App, This may lead to crashes if OpenVR
|
// Going to stop Exiting App, This may lead to crashes if OpenVR
|
||||||
// actually fails to start, HOWEVER based on the HMD errors we are
|
// actually fails to start, HOWEVER based on the HMD errors we are
|
||||||
// probably pre-maturely killing ourselves from some sort of OpenVR race
|
// probably pre-maturely killing ourselves from some sort of OpenVR race
|
||||||
@@ -48,7 +51,7 @@ bool initializeProperly( const OpenVrInitializationType initType )
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG( INFO ) << "OpenVR initialized successfully.";
|
qInfo() << "OpenVR initialized successfully.";
|
||||||
return true;
|
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
|
// 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
|
// fails to load we should have issues. we will re-initialize a few
|
||||||
// times if that is the case and hope that fixes things
|
// times if that is the case and hope that fixes things
|
||||||
LOG( WARNING ) << "OpenVR version is invalid: Interface version "
|
qWarning() << "OpenVR version is invalid: Interface version "
|
||||||
<< interfaceAndVersion << " not found. attempt number: "
|
<< interfaceAndVersion << " not found. attempt number: "
|
||||||
<< std::to_string( trynumber );
|
<< std::to_string( trynumber );
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check whether OpenVR is too outdated
|
// Check whether OpenVR is too outdated
|
||||||
@@ -121,17 +124,87 @@ void initializeOpenVR( const OpenVrInitializationType initType, int count )
|
|||||||
}
|
}
|
||||||
if ( !success && count < 3 )
|
if ( !success && count < 3 )
|
||||||
{
|
{
|
||||||
LOG( WARNING ) << "An error occured on initialization of "
|
qWarning() << "An error occured on initialization of "
|
||||||
"openvr/steamvr attempting again "
|
"openvr/steamvr attempting again "
|
||||||
<< std::to_string( count + 1 ) << " of 3 Attempts";
|
<< std::to_string( count + 1 ) << " of 3 Attempts";
|
||||||
std::this_thread::sleep_for( std::chrono::seconds( 5 ) );
|
std::this_thread::sleep_for( std::chrono::seconds( 5 ) );
|
||||||
// 5.8.1 Unknown if VR shutdown needs to be callled if vr init fails
|
// 5.8.1 Unknown if VR shutdown needs to be callled if vr init fails
|
||||||
initializeOpenVR( initType, ( count + 1 ) );
|
initializeOpenVR( initType, ( count + 1 ) );
|
||||||
}
|
}
|
||||||
if ( count >= 3 )
|
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
|
} // namespace openvr_init
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "../render/RenderBackend.h"
|
||||||
|
|
||||||
namespace openvr_init
|
namespace openvr_init
|
||||||
{
|
{
|
||||||
enum class OpenVrInitializationType
|
enum class OpenVrInitializationType
|
||||||
@@ -10,4 +12,9 @@ enum class OpenVrInitializationType
|
|||||||
|
|
||||||
void initializeOpenVR( const OpenVrInitializationType initType, int count = 0 );
|
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
|
} // namespace openvr_init
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#include "ovr_application_wrapper.h"
|
#include "ovr_application_wrapper.h"
|
||||||
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
constexpr uint32_t bufferLen = 4096;
|
constexpr uint32_t bufferLen = 4096;
|
||||||
namespace ovr_application_wrapper
|
namespace ovr_application_wrapper
|
||||||
{
|
{
|
||||||
@@ -12,9 +14,8 @@ std::string getSceneAppID()
|
|||||||
std::string keys = key;
|
std::string keys = key;
|
||||||
if ( error != vr::VRApplicationError_None )
|
if ( error != vr::VRApplicationError_None )
|
||||||
{
|
{
|
||||||
LOG( WARNING ) << "Could not Get App ID by PID: "
|
qWarning() << "Could not Get App ID by PID: " + std::to_string( error );
|
||||||
+ std::to_string( error );
|
qWarning() << "Attempting to find through Enumeration";
|
||||||
LOG( WARNING ) << "Attempting to find through Enumeration";
|
|
||||||
auto appCount = vr::VRApplications()->GetApplicationCount();
|
auto appCount = vr::VRApplications()->GetApplicationCount();
|
||||||
const uint32_t bufferMax = vr::k_unMaxApplicationKeyLength;
|
const uint32_t bufferMax = vr::k_unMaxApplicationKeyLength;
|
||||||
char cStringOut[bufferMax];
|
char cStringOut[bufferMax];
|
||||||
@@ -25,17 +26,17 @@ std::string getSceneAppID()
|
|||||||
std::string appID( cStringOut );
|
std::string appID( cStringOut );
|
||||||
if ( appID.find( "steam.app" ) != std::string::npos )
|
if ( appID.find( "steam.app" ) != std::string::npos )
|
||||||
{
|
{
|
||||||
LOG( WARNING ) << "Found App ID through Enumeration (only "
|
qWarning() << "Found App ID through Enumeration (only "
|
||||||
"steam apps caught): "
|
"steam apps caught): "
|
||||||
<< appID;
|
<< appID;
|
||||||
if ( error2 == vr::VRApplicationError_None )
|
if ( error2 == vr::VRApplicationError_None )
|
||||||
{
|
{
|
||||||
return appID;
|
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 "error";
|
||||||
}
|
}
|
||||||
return keys;
|
return keys;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <openvr.h>
|
#include <openvr.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <easylogging++.h>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
#include "ovr_overlay_wrapper.h"
|
#include "ovr_overlay_wrapper.h"
|
||||||
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
namespace ovr_overlay_wrapper
|
namespace ovr_overlay_wrapper
|
||||||
{
|
{
|
||||||
@@ -11,10 +14,9 @@ OverlayError createOverlay( const std::string overlayKey,
|
|||||||
overlayKey.c_str(), overlayName.c_str(), overlayHandle );
|
overlayKey.c_str(), overlayName.c_str(), overlayHandle );
|
||||||
if ( oError != vr::VROverlayError_None )
|
if ( oError != vr::VROverlayError_None )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Could not create overlay: " << overlayName
|
qCritical() << "Could not create overlay: " << overlayName << "because "
|
||||||
<< "because "
|
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
|
||||||
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
|
<< " " << customErrorMsg;
|
||||||
<< " " << customErrorMsg;
|
|
||||||
return OverlayError::UndefinedError;
|
return OverlayError::UndefinedError;
|
||||||
}
|
}
|
||||||
return OverlayError::NoError;
|
return OverlayError::NoError;
|
||||||
@@ -30,10 +32,10 @@ OverlayError setOverlayColor( vr::VROverlayHandle_t overlayHandle,
|
|||||||
= vr::VROverlay()->SetOverlayColor( overlayHandle, red, green, blue );
|
= vr::VROverlay()->SetOverlayColor( overlayHandle, red, green, blue );
|
||||||
if ( oError != vr::VROverlayError_None )
|
if ( oError != vr::VROverlayError_None )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Error setting Overlay Color For "
|
qCritical() << "Error setting Overlay Color For "
|
||||||
<< getOverlayKey( overlayHandle ) << " with error: "
|
<< getOverlayKey( overlayHandle ) << " with error: "
|
||||||
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
|
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
|
||||||
<< " " << customErrorMsg;
|
<< " " << customErrorMsg;
|
||||||
return OverlayError::UndefinedError;
|
return OverlayError::UndefinedError;
|
||||||
}
|
}
|
||||||
return OverlayError::NoError;
|
return OverlayError::NoError;
|
||||||
@@ -47,10 +49,10 @@ OverlayError setOverlayAlpha( vr::VROverlayHandle_t overlayHandle,
|
|||||||
= vr::VROverlay()->SetOverlayAlpha( overlayHandle, alpha );
|
= vr::VROverlay()->SetOverlayAlpha( overlayHandle, alpha );
|
||||||
if ( oError != vr::VROverlayError_None )
|
if ( oError != vr::VROverlayError_None )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Error setting Overlay Alpha For "
|
qCritical() << "Error setting Overlay Alpha For "
|
||||||
<< getOverlayKey( overlayHandle ) << " with error: "
|
<< getOverlayKey( overlayHandle ) << " with error: "
|
||||||
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
|
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
|
||||||
<< " " << customErrorMsg;
|
<< " " << customErrorMsg;
|
||||||
return OverlayError::UndefinedError;
|
return OverlayError::UndefinedError;
|
||||||
}
|
}
|
||||||
return OverlayError::NoError;
|
return OverlayError::NoError;
|
||||||
@@ -64,10 +66,10 @@ OverlayError setOverlayWidthInMeters( vr::VROverlayHandle_t overlayHandle,
|
|||||||
overlayHandle, widthInMeters );
|
overlayHandle, widthInMeters );
|
||||||
if ( oError != vr::VROverlayError_None )
|
if ( oError != vr::VROverlayError_None )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Error setting overlay width For "
|
qCritical() << "Error setting overlay width For "
|
||||||
<< getOverlayKey( overlayHandle ) << " with error: "
|
<< getOverlayKey( overlayHandle ) << " with error: "
|
||||||
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
|
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
|
||||||
<< " " << customErrorMsg;
|
<< " " << customErrorMsg;
|
||||||
return OverlayError::UndefinedError;
|
return OverlayError::UndefinedError;
|
||||||
}
|
}
|
||||||
return OverlayError::NoError;
|
return OverlayError::NoError;
|
||||||
@@ -81,34 +83,52 @@ OverlayError setOverlayFromFile( vr::VROverlayHandle_t overlayHandle,
|
|||||||
const auto overlayPath = paths::binaryDirectoryFindFile( fileName );
|
const auto overlayPath = paths::binaryDirectoryFindFile( fileName );
|
||||||
if ( !overlayPath.has_value() )
|
if ( !overlayPath.has_value() )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "File not Found: " << fileName << customErrorMsg;
|
qCritical() << "File not Found: " << fileName << customErrorMsg;
|
||||||
return OverlayError::UndefinedError;
|
return OverlayError::UndefinedError;
|
||||||
}
|
}
|
||||||
vr::VROverlayError oError = vr::VROverlay()->SetOverlayFromFile(
|
vr::VROverlayError oError = vr::VROverlay()->SetOverlayFromFile(
|
||||||
overlayHandle, overlayPath->c_str() );
|
overlayHandle, overlayPath->c_str() );
|
||||||
if ( oError != vr::VROverlayError_None )
|
if ( oError != vr::VROverlayError_None )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Error setting Overlay from file For "
|
qCritical() << "Error setting Overlay from file For "
|
||||||
<< getOverlayKey( overlayHandle ) << " with error: "
|
<< getOverlayKey( overlayHandle ) << " with error: "
|
||||||
<< vr::VROverlay()->GetOverlayErrorNameFromEnum(
|
<< vr::VROverlay()->GetOverlayErrorNameFromEnum(
|
||||||
oError )
|
oError )
|
||||||
<< " " << customErrorMsg;
|
<< " " << customErrorMsg;
|
||||||
return OverlayError::UndefinedError;
|
return OverlayError::UndefinedError;
|
||||||
}
|
}
|
||||||
return OverlayError::NoError;
|
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,
|
OverlayError showOverlay( vr::VROverlayHandle_t overlayHandle,
|
||||||
std::string customErrorMsg )
|
std::string customErrorMsg )
|
||||||
{
|
{
|
||||||
vr::VROverlayError oError = vr::VROverlay()->ShowOverlay( overlayHandle );
|
vr::VROverlayError oError = vr::VROverlay()->ShowOverlay( overlayHandle );
|
||||||
if ( oError != vr::VROverlayError_None )
|
if ( oError != vr::VROverlayError_None )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Error showing overlay For "
|
qCritical() << "Error showing overlay For "
|
||||||
<< getOverlayKey( overlayHandle ) << " with error: "
|
<< getOverlayKey( overlayHandle ) << " with error: "
|
||||||
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
|
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
|
||||||
<< " " << customErrorMsg;
|
<< " " << customErrorMsg;
|
||||||
return OverlayError::UndefinedError;
|
return OverlayError::UndefinedError;
|
||||||
}
|
}
|
||||||
return OverlayError::NoError;
|
return OverlayError::NoError;
|
||||||
@@ -120,10 +140,10 @@ OverlayError hideOverlay( vr::VROverlayHandle_t overlayHandle,
|
|||||||
vr::VROverlayError oError = vr::VROverlay()->HideOverlay( overlayHandle );
|
vr::VROverlayError oError = vr::VROverlay()->HideOverlay( overlayHandle );
|
||||||
if ( oError != vr::VROverlayError_None )
|
if ( oError != vr::VROverlayError_None )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Error hiding overlay For "
|
qCritical() << "Error hiding overlay For "
|
||||||
<< getOverlayKey( overlayHandle ) << " with error: "
|
<< getOverlayKey( overlayHandle ) << " with error: "
|
||||||
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
|
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
|
||||||
<< " " << customErrorMsg;
|
<< " " << customErrorMsg;
|
||||||
return OverlayError::UndefinedError;
|
return OverlayError::UndefinedError;
|
||||||
}
|
}
|
||||||
return OverlayError::NoError;
|
return OverlayError::NoError;
|
||||||
@@ -139,10 +159,10 @@ OverlayError setOverlayTransformAbsolute(
|
|||||||
overlayHandle, trackingOrigin, trackingOriginToOverlayTransform );
|
overlayHandle, trackingOrigin, trackingOriginToOverlayTransform );
|
||||||
if ( oError != vr::VROverlayError_None )
|
if ( oError != vr::VROverlayError_None )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Error setting Overlay Position For "
|
qCritical() << "Error setting Overlay Position For "
|
||||||
<< getOverlayKey( overlayHandle ) << " with error: "
|
<< getOverlayKey( overlayHandle ) << " with error: "
|
||||||
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
|
<< vr::VROverlay()->GetOverlayErrorNameFromEnum( oError )
|
||||||
<< " " << customErrorMsg;
|
<< " " << customErrorMsg;
|
||||||
return OverlayError::UndefinedError;
|
return OverlayError::UndefinedError;
|
||||||
}
|
}
|
||||||
return OverlayError::NoError;
|
return OverlayError::NoError;
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
#include <openvr.h>
|
#include <openvr.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <easylogging++.h>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <QImage>
|
||||||
#include "../utils/paths.h"
|
#include "../utils/paths.h"
|
||||||
|
|
||||||
/* Wrapper For OpenVR's IVR settings class, allows us to do our error logging
|
/* 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,
|
OverlayError setOverlayFromFile( vr::VROverlayHandle_t overlayHandle,
|
||||||
std::string fileName,
|
std::string fileName,
|
||||||
std::string customErrorMsg = "" );
|
std::string customErrorMsg = "" );
|
||||||
|
OverlayError setOverlayFromQImage( vr::VROverlayHandle_t overlayHandle,
|
||||||
|
QImage& image,
|
||||||
|
std::string customErrorMsg = "" );
|
||||||
OverlayError showOverlay( vr::VROverlayHandle_t overlayHandle,
|
OverlayError showOverlay( vr::VROverlayHandle_t overlayHandle,
|
||||||
std::string customErrorMsg = "" );
|
std::string customErrorMsg = "" );
|
||||||
OverlayError hideOverlay( vr::VROverlayHandle_t overlayHandle,
|
OverlayError hideOverlay( vr::VROverlayHandle_t overlayHandle,
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#include "ovr_settings_wrapper.h"
|
#include "ovr_settings_wrapper.h"
|
||||||
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
namespace ovr_settings_wrapper
|
namespace ovr_settings_wrapper
|
||||||
{
|
{
|
||||||
@@ -19,9 +21,9 @@ SettingsError handleErrors( std::string settingsKey,
|
|||||||
{
|
{
|
||||||
return SettingsError::UndefinedError;
|
return SettingsError::UndefinedError;
|
||||||
}
|
}
|
||||||
LOG( ERROR ) << "Could not access \"" << settingsKey << "\" setting: "
|
qCritical() << "Could not access \"" << settingsKey << "\" setting: "
|
||||||
<< vr::VRSettings()->GetSettingsErrorNameFromEnum( error )
|
<< vr::VRSettings()->GetSettingsErrorNameFromEnum( error )
|
||||||
<< " " << customErrorMsg;
|
<< " " << customErrorMsg;
|
||||||
if ( error
|
if ( error
|
||||||
== vr::EVRSettingsError::VRSettingsError_UnsetSettingHasNoDefault )
|
== vr::EVRSettingsError::VRSettingsError_UnsetSettingHasNoDefault )
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <openvr.h>
|
#include <openvr.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <easylogging++.h>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
/* Wrapper For OpenVR's IVR settings class, allows us to do our error logging
|
/* Wrapper For OpenVR's IVR settings class, allows us to do our error logging
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
#include "ovr_system_wrapper.h"
|
#include "ovr_system_wrapper.h"
|
||||||
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace ovr_system_wrapper
|
namespace ovr_system_wrapper
|
||||||
{
|
{
|
||||||
@@ -8,11 +11,11 @@ SystemError handleTrackedPropertyErrors( vr::TrackedDeviceProperty tdp,
|
|||||||
{
|
{
|
||||||
if ( error != vr::TrackedProp_Success )
|
if ( error != vr::TrackedProp_Success )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Could not get Property with error \""
|
qCritical() << "Could not get Property with error \""
|
||||||
<< vr::VRSystem()->GetPropErrorNameFromEnum( error )
|
<< vr::VRSystem()->GetPropErrorNameFromEnum( error )
|
||||||
<< "\" property: "
|
<< "\" property: "
|
||||||
<< "todo"
|
<< "todo"
|
||||||
<< " " << customErrorMsg;
|
<< " " << customErrorMsg;
|
||||||
if ( tdp == vr::Prop_TrackedDeviceProperty_Max )
|
if ( tdp == vr::Prop_TrackedDeviceProperty_Max )
|
||||||
{
|
{
|
||||||
// Just for Error OPPS todo
|
// Just for Error OPPS todo
|
||||||
@@ -295,7 +298,7 @@ std::string getControllerName()
|
|||||||
}
|
}
|
||||||
if ( !isLeftCon && !isRightCon && hasHMD )
|
if ( !isLeftCon && !isRightCon && hasHMD )
|
||||||
{
|
{
|
||||||
LOG( WARNING ) << "WARNING: No Controllers assuming based on HMD";
|
qWarning() << "WARNING: No Controllers assuming based on HMD";
|
||||||
auto hmdName
|
auto hmdName
|
||||||
= getStringTrackedProperty( hmdIdx, vr::Prop_ControllerType_String )
|
= getStringTrackedProperty( hmdIdx, vr::Prop_ControllerType_String )
|
||||||
.second;
|
.second;
|
||||||
@@ -309,27 +312,27 @@ std::string getControllerName()
|
|||||||
}
|
}
|
||||||
if ( hmdName == "vive_pro" )
|
if ( hmdName == "vive_pro" )
|
||||||
{
|
{
|
||||||
LOG( WARNING )
|
qWarning()
|
||||||
<< "Vive Pro Detected, Assuming Knuckles this may be wrong";
|
<< "Vive Pro Detected, Assuming Knuckles this may be wrong";
|
||||||
return "knuckles";
|
return "knuckles";
|
||||||
}
|
}
|
||||||
if ( hmdName == "holographic_hmd" )
|
if ( hmdName == "holographic_hmd" )
|
||||||
{
|
{
|
||||||
LOG( WARNING ) << "WMR detected, Assuming HP CONTROLLERS";
|
qWarning() << "WMR detected, Assuming HP CONTROLLERS";
|
||||||
return "hpmotioncontroller";
|
return "hpmotioncontroller";
|
||||||
}
|
}
|
||||||
if ( hmdName == "rift" )
|
if ( hmdName == "rift" )
|
||||||
{
|
{
|
||||||
return "oculus_touch";
|
return "oculus_touch";
|
||||||
}
|
}
|
||||||
LOG( WARNING ) << "Headset Not recognized Assuming touch controllers "
|
qWarning() << "Headset Not recognized Assuming touch controllers "
|
||||||
+ hmdName;
|
+ hmdName;
|
||||||
return "oculus_touch";
|
return "oculus_touch";
|
||||||
}
|
}
|
||||||
if ( right != left )
|
if ( right != left )
|
||||||
{
|
{
|
||||||
LOG( WARNING ) << "WARNING: Left and Right Controllers are Different, "
|
qWarning() << "WARNING: Left and Right Controllers are Different, "
|
||||||
"Prioritizing right";
|
"Prioritizing right";
|
||||||
}
|
}
|
||||||
if ( rightIdx == 999 )
|
if ( rightIdx == 999 )
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
#include <openvr.h>
|
#include <openvr.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <easylogging++.h>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
/* Wrapper For OpenVR's IVR settings class, allows us to do our error logging
|
/* Wrapper For OpenVR's IVR settings class, allows us to do our error logging
|
||||||
* while also minimizing code
|
* while also minimizing code
|
||||||
|
|||||||
+187
-172
@@ -11,17 +11,27 @@
|
|||||||
#include <QtWidgets/QGraphicsSceneMouseEvent>
|
#include <QtWidgets/QGraphicsSceneMouseEvent>
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
#include <QtWidgets/QGraphicsEllipseItem>
|
#include <QtWidgets/QGraphicsEllipseItem>
|
||||||
|
#include <QQuickGraphicsConfiguration>
|
||||||
#include <QOpenGLExtraFunctions>
|
#include <QOpenGLExtraFunctions>
|
||||||
#include <QCursor>
|
#include <QCursor>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFontInfo>
|
||||||
|
#include <QScreen>
|
||||||
|
#include <QtLogging>
|
||||||
|
#include <QtDebug>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <openvr.h>
|
#include <openvr.h>
|
||||||
#include <easylogging++.h>
|
#include "openvr/ovr_overlay_wrapper.h"
|
||||||
#include "utils/Matrix.h"
|
#include "utils/Matrix.h"
|
||||||
#include "keyboard_input/input_sender.h"
|
#include "keyboard_input/input_sender.h"
|
||||||
#include "settings/settings.h"
|
#include "settings/settings.h"
|
||||||
|
#include <qapplication.h>
|
||||||
|
#include <qlogging.h>
|
||||||
|
#include <qnamespace.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
// application namespace
|
// application namespace
|
||||||
namespace advsettings
|
namespace advsettings
|
||||||
@@ -40,10 +50,13 @@ int verifyCustomTickRate( const int tickRate )
|
|||||||
return tickRate;
|
return tickRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayController::OverlayController( bool desktopMode,
|
OverlayController::OverlayController(
|
||||||
bool noSound,
|
bool desktopMode,
|
||||||
QQmlEngine& qmlEngine )
|
bool noSound,
|
||||||
: QObject(), m_desktopMode( desktopMode ), m_noSound( 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(
|
m_verifiedCustomTickRateMs( verifyCustomTickRate( settings::getSetting(
|
||||||
settings::IntSetting::APPLICATION_customTickRateMs ) ) ),
|
settings::IntSetting::APPLICATION_customTickRateMs ) ) ),
|
||||||
m_actions(), m_alarm()
|
m_actions(), m_alarm()
|
||||||
@@ -60,61 +73,23 @@ OverlayController::OverlayController( bool desktopMode,
|
|||||||
// Throw Error If over 16k characters in path string
|
// Throw Error If over 16k characters in path string
|
||||||
if ( !pathIsGood )
|
if ( !pathIsGood )
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Error Finding VR Runtime Path, Attempting Recovery: ";
|
qCritical() << "Error Finding VR Runtime Path, Attempting Recovery: ";
|
||||||
uint32_t maxLengthRe = requiredLength;
|
uint32_t maxLengthRe = requiredLength;
|
||||||
LOG( INFO ) << "Open VR reporting Required path length of: "
|
qInfo() << "Open VR reporting Required path length of: " << maxLengthRe;
|
||||||
<< maxLengthRe;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_runtimePathUrl = QUrl::fromLocalFile( tempRuntimePath );
|
m_runtimePathUrl = QUrl::fromLocalFile( tempRuntimePath );
|
||||||
LOG( INFO ) << "VR Runtime Path: " << m_runtimePathUrl.toLocalFile();
|
qInfo() << "VR Runtime Path: " << m_runtimePathUrl.toLocalFile();
|
||||||
|
|
||||||
const double initVol = soundVolume();
|
const double initVol = soundVolume();
|
||||||
constexpr auto clickSoundURL = "res/sounds/click.wav";
|
m_activationSoundEffect.setSource( QUrl( ":/qt/qml/AdvancedSettings/src/res/sounds/click.wav" ) );
|
||||||
const auto activationSoundFile
|
m_activationSoundEffect.setVolume( initVol );
|
||||||
= paths::binaryDirectoryFindFile( clickSoundURL );
|
|
||||||
|
|
||||||
if ( activationSoundFile.has_value() )
|
m_focusChangedSoundEffect.setSource( QUrl( ":/qt/qml/AdvancedSettings/src/res/sounds/focus.wav" ) );
|
||||||
{
|
m_focusChangedSoundEffect.setVolume( initVol );
|
||||||
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 );
|
|
||||||
|
|
||||||
if ( focusChangedSoundFile.has_value() )
|
m_alarm01SoundEffect.setSource( QUrl( ":/qt/qml/AdvancedSettings/src/res/sounds/alarm01.wav" ) );
|
||||||
{
|
m_alarm01SoundEffect.setVolume( 1.0 );
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have desktop mode flag ignore waht toggle says otherwise we use
|
// If we have desktop mode flag ignore waht toggle says otherwise we use
|
||||||
// toggle
|
// toggle
|
||||||
@@ -122,28 +97,7 @@ OverlayController::OverlayController( bool desktopMode,
|
|||||||
{
|
{
|
||||||
m_desktopMode = desktopModeToggle();
|
m_desktopMode = desktopModeToggle();
|
||||||
}
|
}
|
||||||
|
m_desktopMode = false;
|
||||||
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 );
|
|
||||||
|
|
||||||
if ( !vr::VROverlay() )
|
if ( !vr::VROverlay() )
|
||||||
{
|
{
|
||||||
@@ -344,11 +298,11 @@ OverlayController::OverlayController( bool desktopMode,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG( INFO ) << "Version Check: Feature disabled. Not checking version.";
|
qInfo() << "Version Check: Feature disabled. Not checking version.";
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG( INFO ) << "OPENSSL VERSION: "
|
qInfo() << "OPENSSL VERSION: "
|
||||||
<< QSslSocket::sslLibraryBuildVersionString();
|
<< QSslSocket::sslLibraryBuildVersionString();
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayController::~OverlayController()
|
OverlayController::~OverlayController()
|
||||||
@@ -373,13 +327,21 @@ void OverlayController::exitApp()
|
|||||||
Shutdown();
|
Shutdown();
|
||||||
QApplication::exit();
|
QApplication::exit();
|
||||||
|
|
||||||
LOG( INFO ) << "All systems exited.";
|
qInfo() << "All systems exited.";
|
||||||
exit( EXIT_SUCCESS );
|
exit( EXIT_SUCCESS );
|
||||||
// Does not fallthrough
|
// 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()
|
void OverlayController::Shutdown()
|
||||||
{
|
{
|
||||||
|
if ( m_shutdownDone )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_shutdownDone = true;
|
||||||
|
|
||||||
disconnect( &m_pumpEventsTimer,
|
disconnect( &m_pumpEventsTimer,
|
||||||
SIGNAL( timeout() ),
|
SIGNAL( timeout() ),
|
||||||
this,
|
this,
|
||||||
@@ -403,7 +365,21 @@ void OverlayController::Shutdown()
|
|||||||
m_pRenderTimer->stop();
|
m_pRenderTimer->stop();
|
||||||
m_pRenderTimer.reset();
|
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,
|
void OverlayController::SetWidget( QQuickItem* quickItem,
|
||||||
@@ -412,6 +388,7 @@ void OverlayController::SetWidget( QQuickItem* quickItem,
|
|||||||
{
|
{
|
||||||
if ( !m_desktopMode )
|
if ( !m_desktopMode )
|
||||||
{
|
{
|
||||||
|
qDebug() << "not desktop mode";
|
||||||
vr::VROverlayError overlayError
|
vr::VROverlayError overlayError
|
||||||
= vr::VROverlay()->CreateDashboardOverlay(
|
= vr::VROverlay()->CreateDashboardOverlay(
|
||||||
key.c_str(),
|
key.c_str(),
|
||||||
@@ -439,47 +416,62 @@ void OverlayController::SetWidget( QQuickItem* quickItem,
|
|||||||
vr::VROverlayFlags_SendVRSmoothScrollEvents,
|
vr::VROverlayFlags_SendVRSmoothScrollEvents,
|
||||||
true );
|
true );
|
||||||
|
|
||||||
constexpr auto thumbiconFilename = "res/img/icons/thumbicon.png";
|
|
||||||
const auto thumbIconPath
|
|
||||||
= paths::binaryDirectoryFindFile( thumbiconFilename );
|
|
||||||
if ( thumbIconPath.has_value() )
|
|
||||||
{
|
{
|
||||||
vr::VROverlay()->SetOverlayFromFile( m_ulOverlayThumbnailHandle,
|
QImage thumbiconImg( ":qt/qml/AdvancedSettings/src/res/img/icons/thumbicon.png");
|
||||||
thumbIconPath->c_str() );
|
ovr_overlay_wrapper::setOverlayFromQImage(
|
||||||
}
|
m_ulOverlayThumbnailHandle, thumbiconImg );
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG( ERROR ) << "Could not find thumbnail icon \""
|
|
||||||
<< thumbiconFilename << "\"";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Too many render calls in too short time overwhelm Qt and an
|
// Too many render calls in too short time overwhelm Qt and an
|
||||||
// assertion gets thrown. Therefore we use an timer to delay render
|
// assertion gets thrown. Therefore we use an timer to delay render
|
||||||
// calls
|
// calls
|
||||||
m_pRenderTimer.reset( new QTimer() );
|
m_pRenderTimer.reset( new QTimer() );
|
||||||
|
m_pRenderTimer->moveToThread( qApp->thread() );
|
||||||
m_pRenderTimer->setSingleShot( true );
|
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(),
|
connect( m_pRenderTimer.get(),
|
||||||
SIGNAL( timeout() ),
|
SIGNAL( timeout() ),
|
||||||
this,
|
this,
|
||||||
SLOT( renderOverlay() ) );
|
SLOT( renderOverlay() ) );
|
||||||
|
|
||||||
QOpenGLFramebufferObjectFormat fboFormat;
|
// Render target through the render backend (public Qt API only).
|
||||||
fboFormat.setAttachment(
|
// Call order: see render/RenderBackend.h.
|
||||||
QOpenGLFramebufferObject::CombinedDepthStencil );
|
m_renderBackend->configureWindow( &m_window );
|
||||||
fboFormat.setTextureTarget( GL_TEXTURE_2D );
|
if ( !m_renderBackend->makeCurrent() )
|
||||||
m_pFbo.reset( new QOpenGLFramebufferObject(
|
throw std::runtime_error( "render backend: makeCurrent failed" );
|
||||||
static_cast<int>( quickItem->width() ),
|
if ( !m_renderControl.initialize() )
|
||||||
static_cast<int>( quickItem->height() ),
|
throw std::runtime_error( "could not initialize m_renderControl" );
|
||||||
fboFormat ) );
|
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() );
|
quickItem->setParentItem( m_window.contentItem() );
|
||||||
m_window.setGeometry( 0,
|
m_window.setGeometry( 0,
|
||||||
0,
|
0,
|
||||||
static_cast<int>( quickItem->width() ),
|
static_cast<int>( quickItem->width() ),
|
||||||
static_cast<int>( quickItem->height() ) );
|
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
|
vr::HmdVector2_t vecWindowSize
|
||||||
= { static_cast<float>( quickItem->width() ),
|
= { static_cast<float>( quickItem->width() ),
|
||||||
@@ -502,15 +494,13 @@ void OverlayController::SetWidget( QQuickItem* quickItem,
|
|||||||
this,
|
this,
|
||||||
SLOT( OnTimeoutPumpEvents() ) );
|
SLOT( OnTimeoutPumpEvents() ) );
|
||||||
|
|
||||||
// Every 1ms we check if the current frame has advanced (for vsync)
|
applyTickInterval();
|
||||||
m_pumpEventsTimer.setInterval( 1 );
|
|
||||||
|
|
||||||
m_pumpEventsTimer.start();
|
m_pumpEventsTimer.start();
|
||||||
|
|
||||||
m_steamVRTabController.initStage2( this );
|
m_steamVRTabController.initStage2( this );
|
||||||
m_chaperoneTabController.initStage2( this );
|
m_chaperoneTabController.initStage2( this );
|
||||||
m_fixFloorTabController.initStage2( this );
|
m_fixFloorTabController.initStage2( this );
|
||||||
m_audioTabController.initStage2();
|
m_audioTabController.initStage2( this );
|
||||||
m_statisticsTabController.initStage2( this );
|
m_statisticsTabController.initStage2( this );
|
||||||
m_settingsTabController.initStage2( this );
|
m_settingsTabController.initStage2( this );
|
||||||
m_utilitiesTabController.initStage2( this );
|
m_utilitiesTabController.initStage2( this );
|
||||||
@@ -526,7 +516,7 @@ void OverlayController::SetWidget( QQuickItem* quickItem,
|
|||||||
autoApplyChaperoneName() );
|
autoApplyChaperoneName() );
|
||||||
if ( chapindex.first )
|
if ( chapindex.first )
|
||||||
{
|
{
|
||||||
LOG( INFO ) << "Auto Applying Chaperone";
|
qInfo() << "Auto Applying Chaperone";
|
||||||
m_chaperoneTabController.applyChaperoneProfile( chapindex.second );
|
m_chaperoneTabController.applyChaperoneProfile( chapindex.second );
|
||||||
// This should be the way to stop room-setup from starting... as it
|
// This should be the way to stop room-setup from starting... as it
|
||||||
// sends steamvr a signal that it is completed
|
// sends steamvr a signal that it is completed
|
||||||
@@ -534,7 +524,7 @@ void OverlayController::SetWidget( QQuickItem* quickItem,
|
|||||||
vr::EChaperoneConfigFile_Live );
|
vr::EChaperoneConfigFile_Live );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG( WARNING ) << "Profile Not Found for Auto Apply Chaperone!";
|
qWarning() << "Profile Not Found for Auto Apply Chaperone!";
|
||||||
}
|
}
|
||||||
auto indexList = ovr_system_wrapper::getAllConnectedDevices();
|
auto indexList = ovr_system_wrapper::getAllConnectedDevices();
|
||||||
|
|
||||||
@@ -546,21 +536,25 @@ void OverlayController::SetWidget( QQuickItem* quickItem,
|
|||||||
auto ctrlRole = info.second;
|
auto ctrlRole = info.second;
|
||||||
if ( vr::TrackedDeviceClass_HMD == ctrlRole )
|
if ( vr::TrackedDeviceClass_HMD == ctrlRole )
|
||||||
{
|
{
|
||||||
LOG(INFO) << "HMD is: " << ovr_system_wrapper::getDeviceName(device);
|
qInfo() << "HMD is: "
|
||||||
|
<< ovr_system_wrapper::getDeviceName( device );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( vr::TrackedControllerRole_LeftHand == ctrlRole )
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
if ( vr::TrackedControllerRole_RightHand == ctrlRole )
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG(INFO)<< "Initilizaion is complete--------------------------------------------------";
|
qInfo() << "Initilizaion is "
|
||||||
|
"complete--------------------------------------------------";
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayController::OnRenderRequest()
|
void OverlayController::OnRenderRequest()
|
||||||
@@ -581,29 +575,41 @@ void OverlayController::renderOverlay()
|
|||||||
&& !vr::VROverlay()->IsOverlayVisible(
|
&& !vr::VROverlay()->IsOverlayVisible(
|
||||||
m_ulOverlayThumbnailHandle ) ) )
|
m_ulOverlayThumbnailHandle ) ) )
|
||||||
return;
|
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.polishItems();
|
||||||
|
m_renderControl.beginFrame();
|
||||||
m_renderControl.sync();
|
m_renderControl.sync();
|
||||||
m_renderControl.render();
|
m_renderControl.render();
|
||||||
|
m_renderControl.endFrame();
|
||||||
|
m_renderBackend->finishFrame();
|
||||||
|
|
||||||
GLuint unTexture = m_pFbo->texture();
|
// --snapshot: writes the rendered overlay to a PNG at most once per
|
||||||
if ( unTexture != 0 )
|
// 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
|
m_snapshotTimer.start();
|
||||||
// To avoid any compiler warning because of cast to a larger
|
const QString tmp = m_snapshotPath + ".tmp";
|
||||||
// pointer type (warning C4312 on VC)
|
if ( m_renderBackend->readback().save( tmp, "PNG" ) )
|
||||||
vr::Texture_t texture = { reinterpret_cast<void*>(
|
{
|
||||||
static_cast<uint64_t>( unTexture ) ),
|
QFile::remove( m_snapshotPath );
|
||||||
vr::TextureType_OpenGL,
|
QFile::rename( tmp, m_snapshotPath );
|
||||||
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_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;
|
// SteamVR reports mouse coordinates in GL convention (y counted from the
|
||||||
#ifdef __linux__
|
// bottom) on every platform and for every graphics API (verified on the
|
||||||
float h = static_cast<float>( m_window.height() );
|
// Steam Frame with OpenGL and Vulkan); Qt counts y from the top.
|
||||||
y = h - y;
|
float y = static_cast<float>( m_window.height() ) - mouse.y;
|
||||||
#endif
|
return { mouse.x, y };
|
||||||
return QPoint( static_cast<int>( mouse.x ), static_cast<int>( y ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayController::processMediaKeyBindings()
|
void OverlayController::processMediaKeyBindings()
|
||||||
@@ -943,6 +948,8 @@ void OverlayController::setVsyncDisabled( bool value, bool notify )
|
|||||||
{
|
{
|
||||||
settings::setSetting( settings::BoolSetting::APPLICATION_vsyncDisabled,
|
settings::setSetting( settings::BoolSetting::APPLICATION_vsyncDisabled,
|
||||||
value );
|
value );
|
||||||
|
// The timer interval depends on the mode, see applyTickInterval().
|
||||||
|
applyTickInterval();
|
||||||
if ( notify )
|
if ( notify )
|
||||||
{
|
{
|
||||||
emit vsyncDisabledChanged( value );
|
emit vsyncDisabledChanged( value );
|
||||||
@@ -1013,7 +1020,7 @@ QString OverlayController::versionCheckText() const
|
|||||||
void OverlayController::setVersionCheckText( QString value, bool notify )
|
void OverlayController::setVersionCheckText( QString value, bool notify )
|
||||||
{
|
{
|
||||||
m_versionCheckText = value;
|
m_versionCheckText = value;
|
||||||
LOG( INFO ) << "m_versionCheckText = " << m_versionCheckText;
|
qInfo() << "m_versionCheckText = " << m_versionCheckText;
|
||||||
if ( notify )
|
if ( notify )
|
||||||
{
|
{
|
||||||
emit versionCheckTextChanged( m_versionCheckText );
|
emit versionCheckTextChanged( m_versionCheckText );
|
||||||
@@ -1053,6 +1060,8 @@ void OverlayController::setCustomTickRateMs( int value, bool notify )
|
|||||||
settings::setSetting( settings::IntSetting::APPLICATION_customTickRateMs,
|
settings::setSetting( settings::IntSetting::APPLICATION_customTickRateMs,
|
||||||
verifiedTickRate );
|
verifiedTickRate );
|
||||||
m_verifiedCustomTickRateMs = verifiedTickRate;
|
m_verifiedCustomTickRateMs = verifiedTickRate;
|
||||||
|
// In "vsync disabled" mode the timer runs at this interval.
|
||||||
|
applyTickInterval();
|
||||||
|
|
||||||
if ( notify )
|
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:
|
// vsync implementation:
|
||||||
// this function triggers every 1ms
|
// this function triggers every 1ms
|
||||||
// this function should remain lightweight and only check if it's time to
|
// this function should remain lightweight and only check if it's time to
|
||||||
@@ -1068,17 +1090,10 @@ void OverlayController::OnTimeoutPumpEvents()
|
|||||||
{
|
{
|
||||||
if ( vsyncDisabled() )
|
if ( vsyncDisabled() )
|
||||||
{
|
{
|
||||||
// check if it's time for a custom tick rate tick
|
// The timer already runs at the custom tick rate: every call is a
|
||||||
if ( m_customTickRateCounter > customTickRateMs() )
|
// tick.
|
||||||
{
|
mainEventLoop();
|
||||||
mainEventLoop();
|
updateRate.incrementCounter();
|
||||||
m_customTickRateCounter = 0;
|
|
||||||
updateRate.incrementCounter();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_customTickRateCounter++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// vsync is enabled
|
// vsync is enabled
|
||||||
@@ -1134,7 +1149,7 @@ void OverlayController::mainEventLoop()
|
|||||||
{
|
{
|
||||||
case vr::VREvent_MouseMove:
|
case vr::VREvent_MouseMove:
|
||||||
{
|
{
|
||||||
QPoint ptNewMouse = getMousePositionForEvent( vrEvent.data.mouse );
|
QPointF ptNewMouse = getMousePositionForEvent( vrEvent.data.mouse );
|
||||||
if ( ptNewMouse != m_ptLastMouse )
|
if ( ptNewMouse != m_ptLastMouse )
|
||||||
{
|
{
|
||||||
QMouseEvent mouseEvent( QEvent::MouseMove,
|
QMouseEvent mouseEvent( QEvent::MouseMove,
|
||||||
@@ -1142,7 +1157,7 @@ void OverlayController::mainEventLoop()
|
|||||||
m_window.mapToGlobal( ptNewMouse ),
|
m_window.mapToGlobal( ptNewMouse ),
|
||||||
Qt::NoButton,
|
Qt::NoButton,
|
||||||
m_lastMouseButtons,
|
m_lastMouseButtons,
|
||||||
nullptr );
|
Qt::NoModifier );
|
||||||
m_ptLastMouse = ptNewMouse;
|
m_ptLastMouse = ptNewMouse;
|
||||||
QCoreApplication::sendEvent( &m_window, &mouseEvent );
|
QCoreApplication::sendEvent( &m_window, &mouseEvent );
|
||||||
OnRenderRequest();
|
OnRenderRequest();
|
||||||
@@ -1152,7 +1167,7 @@ void OverlayController::mainEventLoop()
|
|||||||
|
|
||||||
case vr::VREvent_MouseButtonDown:
|
case vr::VREvent_MouseButtonDown:
|
||||||
{
|
{
|
||||||
QPoint ptNewMouse = getMousePositionForEvent( vrEvent.data.mouse );
|
QPointF ptNewMouse = getMousePositionForEvent( vrEvent.data.mouse );
|
||||||
Qt::MouseButton button
|
Qt::MouseButton button
|
||||||
= vrEvent.data.mouse.button == vr::VRMouseButton_Right
|
= vrEvent.data.mouse.button == vr::VRMouseButton_Right
|
||||||
? Qt::RightButton
|
? Qt::RightButton
|
||||||
@@ -1163,14 +1178,14 @@ void OverlayController::mainEventLoop()
|
|||||||
m_window.mapToGlobal( ptNewMouse ),
|
m_window.mapToGlobal( ptNewMouse ),
|
||||||
button,
|
button,
|
||||||
m_lastMouseButtons,
|
m_lastMouseButtons,
|
||||||
nullptr );
|
Qt::NoModifier );
|
||||||
QCoreApplication::sendEvent( &m_window, &mouseEvent );
|
QCoreApplication::sendEvent( &m_window, &mouseEvent );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case vr::VREvent_MouseButtonUp:
|
case vr::VREvent_MouseButtonUp:
|
||||||
{
|
{
|
||||||
QPoint ptNewMouse = getMousePositionForEvent( vrEvent.data.mouse );
|
QPointF ptNewMouse = getMousePositionForEvent( vrEvent.data.mouse );
|
||||||
Qt::MouseButton button
|
Qt::MouseButton button
|
||||||
= vrEvent.data.mouse.button == vr::VRMouseButton_Right
|
= vrEvent.data.mouse.button == vr::VRMouseButton_Right
|
||||||
? Qt::RightButton
|
? Qt::RightButton
|
||||||
@@ -1181,7 +1196,7 @@ void OverlayController::mainEventLoop()
|
|||||||
m_window.mapToGlobal( ptNewMouse ),
|
m_window.mapToGlobal( ptNewMouse ),
|
||||||
button,
|
button,
|
||||||
m_lastMouseButtons,
|
m_lastMouseButtons,
|
||||||
nullptr );
|
Qt::NoModifier );
|
||||||
QCoreApplication::sendEvent( &m_window, &mouseEvent );
|
QCoreApplication::sendEvent( &m_window, &mouseEvent );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1197,10 +1212,10 @@ void OverlayController::mainEventLoop()
|
|||||||
* ( 360.0f * 8.0f ) ),
|
* ( 360.0f * 8.0f ) ),
|
||||||
static_cast<int>( vrEvent.data.scroll.ydelta
|
static_cast<int>( vrEvent.data.scroll.ydelta
|
||||||
* ( 360.0f * 8.0f ) ) ),
|
* ( 360.0f * 8.0f ) ) ),
|
||||||
0,
|
|
||||||
Qt::Vertical,
|
|
||||||
m_lastMouseButtons,
|
m_lastMouseButtons,
|
||||||
nullptr );
|
Qt::KeyboardModifiers::fromInt( 0 ),
|
||||||
|
Qt::ScrollPhase::NoScrollPhase,
|
||||||
|
false );
|
||||||
QCoreApplication::sendEvent( &m_window, &wheelEvent );
|
QCoreApplication::sendEvent( &m_window, &wheelEvent );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1213,7 +1228,7 @@ void OverlayController::mainEventLoop()
|
|||||||
|
|
||||||
case vr::VREvent_Quit:
|
case vr::VREvent_Quit:
|
||||||
{
|
{
|
||||||
LOG( INFO ) << "Received quit request.";
|
qInfo() << "Received quit request.";
|
||||||
vr::VRSystem()->AcknowledgeQuit_Exiting(); // Let us buy some
|
vr::VRSystem()->AcknowledgeQuit_Exiting(); // Let us buy some
|
||||||
// time just in case
|
// time just in case
|
||||||
|
|
||||||
@@ -1225,14 +1240,14 @@ void OverlayController::mainEventLoop()
|
|||||||
|
|
||||||
case vr::VREvent_DashboardActivated:
|
case vr::VREvent_DashboardActivated:
|
||||||
{
|
{
|
||||||
LOG( DEBUG ) << "Dashboard activated";
|
qDebug() << "Dashboard activated";
|
||||||
m_dashboardVisible = true;
|
m_dashboardVisible = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case vr::VREvent_DashboardDeactivated:
|
case vr::VREvent_DashboardDeactivated:
|
||||||
{
|
{
|
||||||
LOG( DEBUG ) << "Dashboard deactivated";
|
qDebug() << "Dashboard deactivated";
|
||||||
m_dashboardVisible = false;
|
m_dashboardVisible = false;
|
||||||
settings::saveChangedSettings();
|
settings::saveChangedSettings();
|
||||||
if(0UL != m_lastTextUID){
|
if(0UL != m_lastTextUID){
|
||||||
@@ -1412,7 +1427,7 @@ void OverlayController::RotateUniverseCenter(
|
|||||||
// }
|
// }
|
||||||
vr::HmdMatrix34_t curPos;
|
vr::HmdMatrix34_t curPos;
|
||||||
auto calState = vr::VRChaperone()->GetCalibrationState();
|
auto calState = vr::VRChaperone()->GetCalibrationState();
|
||||||
LOG( INFO ) << "Calibration State on Rotate is: " << calState;
|
qInfo() << "Calibration State on Rotate is: " << calState;
|
||||||
if ( universe == vr::TrackingUniverseStanding )
|
if ( universe == vr::TrackingUniverseStanding )
|
||||||
{
|
{
|
||||||
vr::VRChaperoneSetup()->GetWorkingStandingZeroPoseToRawTrackingPose(
|
vr::VRChaperoneSetup()->GetWorkingStandingZeroPoseToRawTrackingPose(
|
||||||
@@ -1427,7 +1442,8 @@ void OverlayController::RotateUniverseCenter(
|
|||||||
{
|
{
|
||||||
//TODO 8/3/26, will see if this causes issues
|
//TODO 8/3/26, will see if this causes issues
|
||||||
//should exclusively happen with openxr
|
//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);
|
vr::VRChaperoneSetup()->GetLiveSeatedZeroPoseToRawTrackingPose(&curPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1709,7 +1725,7 @@ void OverlayController::OnNetworkReply( QNetworkReply* reply )
|
|||||||
if ( reply->error() == QNetworkReply::NoError )
|
if ( reply->error() == QNetworkReply::NoError )
|
||||||
{
|
{
|
||||||
QByteArray replyByteData = reply->readAll();
|
QByteArray replyByteData = reply->readAll();
|
||||||
LOG( INFO ) << "Version Check: Recieved Data: " << replyByteData;
|
qInfo() << "Version Check: Recieved Data: " << replyByteData;
|
||||||
QJsonParseError parseError;
|
QJsonParseError parseError;
|
||||||
m_remoteVersionJsonDocument
|
m_remoteVersionJsonDocument
|
||||||
= QJsonDocument::fromJson( replyByteData, &parseError );
|
= QJsonDocument::fromJson( replyByteData, &parseError );
|
||||||
@@ -1752,10 +1768,9 @@ void OverlayController::OnNetworkReply( QNetworkReply* reply )
|
|||||||
+ QString::number( m_remoteVersionPatch )
|
+ QString::number( m_remoteVersionPatch )
|
||||||
+ ") available." );
|
+ ") available." );
|
||||||
}
|
}
|
||||||
LOG( INFO )
|
qInfo() << "Version Check: Newer version ("
|
||||||
<< "Version Check: Newer version (" << m_remoteVersionMajor
|
<< m_remoteVersionMajor << "." << m_remoteVersionMinor
|
||||||
<< "." << m_remoteVersionMinor << "."
|
<< "." << m_remoteVersionPatch << ") available.";
|
||||||
<< m_remoteVersionPatch << ") available.";
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1764,22 +1779,22 @@ void OverlayController::OnNetworkReply( QNetworkReply* reply )
|
|||||||
setVersionCheckText( m_optionalMessage );
|
setVersionCheckText( m_optionalMessage );
|
||||||
}
|
}
|
||||||
setNewVersionDetected( false );
|
setNewVersionDetected( false );
|
||||||
LOG( INFO ) << "Version Check: Installed version is latest "
|
qInfo() << "Version Check: Installed version is latest "
|
||||||
"release.";
|
"release.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG( ERROR )
|
qCritical()
|
||||||
<< "Version Check: Error parsing json. QJsonParseError = "
|
<< "Version Check: Error parsing json. QJsonParseError = "
|
||||||
<< parseError.error;
|
<< parseError.error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG( ERROR ) << "Version Check: Error connecting to network. "
|
qCritical() << "Version Check: Error connecting to network. "
|
||||||
"QNetworkReply::NetworkError = "
|
"QNetworkReply::NetworkError = "
|
||||||
<< reply->error();
|
<< reply->error();
|
||||||
}
|
}
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-9
@@ -1,9 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <openvr.h>
|
#include <openvr.h>
|
||||||
#include <QtCore/QtCore>
|
#include <QtCore/QtCore>
|
||||||
// because of incompatibilities with QtOpenGL and GLEW we need to cherry pick
|
// because of incompatibilities with QtOpenGL and GLEW we need to cherry pick
|
||||||
// includes
|
// includes
|
||||||
#include <QVector2D>
|
|
||||||
#include <QMatrix4x4>
|
#include <QMatrix4x4>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QVector2D>
|
#include <QVector2D>
|
||||||
@@ -21,8 +22,11 @@
|
|||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
|
#include <QQuickRenderTarget>
|
||||||
|
#include <QElapsedTimer>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <easylogging++.h>
|
#include <optional>
|
||||||
|
#include "render/RenderBackend.h"
|
||||||
|
|
||||||
#include "openvr/openvr_init.h"
|
#include "openvr/openvr_init.h"
|
||||||
|
|
||||||
@@ -105,18 +109,34 @@ private:
|
|||||||
vr::VROverlayHandle_t m_ulOverlayThumbnailHandle
|
vr::VROverlayHandle_t m_ulOverlayThumbnailHandle
|
||||||
= vr::k_ulOverlayHandleInvalid;
|
= 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;
|
QQuickRenderControl m_renderControl;
|
||||||
QQuickWindow m_window{ &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;
|
std::unique_ptr<QOpenGLFramebufferObject> m_pFbo;
|
||||||
|
QQuickRenderTarget m_pRenderTarget;
|
||||||
QOpenGLContext m_openGLContext;
|
QOpenGLContext m_openGLContext;
|
||||||
QOffscreenSurface m_offscreenSurface;
|
QOffscreenSurface m_offscreenSurface;
|
||||||
|
*/
|
||||||
|
|
||||||
QTimer m_pumpEventsTimer;
|
QTimer m_pumpEventsTimer;
|
||||||
std::unique_ptr<QTimer> m_pRenderTimer;
|
std::unique_ptr<QTimer> m_pRenderTimer;
|
||||||
bool m_dashboardVisible = false;
|
bool m_dashboardVisible = false;
|
||||||
|
|
||||||
QPoint m_ptLastMouse;
|
QPointF m_ptLastMouse;
|
||||||
Qt::MouseButtons m_lastMouseButtons = nullptr;
|
Qt::MouseButtons m_lastMouseButtons;
|
||||||
|
|
||||||
bool m_desktopMode;
|
bool m_desktopMode;
|
||||||
bool m_noSound;
|
bool m_noSound;
|
||||||
@@ -143,7 +163,6 @@ private:
|
|||||||
uint64_t m_currentFrame = 0;
|
uint64_t m_currentFrame = 0;
|
||||||
uint64_t m_lastFrame = 0;
|
uint64_t m_lastFrame = 0;
|
||||||
int m_vsyncTooLateCounter = 0;
|
int m_vsyncTooLateCounter = 0;
|
||||||
int m_customTickRateCounter = 0;
|
|
||||||
int m_verifiedCustomTickRateMs = 0;
|
int m_verifiedCustomTickRateMs = 0;
|
||||||
|
|
||||||
input::SteamIVRInput m_actions;
|
input::SteamIVRInput m_actions;
|
||||||
@@ -154,8 +173,9 @@ private:
|
|||||||
QJsonDocument m_remoteVersionJsonDocument = QJsonDocument();
|
QJsonDocument m_remoteVersionJsonDocument = QJsonDocument();
|
||||||
QJsonObject m_remoteVersionJsonObject;
|
QJsonObject m_remoteVersionJsonObject;
|
||||||
|
|
||||||
public: // I know it's an ugly hack to make them public to enable external
|
public:
|
||||||
// access, but I am too lazy to implement getters.
|
// 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;
|
SteamVRTabController m_steamVRTabController;
|
||||||
ChaperoneTabController m_chaperoneTabController;
|
ChaperoneTabController m_chaperoneTabController;
|
||||||
MoveCenterTabController m_moveCenterTabController;
|
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;
|
bool m_incomingReset = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPoint getMousePositionForEvent( vr::VREvent_Mouse_t mouse );
|
QPointF getMousePositionForEvent( vr::VREvent_Mouse_t mouse );
|
||||||
void processInputBindings();
|
void processInputBindings();
|
||||||
void processMediaKeyBindings();
|
void processMediaKeyBindings();
|
||||||
void processMotionBindings();
|
void processMotionBindings();
|
||||||
@@ -179,14 +199,27 @@ private:
|
|||||||
void processRotationBindings();
|
void processRotationBindings();
|
||||||
void processExclusiveInputBinding();
|
void processExclusiveInputBinding();
|
||||||
|
|
||||||
|
void applyTickInterval();
|
||||||
|
|
||||||
bool m_exclusiveState = false;
|
bool m_exclusiveState = false;
|
||||||
bool m_keyPressOneState = false;
|
bool m_keyPressOneState = false;
|
||||||
bool m_keyPressTwoState = false;
|
bool m_keyPressTwoState = false;
|
||||||
|
|
||||||
public:
|
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();
|
virtual ~OverlayController();
|
||||||
|
|
||||||
|
// Diagnosis (--snapshot): see renderOverlay(). Empty: off.
|
||||||
|
void setSnapshotPath( const QString& path )
|
||||||
|
{
|
||||||
|
m_snapshotPath = path;
|
||||||
|
}
|
||||||
|
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
Q_INVOKABLE void exitApp();
|
Q_INVOKABLE void exitApp();
|
||||||
Q_INVOKABLE void setAutoChapProfileName( int index );
|
Q_INVOKABLE void setAutoChapProfileName( int index );
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
"app_key": "steam.overlay.1009850",
|
"app_key": "steam.overlay.1009850",
|
||||||
"launch_type": "binary",
|
"launch_type": "binary",
|
||||||
"binary_path_windows": "AdvancedSettings.exe",
|
"binary_path_windows": "AdvancedSettings.exe",
|
||||||
|
"binary_path_linux": "AdvancedSettings_app",
|
||||||
|
"binary_path_linux_arm": "AdvancedSettings_app",
|
||||||
"is_dashboard_overlay": true,
|
"is_dashboard_overlay": true,
|
||||||
|
|
||||||
"strings": {
|
"strings": {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
};
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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 );
|
||||||
@@ -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,
|
||||||
|
®ion );
|
||||||
|
},
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls 2.0
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import QtQuick.Dialogs 1.2
|
import QtQuick.Dialogs
|
||||||
import ovras.advsettings 1.0
|
import ovras.advsettings 1.0
|
||||||
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
||||||
import "common"
|
import "common"
|
||||||
@@ -415,43 +415,43 @@ MyStackViewPage {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ChaperoneTabController
|
target: ChaperoneTabController
|
||||||
onChaperoneSwitchToBeginnerEnabledChanged: {
|
function onChaperoneSwitchToBeginnerEnabledChanged() {
|
||||||
switchBeginnerToggle.checked = ChaperoneTabController.chaperoneSwitchToBeginnerEnabled
|
switchBeginnerToggle.checked = ChaperoneTabController.chaperoneSwitchToBeginnerEnabled
|
||||||
}
|
}
|
||||||
onChaperoneSwitchToBeginnerDistanceChanged: {
|
function onChaperoneSwitchToBeginnerDistanceChanged() {
|
||||||
var d = ChaperoneTabController.chaperoneSwitchToBeginnerDistance.toFixed(2)
|
var d = ChaperoneTabController.chaperoneSwitchToBeginnerDistance.toFixed(2)
|
||||||
if (d <= switchBeginnerDistanceSlider.to && Math.abs(switchBeginnerDistanceSlider.value - d) > 0.0008) {
|
if (d <= switchBeginnerDistanceSlider.to && Math.abs(switchBeginnerDistanceSlider.value - d) > 0.0008) {
|
||||||
switchBeginnerDistanceSlider.value = d
|
switchBeginnerDistanceSlider.value = d
|
||||||
}
|
}
|
||||||
switchBeginnerDistanceText.text = d
|
switchBeginnerDistanceText.text = d
|
||||||
}
|
}
|
||||||
onChaperoneHapticFeedbackEnabledChanged: {
|
function onChaperoneHapticFeedbackEnabledChanged() {
|
||||||
hapticFeedbackToggle.checked = ChaperoneTabController.chaperoneHapticFeedbackEnabled
|
hapticFeedbackToggle.checked = ChaperoneTabController.chaperoneHapticFeedbackEnabled
|
||||||
}
|
}
|
||||||
onChaperoneHapticFeedbackDistanceChanged: {
|
function onChaperoneHapticFeedbackDistanceChanged() {
|
||||||
var d = ChaperoneTabController.chaperoneHapticFeedbackDistance.toFixed(2)
|
var d = ChaperoneTabController.chaperoneHapticFeedbackDistance.toFixed(2)
|
||||||
if (d <= hapticFeedbackDistanceSlider.to && Math.abs(hapticFeedbackDistanceSlider.value - d) > 0.0008) {
|
if (d <= hapticFeedbackDistanceSlider.to && Math.abs(hapticFeedbackDistanceSlider.value - d) > 0.0008) {
|
||||||
hapticFeedbackDistanceSlider.value = d
|
hapticFeedbackDistanceSlider.value = d
|
||||||
}
|
}
|
||||||
hapticFeedbackDistanceText.text = d
|
hapticFeedbackDistanceText.text = d
|
||||||
}
|
}
|
||||||
onChaperoneAlarmSoundEnabledChanged: {
|
function onChaperoneAlarmSoundEnabledChanged() {
|
||||||
audioWarningToggle.checked = ChaperoneTabController.chaperoneAlarmSoundEnabled
|
audioWarningToggle.checked = ChaperoneTabController.chaperoneAlarmSoundEnabled
|
||||||
}
|
}
|
||||||
onChaperoneAlarmSoundLoopingChanged: {
|
function onChaperoneAlarmSoundLoopingChanged() {
|
||||||
audioWarningLoopingToggle.checked = ChaperoneTabController.chaperoneAlarmSoundLooping
|
audioWarningLoopingToggle.checked = ChaperoneTabController.chaperoneAlarmSoundLooping
|
||||||
}
|
}
|
||||||
onChaperoneAlarmSoundDistanceChanged: {
|
function onChaperoneAlarmSoundDistanceChanged() {
|
||||||
var d = ChaperoneTabController.chaperoneAlarmSoundDistance.toFixed(2)
|
var d = ChaperoneTabController.chaperoneAlarmSoundDistance.toFixed(2)
|
||||||
if (d <= audioWarningDistanceSlider.to && Math.abs(audioWarningDistanceSlider.value - d) > 0.0008) {
|
if (d <= audioWarningDistanceSlider.to && Math.abs(audioWarningDistanceSlider.value - d) > 0.0008) {
|
||||||
audioWarningDistanceSlider.value = d
|
audioWarningDistanceSlider.value = d
|
||||||
}
|
}
|
||||||
audioWarningDistanceText.text = d
|
audioWarningDistanceText.text = d
|
||||||
}
|
}
|
||||||
onChaperoneShowDashboardEnabledChanged: {
|
function onChaperoneShowDashboardEnabledChanged() {
|
||||||
openDashboardToggle.checked = ChaperoneTabController.chaperoneShowDashboardEnabled
|
openDashboardToggle.checked = ChaperoneTabController.chaperoneShowDashboardEnabled
|
||||||
}
|
}
|
||||||
onChaperoneShowDashboardDistanceChanged: {
|
function onChaperoneShowDashboardDistanceChanged() {
|
||||||
var d = ChaperoneTabController.chaperoneShowDashboardDistance.toFixed(2)
|
var d = ChaperoneTabController.chaperoneShowDashboardDistance.toFixed(2)
|
||||||
if (d <= openDashboardDistanceSlider.to && Math.abs(openDashboardDistanceSlider.value - d) > 0.0008) {
|
if (d <= openDashboardDistanceSlider.to && Math.abs(openDashboardDistanceSlider.value - d) > 0.0008) {
|
||||||
openDashboardDistanceSlider.value = d
|
openDashboardDistanceSlider.value = d
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ MyStackViewPage {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: FixFloorTabController
|
target: FixFloorTabController
|
||||||
onStatusMessageSignal: {
|
function onStatusMessageSignal() {
|
||||||
if (statusMessageTimer.running) {
|
if (statusMessageTimer.running) {
|
||||||
statusMessageTimer.stop()
|
statusMessageTimer.stop()
|
||||||
}
|
}
|
||||||
@@ -148,15 +148,15 @@ MyStackViewPage {
|
|||||||
statusMessageTimer.interval = FixFloorTabController.currentStatusMessageTimeout() * 1000
|
statusMessageTimer.interval = FixFloorTabController.currentStatusMessageTimeout() * 1000
|
||||||
statusMessageTimer.start()
|
statusMessageTimer.start()
|
||||||
}
|
}
|
||||||
onMeasureStartSignal: {
|
function onMeasureStartSignal() {
|
||||||
fixButton.enabled = false
|
fixButton.enabled = false
|
||||||
undoFixButton.enabled = false
|
undoFixButton.enabled = false
|
||||||
}
|
}
|
||||||
onMeasureEndSignal: {
|
function onMeasureEndSignal() {
|
||||||
fixButton.enabled = true
|
fixButton.enabled = true
|
||||||
// undoFixButton.enabled = FixFloorTabController.canUndo
|
// undoFixButton.enabled = FixFloorTabController.canUndo
|
||||||
}
|
}
|
||||||
onCanUndoChanged: {
|
function onCanUndoChanged() {
|
||||||
//undoFixButton.enabled = FixFloorTabController.canUndo
|
//undoFixButton.enabled = FixFloorTabController.canUndo
|
||||||
// revert below to this -^
|
// revert below to this -^
|
||||||
undoFixButton.enabled = false
|
undoFixButton.enabled = false
|
||||||
|
|||||||
@@ -524,33 +524,33 @@ MyStackViewPage {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: MoveCenterTabController
|
target: MoveCenterTabController
|
||||||
onOffsetXChanged: {
|
function onOffsetXChanged() {
|
||||||
spaceMoveXText.text = MoveCenterTabController.offsetX.toFixed(2)
|
spaceMoveXText.text = MoveCenterTabController.offsetX.toFixed(2)
|
||||||
}
|
}
|
||||||
onOffsetYChanged: {
|
function onOffsetYChanged() {
|
||||||
spaceMoveYText.text = MoveCenterTabController.offsetY.toFixed(2)
|
spaceMoveYText.text = MoveCenterTabController.offsetY.toFixed(2)
|
||||||
}
|
}
|
||||||
onOffsetZChanged: {
|
function onOffsetZChanged() {
|
||||||
spaceMoveZText.text = MoveCenterTabController.offsetZ.toFixed(2)
|
spaceMoveZText.text = MoveCenterTabController.offsetZ.toFixed(2)
|
||||||
}
|
}
|
||||||
onRotationChanged: {
|
function onRotationChanged() {
|
||||||
spaceRotationSlider.value = ( MoveCenterTabController.rotation / 100 )
|
spaceRotationSlider.value = ( MoveCenterTabController.rotation / 100 )
|
||||||
}
|
}
|
||||||
onTempRotationChanged: {
|
function onTempRotationChanged() {
|
||||||
var intTempRotation = MoveCenterTabController.tempRotation
|
var intTempRotation = MoveCenterTabController.tempRotation
|
||||||
spaceRotationSlider.value = ( intTempRotation / 100 )
|
spaceRotationSlider.value = ( intTempRotation / 100 )
|
||||||
spaceRotationText.text = ( intTempRotation / 100 ) + "°"
|
spaceRotationText.text = ( intTempRotation / 100 ) + "°"
|
||||||
}
|
}
|
||||||
onLockXToggleChanged: {
|
function onLockXToggleChanged() {
|
||||||
lockXToggle.checked = MoveCenterTabController.lockXToggle
|
lockXToggle.checked = MoveCenterTabController.lockXToggle
|
||||||
}
|
}
|
||||||
onLockYToggleChanged: {
|
function onLockYToggleChanged() {
|
||||||
lockYToggle.checked = MoveCenterTabController.lockYToggle
|
lockYToggle.checked = MoveCenterTabController.lockYToggle
|
||||||
}
|
}
|
||||||
onLockZToggleChanged: {
|
function onLockZToggleChanged() {
|
||||||
lockZToggle.checked = MoveCenterTabController.lockZToggle
|
lockZToggle.checked = MoveCenterTabController.lockZToggle
|
||||||
}
|
}
|
||||||
onTrackingUniverseChanged: {
|
function onTrackingUniverseChanged() {
|
||||||
if (MoveCenterTabController.trackingUniverse === 0) {
|
if (MoveCenterTabController.trackingUniverse === 0) {
|
||||||
spaceModeText.text = "Sitting"
|
spaceModeText.text = "Sitting"
|
||||||
spaceSeatedRecenter.visible = true
|
spaceSeatedRecenter.visible = true
|
||||||
@@ -560,7 +560,7 @@ MyStackViewPage {
|
|||||||
spaceModeText.text = "Unknown(" + MoveCenterTabController.trackingUniverse + ")"
|
spaceModeText.text = "Unknown(" + MoveCenterTabController.trackingUniverse + ")"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onOffsetProfilesUpdated: {
|
function onOffsetProfilesUpdated() {
|
||||||
reloadOffsetProfiles()
|
reloadOffsetProfiles()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-38
@@ -1,7 +1,7 @@
|
|||||||
import QtQuick 2.7
|
import QtQuick
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts
|
||||||
import ovras.advsettings 1.0
|
import ovras.advsettings
|
||||||
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
||||||
import "common"
|
import "common"
|
||||||
import "utilities_page"
|
import "utilities_page"
|
||||||
@@ -28,107 +28,107 @@ MyStackViewPage {
|
|||||||
spacing: 10
|
spacing: 10
|
||||||
MyPushButton {
|
MyPushButton {
|
||||||
id: steamVRButton
|
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
|
activationSoundEnabled: false
|
||||||
text: " SteamVR"
|
text: " SteamVR"
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playFocusChangedSound()
|
OverlayController.playFocusChangedSound()
|
||||||
mainView.push(steamVRPage)
|
mainView.push(steamVRPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MyPushButton {
|
MyPushButton {
|
||||||
id: chaperoneButton
|
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
|
activationSoundEnabled: false
|
||||||
text: " Chaperone"
|
text: " Chaperone"
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playFocusChangedSound()
|
OverlayController.playFocusChangedSound()
|
||||||
mainView.push(chaperonePage)
|
mainView.push(chaperonePage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MyPushButton {
|
MyPushButton {
|
||||||
id: playSpaceButton
|
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
|
activationSoundEnabled: false
|
||||||
text: " Offsets"
|
text: " Offsets"
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playFocusChangedSound()
|
OverlayController.playFocusChangedSound()
|
||||||
mainView.push(playspacePage)
|
mainView.push(playspacePage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MyPushButton {
|
MyPushButton {
|
||||||
id: motionButton
|
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
|
activationSoundEnabled: false
|
||||||
text: " Motion"
|
text: " Motion"
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playFocusChangedSound()
|
OverlayController.playFocusChangedSound()
|
||||||
mainView.push(motionPage)
|
mainView.push(motionPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MyPushButton {
|
MyPushButton {
|
||||||
id: rotationButton
|
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
|
activationSoundEnabled: false
|
||||||
text: " Rotation"
|
text: " Rotation"
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playFocusChangedSound()
|
OverlayController.playFocusChangedSound()
|
||||||
mainView.push(rotationPage)
|
mainView.push(rotationPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MyPushButton {
|
MyPushButton {
|
||||||
id: fixFloorButton
|
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
|
activationSoundEnabled: false
|
||||||
text: " Space Fix"
|
text: " Space Fix"
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playFocusChangedSound()
|
OverlayController.playFocusChangedSound()
|
||||||
mainView.push(fixFloorPage)
|
mainView.push(fixFloorPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MyPushButton {
|
MyPushButton {
|
||||||
id: audioButton
|
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
|
activationSoundEnabled: false
|
||||||
text: " Audio"
|
text: " Audio"
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playFocusChangedSound()
|
OverlayController.playFocusChangedSound()
|
||||||
mainView.push(audioPage)
|
mainView.push(audioPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MyPushButton {
|
MyPushButton {
|
||||||
id: videoButton
|
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
|
activationSoundEnabled: false
|
||||||
text: " Video"
|
text: " Video"
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playFocusChangedSound()
|
OverlayController.playFocusChangedSound()
|
||||||
mainView.push(videoPage)
|
mainView.push(videoPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MyPushButton {
|
MyPushButton {
|
||||||
id: utilitiesButton
|
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
|
activationSoundEnabled: false
|
||||||
text: " Utilities"
|
text: " Utilities"
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playFocusChangedSound()
|
OverlayController.playFocusChangedSound()
|
||||||
mainView.push(utilitiesPage)
|
mainView.push(utilitiesPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,12 +136,12 @@ MyStackViewPage {
|
|||||||
|
|
||||||
MyPushButton {
|
MyPushButton {
|
||||||
id: statisticsButton
|
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
|
activationSoundEnabled: false
|
||||||
text: " Statistics"
|
text: " Statistics"
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playFocusChangedSound()
|
OverlayController.playFocusChangedSound()
|
||||||
mainView.push(statisticsPage)
|
mainView.push(statisticsPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,7 +153,7 @@ MyStackViewPage {
|
|||||||
|
|
||||||
MyPushButton {
|
MyPushButton {
|
||||||
id: bindingsButton
|
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
|
activationSoundEnabled: false
|
||||||
text: " Bindings"
|
text: " Bindings"
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@@ -164,12 +164,12 @@ MyStackViewPage {
|
|||||||
|
|
||||||
MyPushButton {
|
MyPushButton {
|
||||||
id: settingsButton
|
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
|
activationSoundEnabled: false
|
||||||
text: " Settings"
|
text: " Settings"
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playFocusChangedSound()
|
OverlayController.playFocusChangedSound()
|
||||||
mainView.push(settingsPage)
|
mainView.push(settingsPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -379,7 +379,7 @@ MyStackViewPage {
|
|||||||
Layout.leftMargin: 12
|
Layout.leftMargin: 12
|
||||||
checkable: true
|
checkable: true
|
||||||
contentItem: Image {
|
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.width: 32
|
||||||
sourceSize.height: 32
|
sourceSize.height: 32
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -470,10 +470,9 @@ MyStackViewPage {
|
|||||||
summaryMicMuteToggle.checked = AudioTabController.micMuted
|
summaryMicMuteToggle.checked = AudioTabController.micMuted
|
||||||
summaryPttToggle.checked = AudioTabController.pttEnabled
|
summaryPttToggle.checked = AudioTabController.pttEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: OverlayController
|
target: OverlayController
|
||||||
onNewVersionDetectedChanged: {
|
function onNewVersionDetectedChanged() {
|
||||||
if (OverlayController.newVersionDetected)
|
if (OverlayController.newVersionDetected)
|
||||||
{
|
{
|
||||||
summaryVersionCheckTextRect.color = "#ff0000"
|
summaryVersionCheckTextRect.color = "#ff0000"
|
||||||
@@ -481,10 +480,10 @@ MyStackViewPage {
|
|||||||
summaryVersionCheckTextRect.color = "#2a2e35"
|
summaryVersionCheckTextRect.color = "#2a2e35"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onVersionCheckTextChanged: {
|
function onVersionCheckTextChanged() {
|
||||||
summaryVersionCheckText.text = OverlayController.versionCheckText
|
summaryVersionCheckText.text = OverlayController.versionCheckText
|
||||||
}
|
}
|
||||||
onDisableVersionCheckChanged: {
|
function onDisableVersionCheckChanged() {
|
||||||
if (OverlayController.disableVersionCheck)
|
if (OverlayController.disableVersionCheck)
|
||||||
{
|
{
|
||||||
summaryVersionCheckText.visible = false
|
summaryVersionCheckText.visible = false
|
||||||
@@ -503,34 +502,34 @@ MyStackViewPage {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ChaperoneTabController
|
target: ChaperoneTabController
|
||||||
onChaperoneProfilesUpdated: {
|
function onChaperoneProfilesUpdated() {
|
||||||
reloadChaperoneProfiles()
|
reloadChaperoneProfiles()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: VideoTabController
|
target: VideoTabController
|
||||||
onVideoProfilesUpdated: {
|
function onVideoProfilesUpdated() {
|
||||||
reloadVideoProfiles()
|
reloadVideoProfiles()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: AudioTabController
|
target: AudioTabController
|
||||||
onMicVolumeChanged: {
|
function onMicVolumeChanged() {
|
||||||
summaryMicVolumeSlider.value = AudioTabController.micVolume
|
summaryMicVolumeSlider.value = AudioTabController.micVolume
|
||||||
}
|
}
|
||||||
onMicMutedChanged: {
|
function onMicMutedChanged() {
|
||||||
summaryMicMuteToggle.checked = AudioTabController.micMuted
|
summaryMicMuteToggle.checked = AudioTabController.micMuted
|
||||||
}
|
}
|
||||||
onPttEnabledChanged: {
|
function onPttEnabledChanged() {
|
||||||
summaryPttToggle.checked = AudioTabController.pttEnabled
|
summaryPttToggle.checked = AudioTabController.pttEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: MoveCenterTabController
|
target: MoveCenterTabController
|
||||||
onTrackingUniverseChanged: {
|
function onTrackingUniverseChanged() {
|
||||||
if (MoveCenterTabController.trackingUniverse === 0) {
|
if (MoveCenterTabController.trackingUniverse === 0) {
|
||||||
summaryPlaySpaceModeText.text = "Sitting"
|
summaryPlaySpaceModeText.text = "Sitting"
|
||||||
} else if (MoveCenterTabController.trackingUniverse === 1) {
|
} else if (MoveCenterTabController.trackingUniverse === 1) {
|
||||||
|
|||||||
@@ -338,66 +338,66 @@ MyStackViewPage {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: SettingsTabController
|
target: SettingsTabController
|
||||||
onAutoStartEnabledChanged: {
|
function onAutoStartEnabledChanged() {
|
||||||
settingsAutoStartToggle.checked = SettingsTabController.autoStartEnabled
|
settingsAutoStartToggle.checked = SettingsTabController.autoStartEnabled
|
||||||
}
|
}
|
||||||
onNativeChaperoneToggleChanged:{
|
function onNativeChaperoneToggleChanged(){
|
||||||
nativeChaperoneToggleButton.checked = SettingsTabController.nativeChaperoneToggle
|
nativeChaperoneToggleButton.checked = SettingsTabController.nativeChaperoneToggle
|
||||||
}
|
}
|
||||||
onOculusSdkToggleChanged:{
|
function onOculusSdkToggleChanged(){
|
||||||
oculusSdkToggleButton.checked = SettingsTabController.oculusSdkToggle
|
oculusSdkToggleButton.checked = SettingsTabController.oculusSdkToggle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: MoveCenterTabController
|
target: MoveCenterTabController
|
||||||
onUniverseCenteredRotationChanged: {
|
function onUniverseCenteredRotationChanged() {
|
||||||
universeCenteredRotationToggle.checked = MoveCenterTabController.universeCenteredRotation
|
universeCenteredRotationToggle.checked = MoveCenterTabController.universeCenteredRotation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: OverlayController
|
target: OverlayController
|
||||||
onVsyncDisabledChanged: {
|
function onVsyncDisabledChanged() {
|
||||||
vsyncDisabledToggle.checked = OverlayController.vsyncDisabled
|
vsyncDisabledToggle.checked = OverlayController.vsyncDisabled
|
||||||
customTickRateText.visible = vsyncDisabledToggle.checked
|
customTickRateText.visible = vsyncDisabledToggle.checked
|
||||||
customTickRateLabel.visible = vsyncDisabledToggle.checked
|
customTickRateLabel.visible = vsyncDisabledToggle.checked
|
||||||
customTickRateMsLabel.visible = vsyncDisabledToggle.checked
|
customTickRateMsLabel.visible = vsyncDisabledToggle.checked
|
||||||
}
|
}
|
||||||
onCrashRecoveryDisabledChanged: {
|
function onCrashRecoveryDisabledChanged() {
|
||||||
disableCrashRecoveryToggle.checked = !OverlayController.crashRecoveryDisabled
|
disableCrashRecoveryToggle.checked = !OverlayController.crashRecoveryDisabled
|
||||||
}
|
}
|
||||||
|
|
||||||
onCustomTickRateMsChanged: {
|
function onCustomTickRateMsChanged() {
|
||||||
customTickRateText.text = OverlayController.customTickRateMs
|
customTickRateText.text = OverlayController.customTickRateMs
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnableDebugChanged: {
|
function onEnableDebugChanged() {
|
||||||
debugStateRow.visible = OverlayController.enableDebug
|
debugStateRow.visible = OverlayController.enableDebug
|
||||||
}
|
}
|
||||||
|
|
||||||
onDebugStateChanged: {
|
function onDebugStateChanged() {
|
||||||
debugStateText.text = OverlayController.debugState
|
debugStateText.text = OverlayController.debugState
|
||||||
}
|
}
|
||||||
onDisableVersionCheckChanged: {
|
function onDisableVersionCheckChanged() {
|
||||||
disableVersionCheckToggle.checked = OverlayController.disableVersionCheck
|
disableVersionCheckToggle.checked = OverlayController.disableVersionCheck
|
||||||
}
|
}
|
||||||
onExclusiveInputEnabledChanged:{
|
function onExclusiveInputEnabledChanged(){
|
||||||
exclusiveInputToggleButton.checked = OverlayController.exclusiveInputEnabled
|
exclusiveInputToggleButton.checked = OverlayController.exclusiveInputEnabled
|
||||||
}
|
}
|
||||||
onAutoApplyChaperoneEnabledChanged: {
|
function onAutoApplyChaperoneEnabledChanged() {
|
||||||
autoApplyChaperoneToggleButton.checked = OverlayController.autoApplyChaperoneEnabled
|
autoApplyChaperoneToggleButton.checked = OverlayController.autoApplyChaperoneEnabled
|
||||||
}
|
}
|
||||||
onSoundVolumeChanged:{
|
function onSoundVolumeChanged(){
|
||||||
volumeSlider.value = OverlayController.soundVolume
|
volumeSlider.value = OverlayController.soundVolume
|
||||||
}
|
}
|
||||||
onDesktopModeToggleChanged:{
|
function onDesktopModeToggleChanged(){
|
||||||
desktopModeToggleButton.checked = OverlayController.desktopModeToggle
|
desktopModeToggleButton.checked = OverlayController.desktopModeToggle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Connections{
|
Connections{
|
||||||
target: ChaperoneTabController
|
target: ChaperoneTabController
|
||||||
onChaperoneProfilesUpdated: {
|
function onChaperoneProfilesUpdated() {
|
||||||
reloadChaperoneProfiles()
|
reloadChaperoneProfiles()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,14 +44,14 @@ RowLayout {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: AudioTabController
|
target: AudioTabController
|
||||||
onPlaybackDeviceIndexChanged: {
|
function onPlaybackDeviceIndexChanged() {
|
||||||
setShownAudioDevice(index)
|
setShownAudioDevice(index)
|
||||||
}
|
}
|
||||||
onPlaybackDeviceListChanged: {
|
function onPlaybackDeviceListChanged() {
|
||||||
audioPlaybackNameCombo.devices = getAudioDeviceList()
|
audioPlaybackNameCombo.devices = getAudioDeviceList()
|
||||||
setShownAudioDevice(AudioTabController.playbackDeviceIndex)
|
setShownAudioDevice(AudioTabController.playbackDeviceIndex)
|
||||||
}
|
}
|
||||||
onPlaybackOverrideChanged: {
|
function onPlaybackOverrideChanged() {
|
||||||
playbackOverrideToggle.value = AudioTabController.playbackOverride
|
playbackOverrideToggle.value = AudioTabController.playbackOverride
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,14 +63,14 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: AudioTabController
|
target: AudioTabController
|
||||||
onMicDeviceIndexChanged: {
|
function onMicDeviceIndexChanged() {
|
||||||
setShownAudioDevice(index)
|
setShownAudioDevice(index)
|
||||||
}
|
}
|
||||||
onRecordingDeviceListChanged: {
|
function onRecordingDeviceListChanged() {
|
||||||
devices = getAudioDeviceList()
|
devices = getAudioDeviceList()
|
||||||
setShownAudioDevice(AudioTabController.micDeviceIndex)
|
setShownAudioDevice(AudioTabController.micDeviceIndex)
|
||||||
}
|
}
|
||||||
onRecordingOverrideChanged: {
|
function onRecordingOverrideChanged() {
|
||||||
recordingOverrideToggle.value = AudioTabController.recordingOverride
|
recordingOverrideToggle.value = AudioTabController.recordingOverride
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ ColumnLayout {
|
|||||||
Layout.leftMargin: 12
|
Layout.leftMargin: 12
|
||||||
checkable: true
|
checkable: true
|
||||||
contentItem: Image {
|
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.width: 32
|
||||||
sourceSize.height: 32
|
sourceSize.height: 32
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -89,7 +89,7 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: AudioTabController
|
target: AudioTabController
|
||||||
onMicDeviceIndexChanged: {
|
function onMicDeviceIndexChanged() {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
audioMicVolumeMinusButton.enabled = false
|
audioMicVolumeMinusButton.enabled = false
|
||||||
audioMicVolumeSlider.enabled = false
|
audioMicVolumeSlider.enabled = false
|
||||||
@@ -102,16 +102,16 @@ ColumnLayout {
|
|||||||
audioMicMuteToggle.enabled = true
|
audioMicMuteToggle.enabled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onMicVolumeChanged: {
|
function onMicVolumeChanged() {
|
||||||
audioMicVolumeSlider.value = AudioTabController.micVolume
|
audioMicVolumeSlider.value = AudioTabController.micVolume
|
||||||
}
|
}
|
||||||
onMicMutedChanged: {
|
function onMicMutedChanged() {
|
||||||
audioMicMuteToggle.checked = AudioTabController.micMuted
|
audioMicMuteToggle.checked = AudioTabController.micMuted
|
||||||
}
|
}
|
||||||
onPlaybackDeviceListChanged: {
|
function onPlaybackDeviceListChanged() {
|
||||||
|
|
||||||
}
|
}
|
||||||
onRecordingDeviceListChanged: {
|
function onRecordingDeviceListChanged() {
|
||||||
if (AudioTabController.micDeviceIndex < 0) {
|
if (AudioTabController.micDeviceIndex < 0) {
|
||||||
audioMicVolumeMinusButton.enabled = false
|
audioMicVolumeMinusButton.enabled = false
|
||||||
audioMicVolumeSlider.enabled = false
|
audioMicVolumeSlider.enabled = false
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: AudioTabController
|
target: AudioTabController
|
||||||
onMirrorDeviceIndexChanged: {
|
function onMirrorDeviceIndexChanged() {
|
||||||
setShownAudioDevice(index)
|
setShownAudioDevice(index)
|
||||||
}
|
}
|
||||||
onPlaybackDeviceListChanged: {
|
function onPlaybackDeviceListChanged() {
|
||||||
audioMirrorNameCombo.devices = getAudioDeviceList()
|
audioMirrorNameCombo.devices = getAudioDeviceList()
|
||||||
setShownAudioDevice(AudioTabController.mirrorDeviceIndex)
|
setShownAudioDevice(AudioTabController.mirrorDeviceIndex)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ ColumnLayout {
|
|||||||
Layout.leftMargin: 12
|
Layout.leftMargin: 12
|
||||||
checkable: true
|
checkable: true
|
||||||
contentItem: Image {
|
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.width: 32
|
||||||
sourceSize.height: 32
|
sourceSize.height: 32
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -89,7 +89,7 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: AudioTabController
|
target: AudioTabController
|
||||||
onMirrorDeviceIndexChanged: {
|
function onMirrorDeviceIndexChanged() {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
audioMirrorNameCombo.deviceIndex = 0
|
audioMirrorNameCombo.deviceIndex = 0
|
||||||
audioMirrorVolumeMinusButton.enabled = false
|
audioMirrorVolumeMinusButton.enabled = false
|
||||||
@@ -104,13 +104,13 @@ ColumnLayout {
|
|||||||
audioMirrorNameCombo.deviceIndex = index + 1
|
audioMirrorNameCombo.deviceIndex = index + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onMirrorVolumeChanged: {
|
function onMirrorVolumeChanged() {
|
||||||
audioMirrorVolumeSlider.value = AudioTabController.mirrorVolume
|
audioMirrorVolumeSlider.value = AudioTabController.mirrorVolume
|
||||||
}
|
}
|
||||||
onMirrorMutedChanged: {
|
function onMirrorMutedChanged() {
|
||||||
audioMirrorMuteToggle.checked = AudioTabController.mirrorMuted
|
audioMirrorMuteToggle.checked = AudioTabController.mirrorMuted
|
||||||
}
|
}
|
||||||
onPlaybackDeviceListChanged: {
|
function onPlaybackDeviceListChanged() {
|
||||||
if (AudioTabController.mirrorDeviceIndex < 0) {
|
if (AudioTabController.mirrorDeviceIndex < 0) {
|
||||||
audioMirrorVolumeMinusButton.enabled = false
|
audioMirrorVolumeMinusButton.enabled = false
|
||||||
audioMirrorVolumeSlider.enabled = false
|
audioMirrorVolumeSlider.enabled = false
|
||||||
|
|||||||
@@ -98,13 +98,13 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: AudioTabController
|
target: AudioTabController
|
||||||
onAudioProfilesUpdated: {
|
function onAudioProfilesUpdated() {
|
||||||
reloadAudioProfiles()
|
reloadAudioProfiles()
|
||||||
}
|
}
|
||||||
onAudioProfileAdded: {
|
function onAudioProfileAdded() {
|
||||||
audioProfileComboBox.currentIndex = AudioTabController.getAudioProfileCount()
|
audioProfileComboBox.currentIndex = AudioTabController.getAudioProfileCount()
|
||||||
}
|
}
|
||||||
onDefaultProfileDisplay: {
|
function onDefaultProfileDisplay() {
|
||||||
audioProfileComboBox.currentIndex = AudioTabController.getDefaultAudioProfileIndex(
|
audioProfileComboBox.currentIndex = AudioTabController.getDefaultAudioProfileIndex(
|
||||||
) + 1
|
) + 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ MyToggleButton {
|
|||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: AudioTabController
|
target: AudioTabController
|
||||||
onMicProximitySensorCanMuteChanged: {
|
function onMicProximitySensorCanMuteChanged() {
|
||||||
audioMuteProximitySensorToggle.checked = AudioTabController.micProximitySensorCanMute
|
audioMuteProximitySensorToggle.checked = AudioTabController.micProximitySensorCanMute
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,13 +39,13 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: AudioTabController
|
target: AudioTabController
|
||||||
onPttEnabledChanged: {
|
function onPttEnabledChanged() {
|
||||||
audioPttEnabledToggle.checked = AudioTabController.pttEnabled
|
audioPttEnabledToggle.checked = AudioTabController.pttEnabled
|
||||||
}
|
}
|
||||||
onMicReversePttChanged: {
|
function onMicReversePttChanged() {
|
||||||
audioPttReverseToggle.checked = AudioTabController.micReversePtt
|
audioPttReverseToggle.checked = AudioTabController.micReversePtt
|
||||||
}
|
}
|
||||||
onPttShowNotificationChanged: {
|
function onPttShowNotificationChanged() {
|
||||||
audioPttShowNotificationToggle.checked = AudioTabController.pttShowNotification
|
audioPttShowNotificationToggle.checked = AudioTabController.pttShowNotification
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls 2.0
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import QtQuick.Dialogs 1.2
|
import QtQuick.Dialogs
|
||||||
import ovras.advsettings 1.0
|
import ovras.advsettings 1.0
|
||||||
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
||||||
import "../common"
|
import "../common"
|
||||||
@@ -494,7 +494,7 @@ MyStackViewPage {
|
|||||||
text: "Proximity Warning Settings"
|
text: "Proximity Warning Settings"
|
||||||
Layout.preferredWidth: 350
|
Layout.preferredWidth: 350
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playFocusChangedSound()
|
OverlayController.playFocusChangedSound()
|
||||||
mainView.push(chaperoneWarningsPage)
|
mainView.push(chaperoneWarningsPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -506,7 +506,7 @@ MyStackViewPage {
|
|||||||
Layout.preferredWidth: 400
|
Layout.preferredWidth: 400
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playFocusChangedSound()
|
OverlayController.playFocusChangedSound()
|
||||||
mainView.push(chaperoneAdditionalPage)
|
mainView.push(chaperoneAdditionalPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -586,26 +586,26 @@ MyStackViewPage {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ChaperoneTabController
|
target: ChaperoneTabController
|
||||||
onBoundsVisibilityChanged: {
|
function onBoundsVisibilityChanged() {
|
||||||
if (Math.abs(chaperoneVisibilitySlider.value - ChaperoneTabController.boundsVisibility) > 0.008) {
|
if (Math.abs(chaperoneVisibilitySlider.value - ChaperoneTabController.boundsVisibility) > 0.008) {
|
||||||
chaperoneVisibilitySlider.value = ChaperoneTabController.boundsVisibility
|
chaperoneVisibilitySlider.value = ChaperoneTabController.boundsVisibility
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onFadeDistanceChanged: {
|
function onFadeDistanceChanged() {
|
||||||
var d = ChaperoneTabController.fadeDistance.toFixed(1)
|
var d = ChaperoneTabController.fadeDistance.toFixed(1)
|
||||||
if (d <= chaperoneFadeDistanceSlider.to && Math.abs(chaperoneFadeDistanceSlider.value - d) > 0.008) {
|
if (d <= chaperoneFadeDistanceSlider.to && Math.abs(chaperoneFadeDistanceSlider.value - d) > 0.008) {
|
||||||
chaperoneFadeDistanceSlider.value = d
|
chaperoneFadeDistanceSlider.value = d
|
||||||
}
|
}
|
||||||
chaperoneFadeDistanceText.text = d
|
chaperoneFadeDistanceText.text = d
|
||||||
}
|
}
|
||||||
onHeightChanged: {
|
function onHeightChanged() {
|
||||||
var h = ChaperoneTabController.height.toFixed(2)
|
var h = ChaperoneTabController.height.toFixed(2)
|
||||||
if (h <= chaperoneHeightSlider.to && Math.abs(chaperoneHeightSlider.value - h) > 0.008) {
|
if (h <= chaperoneHeightSlider.to && Math.abs(chaperoneHeightSlider.value - h) > 0.008) {
|
||||||
chaperoneHeightSlider.value = h
|
chaperoneHeightSlider.value = h
|
||||||
}
|
}
|
||||||
chaperoneHeightText.text = h
|
chaperoneHeightText.text = h
|
||||||
}
|
}
|
||||||
onChaperoneDimHeightChanged: {
|
function onChaperoneDimHeightChanged() {
|
||||||
var dim = ChaperoneTabController.chaperoneDimHeight
|
var dim = ChaperoneTabController.chaperoneDimHeight
|
||||||
if(dim > 0.0){
|
if(dim > 0.0){
|
||||||
chaperoneDisableChaperone.enabled = false;
|
chaperoneDisableChaperone.enabled = false;
|
||||||
@@ -625,16 +625,16 @@ MyStackViewPage {
|
|||||||
chaperoneFadeDistanceText.enabled = true;
|
chaperoneFadeDistanceText.enabled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onCenterMarkerNewChanged: {
|
function onCenterMarkerNewChanged() {
|
||||||
chaperoneCenterMarkerToggle.checked = ChaperoneTabController.centerMarkerNew
|
chaperoneCenterMarkerToggle.checked = ChaperoneTabController.centerMarkerNew
|
||||||
}
|
}
|
||||||
onPlaySpaceMarkerChanged: {
|
function onPlaySpaceMarkerChanged() {
|
||||||
chaperonePlaySpaceToggle.checked = ChaperoneTabController.playSpaceMarker
|
chaperonePlaySpaceToggle.checked = ChaperoneTabController.playSpaceMarker
|
||||||
}
|
}
|
||||||
onForceBoundsChanged: {
|
function onForceBoundsChanged() {
|
||||||
chaperoneForceBoundsToggle.checked = ChaperoneTabController.forceBounds
|
chaperoneForceBoundsToggle.checked = ChaperoneTabController.forceBounds
|
||||||
}
|
}
|
||||||
onChaperoneProfilesUpdated: {
|
function onChaperoneProfilesUpdated() {
|
||||||
reloadChaperoneProfiles()
|
reloadChaperoneProfiles()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls 2.0
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import QtQuick.Dialogs 1.2
|
import QtQuick.Dialogs
|
||||||
import ovras.advsettings 1.0
|
import ovras.advsettings 1.0
|
||||||
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
||||||
import "../../common"
|
import "../../common"
|
||||||
@@ -113,7 +113,7 @@ MyStackViewPage {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ChaperoneTabController
|
target: ChaperoneTabController
|
||||||
onChaperoneDimHeightChanged: {
|
function onChaperoneDimHeightChanged() {
|
||||||
var d = ChaperoneTabController.chaperoneDimHeight.toFixed(2)
|
var d = ChaperoneTabController.chaperoneDimHeight.toFixed(2)
|
||||||
if (d <= dimHeightSlider.to && Math.abs(dimHeightSlider.value - d) > 0.0008) {
|
if (d <= dimHeightSlider.to && Math.abs(dimHeightSlider.value - d) > 0.0008) {
|
||||||
dimHeightSlider.value = d
|
dimHeightSlider.value = d
|
||||||
|
|||||||
+3
-3
@@ -180,17 +180,17 @@ GroupBox {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ChaperoneTabController
|
target: ChaperoneTabController
|
||||||
onChaperoneColorRChanged:{
|
function onChaperoneColorRChanged(){
|
||||||
var redValue = ChaperoneTabController.chaperoneColorR
|
var redValue = ChaperoneTabController.chaperoneColorR
|
||||||
colorRedText.text = redValue
|
colorRedText.text = redValue
|
||||||
chaperoneColorRedSlider.value = redValue
|
chaperoneColorRedSlider.value = redValue
|
||||||
}
|
}
|
||||||
onChaperoneColorGChanged:{
|
function onChaperoneColorGChanged(){
|
||||||
var greenValue = ChaperoneTabController.chaperoneColorG
|
var greenValue = ChaperoneTabController.chaperoneColorG
|
||||||
colorGreenText.text = greenValue
|
colorGreenText.text = greenValue
|
||||||
chaperoneColorGreenSlider.value = greenValue
|
chaperoneColorGreenSlider.value = greenValue
|
||||||
}
|
}
|
||||||
onChaperoneColorBChanged:{
|
function onChaperoneColorBChanged(){
|
||||||
var blueValue = ChaperoneTabController.chaperoneColorB
|
var blueValue = ChaperoneTabController.chaperoneColorB
|
||||||
colorBlueText.text = blueValue
|
colorBlueText.text = blueValue
|
||||||
chaperoneColorBlueSlider.value = blueValue
|
chaperoneColorBlueSlider.value = blueValue
|
||||||
|
|||||||
+2
-2
@@ -70,10 +70,10 @@ GroupBox {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ChaperoneTabController
|
target: ChaperoneTabController
|
||||||
onChaperoneFloorToggleChanged:{
|
function onChaperoneFloorToggleChanged(){
|
||||||
chaperoneFloorToggleBtn.checked = ChaperoneTabController.chaperoneFloorToggle
|
chaperoneFloorToggleBtn.checked = ChaperoneTabController.chaperoneFloorToggle
|
||||||
}
|
}
|
||||||
onCenterMarkerChanged:{
|
function onCenterMarkerChanged(){
|
||||||
legacyCenterMarkerbtn.checked = ChaperoneTabController.centerMarker
|
legacyCenterMarkerbtn.checked = ChaperoneTabController.centerMarker
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-6
@@ -40,7 +40,7 @@ GroupBox {
|
|||||||
Layout.preferredWidth: 200
|
Layout.preferredWidth: 200
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
if(checked){
|
if(checked){
|
||||||
MyResources.playActivationSound()
|
OverlayController.playActivationSound()
|
||||||
ChaperoneTabController.setCollisionBoundStyle(0)
|
ChaperoneTabController.setCollisionBoundStyle(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ GroupBox {
|
|||||||
Layout.preferredWidth: 200
|
Layout.preferredWidth: 200
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
if(checked){
|
if(checked){
|
||||||
MyResources.playActivationSound()
|
OverlayController.playActivationSound()
|
||||||
ChaperoneTabController.setCollisionBoundStyle(1)
|
ChaperoneTabController.setCollisionBoundStyle(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ GroupBox {
|
|||||||
Layout.preferredWidth: 200
|
Layout.preferredWidth: 200
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
if(checked){
|
if(checked){
|
||||||
MyResources.playActivationSound()
|
OverlayController.playActivationSound()
|
||||||
ChaperoneTabController.setCollisionBoundStyle(2)
|
ChaperoneTabController.setCollisionBoundStyle(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ GroupBox {
|
|||||||
Layout.preferredWidth: 200
|
Layout.preferredWidth: 200
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
if(checked){
|
if(checked){
|
||||||
MyResources.playActivationSound()
|
OverlayController.playActivationSound()
|
||||||
ChaperoneTabController.setCollisionBoundStyle(3)
|
ChaperoneTabController.setCollisionBoundStyle(3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,7 +88,7 @@ GroupBox {
|
|||||||
Layout.preferredWidth: 200
|
Layout.preferredWidth: 200
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
if(checked){
|
if(checked){
|
||||||
MyResources.playActivationSound()
|
OverlayController.playActivationSound()
|
||||||
ChaperoneTabController.setCollisionBoundStyle(4)
|
ChaperoneTabController.setCollisionBoundStyle(4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,7 +112,7 @@ GroupBox {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ChaperoneTabController
|
target: ChaperoneTabController
|
||||||
onCollisionBoundStyleChanged:{
|
function onCollisionBoundStyleChanged(){
|
||||||
var collisiontype = ChaperoneTabController.collisionBoundStyle
|
var collisiontype = ChaperoneTabController.collisionBoundStyle
|
||||||
switch(collisiontype){
|
switch(collisiontype){
|
||||||
case 0: denseRadio.checked = true; break;
|
case 0: denseRadio.checked = true; break;
|
||||||
@@ -124,4 +124,7 @@ GroupBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Connections {
|
||||||
|
target: OverlayController
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls 2.0
|
||||||
|
import ovras.advsettings
|
||||||
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
||||||
|
|
||||||
|
|
||||||
@@ -63,11 +64,14 @@ ComboBox {
|
|||||||
|
|
||||||
onActivated: {
|
onActivated: {
|
||||||
if (activeFocus) {
|
if (activeFocus) {
|
||||||
MyResources.playActivationSound()
|
OverlayController.playActivationSound()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
popup.background.color = "#2a2e35"
|
popup.background.color = "#2a2e35"
|
||||||
}
|
}
|
||||||
|
Connections {
|
||||||
|
target: OverlayController
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls 2.0
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
import ovras.advsettings
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
id: myDialogPopup
|
id: myDialogPopup
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls 2.0
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
import ovras.advsettings
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
id: myDialogPopup
|
id: myDialogPopup
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls 2.0
|
||||||
|
import ovras.advsettings
|
||||||
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
@@ -35,7 +36,10 @@ Button {
|
|||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (activationSoundEnabled) {
|
if (activationSoundEnabled) {
|
||||||
MyResources.playActivationSound()
|
OverlayController.playActivationSound()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Connections {
|
||||||
|
target: OverlayController
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls 2.0
|
||||||
|
import ovras.advsettings
|
||||||
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
@@ -24,6 +25,9 @@ Button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playActivationSound()
|
OverlayController.playActivationSound()
|
||||||
|
}
|
||||||
|
Connections {
|
||||||
|
target: OverlayController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls 2.0
|
||||||
|
import ovras.advsettings
|
||||||
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
||||||
|
|
||||||
RadioButton {
|
RadioButton {
|
||||||
@@ -19,6 +20,9 @@ RadioButton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playActivationSound()
|
OverlayController.playActivationSound()
|
||||||
|
}
|
||||||
|
Connections {
|
||||||
|
target: OverlayController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,4 +10,7 @@ QtObject {
|
|||||||
function playFocusChangedSound() {
|
function playFocusChangedSound() {
|
||||||
OverlayController.playFocusChangedSound()
|
OverlayController.playFocusChangedSound()
|
||||||
}
|
}
|
||||||
|
Connections {
|
||||||
|
target: OverlayController
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls 2.0
|
||||||
|
import ovras.advsettings
|
||||||
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
||||||
|
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ Slider {
|
|||||||
|
|
||||||
onValueChanged: {
|
onValueChanged: {
|
||||||
if (activeFocus) {
|
if (activeFocus) {
|
||||||
//MyResources.playActivationSound()
|
//OverlayController.playActivationSound()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls 2.0
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
|
import ovras.advsettings
|
||||||
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
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 ".."
|
import ".."
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -26,7 +27,7 @@ Rectangle {
|
|||||||
enabled: headerShowBackButton
|
enabled: headerShowBackButton
|
||||||
opacity: headerShowBackButton ? 1.0 : 0.0
|
opacity: headerShowBackButton ? 1.0 : 0.0
|
||||||
contentItem: Image {
|
contentItem: Image {
|
||||||
source: "qrc:/common/backarrow"
|
source: "qrc:/qt/qml/AdvancedSettings/src/res/img/common/backarrow.svg"
|
||||||
sourceSize.width: 50
|
sourceSize.width: 50
|
||||||
sourceSize.height: 50
|
sourceSize.height: 50
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -45,7 +46,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playFocusChangedSound()
|
OverlayController.playFocusChangedSound()
|
||||||
stackView.pop()
|
stackView.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,4 +91,7 @@ Rectangle {
|
|||||||
content.Layout.rightMargin = 40
|
content.Layout.rightMargin = 40
|
||||||
content.Layout.bottomMargin = 40
|
content.Layout.bottomMargin = 40
|
||||||
}
|
}
|
||||||
|
Connections {
|
||||||
|
target: OverlayController
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls 2.0
|
||||||
|
import ovras.advsettings
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
color: "#d9dbe0"
|
color: "#d9dbe0"
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls 2.0
|
||||||
|
import ovras.advsettings
|
||||||
import "." // QTBUG-34418, singletons require explicit import to load qmldir file
|
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 ".."
|
import ".."
|
||||||
|
|
||||||
CheckBox {
|
CheckBox {
|
||||||
@@ -31,7 +32,7 @@ CheckBox {
|
|||||||
height: 28
|
height: 28
|
||||||
x: (parent.width - width) / 2
|
x: (parent.width - width) / 2
|
||||||
y: (parent.height - height) / 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.width: width
|
||||||
sourceSize.height: height
|
sourceSize.height: height
|
||||||
visible: parent.parent.checked
|
visible: parent.parent.checked
|
||||||
@@ -60,6 +61,9 @@ CheckBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
MyResources.playActivationSound()
|
OverlayController.playActivationSound()
|
||||||
|
}
|
||||||
|
Connections {
|
||||||
|
target: OverlayController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import QtQuick 2.7
|
import QtQuick
|
||||||
import QtQuick.Controls 2.0
|
// Not QtQuick.Controls.Material: importing a style module fixes that style
|
||||||
import QtQuick.Layouts 1.0
|
// 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 ".."
|
||||||
import "../utilities_page"
|
import "../utilities_page"
|
||||||
import "../audio_page"
|
import "../audio_page"
|
||||||
@@ -11,6 +15,7 @@ import "../steamvr_page"
|
|||||||
import "../rotation_page"
|
import "../rotation_page"
|
||||||
import "../chaperone_page/chaperone_additional"
|
import "../chaperone_page/chaperone_additional"
|
||||||
import "../steamvr_page/steamvr_additional"
|
import "../steamvr_page/steamvr_additional"
|
||||||
|
import ovras.advsettings
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
|||||||
@@ -218,19 +218,19 @@ GroupBox {
|
|||||||
Connections {
|
Connections {
|
||||||
target: MoveCenterTabController
|
target: MoveCenterTabController
|
||||||
|
|
||||||
onGravityActiveChanged: {
|
function onGravityActiveChanged() {
|
||||||
gravityToggleButton.checked = MoveCenterTabController.gravityActive
|
gravityToggleButton.checked = MoveCenterTabController.gravityActive
|
||||||
}
|
}
|
||||||
onGravityStrengthChanged: {
|
function onGravityStrengthChanged() {
|
||||||
gravityStrengthText.text = MoveCenterTabController.gravityStrength.toFixed(2)
|
gravityStrengthText.text = MoveCenterTabController.gravityStrength.toFixed(2)
|
||||||
}
|
}
|
||||||
onMomentumSaveChanged: {
|
function onMomentumSaveChanged() {
|
||||||
momentumToggleButton.checked = MoveCenterTabController.momentumSave
|
momentumToggleButton.checked = MoveCenterTabController.momentumSave
|
||||||
}
|
}
|
||||||
onFlingStrengthChanged: {
|
function onFlingStrengthChanged() {
|
||||||
flingStrengthText.text = MoveCenterTabController.flingStrength.toFixed(2)
|
flingStrengthText.text = MoveCenterTabController.flingStrength.toFixed(2)
|
||||||
}
|
}
|
||||||
onFrictionPercentChanged: {
|
function onFrictionPercentChanged() {
|
||||||
frictionPercentText.text = MoveCenterTabController.frictionPercent + "%"
|
frictionPercentText.text = MoveCenterTabController.frictionPercent + "%"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,10 +88,10 @@ GroupBox {
|
|||||||
Connections {
|
Connections {
|
||||||
target: MoveCenterTabController
|
target: MoveCenterTabController
|
||||||
|
|
||||||
onHeightToggleChanged: {
|
function onHeightToggleChanged() {
|
||||||
heightToggleButton.checked = MoveCenterTabController.heightToggle
|
heightToggleButton.checked = MoveCenterTabController.heightToggle
|
||||||
}
|
}
|
||||||
onHeightToggleOffsetChanged: {
|
function onHeightToggleOffsetChanged() {
|
||||||
heightToggleOffsetText.text = MoveCenterTabController.heightToggleOffset.toFixed(2)
|
heightToggleOffsetText.text = MoveCenterTabController.heightToggleOffset.toFixed(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,19 +156,19 @@ GroupBox {
|
|||||||
Connections {
|
Connections {
|
||||||
target: MoveCenterTabController
|
target: MoveCenterTabController
|
||||||
|
|
||||||
onMoveShortcutLeftChanged: {
|
function onMoveShortcutLeftChanged() {
|
||||||
moveShortcutLeft.checked = MoveCenterTabController.moveShortcutLeft
|
moveShortcutLeft.checked = MoveCenterTabController.moveShortcutLeft
|
||||||
}
|
}
|
||||||
onMoveShortcutRightChanged: {
|
function onMoveShortcutRightChanged() {
|
||||||
moveShortcutRight.checked = MoveCenterTabController.moveShortcutRight
|
moveShortcutRight.checked = MoveCenterTabController.moveShortcutRight
|
||||||
}
|
}
|
||||||
onDragComfortFactorChanged: {
|
function onDragComfortFactorChanged() {
|
||||||
dragComfortSlider.value = MoveCenterTabController.dragComfortFactor
|
dragComfortSlider.value = MoveCenterTabController.dragComfortFactor
|
||||||
}
|
}
|
||||||
onDragBoundsChanged: {
|
function onDragBoundsChanged() {
|
||||||
dragBounds.checked = MoveCenterTabController.dragBounds
|
dragBounds.checked = MoveCenterTabController.dragBounds
|
||||||
}
|
}
|
||||||
onDragMultChanged: {
|
function onDragMultChanged() {
|
||||||
dragMultText.text = MoveCenterTabController.dragMult.toFixed(2)
|
dragMultText.text = MoveCenterTabController.dragMult.toFixed(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -312,23 +312,23 @@ GroupBox {
|
|||||||
Connections {
|
Connections {
|
||||||
target: RotationTabController
|
target: RotationTabController
|
||||||
|
|
||||||
onAutoTurnEnabledChanged: {
|
function onAutoTurnEnabledChanged() {
|
||||||
autoTurn.checked = RotationTabController.autoTurnEnabled
|
autoTurn.checked = RotationTabController.autoTurnEnabled
|
||||||
}
|
}
|
||||||
onAutoTurnActivationDistanceChanged: {
|
function onAutoTurnActivationDistanceChanged() {
|
||||||
activationSlider.value = RotationTabController.autoTurnActivationDistance.toFixed(2)
|
activationSlider.value = RotationTabController.autoTurnActivationDistance.toFixed(2)
|
||||||
}
|
}
|
||||||
onAutoTurnDeactivationDistanceChanged:{
|
function onAutoTurnDeactivationDistanceChanged(){
|
||||||
deactivationSlider.value = RotationTabController.autoTurnDeactivationDistance.toFixed(2)
|
deactivationSlider.value = RotationTabController.autoTurnDeactivationDistance.toFixed(2)
|
||||||
}
|
}
|
||||||
onAutoTurnUseCornerAngleChanged:{
|
function onAutoTurnUseCornerAngleChanged(){
|
||||||
cornerAngle.checked = RotationTabController.autoTurnUseCornerAngle
|
cornerAngle.checked = RotationTabController.autoTurnUseCornerAngle
|
||||||
}
|
}
|
||||||
onAutoTurnSpeedChanged:{
|
function onAutoTurnSpeedChanged(){
|
||||||
var val = RotationTabController.autoTurnSpeed
|
var val = RotationTabController.autoTurnSpeed
|
||||||
speedSlider.value = ((val/100).toFixed())
|
speedSlider.value = ((val/100).toFixed())
|
||||||
}
|
}
|
||||||
onAutoTurnModeChanged:{
|
function onAutoTurnModeChanged(){
|
||||||
if(RotationTabController.autoTurnMode === 1){
|
if(RotationTabController.autoTurnMode === 1){
|
||||||
|
|
||||||
autoTurnModeToggle.checked = true;
|
autoTurnModeToggle.checked = true;
|
||||||
@@ -338,13 +338,13 @@ GroupBox {
|
|||||||
autoTurnModeToggle.checked = false;
|
autoTurnModeToggle.checked = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onMinCordTangleChanged:{
|
function onMinCordTangleChanged(){
|
||||||
detangleAngleStartText.text = parseInt(RotationTabController.minCordTangle*(180/Math.PI))+ "°"
|
detangleAngleStartText.text = parseInt(RotationTabController.minCordTangle*(180/Math.PI))+ "°"
|
||||||
}
|
}
|
||||||
onCordDetangleAngleChanged:{
|
function onCordDetangleAngleChanged(){
|
||||||
detangleAngleAssistText.text = parseInt(RotationTabController.cordDetangleAngle*(180/Math.PI))+ "°"
|
detangleAngleAssistText.text = parseInt(RotationTabController.cordDetangleAngle*(180/Math.PI))+ "°"
|
||||||
}
|
}
|
||||||
onAutoTurnShowNotificationChanged:{
|
function onAutoTurnShowNotificationChanged(){
|
||||||
autoTurnNotificationToggle.checked = RotationTabController.autoTurnShowNotification
|
autoTurnNotificationToggle.checked = RotationTabController.autoTurnShowNotification
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,10 +115,10 @@ GroupBox {
|
|||||||
Connections {
|
Connections {
|
||||||
target: RotationTabController
|
target: RotationTabController
|
||||||
|
|
||||||
onVestibularMotionEnabledChanged:{
|
function onVestibularMotionEnabledChanged(){
|
||||||
redirectedModeToggle.checked = RotationTabController.vestibularMotionEnabled
|
redirectedModeToggle.checked = RotationTabController.vestibularMotionEnabled
|
||||||
}
|
}
|
||||||
onVestibularMotionRadiusChanged:{
|
function onVestibularMotionRadiusChanged(){
|
||||||
redirectedWalkingRadiusText.text = ((RotationTabController.vestibularMotionRadius).toFixed(2))
|
redirectedWalkingRadiusText.text = ((RotationTabController.vestibularMotionRadius).toFixed(2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user