Refactor IVRInput logic into several classes
Keeping the action name, handle and type together allows for better error messages and limited run time type checking, to ensure that the wrong function isn't called. It is necessary to initialize the manifest in a class, since the Actions depend on this being set. Several files have been moved around to be more consistent.
This commit is contained in:
@@ -2,10 +2,13 @@
|
||||
#include <openvr.h>
|
||||
#include <easylogging++.h>
|
||||
|
||||
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
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include <openvr.h>
|
||||
#include <easylogging++.h>
|
||||
|
||||
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
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include <QStandardPaths>
|
||||
#include <openvr.h>
|
||||
#include <easylogging++.h>
|
||||
|
||||
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
|
||||
@@ -0,0 +1,114 @@
|
||||
#include "steamivrinput.h"
|
||||
#include <openvr.h>
|
||||
#include <iostream>
|
||||
#include <QStandardPaths>
|
||||
#include <easylogging++.h>
|
||||
|
||||
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
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvr.h>
|
||||
#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
|
||||
@@ -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!";
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
#include "steamivrinput.h"
|
||||
#include <openvr.h>
|
||||
#include <iostream>
|
||||
#include <QStandardPaths>
|
||||
#include <easylogging++.h>
|
||||
|
||||
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.";
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvr.h>
|
||||
#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
|
||||
Reference in New Issue
Block a user