From 39f81f39ab03bbea8a530b53a26f9ee17b70d470 Mon Sep 17 00:00:00 2001 From: Mel Klimushyn Date: Sat, 21 Jan 2017 18:56:03 -0800 Subject: [PATCH] Add optional toggle of the Y offset to MoveCenterTabController. Allows short players and wheelchair users to fully interact with height-limited applications by both boosting their height and being able to touch the in-game floor when necessary. --- bin/win64/res/qml/PlayspacePage.qml | 262 +++++++++++++++++- .../MoveCenterTabController.cpp | 236 ++++++++++++++++ src/tabcontrollers/MoveCenterTabController.h | 43 +++ 3 files changed, 528 insertions(+), 13 deletions(-) diff --git a/bin/win64/res/qml/PlayspacePage.qml b/bin/win64/res/qml/PlayspacePage.qml index 62ad039b..20283db8 100644 --- a/bin/win64/res/qml/PlayspacePage.qml +++ b/bin/win64/res/qml/PlayspacePage.qml @@ -8,6 +8,149 @@ import matzman666.advsettings 1.0 MyStackViewPage { headerText: "Play Space Settings" + MyDialogOkCancelPopup { + id: pttControllerConfigDialog + property int controllerIndex: -1 + dialogTitle: "Controller Configuration" + dialogWidth: 600 + dialogHeight: 620 + dialogContentItem: ColumnLayout { + MyText { + text: "Buttons" + } + Rectangle { + color: "#cccccc" + height: 1 + Layout.fillWidth: true + } + MyToggleButton { + id: pttControllerConfigGripButtonToggle + text: "Grip" + } + MyToggleButton { + id: pttControllerConfigMenuButtonToggle + text: "Menu" + } + MyToggleButton { + id: pttControllerConfigTriggerButtonToggle + text: "Trigger" + } + + MyText { + Layout.topMargin: 32 + text: "Touchpad" + } + Rectangle { + color: "#cccccc" + height: 1 + Layout.fillWidth: true + } + RowLayout { + spacing: 64 + MyToggleButton { + id: pttControllerConfigPadLeftToggle + text: "Left" + } + MyToggleButton { + id: pttControllerConfigPadRightToggle + text: "Right" + } + } + RowLayout { + spacing: 64 + MyToggleButton { + id: pttControllerConfigPadTopToggle + text: "Top" + } + MyToggleButton { + id: pttControllerConfigPadBottomToggle + text: "Bottom" + } + } + RowLayout { + MyText { + text: "Mode:" + } + MyComboBox { + id: pttControllerConfigPadModeCombo + Layout.preferredWidth: 250 + model: ["Disabled", "Touched", "Pressed"] + } + } + } + onClosed: { + if (okClicked) { + var buttons = [] + if (pttControllerConfigGripButtonToggle.checked) { + buttons.push(2) + } + if (pttControllerConfigMenuButtonToggle.checked) { + buttons.push(1) + } + var triggerMode = 0 + if (pttControllerConfigTriggerButtonToggle.checked) { + triggerMode = 1 + } + var padAreas = 0 + if (pttControllerConfigPadLeftToggle.checked) { + padAreas |= (1 << 0) + } + if (pttControllerConfigPadTopToggle.checked) { + padAreas |= (1 << 1) + } + if (pttControllerConfigPadRightToggle.checked) { + padAreas |= (1 << 2) + } + if (pttControllerConfigPadBottomToggle.checked) { + padAreas |= (1 << 3) + } + MoveCenterTabController.setPttControllerConfig(controllerIndex, buttons, triggerMode, pttControllerConfigPadModeCombo.currentIndex, padAreas); + } + } + function openPopup(controller) { + if (controller === 0) { + dialogTitle = "Left Controller Configuration" + controllerIndex = 0 + } else { + dialogTitle = "Right Controller Configuration" + controllerIndex = 1 + } + pttControllerConfigGripButtonToggle.checked = false + pttControllerConfigMenuButtonToggle.checked = false + pttControllerConfigTriggerButtonToggle.checked = false + pttControllerConfigPadLeftToggle.checked = false + pttControllerConfigPadTopToggle.checked = false + pttControllerConfigPadRightToggle.checked = false + pttControllerConfigPadBottomToggle.checked = false + pttControllerConfigPadModeCombo.currentIndex = MoveCenterTabController.pttTouchpadMode(controllerIndex) + var buttons = MoveCenterTabController.pttDigitalButtons(controllerIndex) + for (var i = 0; i < buttons.length; i++) { + if (buttons[i] == 1) { + pttControllerConfigMenuButtonToggle.checked = true + } else if (buttons[i] == 2) { + pttControllerConfigGripButtonToggle.checked = true + } + } + if (MoveCenterTabController.pttTriggerMode(controllerIndex) > 0) { + pttControllerConfigTriggerButtonToggle.checked = true + } + var padAreas = MoveCenterTabController.pttTouchpadArea(controllerIndex) + if (padAreas & (1 << 0)) { + pttControllerConfigPadLeftToggle.checked = true + } + if (padAreas & (1 << 1)) { + pttControllerConfigPadTopToggle.checked = true + } + if (padAreas & (1 << 2)) { + pttControllerConfigPadRightToggle.checked = true + } + if (padAreas & (1 << 3)) { + pttControllerConfigPadBottomToggle.checked = true + } + open() + } + } + content: ColumnLayout { spacing: 18 @@ -63,10 +206,10 @@ MyStackViewPage { } } - MyTextField { + MyTextField { id: playSpaceMoveXText text: "0.00" - keyBoardUID: 101 + keyBoardUID: 101 Layout.preferredWidth: 140 Layout.leftMargin: 10 Layout.rightMargin: 10 @@ -265,22 +408,100 @@ MyStackViewPage { } } - MyToggleButton { - id: playspaceAdjustChaperoneToggle - text: "Adjust Chaperone" - onCheckedChanged: { - MoveCenterTabController.adjustChaperone = this.checked + GroupBox { + Layout.fillWidth: true + + label: MyText { + leftPadding: 10 + text: "Push-to-Toggle Y-Axis Offset" + bottomPadding: -10 + } + + background: Rectangle { + color: "transparent" + border.color: "#ffffff" + radius: 8 + } + + ColumnLayout { + anchors.fill: parent + + Rectangle { + color: "#ffffff" + height: 1 + Layout.fillWidth: true + Layout.bottomMargin: 5 + } + + ColumnLayout { + RowLayout { + MyToggleButton { + id: offsetYPttEnabledToggle + Layout.preferredWidth: 260 + text: "Enabled" + onClicked: { + MoveCenterTabController.pttEnabled = checked + } + } + MyToggleButton { + id: offsetYPttLeftControllerToggle + text: "Left Controller" + onClicked: { + MoveCenterTabController.setPttLeftControllerEnabled(checked, false) + } + } + MyPushButton { + text: "Configure" + onClicked: { + pttControllerConfigDialog.openPopup(0) + } + } + Item { + Layout.fillWidth: true + } + MyToggleButton { + id: offsetYPttRightControllerToggle + text: "Right Controller" + onClicked: { + MoveCenterTabController.setPttRightControllerEnabled(checked, false) + } + } + MyPushButton { + text: "Configure" + onClicked: { + pttControllerConfigDialog.openPopup(1) + } + } + } + } } } Item { Layout.fillHeight: true; Layout.fillWidth: true} - MyPushButton { - id: playspaceResetButton - Layout.preferredWidth: 250 - text: "Reset" - onClicked: { - MoveCenterTabController.reset() + ColumnLayout { + Layout.fillWidth: true + + RowLayout { + MyToggleButton { + id: playspaceAdjustChaperoneToggle + text: "Adjust Chaperone" + Layout.preferredWidth: 250 + onCheckedChanged: { + MoveCenterTabController.adjustChaperone = this.checked + } + } + + Item { Layout.fillWidth: true} + + MyPushButton { + id: playspaceResetButton + Layout.preferredWidth: 250 + text: "Reset" + onClicked: { + MoveCenterTabController.reset() + } + } } } @@ -305,6 +526,9 @@ MyStackViewPage { } else { playspaceModeText.text = "Unknown(" + MoveCenterTabController.trackingUniverse + ")" } + offsetYPttEnabledToggle.checked = MoveCenterTabController.pttEnabled + offsetYPttLeftControllerToggle.checked = MoveCenterTabController.pttLeftControllerEnabled + offsetYPttRightControllerToggle.checked = MoveCenterTabController.pttRightControllerEnabled } Connections { @@ -341,6 +565,18 @@ MyStackViewPage { playspaceModeText.text = "Unknown(" + MoveCenterTabController.trackingUniverse + ")" } } + onPttEnabledChanged: { + offsetYPttEnabledToggle.checked = MoveCenterTabController.pttEnabled + } + onPttActiveChanged: { + + } + onPttLeftControllerEnabledChanged: { + offsetYPttLeftControllerToggle.checked = MoveCenterTabController.pttLeftControllerEnabled + } + onPttRightControllerEnabledChanged: { + offsetYPttRightControllerToggle.checked = MoveCenterTabController.pttRightControllerEnabled + } } } diff --git a/src/tabcontrollers/MoveCenterTabController.cpp b/src/tabcontrollers/MoveCenterTabController.cpp index 26977d22..5a5234ac 100644 --- a/src/tabcontrollers/MoveCenterTabController.cpp +++ b/src/tabcontrollers/MoveCenterTabController.cpp @@ -14,6 +14,8 @@ void MoveCenterTabController::initStage1() { if (value.isValid() && !value.isNull()) { m_adjustChaperone = value.toBool(); } + + reloadPttConfig(); } void MoveCenterTabController::initStage2(OverlayController * parent, QQuickWindow * widget) { @@ -135,6 +137,72 @@ void MoveCenterTabController::setAdjustChaperone(bool value, bool notify) { } } +bool MoveCenterTabController::pttEnabled() const { + return m_pttEnabled; +} + +void MoveCenterTabController::setPttEnabled(bool value, bool notify, bool save) { + std::lock_guard lock(eventLoopMutex); + if (value != m_pttEnabled) { + m_pttEnabled = value; + if (notify) { + emit pttEnabledChanged(value); + } + if (save) { + savePttConfig(); + } + } +} + +void MoveCenterTabController::offsetYToggle(bool enabled) { + float delta; + if (enabled) { + m_toggleOffsetY = m_offsetY; + delta = -m_toggleOffsetY; + } else { + delta = m_toggleOffsetY; + } + parent->AddOffsetToUniverseCenter((vr::TrackingUniverseOrigin)m_trackingUniverse, 1, delta, m_adjustChaperone); +} + +bool MoveCenterTabController::pttActive() const { + return m_pttActive; +} + +bool MoveCenterTabController::pttLeftControllerEnabled() const { + return m_pttLeftControllerEnabled; +} + +void MoveCenterTabController::setPttLeftControllerEnabled(bool value, bool notify, bool save) { + std::lock_guard lock(eventLoopMutex); + if (value != m_pttLeftControllerEnabled) { + m_pttLeftControllerEnabled = value; + if (notify) { + emit pttLeftControllerEnabledChanged(value); + } + if (save) { + savePttConfig(); + } + } +} + +bool MoveCenterTabController::pttRightControllerEnabled() const { + return m_pttRightControllerEnabled; +} + +void MoveCenterTabController::setPttRightControllerEnabled(bool value, bool notify, bool save) { + std::lock_guard lock(eventLoopMutex); + if (value != m_pttRightControllerEnabled) { + m_pttRightControllerEnabled = value; + if (notify) { + emit pttRightControllerEnabledChanged(value); + } + if (save) { + savePttConfig(); + } + } +} + void MoveCenterTabController::modOffsetX(float value, bool notify) { if (m_rotation == 0) { parent->AddOffsetToUniverseCenter((vr::TrackingUniverseOrigin)m_trackingUniverse, 0, value, m_adjustChaperone); @@ -184,6 +252,7 @@ void MoveCenterTabController::reset() { vr::VRChaperoneSetup()->CommitWorkingCopy(vr::EChaperoneConfigFile_Live); m_offsetX = 0.0f; m_offsetY = 0.0f; + m_toggleOffsetY = 0.0f; m_offsetZ = 0.0f; m_rotation = 0; emit offsetXChanged(m_offsetX); @@ -193,12 +262,179 @@ void MoveCenterTabController::reset() { } void MoveCenterTabController::eventLoopTick(vr::ETrackingUniverseOrigin universe) { + if (!eventLoopMutex.try_lock()) { + return; + } if (settingsUpdateCounter >= 50) { setTrackingUniverse((int)universe); settingsUpdateCounter = 0; } else { settingsUpdateCounter++; } + static auto handleControllerState = [](const vr::VRControllerState_t& state, PttControllerConfig* config) -> bool { + if (state.ulButtonPressed & config->digitalButtonMask) { + return true; + } + if (config->triggerMode) { + if (state.ulButtonPressed & vr::ButtonMaskFromId(vr::k_EButton_SteamVR_Trigger) + || state.ulButtonTouched & vr::ButtonMaskFromId(vr::k_EButton_SteamVR_Trigger)) { + return true; + } + } + if (config->touchpadMode) { + if (((config->touchpadMode & 1) && (state.ulButtonTouched & vr::ButtonMaskFromId(vr::k_EButton_SteamVR_Touchpad))) + || ((config->touchpadMode & 2) && (state.ulButtonPressed & vr::ButtonMaskFromId(vr::k_EButton_SteamVR_Touchpad)))) { + if (config->touchpadAreas == (PAD_AREA_LEFT + PAD_AREA_TOP + PAD_AREA_RIGHT + PAD_AREA_BOTTOM)) { + return true; + } else { + float x = state.rAxis[0].x; + float y = state.rAxis[0].y; + if (std::abs(x) >= 0.2 || std::abs(y) >= 0.2) { // deadzone in the middle + if (x < 0 && std::abs(y) < -x && (config->touchpadAreas & PAD_AREA_LEFT)) { + return true; + } else if (y > 0 && std::abs(x) < y && (config->touchpadAreas & PAD_AREA_TOP)) { + return true; + } else if (x > 0 && std::abs(y) < x && (config->touchpadAreas & PAD_AREA_RIGHT)) { + return true; + } else if (y < 0 && std::abs(x) < -y && (config->touchpadAreas & PAD_AREA_BOTTOM)) { + return true; + } + } + } + } + } + return false; + }; + if (m_pttEnabled) { + bool newState = false; + if (m_pttLeftControllerEnabled) { + auto leftId = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_LeftHand); + if (leftId != vr::k_unTrackedDeviceIndexInvalid) { + vr::VRControllerState_t state; + if (vr::VRSystem()->GetControllerState(leftId, &state, sizeof(vr::VRControllerState_t))) { + newState |= handleControllerState(state, m_pttControllerConfigs); + } + } + } + if (m_pttRightControllerEnabled) { + auto rightId = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_RightHand); + if (rightId != vr::k_unTrackedDeviceIndexInvalid) { + vr::VRControllerState_t state; + if (vr::VRSystem()->GetControllerState(rightId, &state, sizeof(vr::VRControllerState_t))) { + newState |= handleControllerState(state, m_pttControllerConfigs + 1); + } + } + } + if (newState && !m_pttActive) { + m_pttActive = true; + offsetYToggle(true); + emit pttActiveChanged(m_pttActive); + } else if (!newState && m_pttActive) { + m_pttActive = false; + offsetYToggle(false); + emit pttActiveChanged(m_pttActive); + } + } + eventLoopMutex.unlock(); +} + +void MoveCenterTabController::setPttControllerConfig(unsigned controller, QVariantList buttons, unsigned triggerMode, unsigned padMode, unsigned padAreas) { + std::lock_guard lock(eventLoopMutex); + if (controller < 2) { + uint64_t buttonMask = 0; + for (auto b : buttons) { + buttonMask |= vr::ButtonMaskFromId((vr::EVRButtonId)b.toInt()); + } + m_pttControllerConfigs[controller].digitalButtons = buttons; + m_pttControllerConfigs[controller].digitalButtonMask = buttonMask; + m_pttControllerConfigs[controller].triggerMode = triggerMode; + m_pttControllerConfigs[controller].touchpadMode = padMode; + m_pttControllerConfigs[controller].touchpadAreas = padAreas; + savePttConfig(); + } +} + +void MoveCenterTabController::reloadPttConfig() { + std::lock_guard lock(eventLoopMutex); + auto settings = OverlayController::appSettings(); + settings->beginGroup("playspaceSettings"); + setPttEnabled(settings->value("pttEnabled", false).toBool(), true, false); + setPttLeftControllerEnabled(settings->value("pttLeftControllerEnabled", false).toBool(), true, false); + setPttRightControllerEnabled(settings->value("pttRightControllerEnabled", false).toBool(), true, false); + m_pttControllerConfigs[0].digitalButtons = settings->value("pttDigitalButtons_0").toList(); + uint64_t buttonMask = 0; + for (auto b : m_pttControllerConfigs[0].digitalButtons) { + buttonMask |= vr::ButtonMaskFromId((vr::EVRButtonId)b.toInt()); + } + m_pttControllerConfigs[0].digitalButtonMask = buttonMask; + m_pttControllerConfigs[0].triggerMode = settings->value("pttTriggerMode_0", 0).toUInt(); + m_pttControllerConfigs[0].touchpadMode = settings->value("pttTouchpadMode_0", 0).toUInt(); + m_pttControllerConfigs[0].touchpadAreas = settings->value("pttTouchPadAreas_0", 0).toUInt(); + m_pttControllerConfigs[1].digitalButtons = settings->value("pttDigitalButtons_1").toList(); + buttonMask = 0; + for (auto b : m_pttControllerConfigs[0].digitalButtons) { + buttonMask |= vr::ButtonMaskFromId((vr::EVRButtonId)b.toInt()); + } + m_pttControllerConfigs[1].digitalButtonMask = buttonMask; + m_pttControllerConfigs[1].triggerMode = settings->value("pttTriggerMode_1", 0).toUInt(); + m_pttControllerConfigs[1].touchpadMode = settings->value("pttTouchpadMode_1", 0).toUInt(); + m_pttControllerConfigs[1].touchpadAreas = settings->value("pttTouchPadAreas_1", 0).toUInt(); + settings->endGroup(); +} + +void MoveCenterTabController::savePttConfig() { + auto settings = OverlayController::appSettings(); + settings->beginGroup("playspaceSettings"); + settings->setValue("pttEnabled", pttEnabled()); + settings->setValue("pttLeftControllerEnabled", pttLeftControllerEnabled()); + settings->setValue("pttRightControllerEnabled", pttRightControllerEnabled()); + settings->setValue("pttDigitalButtons_0", m_pttControllerConfigs[0].digitalButtons); + settings->setValue("pttTriggerMode_0", m_pttControllerConfigs[0].triggerMode); + settings->setValue("pttTouchpadMode_0", m_pttControllerConfigs[0].touchpadMode); + settings->setValue("pttTouchPadAreas_0", m_pttControllerConfigs[0].touchpadAreas); + settings->setValue("pttDigitalButtons_1", m_pttControllerConfigs[1].digitalButtons); + settings->setValue("pttTriggerMode_1", m_pttControllerConfigs[1].triggerMode); + settings->setValue("pttTouchpadMode_1", m_pttControllerConfigs[1].touchpadMode); + settings->setValue("pttTouchPadAreas_1", m_pttControllerConfigs[1].touchpadAreas); + settings->endGroup(); +} + + +QVariantList MoveCenterTabController::pttDigitalButtons(unsigned controller) { + if (controller < 2) { + return m_pttControllerConfigs[controller].digitalButtons; + } + return QVariantList(); +} + +unsigned long MoveCenterTabController::pttDigitalButtonMask(unsigned controller) { + if (controller < 2) { + return m_pttControllerConfigs[controller].digitalButtonMask; + } + return 0; +} + +unsigned MoveCenterTabController::pttTouchpadMode(unsigned controller) { + if (controller < 2) { + return m_pttControllerConfigs[controller].touchpadMode; + } + return 0; +} + +unsigned MoveCenterTabController::pttTriggerMode(unsigned controller) { + if (controller < 2) { + return m_pttControllerConfigs[controller].triggerMode; + } + return 0; } +unsigned MoveCenterTabController::pttTouchpadArea(unsigned controller) { + if (controller < 2) { + return m_pttControllerConfigs[controller].touchpadAreas; + } + return 0; +} + + + } // namespace advconfig diff --git a/src/tabcontrollers/MoveCenterTabController.h b/src/tabcontrollers/MoveCenterTabController.h index 270582e5..ad9efe58 100644 --- a/src/tabcontrollers/MoveCenterTabController.h +++ b/src/tabcontrollers/MoveCenterTabController.h @@ -1,8 +1,11 @@ #pragma once +#include "AudioTabController.h" #include +#include #include +#include class QQuickWindow; // application namespace @@ -20,20 +23,36 @@ class MoveCenterTabController : public QObject { Q_PROPERTY(float offsetZ READ offsetZ WRITE setOffsetZ NOTIFY offsetZChanged) Q_PROPERTY(int rotation READ rotation WRITE setRotation NOTIFY rotationChanged) Q_PROPERTY(bool adjustChaperone READ adjustChaperone WRITE setAdjustChaperone NOTIFY adjustChaperoneChanged) + Q_PROPERTY(bool pttEnabled READ pttEnabled WRITE setPttEnabled NOTIFY pttEnabledChanged) + Q_PROPERTY(bool pttActive READ pttActive NOTIFY pttActiveChanged) + Q_PROPERTY(bool pttLeftControllerEnabled READ pttLeftControllerEnabled WRITE setPttLeftControllerEnabled NOTIFY pttLeftControllerEnabledChanged) + Q_PROPERTY(bool pttRightControllerEnabled READ pttRightControllerEnabled WRITE setPttRightControllerEnabled NOTIFY pttRightControllerEnabledChanged) private: + void offsetYToggle(bool enabled); + OverlayController* parent; QQuickWindow* widget; int m_trackingUniverse = (int)vr::TrackingUniverseStanding; float m_offsetX = 0.0f; float m_offsetY = 0.0f; + float m_toggleOffsetY = 0.0f; float m_offsetZ = 0.0f; int m_rotation = 0; bool m_adjustChaperone = true; + bool m_pttEnabled = false; + bool m_pttActive = false; + bool m_pttShowNotification = false; + bool m_pttLeftControllerEnabled = false; + bool m_pttRightControllerEnabled = false; + PttControllerConfig m_pttControllerConfigs[2]; + unsigned settingsUpdateCounter = 0; + std::recursive_mutex eventLoopMutex; + public: void initStage1(); void initStage2(OverlayController* parent, QQuickWindow* widget); @@ -46,6 +65,19 @@ class MoveCenterTabController : public QObject { int rotation() const; bool adjustChaperone() const; + bool pttEnabled() const; + bool pttActive() const; + bool pttLeftControllerEnabled() const; + bool pttRightControllerEnabled() const; + void reloadPttConfig(); + void savePttConfig(); + + Q_INVOKABLE QVariantList pttDigitalButtons(unsigned controller); + Q_INVOKABLE unsigned long pttDigitalButtonMask(unsigned controller); + Q_INVOKABLE unsigned pttTouchpadMode(unsigned controller); + Q_INVOKABLE unsigned pttTriggerMode(unsigned controller); + Q_INVOKABLE unsigned pttTouchpadArea(unsigned controller); + public slots: int trackingUniverse() const; void setTrackingUniverse(int value, bool notify = true); @@ -63,6 +95,12 @@ public slots: void modOffsetZ(float value, bool notify = true); void reset(); + void setPttEnabled(bool value, bool notify = true, bool save = true); + void setPttLeftControllerEnabled(bool value, bool notify = true, bool save = true); + void setPttRightControllerEnabled(bool value, bool notify = true, bool save = true); + + void setPttControllerConfig(unsigned controller, QVariantList buttons, unsigned triggerMode, unsigned padMode, unsigned padAreas); + signals: void trackingUniverseChanged(int value); void offsetXChanged(float value); @@ -70,6 +108,11 @@ public slots: void offsetZChanged(float value); void rotationChanged(int value); void adjustChaperoneChanged(bool value); + + void pttEnabledChanged(bool value); + void pttActiveChanged(bool value); + void pttLeftControllerEnabledChanged(bool value); + void pttRightControllerEnabledChanged(bool value); }; } // namespace advsettings