diff --git a/Readme.md b/Readme.md index 2f73864..44f17df 100644 --- a/Readme.md +++ b/Readme.md @@ -313,8 +313,8 @@ Allows users to temporarily move and rotate the center of the playspace. This al - **Fix Floor** Allows you to fix the height of your floor. Just place one controller on your floor and press the button. - **Recenter Playspace** Besides fixing the floor height, also recenters the place space around the controller on the floor. -- **Undo Fix** Removes last "fix" **(Temporarily disabled pending integration into new motion system.)** - **Apply Space Settings Offsets as Center** Takes current values from Offsets page and re-caliberates center/rotation. **Caution** The reset function will then refer to this location as the new zero location, overriding the old zero location. +- **Revert All Changes from This Session** Reverts the chaperone config back to the state it was in when launching vr. (This applies the Autosaved Profile from the Chaperone Tab). ## - Audio Page @@ -390,6 +390,7 @@ Allows users to temporarily move and rotate the center of the playspace. This al - **Universe-Centered Rotation:** Causes Rotation to be applied to Universe Center Rather than HMD (Disables offsets automatically compensating to pivot at the HMD). - **Enable Auto-set Seated Offsets During Seated Recenter:** Stops the Space Offsets tab offsets from moving you to the center during seated mode recentering. (In case the offset recentering interferes with position in some applications). - **Disable All Motion Features When in Seated Mode:** Use this if Advanced Settings causes strange positioning in a seated application. (Different apps handle seated mode in different ways, some are less compatible with Advanced Settings). Note: all Space Offsets and Motion tab features will be disabled. +- **Disable Automatic Crash Recovery of Chaperone Config:** This prevents applying of the last good autosaved chaperone profile when starting up after not shutting down properly. - **Disable App Vsync:** Allows setting a custom base update rate for Advanced Settings. (Might be useful on HMDs with very high or very low refresh rates). # How to Compile diff --git a/docs/screenshots/FloorFixPage.png b/docs/screenshots/FloorFixPage.png index 2d83c84..2e3341b 100644 Binary files a/docs/screenshots/FloorFixPage.png and b/docs/screenshots/FloorFixPage.png differ diff --git a/docs/screenshots/SettingsPage.png b/docs/screenshots/SettingsPage.png index badcdd7..ef0b829 100644 Binary files a/docs/screenshots/SettingsPage.png and b/docs/screenshots/SettingsPage.png differ diff --git a/src/overlaycontroller.cpp b/src/overlaycontroller.cpp index 34d982e..8c22518 100644 --- a/src/overlaycontroller.cpp +++ b/src/overlaycontroller.cpp @@ -300,7 +300,8 @@ OverlayController::OverlayController( bool desktopMode, } appSettings()->endGroup(); - // Keep the settings for vsyncDisabled here in main overlaycontroller + // Keep the settings for vsyncDisabled and crash recovery here in main + // overlaycontroller appSettings()->beginGroup( "applicationSettings" ); auto value = appSettings()->value( "vsyncDisabled", m_vsyncDisabled ); if ( value.isValid() && !value.isNull() ) @@ -324,6 +325,18 @@ OverlayController::OverlayController( bool desktopMode, m_customTickRateMs = value.toInt(); } } + value = appSettings()->value( "previousShutdownSafe", + m_previousShutdownSafe ); + if ( value.isValid() && !value.isNull() ) + { + m_previousShutdownSafe = value.toBool(); + } + value = appSettings()->value( "crashRecoveryDisabled", + m_crashRecoveryDisabled ); + if ( value.isValid() && !value.isNull() ) + { + m_crashRecoveryDisabled = value.toBool(); + } appSettings()->endGroup(); } @@ -365,6 +378,9 @@ void OverlayController::Shutdown() m_pFbo.reset(); m_pOpenGLContext.reset(); m_pOffscreenSurface.reset(); + + // save to settings that shutdown was safe + setPreviousShutdownSafe( true ); } void OverlayController::SetWidget( QQuickItem* quickItem, @@ -716,6 +732,28 @@ void OverlayController::processInputBindings() processKeyboardBindings(); } +bool OverlayController::crashRecoveryDisabled() const +{ + return m_crashRecoveryDisabled; +} + +void OverlayController::setCrashRecoveryDisabled( bool value, bool notify ) +{ + if ( m_crashRecoveryDisabled == value ) + { + return; + } + m_crashRecoveryDisabled = value; + appSettings()->beginGroup( "applicationSettings" ); + appSettings()->setValue( "crashRecoveryDisabled", m_crashRecoveryDisabled ); + appSettings()->endGroup(); + appSettings()->sync(); + if ( notify ) + { + emit crashRecoveryDisabledChanged( m_crashRecoveryDisabled ); + } +} + bool OverlayController::vsyncDisabled() const { return m_vsyncDisabled; @@ -738,6 +776,19 @@ void OverlayController::setVsyncDisabled( bool value, bool notify ) } } +void OverlayController::setPreviousShutdownSafe( bool value ) +{ + if ( m_previousShutdownSafe == value ) + { + return; + } + m_previousShutdownSafe = value; + appSettings()->beginGroup( "applicationSettings" ); + appSettings()->setValue( "previousShutdownSafe", m_previousShutdownSafe ); + appSettings()->endGroup(); + appSettings()->sync(); +} + int OverlayController::customTickRateMs() const { return m_customTickRateMs; diff --git a/src/overlaycontroller.h b/src/overlaycontroller.h index 6df5110..26b3bd3 100644 --- a/src/overlaycontroller.h +++ b/src/overlaycontroller.h @@ -77,6 +77,9 @@ class OverlayController : public QObject { Q_OBJECT Q_PROPERTY( bool m_desktopMode READ isDesktopMode ) + Q_PROPERTY( + bool crashRecoveryDisabled READ crashRecoveryDisabled WRITE + setCrashRecoveryDisabled NOTIFY crashRecoveryDisabledChanged ) Q_PROPERTY( bool vsyncDisabled READ vsyncDisabled WRITE setVsyncDisabled NOTIFY vsyncDisabledChanged ) Q_PROPERTY( int customTickRateMs READ customTickRateMs WRITE @@ -101,7 +104,9 @@ private: Qt::MouseButtons m_lastMouseButtons = nullptr; bool m_desktopMode; + bool m_previousShutdownSafe = true; bool m_noSound; + bool m_crashRecoveryDisabled = false; bool m_vsyncDisabled = false; int m_customTickRateMs = 20; @@ -182,6 +187,12 @@ public: return m_desktopMode; } + bool isPreviousShutdownSafe() + { + return m_previousShutdownSafe; + } + void setPreviousShutdownSafe( bool value ); + utils::ChaperoneUtils& chaperoneUtils() noexcept { return m_chaperoneUtils; @@ -199,6 +210,7 @@ public: vr::VREvent_t* pEvent ); void mainEventLoop(); + bool crashRecoveryDisabled() const; bool vsyncDisabled() const; int customTickRateMs() const; @@ -215,11 +227,13 @@ public slots: void setAlarm01SoundVolume( float vol ); void cancelAlarm01Sound(); + void setCrashRecoveryDisabled( bool value, bool notify = true ); void setVsyncDisabled( bool value, bool notify = true ); void setCustomTickRateMs( int value, bool notify = true ); signals: void keyBoardInputSignal( QString input, unsigned long userValue = 0 ); + void crashRecoveryDisabledChanged( bool value ); void vsyncDisabledChanged( bool value ); void customTickRateMsChanged( int value ); diff --git a/src/res/qml/FixFloorPage.qml b/src/res/qml/FixFloorPage.qml index 3041972..641b35a 100644 --- a/src/res/qml/FixFloorPage.qml +++ b/src/res/qml/FixFloorPage.qml @@ -59,7 +59,7 @@ MyStackViewPage { id: fixButton Layout.fillWidth: true text: "Fix Floor" - Layout.preferredHeight: 100 + Layout.preferredHeight: 80 onClicked: { FixFloorTabController.fixFloorClicked() } @@ -69,7 +69,7 @@ MyStackViewPage { id: recenterButton Layout.fillWidth: true text: "Recenter Space" - Layout.preferredHeight: 100 + Layout.preferredHeight: 80 onClicked: { FixFloorTabController.recenterClicked() } @@ -91,7 +91,7 @@ MyStackViewPage { id: zeroSpaceButton Layout.fillWidth: true text: "Apply Space Settings Offsets as Center" - Layout.preferredHeight: 100 + Layout.preferredHeight: 80 onClicked: { MoveCenterTabController.zeroOffsets() } @@ -101,6 +101,20 @@ MyStackViewPage { Layout.preferredHeight: 32 } + MyPushButton { + id: revertButton + Layout.fillWidth: true + text: "Revert All Changes from This Session" + Layout.preferredHeight: 80 + onClicked: { + ChaperoneTabController.applyAutosavedProfile() + } + } + + Item { + Layout.preferredHeight: 32 + } + Component.onCompleted: { statusMessageText.text = "" //undoFixButton.enabled = FixFloorTabController.canUndo @@ -110,6 +124,7 @@ MyStackViewPage { fixButton.enabled = false recenterButton.enabled = false zeroSpaceButton.enabled = false + revertButton.enabled = false undoFixButton.enabled = false seatedWarningText.visible = true } @@ -155,12 +170,14 @@ MyStackViewPage { fixButton.enabled = false recenterButton.enabled = false zeroSpaceButton.enabled = false + revertButton.enabled = false undoFixButton.enabled = false seatedWarningText.visible = true } else if (MoveCenterTabController.trackingUniverse === 1) { fixButton.enabled = true recenterButton.enabled = true zeroSpaceButton.enabled = true + revertButton.enabled = true // undoFixButton.enabled = true // TODO Fix Undo Feature -^ seatedWarningText.visible = false @@ -168,6 +185,7 @@ MyStackViewPage { fixButton.enabled = false recenterButton.enabled = false zeroSpaceButton.enabled = false + revertButton.enabled = false undoFixButton.enabled = false seatedWarningText.visible = false } diff --git a/src/res/qml/SettingsPage.qml b/src/res/qml/SettingsPage.qml index 2adb35c..ad4b1ab 100644 --- a/src/res/qml/SettingsPage.qml +++ b/src/res/qml/SettingsPage.qml @@ -58,6 +58,13 @@ MyStackViewPage { } } + MyToggleButton { + id: disableCrashRecoveryToggle + text: "Disable Automatic Crash Recovery of Chaperone Config" + onCheckedChanged: { + OverlayController.setCrashRecoveryDisabled(checked, true) + } + } RowLayout { Layout.fillWidth: true @@ -126,6 +133,7 @@ MyStackViewPage { enableSeatedOffsetsRecenterToggle.checked = MoveCenterTabController.enableSeatedOffsetsRecenter disableSeatedMotionToggle.checked = MoveCenterTabController.disableSeatedMotion + disableCrashRecoveryToggle.checked = OverlayController.crashRecoveryDisabled customTickRateText.text = OverlayController.customTickRateMs vsyncDisabledToggle.checked = OverlayController.vsyncDisabled customTickRateText.visible = vsyncDisabledToggle.checked @@ -167,6 +175,10 @@ MyStackViewPage { customTickRateLabel.visible = vsyncDisabledToggle.checked customTickRateMsLabel.visible = vsyncDisabledToggle.checked } + onCrashRecoveryDisabledChanged: { + disableCrashRecoveryToggle.checked = OverlayController.crashRecoveryDisabled + } + onCustomTickRateMsChanged: { customTickRateText.text = OverlayController.customTickRateMs } diff --git a/src/tabcontrollers/ChaperoneTabController.cpp b/src/tabcontrollers/ChaperoneTabController.cpp index 235a1d2..ab48a80 100644 --- a/src/tabcontrollers/ChaperoneTabController.cpp +++ b/src/tabcontrollers/ChaperoneTabController.cpp @@ -644,60 +644,7 @@ void ChaperoneTabController::eventLoopTick( { if ( parent->isDashboardVisible() ) { - vr::EVRSettingsError vrSettingsError; - float vis = static_cast( vr::VRSettings()->GetInt32( - vr::k_pch_CollisionBounds_Section, - vr::k_pch_CollisionBounds_ColorGammaA_Int32, - &vrSettingsError ) ); - if ( vrSettingsError != vr::VRSettingsError_None ) - { - LOG( WARNING ) - << "Could not read \"" - << vr::k_pch_CollisionBounds_ColorGammaA_Int32 - << "\" setting: " - << vr::VRSettings()->GetSettingsErrorNameFromEnum( - vrSettingsError ); - } - setBoundsVisibility( vis / 255.0f ); - if ( vrSettingsError != vr::VRSettingsError_None ) - { - LOG( WARNING ) - << "Could not read \"" - << vr::k_pch_CollisionBounds_FadeDistance_Float - << "\" setting: " - << vr::VRSettings()->GetSettingsErrorNameFromEnum( - vrSettingsError ); - } - - auto cm = vr::VRSettings()->GetBool( - vr::k_pch_CollisionBounds_Section, - vr::k_pch_CollisionBounds_CenterMarkerOn_Bool, - &vrSettingsError ); - if ( vrSettingsError != vr::VRSettingsError_None ) - { - LOG( WARNING ) - << "Could not read \"" - << vr::k_pch_CollisionBounds_CenterMarkerOn_Bool - << "\" setting: " - << vr::VRSettings()->GetSettingsErrorNameFromEnum( - vrSettingsError ); - } - setCenterMarker( cm ); - auto ps = vr::VRSettings()->GetBool( - vr::k_pch_CollisionBounds_Section, - vr::k_pch_CollisionBounds_PlaySpaceOn_Bool, - &vrSettingsError ); - if ( vrSettingsError != vr::VRSettingsError_None ) - { - LOG( WARNING ) - << "Could not read \"" - << vr::k_pch_CollisionBounds_PlaySpaceOn_Bool - << "\" setting: " - << vr::VRSettings()->GetSettingsErrorNameFromEnum( - vrSettingsError ); - } - setPlaySpaceMarker( ps ); - updateHeight( getBoundsMaxY() ); + updateChaperoneSettings(); } settingsUpdateCounter = 0; } @@ -707,6 +654,60 @@ void ChaperoneTabController::eventLoopTick( } } +void ChaperoneTabController::updateChaperoneSettings() +{ + vr::EVRSettingsError vrSettingsError; + float vis = static_cast( + vr::VRSettings()->GetInt32( vr::k_pch_CollisionBounds_Section, + vr::k_pch_CollisionBounds_ColorGammaA_Int32, + &vrSettingsError ) ); + if ( vrSettingsError != vr::VRSettingsError_None ) + { + LOG( WARNING ) << "Could not read \"" + << vr::k_pch_CollisionBounds_ColorGammaA_Int32 + << "\" setting: " + << vr::VRSettings()->GetSettingsErrorNameFromEnum( + vrSettingsError ); + } + setBoundsVisibility( vis / 255.0f ); + if ( vrSettingsError != vr::VRSettingsError_None ) + { + LOG( WARNING ) << "Could not read \"" + << vr::k_pch_CollisionBounds_FadeDistance_Float + << "\" setting: " + << vr::VRSettings()->GetSettingsErrorNameFromEnum( + vrSettingsError ); + } + + auto cm = vr::VRSettings()->GetBool( + vr::k_pch_CollisionBounds_Section, + vr::k_pch_CollisionBounds_CenterMarkerOn_Bool, + &vrSettingsError ); + if ( vrSettingsError != vr::VRSettingsError_None ) + { + LOG( WARNING ) << "Could not read \"" + << vr::k_pch_CollisionBounds_CenterMarkerOn_Bool + << "\" setting: " + << vr::VRSettings()->GetSettingsErrorNameFromEnum( + vrSettingsError ); + } + setCenterMarker( cm ); + auto ps + = vr::VRSettings()->GetBool( vr::k_pch_CollisionBounds_Section, + vr::k_pch_CollisionBounds_PlaySpaceOn_Bool, + &vrSettingsError ); + if ( vrSettingsError != vr::VRSettingsError_None ) + { + LOG( WARNING ) << "Could not read \"" + << vr::k_pch_CollisionBounds_PlaySpaceOn_Bool + << "\" setting: " + << vr::VRSettings()->GetSettingsErrorNameFromEnum( + vrSettingsError ); + } + setPlaySpaceMarker( ps ); + updateHeight( getBoundsMaxY() ); +} + float ChaperoneTabController::boundsVisibility() const { return m_visibility; @@ -1520,6 +1521,91 @@ void ChaperoneTabController::deleteChaperoneProfile( unsigned index ) } } +std::pair + ChaperoneTabController::getChaperoneProfileIndexFromName( std::string name ) +{ + std::pair result = { false, 0 }; + unsigned profileIndex = 0; + for ( auto& p : chaperoneProfiles ) + { + if ( name == p.profileName ) + { + result.first = true; + result.second = profileIndex; + break; + } + profileIndex++; + } + return result; +} + +void ChaperoneTabController::createNewAutosaveProfile() +{ + // update settings to live chaperone + updateChaperoneSettings(); + + // lookup the index for old autosave and delete it + std::pair previousAutosaveIndexLookup + = getChaperoneProfileIndexFromName( "«Autosaved Profile (previous)»" ); + if ( previousAutosaveIndexLookup.first ) + { + deleteChaperoneProfile( previousAutosaveIndexLookup.second ); + } + else + { + LOG( INFO ) + << "[Chaperone Autosave] «Autosaved Profile (previous)» not found"; + } + + // lookup the index for current autosave and rename to old + std::pair currentAutosaveIndexLookup + = getChaperoneProfileIndexFromName( "«Autosaved Profile»" ); + if ( currentAutosaveIndexLookup.first ) + { + chaperoneProfiles[currentAutosaveIndexLookup.second].profileName + = "«Autosaved Profile (previous)»"; + saveChaperoneProfiles(); + OverlayController::appSettings()->sync(); + emit chaperoneProfilesUpdated(); + } + else + { + LOG( INFO ) << "[Chaperone Autosave] «Autosaved Profile» not found"; + } + + // create a new autosave from current chaperone (all options set true) + addChaperoneProfile( "«Autosaved Profile»", + true, + true, + true, + true, + true, + true, + true, + true, + true, + true ); +} + +void ChaperoneTabController::applyAutosavedProfile() +{ + // lookup index of autosave + std::pair autosaveIndexLookup + = getChaperoneProfileIndexFromName( "«Autosaved Profile»" ); + + // check if it exists + if ( autosaveIndexLookup.first ) + { + // apply if it exists + applyChaperoneProfile( autosaveIndexLookup.second ); + } + else + { + LOG( WARNING ) << "Failed to apply chaperone Autosaved Profile " + "(autosave doesn't exist)"; + } +} + float ChaperoneTabController::getBoundsMaxY() { unsigned collisionBoundsCount = 0; diff --git a/src/tabcontrollers/ChaperoneTabController.h b/src/tabcontrollers/ChaperoneTabController.h index 4dbc443..1acf23d 100644 --- a/src/tabcontrollers/ChaperoneTabController.h +++ b/src/tabcontrollers/ChaperoneTabController.h @@ -157,6 +157,7 @@ private: bool m_chaperoneShowDashboardActive = false; unsigned settingsUpdateCounter = 0; + void updateChaperoneSettings(); bool m_isHapticGood = true; bool m_isHMDActive = false; @@ -165,6 +166,8 @@ private: bool m_leftHapticClickActivated = false; bool m_rightHapticClickActivated = false; + bool m_autosaveComplete = false; + int m_updateTicksChaperoneReload = 0; vr::VRActionHandle_t m_rightActionHandle; @@ -219,6 +222,9 @@ public: Q_INVOKABLE QString getChaperoneProfileName( unsigned index ); float getBoundsMaxY(); + std::pair + getChaperoneProfileIndexFromName( std::string name ); + void createNewAutosaveProfile(); // actions void addLeftHapticClick( bool leftHapticClickPressed ); @@ -265,6 +271,7 @@ public slots: bool includesProximityWarningSettings ); void applyChaperoneProfile( unsigned index ); void deleteChaperoneProfile( unsigned index ); + void applyAutosavedProfile(); void reset(); diff --git a/src/tabcontrollers/MoveCenterTabController.cpp b/src/tabcontrollers/MoveCenterTabController.cpp index 62691e1..535b3bc 100644 --- a/src/tabcontrollers/MoveCenterTabController.cpp +++ b/src/tabcontrollers/MoveCenterTabController.cpp @@ -1215,6 +1215,11 @@ void MoveCenterTabController::setEnableSeatedOffsetsRecenter( bool value, << m_enableSeatedOffsetsRecenter; } +bool MoveCenterTabController::isInitComplete() const +{ + return m_initComplete; +} + bool MoveCenterTabController::disableSeatedMotion() const { return m_disableSeatedMotion; @@ -1414,7 +1419,30 @@ void MoveCenterTabController::zeroOffsets() if ( !m_initComplete ) { setTrackingUniverse( vr::VRCompositor()->GetTrackingSpace() ); - m_initComplete = true; + if ( parent->isPreviousShutdownSafe() ) + { + // all init complete, safe to autosave chaperone profile + parent->m_chaperoneTabController.createNewAutosaveProfile(); + m_initComplete = true; + } + else + { + // shutdown was unsafe last session! + LOG( WARNING ) + << "DETECTED UNSAFE SHUTDOWN FROM LAST SESSION"; + m_initComplete = true; + if ( !parent->crashRecoveryDisabled() ) + { + parent->m_chaperoneTabController + .applyAutosavedProfile(); + LOG( INFO ) + << "Applying last good chaperone profile autosave"; + } + } + // Now mark previous shutdown as unsafe in case we crash some + // time during this session. Previous shutdown will be marked as + // being safe once more just before our app shuts down properly. + parent->setPreviousShutdownSafe( false ); } } if ( m_roomSetupModeDetected ) diff --git a/src/tabcontrollers/MoveCenterTabController.h b/src/tabcontrollers/MoveCenterTabController.h index bfdfef2..d3b7499 100644 --- a/src/tabcontrollers/MoveCenterTabController.h +++ b/src/tabcontrollers/MoveCenterTabController.h @@ -259,6 +259,7 @@ public: bool universeCenteredRotation() const; bool enableSeatedOffsetsRecenter() const; bool disableSeatedMotion() const; + bool isInitComplete() const; double getHmdYawTotal(); void resetHmdYawTotal(); void incomingSeatedReset();