diff --git a/advancedSettings.pro b/advancedSettings.pro index 03b332c..e9d529d 100644 --- a/advancedSettings.pro +++ b/advancedSettings.pro @@ -57,7 +57,9 @@ SOURCES += src/main.cpp\ src/tabcontrollers/AccessibilityTabController.cpp \ src/utils/ChaperoneUtils.cpp \ src/tabcontrollers/audiomanager/AudioManagerDummy.cpp \ - src/tabcontrollers/keyboardinput/KeyboardInputDummy.cpp + src/tabcontrollers/keyboardinput/KeyboardInputDummy.cpp \ + src/overlaycontroller/openvr_init.cpp + HEADERS += src/overlaycontroller.h \ src/tabcontrollers/AudioTabController.h \ @@ -76,7 +78,8 @@ HEADERS += src/overlaycontroller.h \ src/utils/Matrix.h \ src/utils/ChaperoneUtils.h \ src/tabcontrollers/audiomanager/AudioManagerDummy.h \ - src/tabcontrollers/keyboardinput/KeyboardInputDummy.h + src/tabcontrollers/keyboardinput/KeyboardInputDummy.h \ + src/overlaycontroller/openvr_init.h win32 { SOURCES += src/tabcontrollers/audiomanager/AudioManagerWindows.cpp \ diff --git a/src/overlaycontroller.cpp b/src/overlaycontroller.cpp index a585437..79dde52 100644 --- a/src/overlaycontroller.cpp +++ b/src/overlaycontroller.cpp @@ -31,26 +31,13 @@ QSettings* OverlayController::_appSettings = nullptr; OverlayController::OverlayController( bool desktopMode, bool noSound, QQmlEngine& qmlEngine ) - : QObject(), m_desktopMode( desktopMode ), m_noSound( noSound ) + : QObject(), m_desktopMode( desktopMode ), m_noSound( noSound ), + openVrInit() { - // Loading the OpenVR Runtime - auto initError = vr::VRInitError_None; - vr::VR_Init( &initError, vr::VRApplication_Overlay ); - if ( initError != vr::VRInitError_None ) - { - if ( initError == vr::VRInitError_Init_HmdNotFound - || initError == vr::VRInitError_Init_HmdNotFoundPresenceFailed ) - { - QMessageBox::critical( nullptr, - "OpenVR Advanced Settings Overlay", - "Could not find HMD!" ); - } - throw std::runtime_error( - std::string( "Failed to initialize OpenVR: " ) - + std::string( - vr::VR_GetVRInitErrorAsEnglishDescription( initError ) ) ); - } - + // Despite arguably being OpenVR init code, the call is still here because + // the TabController uses this directly. Offering it through OpenVR_Init + // might be an option, but it might scope creep OpenVR_Init which currently + // doesn't contain any member variables. m_runtimePathUrl = QUrl::fromLocalFile( vr::VR_RuntimePath() ); LOG( INFO ) << "VR Runtime Path: " << m_runtimePathUrl.toLocalFile(); @@ -101,96 +88,6 @@ OverlayController::OverlayController( bool desktopMode, << alarm01SoundFile; } - // Check whether OpenVR is too outdated - if ( !vr::VR_IsInterfaceVersionValid( vr::IVRSystem_Version ) ) - { - QMessageBox::critical( - nullptr, - "OpenVR Advanced Settings Overlay", - "OpenVR version is too outdated. Please update OpenVR." ); - throw std::runtime_error( - std::string( "OpenVR version is too outdated: Interface version " ) - + std::string( vr::IVRSystem_Version ) - + std::string( " not found." ) ); - } - else if ( !vr::VR_IsInterfaceVersionValid( vr::IVRSettings_Version ) ) - { - QMessageBox::critical( - nullptr, - "OpenVR Advanced Settings Overlay", - "OpenVR version is too outdated. Please update OpenVR." ); - throw std::runtime_error( - std::string( "OpenVR version is too outdated: Interface version " ) - + std::string( vr::IVRSettings_Version ) - + std::string( " not found." ) ); - } - else if ( !vr::VR_IsInterfaceVersionValid( vr::IVROverlay_Version ) ) - { - QMessageBox::critical( - nullptr, - "OpenVR Advanced Settings Overlay", - "OpenVR version is too outdated. Please update OpenVR." ); - throw std::runtime_error( - std::string( "OpenVR version is too outdated: Interface version " ) - + std::string( vr::IVROverlay_Version ) - + std::string( " not found." ) ); - } - else if ( !vr::VR_IsInterfaceVersionValid( vr::IVRApplications_Version ) ) - { - QMessageBox::critical( - nullptr, - "OpenVR Advanced Settings Overlay", - "OpenVR version is too outdated. Please update OpenVR." ); - throw std::runtime_error( - std::string( "OpenVR version is too outdated: Interface version " ) - + std::string( vr::IVRApplications_Version ) - + std::string( " not found." ) ); - } - else if ( !vr::VR_IsInterfaceVersionValid( vr::IVRChaperone_Version ) ) - { - QMessageBox::critical( - nullptr, - "OpenVR Advanced Settings Overlay", - "OpenVR version is too outdated. Please update OpenVR." ); - throw std::runtime_error( - std::string( "OpenVR version is too outdated: Interface version " ) - + std::string( vr::IVRChaperone_Version ) - + std::string( " not found." ) ); - } - else if ( !vr::VR_IsInterfaceVersionValid( vr::IVRChaperoneSetup_Version ) ) - { - QMessageBox::critical( - nullptr, - "OpenVR Advanced Settings Overlay", - "OpenVR version is too outdated. Please update OpenVR." ); - throw std::runtime_error( - std::string( "OpenVR version is too outdated: Interface version " ) - + std::string( vr::IVRChaperoneSetup_Version ) - + std::string( " not found." ) ); - } - else if ( !vr::VR_IsInterfaceVersionValid( vr::IVRCompositor_Version ) ) - { - QMessageBox::critical( - nullptr, - "OpenVR Advanced Settings Overlay", - "OpenVR version is too outdated. Please update OpenVR." ); - throw std::runtime_error( - std::string( "OpenVR version is too outdated: Interface version " ) - + std::string( vr::IVRCompositor_Version ) - + std::string( " not found." ) ); - } - else if ( !vr::VR_IsInterfaceVersionValid( vr::IVRNotifications_Version ) ) - { - QMessageBox::critical( - nullptr, - "OpenVR Advanced Settings Overlay", - "OpenVR version is too outdated. Please update OpenVR." ); - throw std::runtime_error( - std::string( "OpenVR version is too outdated: Interface version " ) - + std::string( vr::IVRNotifications_Version ) - + std::string( " not found." ) ); - } - QSurfaceFormat format; // Qt's QOpenGLPaintDevice is not compatible with OpenGL versions >= 3.0 // NVIDIA does not care, but unfortunately AMD does diff --git a/src/overlaycontroller.h b/src/overlaycontroller.h index 8b22da9..34bb741 100644 --- a/src/overlaycontroller.h +++ b/src/overlaycontroller.h @@ -23,6 +23,9 @@ #include #include #include + +#include "overlaycontroller/openvr_init.h" + #include "utils/ChaperoneUtils.h" #include "tabcontrollers/SteamVRTabController.h" @@ -80,6 +83,11 @@ private: QSoundEffect m_focusChangedSoundEffect; QSoundEffect m_alarm01SoundEffect; + // OpenVR_Init must be declared before any other class that uses OpenVR + // function calls since objects are initialized in order of declaration in + // the class. + OpenVR_Init openVrInit; + public: // I know it's an ugly hack to make them public to enable external // access, but I am too lazy to implement getters. SteamVRTabController m_steamVRTabController; diff --git a/src/overlaycontroller/openvr_init.cpp b/src/overlaycontroller/openvr_init.cpp new file mode 100644 index 0000000..5cc678c --- /dev/null +++ b/src/overlaycontroller/openvr_init.cpp @@ -0,0 +1,78 @@ +#include +#include +#include +#include "openvr_init.h" + +OpenVR_Init::OpenVR_Init() +{ + // Loading the OpenVR Runtime + auto initError = vr::VRInitError_None; + vr::VR_Init( &initError, vr::VRApplication_Overlay ); + if ( initError != vr::VRInitError_None ) + { + if ( initError == vr::VRInitError_Init_HmdNotFound + || initError == vr::VRInitError_Init_HmdNotFoundPresenceFailed ) + { + QMessageBox::critical( nullptr, + "OpenVR Advanced Settings Overlay", + "Could not find HMD!" ); + } + throw std::runtime_error( + std::string( "Failed to initialize OpenVR: " ) + + std::string( + vr::VR_GetVRInitErrorAsEnglishDescription( initError ) ) ); + } + + // The function call and error message was the same for all version checks. + // Specific error messages are unlikely to be necessary since both the type + // and version are in the string and will be output. + auto reportVersionError = []( const char* const interfaceAndVersion ) { + QMessageBox::critical( + nullptr, + "OpenVR Advanced Settings Overlay", + "OpenVR version is too outdated. Please update OpenVR." ); + throw std::runtime_error( + std::string( "OpenVR version is too outdated: Interface version " ) + + std::string( interfaceAndVersion ) + + std::string( " not found." ) ); + }; + + // Check whether OpenVR is too outdated + if ( !vr::VR_IsInterfaceVersionValid( vr::IVRSystem_Version ) ) + { + reportVersionError( vr::IVRSystem_Version ); + } + else if ( !vr::VR_IsInterfaceVersionValid( vr::IVRSettings_Version ) ) + { + reportVersionError( vr::IVRSettings_Version ); + } + else if ( !vr::VR_IsInterfaceVersionValid( vr::IVROverlay_Version ) ) + { + reportVersionError( vr::IVROverlay_Version ); + } + else if ( !vr::VR_IsInterfaceVersionValid( vr::IVRApplications_Version ) ) + { + reportVersionError( vr::IVRApplications_Version ); + } + else if ( !vr::VR_IsInterfaceVersionValid( vr::IVRChaperone_Version ) ) + { + reportVersionError( vr::IVRChaperone_Version ); + } + else if ( !vr::VR_IsInterfaceVersionValid( vr::IVRChaperoneSetup_Version ) ) + { + reportVersionError( vr::IVRChaperoneSetup_Version ); + } + else if ( !vr::VR_IsInterfaceVersionValid( vr::IVRCompositor_Version ) ) + { + reportVersionError( vr::IVRCompositor_Version ); + } + else if ( !vr::VR_IsInterfaceVersionValid( vr::IVRNotifications_Version ) ) + { + reportVersionError( vr::IVRNotifications_Version ); + } +} + +OpenVR_Init::~OpenVR_Init() +{ + vr::VR_Shutdown(); +} diff --git a/src/overlaycontroller/openvr_init.h b/src/overlaycontroller/openvr_init.h new file mode 100644 index 0000000..bdec289 --- /dev/null +++ b/src/overlaycontroller/openvr_init.h @@ -0,0 +1,35 @@ +#pragma once + +/*! +Initializes OpenVR and checks validity of interface versions. Shuts down OpenVR +when destroyed. + +This object should be declared above all other objects in the class file that +use OpenVR calls, since the order of declaration decides which objects are +initialized first. + +The comment/tooltips have not been provided for the constructor/destructor since +they do not need explanation due to the straightforward nature of their +implementation. +*/ +class OpenVR_Init +{ +public: + OpenVR_Init(); + ~OpenVR_Init(); + + // As per the Rule of Five, the following constructors and assignments can + // be created by the compiler automatically if they are used in the code. + // The compiler will not be able to correctly create this class and it + // wouldn't make sense to copy a class that just starts OpenVR. + // They are explicitly deleted to make sure they aren't used, and to + // document that they shouldn't be used. + // Copy constructor + OpenVR_Init( const OpenVR_Init& ) = delete; + // Copy assignment + const OpenVR_Init& operator=( const OpenVR_Init& ) = delete; + // Move constructor + OpenVR_Init( OpenVR_Init&& ) = delete; + // Move assignment + const OpenVR_Init operator=( OpenVR_Init&& ) = delete; +};