diff --git a/advancedSettings.pro b/advancedSettings.pro index 20b477a..333b26a 100644 --- a/advancedSettings.pro +++ b/advancedSettings.pro @@ -59,7 +59,7 @@ SOURCES += src/main.cpp\ src/tabcontrollers/audiomanager/AudioManagerDummy.cpp \ src/tabcontrollers/keyboardinput/KeyboardInputDummy.cpp \ src/overlaycontroller/openvr_init.cpp \ - src/steamivrinput.cpp + src/ivrinput/steamivrinput.cpp HEADERS += src/overlaycontroller.h \ src/tabcontrollers/AudioTabController.h \ @@ -80,8 +80,10 @@ HEADERS += src/overlaycontroller.h \ src/tabcontrollers/audiomanager/AudioManagerDummy.h \ src/tabcontrollers/keyboardinput/KeyboardInputDummy.h \ src/overlaycontroller/openvr_init.h \ - src/steamivrinput.h \ - src/ivrinput/ivrinput_action.h + src/ivrinput/ivrinput_action.h \ + src/ivrinput/ivrinput_manifest.h \ + src/ivrinput/ivrinput_action_set.h \ + src/ivrinput/steamivrinput.h win32 { SOURCES += src/tabcontrollers/audiomanager/AudioManagerWindows.cpp \ diff --git a/src/ivrinput/ivrinput_action.h b/src/ivrinput/ivrinput_action.h index 85d2463..f3fe439 100644 --- a/src/ivrinput/ivrinput_action.h +++ b/src/ivrinput/ivrinput_action.h @@ -2,10 +2,13 @@ #include #include +namespace input +{ enum class ActionType { Undefined, Digital, + Analog, }; class Action @@ -22,21 +25,23 @@ public: << "'. OpenVR Error: " << error; } } - vr::VRActionHandle_t handle() + vr::VRActionHandle_t handle() const noexcept { return m_handle; } - const char* name() + std::string name() const noexcept { return m_name; } - ActionType type() + ActionType type() const noexcept { return m_type; } private: vr::VRActionHandle_t m_handle = 0; - const char* m_name; - ActionType m_type = ActionType::Undefined; + const std::string m_name; + const ActionType m_type = ActionType::Undefined; }; + +} // namespace input diff --git a/src/ivrinput/ivrinput_action_set.h b/src/ivrinput/ivrinput_action_set.h new file mode 100644 index 0000000..66b35a0 --- /dev/null +++ b/src/ivrinput/ivrinput_action_set.h @@ -0,0 +1,33 @@ +#pragma once +#include +#include + +namespace input +{ +class ActionSet +{ +public: + ActionSet( const char* setName ) : m_name( setName ) + { + auto error = vr::VRInput()->GetActionSetHandle( setName, &m_handle ); + if ( error != vr::EVRInputError::VRInputError_None ) + { + LOG( ERROR ) << "Error getting handle for '" << setName + << "'. OpenVR Error: " << error; + } + } + std::string name() + { + return m_name; + } + vr::VRActionSetHandle_t handle() + { + return m_handle; + } + +private: + const std::string m_name; + vr::VRActionSetHandle_t m_handle = 0; +}; + +} // namespace input diff --git a/src/ivrinput/ivrinput_manifest.h b/src/ivrinput/ivrinput_manifest.h new file mode 100644 index 0000000..f00494e --- /dev/null +++ b/src/ivrinput/ivrinput_manifest.h @@ -0,0 +1,44 @@ +#pragma once +#include +#include +#include + +namespace input +{ +/*! +Initializes IVRInput to point at the correct manifest. Can't be done in the +constructor body since objects being initialized require this to be set. +*/ +class Manifest +{ +public: + Manifest() + { + QString actionManifestPath = QStandardPaths::locate( + QStandardPaths::AppDataLocation, + QStringLiteral( "action_manifest.json" ) ); + + auto error = vr::VRInput()->SetActionManifestPath( + actionManifestPath.toStdString().c_str() ); + if ( error != vr::EVRInputError::VRInputError_None ) + { + LOG( ERROR ) << "Error setting action manifest path: " + << actionManifestPath << ". OpenVR Error: " << error; + } + } + + // Explicitly deleted because this object should do nothing but run the + // constructor during initialization. + // Destructor left blank on purpose since there are no terminating calls. + ~Manifest() {} + // Copy constructor + Manifest( const Manifest& ) = delete; + // Copy assignment + Manifest& operator=( const Manifest& ) = delete; + // Move constructor + Manifest( const Manifest&& ) = delete; + // Move assignment + Manifest& operator=( const Manifest&& ) = delete; +}; + +} // namespace input diff --git a/src/ivrinput/steamivrinput.cpp b/src/ivrinput/steamivrinput.cpp new file mode 100644 index 0000000..55ba537 --- /dev/null +++ b/src/ivrinput/steamivrinput.cpp @@ -0,0 +1,114 @@ +#include "steamivrinput.h" +#include +#include +#include +#include + +namespace input +{ +vr::InputDigitalActionData_t getDigitalActionData( Action& action ) +{ + if ( action.type() != ActionType::Digital ) + { + LOG( ERROR ) + << "Action was passed to IVRInput getDigitalActionData without " + "being a digital type. Action: " + << action.name(); + + throw std::runtime_error( + "Action was passed to IVRInput getDigitalActionData without being " + "a digital type. See log for details." ); + } + + vr::InputDigitalActionData_t handleData = {}; + + const auto error = vr::VRInput()->GetDigitalActionData( + action.handle(), + &handleData, + sizeof( handleData ), + vr::k_ulInvalidInputValueHandle ); + + if ( error != vr::EVRInputError::VRInputError_None ) + { + LOG( ERROR ) << "Error getting IVRInput Digital Action Data for handle " + << action.name() << ". SteamVR Error: " << error; + } + + return handleData; +} + +vr::InputAnalogActionData_t getAnalogActionData( Action& action ) +{ + if ( action.type() != ActionType::Analog ) + { + LOG( ERROR ) + << "Action was passed to IVRInput getAnalogActionData without " + "being an analog type. Action: " + << action.name(); + + throw std::runtime_error( + "Action was passed to IVRInput getAnalogActionData without being " + "an analog type. See log for details." ); + } + + vr::InputAnalogActionData_t handleData = {}; + + const auto error + = vr::VRInput()->GetAnalogActionData( action.handle(), + &handleData, + sizeof( handleData ), + vr::k_ulInvalidInputValueHandle ); + + if ( error != vr::EVRInputError::VRInputError_None ) + { + LOG( ERROR ) << "Error getting IVRInput Digital Action Data for handle " + << action.name() << ". SteamVR Error: " << error; + } + + return handleData; +} + +bool isDigitalActionActivatedOnce( Action& action ) +{ + const auto handleData = getDigitalActionData( action ); + + return handleData.bState && handleData.bChanged; +} + +bool isDigitalActionActivatedConstant( Action& action ) +{ + const auto handleData = getDigitalActionData( action ); + + return handleData.bState; +} + +SteamIVRInput::SteamIVRInput() + : m_manifest(), m_mainSet( input_strings::k_setMain ), + m_nextTrack( input_strings::k_actionNextTrack, ActionType::Digital ) +{ + m_activeActionSet.ulActionSet = m_mainSet.handle(); + m_activeActionSet.ulRestrictedToDevice = vr::k_ulInvalidInputValueHandle; + m_activeActionSet.nPriority = 0; +} + +bool SteamIVRInput::nextSong() +{ + return isDigitalActionActivatedOnce( m_nextTrack ); +} + +void SteamIVRInput::Loop() +{ + constexpr auto numberOfSets = 1; + + const auto error = vr::VRInput()->UpdateActionState( + &m_activeActionSet, sizeof( m_activeActionSet ), numberOfSets ); + + if ( error != vr::EVRInputError::VRInputError_None ) + { + LOG( ERROR ) + << "Error during IVRInput action state update. OpenVR Error: " + << error; + } +} + +} // namespace input diff --git a/src/ivrinput/steamivrinput.h b/src/ivrinput/steamivrinput.h new file mode 100644 index 0000000..158a542 --- /dev/null +++ b/src/ivrinput/steamivrinput.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include "ivrinput_action.h" +#include "ivrinput_manifest.h" +#include "ivrinput_action_set.h" + +namespace input +{ +class SteamIVRInput +{ +public: + SteamIVRInput(); + void Loop(); + bool nextSong(); + + // Destructor. There are no terminating calls for the IVRInput API, so it + // is left blank. + ~SteamIVRInput() {} + + // These have been explicitly deleted to make sure there are no attempts at + // copying the class in weird ways. It is not worth defining what should + // happen on copy because it simply shouldn't be done. + // They are declared public because compilers will give wrong error messages + // if they are private. + // Copy constructor + SteamIVRInput( const SteamIVRInput& ) = delete; + // Copy assignment + SteamIVRInput& operator=( const SteamIVRInput& ) = delete; + // Move constructor + SteamIVRInput( SteamIVRInput&& ) = delete; + // Move assignment + SteamIVRInput& operator=( const SteamIVRInput&& ) = delete; + +private: + Manifest m_manifest; + ActionSet m_mainSet; + vr::VRActiveActionSet_t m_activeActionSet = {}; + + Action m_nextTrack; +}; + +namespace input_strings +{ + constexpr auto k_actionNextTrack = "/actions/main/in/PlayNextTrack"; + constexpr auto k_setMain = "/actions/main"; + +} // namespace input_strings + +} // namespace input diff --git a/src/overlaycontroller.cpp b/src/overlaycontroller.cpp index 288fba7..1f94e98 100644 --- a/src/overlaycontroller.cpp +++ b/src/overlaycontroller.cpp @@ -32,7 +32,7 @@ OverlayController::OverlayController( bool desktopMode, bool noSound, QQmlEngine& qmlEngine ) : QObject(), m_desktopMode( desktopMode ), m_noSound( noSound ), - m_openVrInit() + m_openVrInit(), m_actions() { // Despite arguably being OpenVR init code, the call is still here because // the TabController uses this directly. Offering it through OpenVR_Init @@ -118,7 +118,6 @@ OverlayController::OverlayController( bool desktopMode, nullptr, "OpenVR Advanced Settings Overlay", "Is OpenVR running?" ); throw std::runtime_error( std::string( "No Overlay interface" ) ); } - m_actions.Init(); // Init controllers m_steamVRTabController.initStage1(); m_chaperoneTabController.initStage1(); @@ -479,7 +478,7 @@ void OverlayController::OnTimeoutPumpEvents() m_actions.Loop(); - if ( m_actions.nextSongSet() ) + if ( m_actions.nextSong() ) { LOG( INFO ) << "NEXT SONG SET!"; } diff --git a/src/overlaycontroller.h b/src/overlaycontroller.h index 9442b06..cb7beaa 100644 --- a/src/overlaycontroller.h +++ b/src/overlaycontroller.h @@ -39,7 +39,7 @@ #include "tabcontrollers/UtilitiesTabController.h" #include "tabcontrollers/AccessibilityTabController.h" -#include "steamivrinput.h" +#include "ivrinput/steamivrinput.h" // application namespace namespace advsettings @@ -92,7 +92,7 @@ private: public: // I know it's an ugly hack to make them public to enable external // access, but I am too lazy to implement getters. - SteamIVRInput m_actions; + input::SteamIVRInput m_actions; SteamVRTabController m_steamVRTabController; ChaperoneTabController m_chaperoneTabController; MoveCenterTabController m_moveCenterTabController; diff --git a/src/overlaycontroller/openvr_init.cpp b/src/overlaycontroller/openvr_init.cpp index 5cc678c..b63ba1a 100644 --- a/src/overlaycontroller/openvr_init.cpp +++ b/src/overlaycontroller/openvr_init.cpp @@ -70,6 +70,10 @@ OpenVR_Init::OpenVR_Init() { reportVersionError( vr::IVRNotifications_Version ); } + else if ( !vr::VR_IsInterfaceVersionValid( vr::IVRInput_Version ) ) + { + reportVersionError( vr::IVRInput_Version ); + } } OpenVR_Init::~OpenVR_Init() diff --git a/src/steamivrinput.cpp b/src/steamivrinput.cpp deleted file mode 100644 index 69edfda..0000000 --- a/src/steamivrinput.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "steamivrinput.h" -#include -#include -#include -#include - -void setActionManifestPath( const QString actionManifestPath ) -{ - auto error = vr::VRInput()->SetActionManifestPath( - actionManifestPath.toStdString().c_str() ); - if ( error != vr::EVRInputError::VRInputError_None ) - { - LOG( ERROR ) << "Error setting action manifest path: " - << actionManifestPath << ". OpenVR Error: " << error; - } -} - -vr::VRActionHandle_t getActionHandle( const char* const action ) -{ - vr::VRActionHandle_t actionHandle = 0; - - auto error = vr::VRInput()->GetActionHandle( action, &actionHandle ); - - if ( error != vr::EVRInputError::VRInputError_None ) - { - LOG( ERROR ) << "Error getting handle for '" << action - << "'. OpenVR Error: " << error; - } - - return actionHandle; -} - -vr::VRActionSetHandle_t getActionSetHandle( const char* const set ) -{ - vr::VRActionSetHandle_t setHandle = 0; - - auto error = vr::VRInput()->GetActionSetHandle( set, &setHandle ); - if ( error != vr::EVRInputError::VRInputError_None ) - { - LOG( ERROR ) << "Error getting handle for '" << set - << "'. OpenVR Error: " << error; - } - - return setHandle; -} - -vr::InputDigitalActionData_t getDigitalActionData( vr::VRActionHandle_t handle ) -{ - vr::InputDigitalActionData_t handleData = {}; - - auto error = vr::VRInput()->GetDigitalActionData( - handle, - &handleData, - sizeof( handleData ), - vr::k_ulInvalidInputValueHandle ); - - if ( error != vr::EVRInputError::VRInputError_None ) - { - LOG( ERROR ) << "Error getting Digital Action Data for handle " - << handle << ". SteamVR Error: " << error; - } - - return handleData; -} - -bool isActionActivatedOnce( vr::VRActionHandle_t handle ) -{ - auto handleData = getDigitalActionData( handle ); - - return handleData.bState && handleData.bChanged; -} - -void SteamIVRInput::Init() -{ - QString actionManifestPath - = QStandardPaths::locate( QStandardPaths::AppDataLocation, - QStringLiteral( "action_manifest.json" ) ); - setActionManifestPath( actionManifestPath ); - - m_nextSongHandler = getActionHandle( input_strings::k_actionNextTrack ); - - m_mainSetHandler = getActionSetHandle( input_strings::k_setMain ); - - m_activeActionSet.ulActionSet = m_mainSetHandler; - m_activeActionSet.ulRestrictedToDevice = vr::k_ulInvalidInputValueHandle; - m_activeActionSet.nPriority = 0; -} - -bool SteamIVRInput::nextSongSet() -{ - return isActionActivatedOnce( m_nextSongHandler ); -} - -void SteamIVRInput::Loop() -{ - auto error = vr::VRInput()->UpdateActionState( - &m_activeActionSet, sizeof( m_activeActionSet ), 1 ); - - if ( error != vr::EVRInputError::VRInputError_None ) - { - LOG( ERROR ) << "Loop error."; - } -} diff --git a/src/steamivrinput.h b/src/steamivrinput.h deleted file mode 100644 index 06f0adc..0000000 --- a/src/steamivrinput.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include "ivrinput/ivrinput_action.h" - -class SteamIVRInput -{ -public: - // SteamIVRInput(); - void Init(); - void Loop(); - bool nextSongSet(); - - // These have been explicitly deleted to make sure there are no attempts at - // copying the class in weird ways. It is not worth defining what should - // happen on copy because it simply shouldn't be done. - // Destructor. There are not terminating calls for the IVRInput API, so it - // is unneeded. - //~SteamIVRInput() = delete; - //// Copy constructor - // SteamIVRInput( const SteamIVRInput& ) = delete; - //// Copy assignment - // SteamIVRInput& operator=( const SteamIVRInput& ) = delete; - //// Move constructor - // SteamIVRInput( SteamIVRInput&& ) = delete; - //// Move assignment - // SteamIVRInput& operator=( const SteamIVRInput&& ) = delete; - -private: - vr::VRActionHandle_t m_nextSongHandler = {}; - vr::VRActionSetHandle_t m_mainSetHandler = {}; - vr::VRActiveActionSet_t m_activeActionSet = {}; - vr::InputDigitalActionData_t m_nextSongData = {}; -}; - -namespace input_strings -{ -constexpr auto k_actionNextTrack = "/actions/main/in/PlayNextTrack"; -constexpr auto k_setMain = "/actions/main"; - -} // namespace input_strings