From 5accfd478a6ffdededbc46e77777f127d696f647 Mon Sep 17 00:00:00 2001 From: Vasily Date: Thu, 17 Jan 2019 19:35:30 +0100 Subject: [PATCH 01/14] Implement beta IVRInput system for testing In order to test new system: Build and deploy application. Copy both files from IVRInput/ dir into the bin/win64 folder. Change const std::string m_actionManifestPath in steamivrinput.h to be directly to your action_manifest.json file. Run and turn on a Vive controller. Press the menu button. If logs say "NEXT SONG SET!" it works. --- IVRInput/action_manifest.json | 30 ++++++++++++++ IVRInput/legacy_actions.json | 42 ++++++++++++++++++++ advancedSettings.pro | 7 ++-- src/overlaycontroller.cpp | 9 ++++- src/overlaycontroller.h | 3 ++ src/steamivrinput.cpp | 75 +++++++++++++++++++++++++++++++++++ src/steamivrinput.h | 22 ++++++++++ 7 files changed, 184 insertions(+), 4 deletions(-) create mode 100644 IVRInput/action_manifest.json create mode 100644 IVRInput/legacy_actions.json create mode 100644 src/steamivrinput.cpp create mode 100644 src/steamivrinput.h diff --git a/IVRInput/action_manifest.json b/IVRInput/action_manifest.json new file mode 100644 index 0000000..7accc3a --- /dev/null +++ b/IVRInput/action_manifest.json @@ -0,0 +1,30 @@ +{ + "default_bindings": [ + { + "controller_type": "vive_controller", + "binding_url": "legacy_actions.json" + } + ], + "actions": [ + { + "name": "/actions/main/in/PlayNextTrack", + "requirement": "optional", + "type": "boolean" + } + ], + "action_sets": [ + { + "name": "/actions/main", + "usage": "leftright" + } + ], + "localization" : [ + { + "language_tag": "en", + + "/actions/main" : "My Game Actions", + + "/actions/main/in/PlayNextTrack" : "Play Next Track" + } + ] +} \ No newline at end of file diff --git a/IVRInput/legacy_actions.json b/IVRInput/legacy_actions.json new file mode 100644 index 0000000..3e2094c --- /dev/null +++ b/IVRInput/legacy_actions.json @@ -0,0 +1,42 @@ +{ + "alias_info" : {}, + "app_key" : "matzman666.advancedsettings", + "bindings" : { + "/actions/main" : { + "sources" : [ + { + "inputs" : { + "click" : { + "output" : "/actions/main/in/playnexttrack" + } + }, + "mode" : "button", + "path" : "/user/hand/left/input/application_menu" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/main/in/playnexttrack" + } + }, + "mode" : "button", + "path" : "/user/hand/right/input/application_menu" + }, + { + "inputs" : { + "click" : { + "output" : "/actions/main/in/playnexttrack" + } + }, + "mode" : "button", + "path" : "/user/hand/right/input/grip" + } + ] + } + }, + "controller_type" : "vive_controller", + "description" : "", + "name" : "My My Test configuration for Vive Controller", + "options" : {}, + "simulated_actions" : [] +} diff --git a/advancedSettings.pro b/advancedSettings.pro index e9d529d..9ed0a57 100644 --- a/advancedSettings.pro +++ b/advancedSettings.pro @@ -58,8 +58,8 @@ SOURCES += src/main.cpp\ src/utils/ChaperoneUtils.cpp \ src/tabcontrollers/audiomanager/AudioManagerDummy.cpp \ src/tabcontrollers/keyboardinput/KeyboardInputDummy.cpp \ - src/overlaycontroller/openvr_init.cpp - + src/overlaycontroller/openvr_init.cpp \ + src/steamivrinput.cpp HEADERS += src/overlaycontroller.h \ src/tabcontrollers/AudioTabController.h \ @@ -79,7 +79,8 @@ HEADERS += src/overlaycontroller.h \ src/utils/ChaperoneUtils.h \ src/tabcontrollers/audiomanager/AudioManagerDummy.h \ src/tabcontrollers/keyboardinput/KeyboardInputDummy.h \ - src/overlaycontroller/openvr_init.h + src/overlaycontroller/openvr_init.h \ + src/steamivrinput.h win32 { SOURCES += src/tabcontrollers/audiomanager/AudioManagerWindows.cpp \ diff --git a/src/overlaycontroller.cpp b/src/overlaycontroller.cpp index b8cd84b..170cdba 100644 --- a/src/overlaycontroller.cpp +++ b/src/overlaycontroller.cpp @@ -118,7 +118,7 @@ OverlayController::OverlayController( bool desktopMode, nullptr, "OpenVR Advanced Settings Overlay", "Is OpenVR running?" ); throw std::runtime_error( std::string( "No Overlay interface" ) ); } - + m_actions.Init( false ); // Init controllers m_steamVRTabController.initStage1(); m_chaperoneTabController.initStage1(); @@ -477,6 +477,13 @@ void OverlayController::OnTimeoutPumpEvents() if ( !vr::VRSystem() ) return; + m_actions.Loop(); + + if ( m_actions.nextSongSet() ) + { + LOG( INFO ) << "NEXT SONG SET!"; + } + vr::VREvent_t vrEvent; bool chaperoneDataAlreadyUpdated = false; while ( pollNextEvent( m_ulOverlayHandle, &vrEvent ) ) diff --git a/src/overlaycontroller.h b/src/overlaycontroller.h index 9cefa65..9442b06 100644 --- a/src/overlaycontroller.h +++ b/src/overlaycontroller.h @@ -39,6 +39,8 @@ #include "tabcontrollers/UtilitiesTabController.h" #include "tabcontrollers/AccessibilityTabController.h" +#include "steamivrinput.h" + // application namespace namespace advsettings { @@ -90,6 +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; SteamVRTabController m_steamVRTabController; ChaperoneTabController m_chaperoneTabController; MoveCenterTabController m_moveCenterTabController; diff --git a/src/steamivrinput.cpp b/src/steamivrinput.cpp new file mode 100644 index 0000000..d8269dc --- /dev/null +++ b/src/steamivrinput.cpp @@ -0,0 +1,75 @@ +#include "steamivrinput.h" +#include +#include + +void SteamIVRInput::Init( const bool init ) +{ + if ( init ) + { + auto initError = vr::VRInitError_None; + vr::VR_Init( &initError, vr::VRApplication_Overlay ); + if ( initError != vr::VRInitError_None ) + { + std::cout << "SteamVR init error."; + } + } + + auto error + = vr::VRInput()->SetActionManifestPath( m_actionManifestPath.c_str() ); + if ( error != vr::EVRInputError::VRInputError_None ) + { + std::cout << "Action manifest error\n"; + } + + // Get action handle + error = vr::VRInput()->GetActionHandle( "/actions/main/in/PlayNextTrack", + &m_nextSongHandler ); + if ( error != vr::EVRInputError::VRInputError_None ) + { + std::cout << "Handle error.\n"; + } + + error = vr::VRInput()->GetActionSetHandle( "/actions/main", + &m_mainSetHandler ); + if ( error != vr::EVRInputError::VRInputError_None ) + { + std::cout << "Handle error.\n"; + } + + m_activeActionSet.ulActionSet = m_mainSetHandler; + m_activeActionSet.ulRestrictedToDevice = vr::k_ulInvalidInputValueHandle; + m_activeActionSet.nPriority = 0; +} + +bool SteamIVRInput::nextSongSet() +{ + auto e = vr::VRInput()->GetDigitalActionData( + m_nextSongHandler, + &m_nextSongData, + sizeof( m_nextSongData ), + vr::k_ulInvalidInputValueHandle ); + + if ( e != vr::EVRInputError::VRInputError_None ) + { + std::cout << e; + std::cout << "GetDigitalAction error.\n"; + } + + if ( m_nextSongData.bActive ) + { + std::cout << "Action Set Active!\n"; + } + + return m_nextSongData.bState; +} + +void SteamIVRInput::Loop() +{ + auto error = vr::VRInput()->UpdateActionState( + &m_activeActionSet, sizeof( m_activeActionSet ), 1 ); + + if ( error != vr::EVRInputError::VRInputError_None ) + { + std::cout << "Loop error.\n"; + } +} diff --git a/src/steamivrinput.h b/src/steamivrinput.h new file mode 100644 index 0000000..dea0dac --- /dev/null +++ b/src/steamivrinput.h @@ -0,0 +1,22 @@ +#ifndef STEAMIVRINPUT_H +#define STEAMIVRINPUT_H + +#include + +class SteamIVRInput +{ +public: + void Init( const bool init ); + void Loop(); + bool nextSongSet(); + +private: + const std::string m_actionManifestPath + = "YOUR_PATH_HERE\\action_manifest.json"; + vr::VRActionHandle_t m_nextSongHandler = {}; + vr::VRActionSetHandle_t m_mainSetHandler = {}; + vr::VRActiveActionSet_t m_activeActionSet = {}; + vr::InputDigitalActionData_t m_nextSongData = {}; +}; + +#endif // STEAMIVRINPUT_H From 586ee558c416aa49b101f27a49595533e40eee51 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 17 Jan 2019 22:00:10 +0100 Subject: [PATCH 02/14] determine vrinput manifest path dynamically --- src/steamivrinput.cpp | 8 ++++++-- src/steamivrinput.h | 2 -- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/steamivrinput.cpp b/src/steamivrinput.cpp index d8269dc..57ba219 100644 --- a/src/steamivrinput.cpp +++ b/src/steamivrinput.cpp @@ -1,6 +1,7 @@ #include "steamivrinput.h" #include #include +#include void SteamIVRInput::Init( const bool init ) { @@ -14,8 +15,11 @@ void SteamIVRInput::Init( const bool init ) } } - auto error - = vr::VRInput()->SetActionManifestPath( m_actionManifestPath.c_str() ); + QString manifestPath + = QStandardPaths::locate( QStandardPaths::AppDataLocation, + QStringLiteral( "action_manifest.json" ) ); + auto error = vr::VRInput()->SetActionManifestPath( + manifestPath.toStdString().c_str() ); if ( error != vr::EVRInputError::VRInputError_None ) { std::cout << "Action manifest error\n"; diff --git a/src/steamivrinput.h b/src/steamivrinput.h index dea0dac..91e50f8 100644 --- a/src/steamivrinput.h +++ b/src/steamivrinput.h @@ -11,8 +11,6 @@ public: bool nextSongSet(); private: - const std::string m_actionManifestPath - = "YOUR_PATH_HERE\\action_manifest.json"; vr::VRActionHandle_t m_nextSongHandler = {}; vr::VRActionSetHandle_t m_mainSetHandler = {}; vr::VRActiveActionSet_t m_activeActionSet = {}; From 1585322c9d48a2b73328398f7912cb8d1c4fecb9 Mon Sep 17 00:00:00 2001 From: Vasily Date: Sat, 19 Jan 2019 13:51:41 +0100 Subject: [PATCH 03/14] Move action manifest files to src/package_files Putting them in the new directory makes the automatically get included for deployment. legacy_actions.json was not a good name for the file, since it actually contained default bindings for Vive controllers. vive_defaults.json better explains what it is. --- {IVRInput => src/package_files}/action_manifest.json | 2 +- .../package_files/default_action_manifests/vive_defaults.json | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename {IVRInput => src/package_files}/action_manifest.json (87%) rename IVRInput/legacy_actions.json => src/package_files/default_action_manifests/vive_defaults.json (100%) diff --git a/IVRInput/action_manifest.json b/src/package_files/action_manifest.json similarity index 87% rename from IVRInput/action_manifest.json rename to src/package_files/action_manifest.json index 7accc3a..0b1a38c 100644 --- a/IVRInput/action_manifest.json +++ b/src/package_files/action_manifest.json @@ -2,7 +2,7 @@ "default_bindings": [ { "controller_type": "vive_controller", - "binding_url": "legacy_actions.json" + "binding_url": "default_action_manifests/vive_defaults.json" } ], "actions": [ diff --git a/IVRInput/legacy_actions.json b/src/package_files/default_action_manifests/vive_defaults.json similarity index 100% rename from IVRInput/legacy_actions.json rename to src/package_files/default_action_manifests/vive_defaults.json From 8bcab9b2c09e13480fa663b8363c02269b8a9395 Mon Sep 17 00:00:00 2001 From: Vasily Date: Sat, 19 Jan 2019 16:14:05 +0100 Subject: [PATCH 04/14] Refactor ivrinput functions to be more generic --- advancedSettings.pro | 3 +- src/ivrinput/ivrinput_action.h | 42 ++++++++++++ src/overlaycontroller.cpp | 2 +- src/steamivrinput.cpp | 118 ++++++++++++++++++++------------- src/steamivrinput.h | 29 ++++++-- 5 files changed, 141 insertions(+), 53 deletions(-) create mode 100644 src/ivrinput/ivrinput_action.h diff --git a/advancedSettings.pro b/advancedSettings.pro index 9ed0a57..20b477a 100644 --- a/advancedSettings.pro +++ b/advancedSettings.pro @@ -80,7 +80,8 @@ HEADERS += src/overlaycontroller.h \ src/tabcontrollers/audiomanager/AudioManagerDummy.h \ src/tabcontrollers/keyboardinput/KeyboardInputDummy.h \ src/overlaycontroller/openvr_init.h \ - src/steamivrinput.h + src/steamivrinput.h \ + src/ivrinput/ivrinput_action.h win32 { SOURCES += src/tabcontrollers/audiomanager/AudioManagerWindows.cpp \ diff --git a/src/ivrinput/ivrinput_action.h b/src/ivrinput/ivrinput_action.h new file mode 100644 index 0000000..85d2463 --- /dev/null +++ b/src/ivrinput/ivrinput_action.h @@ -0,0 +1,42 @@ +#pragma once +#include +#include + +enum class ActionType +{ + Undefined, + Digital, +}; + +class Action +{ +public: + Action( const char* const actionName, const ActionType type ) + : m_name( actionName ), m_type( type ) + { + auto error = vr::VRInput()->GetActionHandle( actionName, &m_handle ); + + if ( error != vr::EVRInputError::VRInputError_None ) + { + LOG( ERROR ) << "Error getting handle for '" << actionName + << "'. OpenVR Error: " << error; + } + } + vr::VRActionHandle_t handle() + { + return m_handle; + } + const char* name() + { + return m_name; + } + ActionType type() + { + return m_type; + } + +private: + vr::VRActionHandle_t m_handle = 0; + const char* m_name; + ActionType m_type = ActionType::Undefined; +}; diff --git a/src/overlaycontroller.cpp b/src/overlaycontroller.cpp index 170cdba..288fba7 100644 --- a/src/overlaycontroller.cpp +++ b/src/overlaycontroller.cpp @@ -118,7 +118,7 @@ OverlayController::OverlayController( bool desktopMode, nullptr, "OpenVR Advanced Settings Overlay", "Is OpenVR running?" ); throw std::runtime_error( std::string( "No Overlay interface" ) ); } - m_actions.Init( false ); + m_actions.Init(); // Init controllers m_steamVRTabController.initStage1(); m_chaperoneTabController.initStage1(); diff --git a/src/steamivrinput.cpp b/src/steamivrinput.cpp index 57ba219..69edfda 100644 --- a/src/steamivrinput.cpp +++ b/src/steamivrinput.cpp @@ -2,43 +2,84 @@ #include #include #include +#include -void SteamIVRInput::Init( const bool init ) +void setActionManifestPath( const QString actionManifestPath ) { - if ( init ) + auto error = vr::VRInput()->SetActionManifestPath( + actionManifestPath.toStdString().c_str() ); + if ( error != vr::EVRInputError::VRInputError_None ) { - auto initError = vr::VRInitError_None; - vr::VR_Init( &initError, vr::VRApplication_Overlay ); - if ( initError != vr::VRInitError_None ) - { - std::cout << "SteamVR init error."; - } + 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; } - QString manifestPath + 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" ) ); - auto error = vr::VRInput()->SetActionManifestPath( - manifestPath.toStdString().c_str() ); - if ( error != vr::EVRInputError::VRInputError_None ) - { - std::cout << "Action manifest error\n"; - } + setActionManifestPath( actionManifestPath ); - // Get action handle - error = vr::VRInput()->GetActionHandle( "/actions/main/in/PlayNextTrack", - &m_nextSongHandler ); - if ( error != vr::EVRInputError::VRInputError_None ) - { - std::cout << "Handle error.\n"; - } + m_nextSongHandler = getActionHandle( input_strings::k_actionNextTrack ); - error = vr::VRInput()->GetActionSetHandle( "/actions/main", - &m_mainSetHandler ); - if ( error != vr::EVRInputError::VRInputError_None ) - { - std::cout << "Handle error.\n"; - } + m_mainSetHandler = getActionSetHandle( input_strings::k_setMain ); m_activeActionSet.ulActionSet = m_mainSetHandler; m_activeActionSet.ulRestrictedToDevice = vr::k_ulInvalidInputValueHandle; @@ -47,24 +88,7 @@ void SteamIVRInput::Init( const bool init ) bool SteamIVRInput::nextSongSet() { - auto e = vr::VRInput()->GetDigitalActionData( - m_nextSongHandler, - &m_nextSongData, - sizeof( m_nextSongData ), - vr::k_ulInvalidInputValueHandle ); - - if ( e != vr::EVRInputError::VRInputError_None ) - { - std::cout << e; - std::cout << "GetDigitalAction error.\n"; - } - - if ( m_nextSongData.bActive ) - { - std::cout << "Action Set Active!\n"; - } - - return m_nextSongData.bState; + return isActionActivatedOnce( m_nextSongHandler ); } void SteamIVRInput::Loop() @@ -74,6 +98,6 @@ void SteamIVRInput::Loop() if ( error != vr::EVRInputError::VRInputError_None ) { - std::cout << "Loop error.\n"; + LOG( ERROR ) << "Loop error."; } } diff --git a/src/steamivrinput.h b/src/steamivrinput.h index 91e50f8..06f0adc 100644 --- a/src/steamivrinput.h +++ b/src/steamivrinput.h @@ -1,15 +1,31 @@ -#ifndef STEAMIVRINPUT_H -#define STEAMIVRINPUT_H +#pragma once #include +#include "ivrinput/ivrinput_action.h" class SteamIVRInput { public: - void Init( const bool init ); + // 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 = {}; @@ -17,4 +33,9 @@ private: vr::InputDigitalActionData_t m_nextSongData = {}; }; -#endif // STEAMIVRINPUT_H +namespace input_strings +{ +constexpr auto k_actionNextTrack = "/actions/main/in/PlayNextTrack"; +constexpr auto k_setMain = "/actions/main"; + +} // namespace input_strings From d7e3df185b0d11a2a71ea3bcde9afa7de928ede1 Mon Sep 17 00:00:00 2001 From: Vasily Date: Sat, 19 Jan 2019 21:25:15 +0100 Subject: [PATCH 05/14] 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. --- advancedSettings.pro | 8 +- src/ivrinput/ivrinput_action.h | 15 ++-- src/ivrinput/ivrinput_action_set.h | 33 ++++++++ src/ivrinput/ivrinput_manifest.h | 44 ++++++++++ src/ivrinput/steamivrinput.cpp | 114 ++++++++++++++++++++++++++ src/ivrinput/steamivrinput.h | 50 +++++++++++ src/overlaycontroller.cpp | 5 +- src/overlaycontroller.h | 4 +- src/overlaycontroller/openvr_init.cpp | 4 + src/steamivrinput.cpp | 103 ----------------------- src/steamivrinput.h | 41 --------- 11 files changed, 264 insertions(+), 157 deletions(-) create mode 100644 src/ivrinput/ivrinput_action_set.h create mode 100644 src/ivrinput/ivrinput_manifest.h create mode 100644 src/ivrinput/steamivrinput.cpp create mode 100644 src/ivrinput/steamivrinput.h delete mode 100644 src/steamivrinput.cpp delete mode 100644 src/steamivrinput.h 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 From 01b0070e664ac792895e41ffd2b511f1442184da Mon Sep 17 00:00:00 2001 From: Vasily Date: Sat, 19 Jan 2019 21:32:12 +0100 Subject: [PATCH 06/14] Rename steamivrinput.h/.cpp to ivrinput.h/.cpp The name is shorter and identical to the folder name, emphasizing that this is the main file in this module. --- advancedSettings.pro | 4 ++-- src/ivrinput/{steamivrinput.cpp => ivrinput.cpp} | 2 +- src/ivrinput/{steamivrinput.h => ivrinput.h} | 0 src/overlaycontroller.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/ivrinput/{steamivrinput.cpp => ivrinput.cpp} (99%) rename src/ivrinput/{steamivrinput.h => ivrinput.h} (100%) diff --git a/advancedSettings.pro b/advancedSettings.pro index 333b26a..a16d072 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/ivrinput/steamivrinput.cpp + src/ivrinput/ivrinput.cpp HEADERS += src/overlaycontroller.h \ src/tabcontrollers/AudioTabController.h \ @@ -83,7 +83,7 @@ HEADERS += src/overlaycontroller.h \ src/ivrinput/ivrinput_action.h \ src/ivrinput/ivrinput_manifest.h \ src/ivrinput/ivrinput_action_set.h \ - src/ivrinput/steamivrinput.h + src/ivrinput/ivrinput.h win32 { SOURCES += src/tabcontrollers/audiomanager/AudioManagerWindows.cpp \ diff --git a/src/ivrinput/steamivrinput.cpp b/src/ivrinput/ivrinput.cpp similarity index 99% rename from src/ivrinput/steamivrinput.cpp rename to src/ivrinput/ivrinput.cpp index 55ba537..a44fdb0 100644 --- a/src/ivrinput/steamivrinput.cpp +++ b/src/ivrinput/ivrinput.cpp @@ -1,4 +1,4 @@ -#include "steamivrinput.h" +#include "ivrinput.h" #include #include #include diff --git a/src/ivrinput/steamivrinput.h b/src/ivrinput/ivrinput.h similarity index 100% rename from src/ivrinput/steamivrinput.h rename to src/ivrinput/ivrinput.h diff --git a/src/overlaycontroller.h b/src/overlaycontroller.h index cb7beaa..89addf0 100644 --- a/src/overlaycontroller.h +++ b/src/overlaycontroller.h @@ -39,7 +39,7 @@ #include "tabcontrollers/UtilitiesTabController.h" #include "tabcontrollers/AccessibilityTabController.h" -#include "ivrinput/steamivrinput.h" +#include "ivrinput/ivrinput.h" // application namespace namespace advsettings From 0c71c289b9e2ef9809ce8aeb9fef0e4708fe5c42 Mon Sep 17 00:00:00 2001 From: Vasily Date: Sun, 20 Jan 2019 20:13:05 +0100 Subject: [PATCH 07/14] Finalize API and add comments --- src/ivrinput/ivrinput.cpp | 52 +++++++++++++++++++++++++++++- src/ivrinput/ivrinput.h | 33 +++++++++++++++++-- src/ivrinput/ivrinput_action.h | 19 +++++++++++ src/ivrinput/ivrinput_action_set.h | 9 ++++++ src/overlaycontroller.cpp | 2 +- 5 files changed, 111 insertions(+), 4 deletions(-) diff --git a/src/ivrinput/ivrinput.cpp b/src/ivrinput/ivrinput.cpp index a44fdb0..fa5aa9a 100644 --- a/src/ivrinput/ivrinput.cpp +++ b/src/ivrinput/ivrinput.cpp @@ -6,6 +6,17 @@ namespace input { +/*! +Wrapper around the IVRInput GetAnalogActionData with error handling. + +The struct is created and zero initalized in this function instead of being +passed as reference since the size is currently only 28 bytes, and currently +only two bools are used from the entire struct. +MSVC on /O2 will likely completely optimize the function call away but even if +it isn't the struct will still need to be created somewhere, and with move +semantics it almost doesn't matter if the struct is created in the calling +function and passed as reference, or created in this function. +*/ vr::InputDigitalActionData_t getDigitalActionData( Action& action ) { if ( action.type() != ActionType::Digital ) @@ -37,6 +48,17 @@ vr::InputDigitalActionData_t getDigitalActionData( Action& action ) return handleData; } +/*! +Wrapper around the IVRInput GetAnalogActionData with error handling. + +The struct is created and zero initalized in this function instead of being +passed as reference since the size is currently only 28 bytes, and currently +only two bools are used from the entire struct. +MSVC on /O2 will likely completely optimize the function call away but even if +it isn't the struct will still need to be created somewhere, and with move +semantics it almost doesn't matter if the struct is created in the calling +function and passed as reference, or created in this function. +*/ vr::InputAnalogActionData_t getAnalogActionData( Action& action ) { if ( action.type() != ActionType::Analog ) @@ -68,6 +90,12 @@ vr::InputAnalogActionData_t getAnalogActionData( Action& action ) return handleData; } +/*! +Gets the action state of an Action. +This will only return true the first time the button is released. It is +necessary to release the button and the push it again in order for this function +to return true again. +*/ bool isDigitalActionActivatedOnce( Action& action ) { const auto handleData = getDigitalActionData( action ); @@ -75,6 +103,10 @@ bool isDigitalActionActivatedOnce( Action& action ) return handleData.bState && handleData.bChanged; } +/*! +Gets the action state of an Action. +This will continually return true while the button is held down. +*/ bool isDigitalActionActivatedConstant( Action& action ) { const auto handleData = getDigitalActionData( action ); @@ -82,6 +114,14 @@ bool isDigitalActionActivatedConstant( Action& action ) return handleData.bState; } +/*! +Sets up the input systems. +Actions should be added to the initializer list before the body of the +constructor. Actions should be certain that the action name strings are +correctly input, and that the correct type is provided. +Wrong action names may not result in direct error messages, but just features +mystically not working. +*/ SteamIVRInput::SteamIVRInput() : m_manifest(), m_mainSet( input_strings::k_setMain ), m_nextTrack( input_strings::k_actionNextTrack, ActionType::Digital ) @@ -90,13 +130,23 @@ SteamIVRInput::SteamIVRInput() m_activeActionSet.ulRestrictedToDevice = vr::k_ulInvalidInputValueHandle; m_activeActionSet.nPriority = 0; } +/*! +Returns true if the next media track should be played. +Will only return true the first time that the button is pressed. Holding the +button down will result in false until it has been released and pushed again. +*/ bool SteamIVRInput::nextSong() { return isDigitalActionActivatedOnce( m_nextTrack ); } -void SteamIVRInput::Loop() +/*! +Updates the active action set(s). +Should be called every frame, or however often you want the input system to +update state. +*/ +void SteamIVRInput::UpdateStates() { constexpr auto numberOfSets = 1; diff --git a/src/ivrinput/ivrinput.h b/src/ivrinput/ivrinput.h index 158a542..7bd2736 100644 --- a/src/ivrinput/ivrinput.h +++ b/src/ivrinput/ivrinput.h @@ -7,17 +7,40 @@ namespace input { +/*! +Responsible for controller input. + +UpdateStates should be called every frame. + +Binary actions should only have one function, +and they should reflect the expected behaviour. I.e. if a button being pressed +and held should only return true the first time, that logic should be in the +function. + +An action in the IVRInput API is entered in the actions manifest. This is a +.json file that is included with the final binary. +Currently an Action is created with a string and a type. The type determines +which function should be used for getting information from the API. The type in +the actions manifest must match the type in the source code. + +Actions should be added as member objects to SteamIVRInput and initialized in +the constructor. They should then make a small number of accessor functions +available for outside use. The internal structs of the IVRInput API are not +entirely stable, and should not be leaked outside this class. + +The name of the actions manifest must be set in ivrinput_manifest.h. +*/ class SteamIVRInput { public: SteamIVRInput(); - void Loop(); + + void UpdateStates(); 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. @@ -34,12 +57,18 @@ public: private: Manifest m_manifest; + ActionSet m_mainSet; vr::VRActiveActionSet_t m_activeActionSet = {}; Action m_nextTrack; }; +/*! +The IVRInput system is _very_ finnicky about strings matching and if they don't +match there are usually no errors logged reflecting that. constexpr auto strings +should be used to refer to all action manifest strings. +*/ namespace input_strings { constexpr auto k_actionNextTrack = "/actions/main/in/PlayNextTrack"; diff --git a/src/ivrinput/ivrinput_action.h b/src/ivrinput/ivrinput_action.h index f3fe439..09ca000 100644 --- a/src/ivrinput/ivrinput_action.h +++ b/src/ivrinput/ivrinput_action.h @@ -4,6 +4,11 @@ namespace input { +/*! +The type of function that should be called to get data for the action. +The IVRInput API does not give explicit warnings when the wrong functions are +called. This enum is an attempt at runtime checking. +*/ enum class ActionType { Undefined, @@ -11,6 +16,10 @@ enum class ActionType Analog, }; +/*! +Represents a single action from the actions manifest. Should be instantiated +with a name similar to that of the one in the actions manifest. +*/ class Action { public: @@ -25,14 +34,24 @@ public: << "'. OpenVR Error: " << error; } } + /*! + An API internal handle that identifies an action. + */ vr::VRActionHandle_t handle() const noexcept { return m_handle; } + /*! + The actions manfiest name of the action. Used for error reporting. + */ std::string name() const noexcept { return m_name; } + /*! + The type of the action. Used for runtime checking that the wrong functions + aren't called. + */ ActionType type() const noexcept { return m_type; diff --git a/src/ivrinput/ivrinput_action_set.h b/src/ivrinput/ivrinput_action_set.h index 66b35a0..70b2e5f 100644 --- a/src/ivrinput/ivrinput_action_set.h +++ b/src/ivrinput/ivrinput_action_set.h @@ -4,6 +4,9 @@ namespace input { +/*! +Represents an action set from the action manifest. +*/ class ActionSet { public: @@ -16,10 +19,16 @@ public: << "'. OpenVR Error: " << error; } } + /*! + The actions manfiest name of the set. Used for error reporting. + */ std::string name() { return m_name; } + /*! + An API internal handle that identifies a set. + */ vr::VRActionSetHandle_t handle() { return m_handle; diff --git a/src/overlaycontroller.cpp b/src/overlaycontroller.cpp index 1f94e98..314b068 100644 --- a/src/overlaycontroller.cpp +++ b/src/overlaycontroller.cpp @@ -476,7 +476,7 @@ void OverlayController::OnTimeoutPumpEvents() if ( !vr::VRSystem() ) return; - m_actions.Loop(); + m_actions.UpdateStates(); if ( m_actions.nextSong() ) { From 706f21ebea96328174388ec7480ee9eb8016d8c0 Mon Sep 17 00:00:00 2001 From: Vasily Date: Sun, 20 Jan 2019 22:05:21 +0100 Subject: [PATCH 08/14] Add ability to change IVRInput bindings, move openvr_init into namespace openvr_init was not in a namespace. This led to it being globally available. This was not intended. The solution to overlays not showing up in the bindings interface was chosen for ease of implementation and relative ease of end user use. The Kinect2VR project are using a solution where there is a button to shutdown background mode and use scene mode temporarily. This did not work for our project for unknown reasons. The IVRInput system would become non responsive and spam error messages when VR was shut down. Implementation is here: https://github.com/sharkyh20/KinectToVR/blob/12aeb8b33ab20eeb588c72c5d5186dd4d87784d7/SFMLProject/inc/GUIHandler.h#L700 The command line flag will be represented as a .bat file for the end user, like desktop mode and restarting the VR server. When Valve solves the issue of overlays not showing up in the bindings menu, the entire function can essentially be deleted. Official issues here: https://github.com/ValveSoftware/openvr/issues/914 https://github.com/ValveSoftware/openvr/issues/926 https://github.com/ValveSoftware/openvr/issues/879 It is also possible to make the app appear by changing the C:\Program Files (x86)\Steam\config\vrappconfig\*.vrappconfig file for the project to have a "last_launch_time" of something greater than zero. From experimentation it seems that there's a cutoff time around 2 weeks before the current time. Setting it to the day before seems to be a good bet. (In unix time). --- src/main.cpp | 12 +++++ src/overlaycontroller.h | 2 +- src/overlaycontroller/openvr_init.cpp | 68 ++++++++++++++++++++++++++- src/overlaycontroller/openvr_init.h | 5 ++ 4 files changed, 84 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 65eb57a..cbc85c6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,6 +46,7 @@ constexpr auto kNoSound = "-nosound"; constexpr auto kNoManifest = "-nomanifest"; constexpr auto kInstallManifest = "-installmanifest"; constexpr auto kRemoveManifest = "-removemanifest"; +constexpr auto kEnableBindingsInterface = "-bindingsinterface"; } // namespace argument namespace manifest @@ -357,6 +358,8 @@ int main( int argc, char* argv[] ) argc, argv, argument::kInstallManifest ); const bool removeManifest = argument::CheckCommandLineArgument( argc, argv, argument::kRemoveManifest ); + const bool bindingsInterface = argument::CheckCommandLineArgument( + argc, argv, argument::kEnableBindingsInterface ); // If a command line arg is set, make sure the logs reflect that. LOG_IF( desktopMode, INFO ) << "Desktop mode enabled."; @@ -364,6 +367,15 @@ int main( int argc, char* argv[] ) LOG_IF( noManifest, INFO ) << "vrmanifest disabled."; LOG_IF( installManifest, INFO ) << "Install manifest enabled."; LOG_IF( removeManifest, INFO ) << "Remove manifest enabled."; + LOG_IF( bindingsInterface, INFO ) << "Bindings interface enabled."; + + if ( bindingsInterface ) + { + // This is a temporary solution until Valve fixes overlays not + // showing up in the bindings interface. See function description for + // more info. + openvr_init::openBindingsMenu(); + } // It is important that either install_manifest or remove_manifest are true, // otherwise the handleManifests function will not behave properly. diff --git a/src/overlaycontroller.h b/src/overlaycontroller.h index 89addf0..7f34462 100644 --- a/src/overlaycontroller.h +++ b/src/overlaycontroller.h @@ -88,7 +88,7 @@ private: // 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 m_openVrInit; + openvr_init::OpenVR_Init m_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. diff --git a/src/overlaycontroller/openvr_init.cpp b/src/overlaycontroller/openvr_init.cpp index b63ba1a..558d372 100644 --- a/src/overlaycontroller/openvr_init.cpp +++ b/src/overlaycontroller/openvr_init.cpp @@ -2,10 +2,13 @@ #include #include #include "openvr_init.h" +#include +#include "../ivrinput/ivrinput_manifest.h" -OpenVR_Init::OpenVR_Init() +namespace openvr_init +{ +void initializeProperly() { - // Loading the OpenVR Runtime auto initError = vr::VRInitError_None; vr::VR_Init( &initError, vr::VRApplication_Overlay ); if ( initError != vr::VRInitError_None ) @@ -22,6 +25,66 @@ OpenVR_Init::OpenVR_Init() + std::string( vr::VR_GetVRInitErrorAsEnglishDescription( initError ) ) ); } + else + { + LOG( INFO ) << "OpenVR initialized successfully."; + } +} + +/*! +Initializes OpenVR to be in the bindings menu and then exits. +This is needed because all applications initialized with OpenVR that aren't of +type vr::VRApplication_Scene do not show up the in bindings menu. + +This is obviously an ugly hack and should be done away with as soon as Valve +fixes this issue. + +Official Issues for this: +https://github.com/ValveSoftware/openvr/issues/914 +https://github.com/ValveSoftware/openvr/issues/926 +https://github.com/ValveSoftware/openvr/issues/879 + +You can also make it appear by changing the +C:\Program Files (x86)\Steam\config\vrappconfig\*.vrappconfig file for your +project to have a "last_launch_time" of something greater than zero. +From experimentation it seems that there's a cutoff time around 2 weeks before +the current time. Setting it to the day before seems to be a good bet. (In unix +time). +*/ +[[noreturn]] void openBindingsMenu() +{ + // Prefix the logs with this to visibly document that this isn't how things + // are supposed to be. Hopefully someone reads the logs and comes here after + // Valve has fixed it. + constexpr auto logPrefix = "[HACK]::openBindingsMenu "; + + auto initError = vr::VRInitError_None; + LOG( INFO ) << logPrefix << "Initializing application as scene."; + + vr::VR_Init( &initError, vr::VRApplication_Scene ); + + if ( initError != vr::VRInitError_None ) + { + LOG( ERROR ) << logPrefix << "Error initializing application as scene."; + exit( 2 ); + } + else + { + LOG( INFO ) << logPrefix << "Application initialized as scene."; + } + + input::Manifest manifest; + + // SteamVR just needs a good enough time to start up comfortably and for the + // user to get into the bindings menu. + while ( true ) + { + } +} + +OpenVR_Init::OpenVR_Init() +{ + initializeProperly(); // 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 @@ -80,3 +143,4 @@ OpenVR_Init::~OpenVR_Init() { vr::VR_Shutdown(); } +} // namespace openvr_init diff --git a/src/overlaycontroller/openvr_init.h b/src/overlaycontroller/openvr_init.h index bdec289..d150dcd 100644 --- a/src/overlaycontroller/openvr_init.h +++ b/src/overlaycontroller/openvr_init.h @@ -1,5 +1,9 @@ #pragma once +namespace openvr_init +{ +[[noreturn]] void openBindingsMenu(); + /*! Initializes OpenVR and checks validity of interface versions. Shuts down OpenVR when destroyed. @@ -33,3 +37,4 @@ public: // Move assignment const OpenVR_Init operator=( OpenVR_Init&& ) = delete; }; +} // namespace openvr_init From 5ead96bc98e957c29520461653ea9d8b89d501e7 Mon Sep 17 00:00:00 2001 From: Vasily Date: Sun, 20 Jan 2019 22:16:02 +0100 Subject: [PATCH 09/14] Update installer script to include action manifest --- installer/installer.nsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/installer/installer.nsi b/installer/installer.nsi index f3d09b0..aec2452 100644 --- a/installer/installer.nsi +++ b/installer/installer.nsi @@ -109,7 +109,13 @@ Section "Install" SecInstall File "${BASEDIR}\LICENSE-GPL.txt" File "${BASEDIR}\LICENSE-MIT.txt" File "${BASEDIR}\LICENSE-VALVE.txt" + File "${BASEDIR}\*.exe" + ;Any action manifests + File "${BASEDIR}\*.json" + ;And their defaults + File "${BASEDIR}\default_action_manifests\*.json" + File "${THIRDDIR}\openvr\bin\win64\*.dll" File "${BASEDIR}\*.dll" File "${PACKAGEDIR}\*.bat" From 4152333f2010f24369d1d9abfe154fce745a1559 Mon Sep 17 00:00:00 2001 From: Vasily Date: Sun, 20 Jan 2019 22:34:20 +0100 Subject: [PATCH 10/14] Update documentation to reflect new bindings interface --- Readme.md | 23 ++++++++++++++++++++++- src/package_files/bindingsinterface.bat | 1 + 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 src/package_files/bindingsinterface.bat diff --git a/Readme.md b/Readme.md index 010b8f2..008bd58 100644 --- a/Readme.md +++ b/Readme.md @@ -7,6 +7,7 @@ > * [Usage](#usage) > * [Installer](#installer) > * [Standalone](#standalone) + > * [Bindings](#bindings) > * [Command Line Arguments](#command_line_arguments) > * [Logging Config](#logging_config) > * [Preview Builds](#preview_builds) @@ -79,10 +80,28 @@ To install for the first time simply start AdvancedSettings.exe once while Steam To upgrade an existing installation first stop SteamVR and delete the old application folder (or override it). Then start SteamVR and run AdvancedSettings.exe once. + +## Bindings + +Advanced Settings supports the OpenVR IVRInput API. In order to make the bindings interface visible, run the `bindingsinterface.bat` file from the install directory. When SteamVR starts, click the menu -> Settings -> Controller Binding. + +If you just run the application normally it will not show up in the bindings interface, this is an OpenVR bug. + +After you're done configuring your bindings, close SteamVR and start it up again. You are now using your new bindings. + +By default no buttons are bound. + +The following actions exist: + +| Action | Type | Explanation | +| ------------- | ------------- | ------------ | +| PlayNextSong | Binary/Button | The same as using the media keys. Plays the next song. | +| TODO | TODO | TODO | + ## Command Line Arguments -The application (`AdvancedSettings.exe`) can be run with the following five optional arguments: +The application (`AdvancedSettings.exe`) can be run with the following optional arguments: `"-desktop"`: Creates a settings window on the desktop as well as in VR. Running `startdesktopmode.bat` in the install directory has the same effect. @@ -94,6 +113,8 @@ The application (`AdvancedSettings.exe`) can be run with the following five opti `"-removemanifest"`: Force uninstalls the `.vrmanifest`. This should be done every time the application is uninstalled. On Windows it is automatically done by the uninstaller. The program will exit early when this flag is set. +`"-bindingsinterface"`: Makes Advanced Settings show up in the SteamVR bindings interface. This is necessary for binding keys to actions. This is the same as running `bindingsinterface.bat` from the executable directory. + In addition, the application can receive the command line arguments from the [Easylogging++ library](https://github.com/zuhd-org/easyloggingpp#application-arguments). diff --git a/src/package_files/bindingsinterface.bat b/src/package_files/bindingsinterface.bat new file mode 100644 index 0000000..0cd6e03 --- /dev/null +++ b/src/package_files/bindingsinterface.bat @@ -0,0 +1 @@ +start AdvancedSettings.exe -bindingsinterface \ No newline at end of file From 3a62ec89bef4501cc9119e58e04e4698a5ed2da5 Mon Sep 17 00:00:00 2001 From: Vasily Date: Sun, 20 Jan 2019 22:54:30 +0100 Subject: [PATCH 11/14] Add comment to openvr_init.cpp --- src/overlaycontroller/openvr_init.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/overlaycontroller/openvr_init.cpp b/src/overlaycontroller/openvr_init.cpp index 558d372..f7a3f5b 100644 --- a/src/overlaycontroller/openvr_init.cpp +++ b/src/overlaycontroller/openvr_init.cpp @@ -73,6 +73,8 @@ time). LOG( INFO ) << logPrefix << "Application initialized as scene."; } + // This needs to be initialized, otherwise the bindings won't show up in the + // menu. input::Manifest manifest; // SteamVR just needs a good enough time to start up comfortably and for the From 667711246ed0abb5d24dfc724575520cdd38d502 Mon Sep 17 00:00:00 2001 From: Vasily Date: Mon, 21 Jan 2019 21:11:28 +0100 Subject: [PATCH 12/14] Add media key bindings, update docs All bindings are disabled by default. This is to prevent confusing behaviour from users who do not know about the new bindings. Forcing users to manually set up their bindings will also enable them to play around with the SteamVR bindings interface, which allows for much more versatile binding options that our current offerings. All actions are curently in main action set. If the selection of bindings was greatly expanded it could be beneficial to encapsulate the options more. The current code is more intended as a proof of concept until our full binding needs have been uncovered. The processInputBindings function should probably also be split into several functions of related bindings, as the amount of bindings grows. --- Readme.md | 17 +++++--- src/ivrinput/ivrinput.cpp | 40 ++++++++++++++++++- src/ivrinput/ivrinput.h | 13 +++++- src/overlaycontroller.cpp | 32 +++++++++++++-- src/overlaycontroller.h | 4 +- src/package_files/action_manifest.json | 24 +++++++++-- .../vive_defaults.json | 34 ++-------------- 7 files changed, 117 insertions(+), 47 deletions(-) diff --git a/Readme.md b/Readme.md index 008bd58..a5b2af1 100644 --- a/Readme.md +++ b/Readme.md @@ -83,20 +83,25 @@ To upgrade an existing installation first stop SteamVR and delete the old applic ## Bindings -Advanced Settings supports the OpenVR IVRInput API. In order to make the bindings interface visible, run the `bindingsinterface.bat` file from the install directory. When SteamVR starts, click the menu -> Settings -> Controller Binding. +Advanced Settings supports the OpenVR IVRInput API. In order to make the bindings interface visible, run the `bindingsinterface.bat` file from the install directory. After you're done configuring your bindings, close SteamVR and start it up again. You are now using your new bindings. -If you just run the application normally it will not show up in the bindings interface, this is an OpenVR bug. +Alternatively, you can change the `matzman666.AdvancedSettings.vrappconfig` in `C:\Program Files (x86)\Steam\config\vrappconfig` to have a `last_launch_time` close to the current date (in [Unix time](https://en.wikipedia.org/wiki/Unix_time)). If in doubt, try using the value from [this](https://time.is/Unix_time_now) site. Running the application normally will now make it show up in the bindings interface. -After you're done configuring your bindings, close SteamVR and start it up again. You are now using your new bindings. +The above two solutions are workarounds to a bug in the OpenVR API. + +After doing one of the above workarounds, when SteamVR starts click the menu -> Settings -> Controller Binding. By default no buttons are bound. -The following actions exist: +The following actions currently exist: | Action | Type | Explanation | | ------------- | ------------- | ------------ | -| PlayNextSong | Binary/Button | The same as using the media keys. Plays the next song. | -| TODO | TODO | TODO | +| NextSong | Binary/Button | The same as using the media keys. Tells a media player to play the next song.| +| PreviousSong | Binary/Button | The same as using the media keys. Tells a media player to play the previous song. | +| pausePlaySong | Binary/Button | The same as using the media keys. Tells the media player to press play/pause. | +| stopSong | Binary/Button | The same as using the media keys. Tells the media player to stop playback. | + ## Command Line Arguments diff --git a/src/ivrinput/ivrinput.cpp b/src/ivrinput/ivrinput.cpp index fa5aa9a..5e0e792 100644 --- a/src/ivrinput/ivrinput.cpp +++ b/src/ivrinput/ivrinput.cpp @@ -124,7 +124,12 @@ mystically not working. */ SteamIVRInput::SteamIVRInput() : m_manifest(), m_mainSet( input_strings::k_setMain ), - m_nextTrack( input_strings::k_actionNextTrack, ActionType::Digital ) + m_nextTrack( input_strings::k_actionNextTrack, ActionType::Digital ), + m_previousTrack( input_strings::k_actionPreviousTrack, + ActionType::Digital ), + m_pausePlayTrack( input_strings::k_actionPausePlayTrack, + ActionType::Digital ), + m_stopTrack( input_strings::k_actionStopTrack, ActionType::Digital ) { m_activeActionSet.ulActionSet = m_mainSet.handle(); m_activeActionSet.ulRestrictedToDevice = vr::k_ulInvalidInputValueHandle; @@ -141,6 +146,39 @@ bool SteamIVRInput::nextSong() return isDigitalActionActivatedOnce( m_nextTrack ); } +/*! +Returns true if the previous media track should be played. + +Will only return true the first time that the button is pressed. Holding the +button down will result in false until it has been released and pushed again. +*/ +bool SteamIVRInput::previousSong() +{ + return isDigitalActionActivatedOnce( m_previousTrack ); +} + +/*! +Returns true if media playback should be paused/played. + +Will only return true the first time that the button is pressed. Holding the +button down will result in false until it has been released and pushed again. +*/ +bool SteamIVRInput::pausePlaySong() +{ + return isDigitalActionActivatedOnce( m_pausePlayTrack ); +} + +/*! +Returns true if media playback should be stopped. + +Will only return true the first time that the button is pressed. Holding the +button down will result in false until it has been released and pushed again. +*/ +bool SteamIVRInput::stopSong() +{ + return isDigitalActionActivatedOnce( m_stopTrack ); +} + /*! Updates the active action set(s). Should be called every frame, or however often you want the input system to diff --git a/src/ivrinput/ivrinput.h b/src/ivrinput/ivrinput.h index 7bd2736..975a447 100644 --- a/src/ivrinput/ivrinput.h +++ b/src/ivrinput/ivrinput.h @@ -37,6 +37,9 @@ public: void UpdateStates(); bool nextSong(); + bool previousSong(); + bool pausePlaySong(); + bool stopSong(); // Destructor. There are no terminating calls for the IVRInput API, so it // is left blank. @@ -61,7 +64,11 @@ private: ActionSet m_mainSet; vr::VRActiveActionSet_t m_activeActionSet = {}; + // Music player bindings Action m_nextTrack; + Action m_previousTrack; + Action m_pausePlayTrack; + Action m_stopTrack; }; /*! @@ -71,7 +78,11 @@ should be used to refer to all action manifest strings. */ namespace input_strings { - constexpr auto k_actionNextTrack = "/actions/main/in/PlayNextTrack"; + constexpr auto k_actionNextTrack = "/actions/main/in/NextTrack"; + constexpr auto k_actionPreviousTrack = "/actions/main/in/PreviousTrack"; + constexpr auto k_actionPausePlayTrack = "/actions/main/in/PausePlayTrack"; + constexpr auto k_actionStopTrack = "/actions/main/in/StopTrack"; + constexpr auto k_setMain = "/actions/main"; } // namespace input_strings diff --git a/src/overlaycontroller.cpp b/src/overlaycontroller.cpp index 314b068..59f2dd3 100644 --- a/src/overlaycontroller.cpp +++ b/src/overlaycontroller.cpp @@ -471,6 +471,33 @@ QPoint OverlayController::getMousePositionForEvent( vr::VREvent_Mouse_t mouse ) return QPoint( mouse.x, y ); } +/*! +Checks if an action has been activated and dispatches the related action if it +has been. + +This function should probably be split into several functions that are specific +to a binding type as the amount of actions grows. +*/ +void OverlayController::processInputBindings() +{ + if ( m_actions.nextSong() ) + { + m_utilitiesTabController.sendMediaNextSong(); + } + if ( m_actions.previousSong() ) + { + m_utilitiesTabController.sendMediaPreviousSong(); + } + if ( m_actions.pausePlaySong() ) + { + m_utilitiesTabController.sendMediaPausePlay(); + } + if ( m_actions.stopSong() ) + { + m_utilitiesTabController.sendMediaStopSong(); + } +} + void OverlayController::OnTimeoutPumpEvents() { if ( !vr::VRSystem() ) @@ -478,10 +505,7 @@ void OverlayController::OnTimeoutPumpEvents() m_actions.UpdateStates(); - if ( m_actions.nextSong() ) - { - LOG( INFO ) << "NEXT SONG SET!"; - } + processInputBindings(); vr::VREvent_t vrEvent; bool chaperoneDataAlreadyUpdated = false; diff --git a/src/overlaycontroller.h b/src/overlaycontroller.h index 7f34462..06dc94b 100644 --- a/src/overlaycontroller.h +++ b/src/overlaycontroller.h @@ -90,9 +90,10 @@ private: // the class. openvr_init::OpenVR_Init m_openVrInit; + input::SteamIVRInput m_actions; + public: // I know it's an ugly hack to make them public to enable external // access, but I am too lazy to implement getters. - input::SteamIVRInput m_actions; SteamVRTabController m_steamVRTabController; ChaperoneTabController m_chaperoneTabController; MoveCenterTabController m_moveCenterTabController; @@ -106,6 +107,7 @@ public: // I know it's an ugly hack to make them public to enable external private: QPoint getMousePositionForEvent( vr::VREvent_Mouse_t mouse ); + void processInputBindings(); public: OverlayController( bool desktopMode, bool noSound, QQmlEngine& qmlEngine ); diff --git a/src/package_files/action_manifest.json b/src/package_files/action_manifest.json index 0b1a38c..d1e40ff 100644 --- a/src/package_files/action_manifest.json +++ b/src/package_files/action_manifest.json @@ -7,7 +7,22 @@ ], "actions": [ { - "name": "/actions/main/in/PlayNextTrack", + "name": "/actions/main/in/NextTrack", + "requirement": "optional", + "type": "boolean" + }, + { + "name": "/actions/main/in/PreviousTrack", + "requirement": "optional", + "type": "boolean" + }, + { + "name": "/actions/main/in/PausePlayTrack", + "requirement": "optional", + "type": "boolean" + }, + { + "name": "/actions/main/in/StopTrack", "requirement": "optional", "type": "boolean" } @@ -22,9 +37,12 @@ { "language_tag": "en", - "/actions/main" : "My Game Actions", + "/actions/main" : "Advanced Settings Actions", - "/actions/main/in/PlayNextTrack" : "Play Next Track" + "/actions/main/in/NextTrack" : "Play Next Track", + "/actions/main/in/PreviousTrack" : "Play Previous Track", + "/actions/main/in/PausePlayTrack" : "Pause/Play Track", + "/actions/main/in/StopTrack" : "Stop Track" } ] } \ No newline at end of file diff --git a/src/package_files/default_action_manifests/vive_defaults.json b/src/package_files/default_action_manifests/vive_defaults.json index 3e2094c..22e600c 100644 --- a/src/package_files/default_action_manifests/vive_defaults.json +++ b/src/package_files/default_action_manifests/vive_defaults.json @@ -3,40 +3,12 @@ "app_key" : "matzman666.advancedsettings", "bindings" : { "/actions/main" : { - "sources" : [ - { - "inputs" : { - "click" : { - "output" : "/actions/main/in/playnexttrack" - } - }, - "mode" : "button", - "path" : "/user/hand/left/input/application_menu" - }, - { - "inputs" : { - "click" : { - "output" : "/actions/main/in/playnexttrack" - } - }, - "mode" : "button", - "path" : "/user/hand/right/input/application_menu" - }, - { - "inputs" : { - "click" : { - "output" : "/actions/main/in/playnexttrack" - } - }, - "mode" : "button", - "path" : "/user/hand/right/input/grip" - } - ] + "sources" : [] } }, "controller_type" : "vive_controller", - "description" : "", - "name" : "My My Test configuration for Vive Controller", + "description" : "Nothing has been bound by default.", + "name" : "Empty Bindings (default)", "options" : {}, "simulated_actions" : [] } From 1ae434e9877961b97d5d28ab4eb5b0eed1fb33b3 Mon Sep 17 00:00:00 2001 From: Vasily Date: Tue, 22 Jan 2019 18:33:38 +0100 Subject: [PATCH 13/14] Swap recommended ways of getting bindings to show up Running with "-bindingsinterface" does not allow for continually updating the bindings or playing around with them. While changing the unix time in the vrappconfig is slightly harder for casual users, it will also give them a way better user experience. --- Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index a5b2af1..3c443e6 100644 --- a/Readme.md +++ b/Readme.md @@ -83,9 +83,9 @@ To upgrade an existing installation first stop SteamVR and delete the old applic ## Bindings -Advanced Settings supports the OpenVR IVRInput API. In order to make the bindings interface visible, run the `bindingsinterface.bat` file from the install directory. After you're done configuring your bindings, close SteamVR and start it up again. You are now using your new bindings. +Advanced Settings supports the OpenVR IVRInput API. In order to make the bindings interface visible, change the `matzman666.AdvancedSettings.vrappconfig` in `C:\Program Files (x86)\Steam\config\vrappconfig` to have a `last_launch_time` close to the current date (in [Unix time](https://en.wikipedia.org/wiki/Unix_time)). If in doubt, try using the value from [this](https://time.is/Unix_time_now) site. Running the application normally will now make it show up in the bindings interface. -Alternatively, you can change the `matzman666.AdvancedSettings.vrappconfig` in `C:\Program Files (x86)\Steam\config\vrappconfig` to have a `last_launch_time` close to the current date (in [Unix time](https://en.wikipedia.org/wiki/Unix_time)). If in doubt, try using the value from [this](https://time.is/Unix_time_now) site. Running the application normally will now make it show up in the bindings interface. +Alternatively, if the above doesn't work, run the `bindingsinterface.bat` file from the install directory. After you're done configuring your bindings, close SteamVR and start it up again. You are now using your new bindings. Advanced Settings will not show up in the bindings menu unless you're actively running the application with `bindingsinterface.bat`. Advanced Settings will not work normally while running with `bindingsinterface.bat`. The above two solutions are workarounds to a bug in the OpenVR API. From 7531fc61ea6bc145aab044c496467f74e857e6d8 Mon Sep 17 00:00:00 2001 From: Vasily Date: Tue, 22 Jan 2019 20:36:42 +0100 Subject: [PATCH 14/14] Add browser bindings viewer to readme Collecting information in the readme prevents users from having to Google it. --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 3c443e6..97b63e8 100644 --- a/Readme.md +++ b/Readme.md @@ -89,7 +89,7 @@ Alternatively, if the above doesn't work, run the `bindingsinterface.bat` file f The above two solutions are workarounds to a bug in the OpenVR API. -After doing one of the above workarounds, when SteamVR starts click the menu -> Settings -> Controller Binding. +After doing one of the above workarounds, when SteamVR starts click the menu -> Settings -> Controller Binding. You can also visit [this](127.0.0.1:8998/dashboard/controllerbinding.html) site in your internet browser. It's the same as the built in SteamVR bindings menu. SteamVR must be running for it to work. By default no buttons are bound.