Merge pull request #59 from OpenVR-Advanced-Settings/new-input-api
Add IVRInput bindings for media keys
This commit is contained in:
@@ -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,33 @@ 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.
|
||||
|
||||
<a name="bindings"></a>
|
||||
## 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, 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.
|
||||
|
||||
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.
|
||||
|
||||
The following actions currently exist:
|
||||
|
||||
| Action | Type | Explanation |
|
||||
| ------------- | ------------- | ------------ |
|
||||
| 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. |
|
||||
|
||||
|
||||
<a name="command_line_arguments"></a>
|
||||
## 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 +118,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).
|
||||
|
||||
<a name="logging_config"></a>
|
||||
|
||||
@@ -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/ivrinput/ivrinput.cpp
|
||||
|
||||
HEADERS += src/overlaycontroller.h \
|
||||
src/tabcontrollers/AudioTabController.h \
|
||||
@@ -79,7 +79,11 @@ 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/ivrinput/ivrinput_action.h \
|
||||
src/ivrinput/ivrinput_manifest.h \
|
||||
src/ivrinput/ivrinput_action_set.h \
|
||||
src/ivrinput/ivrinput.h
|
||||
|
||||
win32 {
|
||||
SOURCES += src/tabcontrollers/audiomanager/AudioManagerWindows.cpp \
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
#include "ivrinput.h"
|
||||
#include <openvr.h>
|
||||
#include <iostream>
|
||||
#include <QStandardPaths>
|
||||
#include <easylogging++.h>
|
||||
|
||||
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 )
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/*!
|
||||
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 )
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/*!
|
||||
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 );
|
||||
|
||||
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 );
|
||||
|
||||
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 ),
|
||||
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;
|
||||
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 );
|
||||
}
|
||||
|
||||
/*!
|
||||
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
|
||||
update state.
|
||||
*/
|
||||
void SteamIVRInput::UpdateStates()
|
||||
{
|
||||
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,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvr.h>
|
||||
#include "ivrinput_action.h"
|
||||
#include "ivrinput_manifest.h"
|
||||
#include "ivrinput_action_set.h"
|
||||
|
||||
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 UpdateStates();
|
||||
bool nextSong();
|
||||
bool previousSong();
|
||||
bool pausePlaySong();
|
||||
bool stopSong();
|
||||
|
||||
// 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 = {};
|
||||
|
||||
// Music player bindings
|
||||
Action m_nextTrack;
|
||||
Action m_previousTrack;
|
||||
Action m_pausePlayTrack;
|
||||
Action m_stopTrack;
|
||||
};
|
||||
|
||||
/*!
|
||||
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/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
|
||||
|
||||
} // namespace input
|
||||
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
#include <openvr.h>
|
||||
#include <easylogging++.h>
|
||||
|
||||
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,
|
||||
Digital,
|
||||
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:
|
||||
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;
|
||||
}
|
||||
}
|
||||
/*!
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
vr::VRActionHandle_t m_handle = 0;
|
||||
const std::string m_name;
|
||||
const ActionType m_type = ActionType::Undefined;
|
||||
};
|
||||
|
||||
} // namespace input
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
#include <openvr.h>
|
||||
#include <easylogging++.h>
|
||||
|
||||
namespace input
|
||||
{
|
||||
/*!
|
||||
Represents an action set from the action manifest.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
/*!
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
@@ -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.
|
||||
|
||||
@@ -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" ) );
|
||||
}
|
||||
|
||||
// Init controllers
|
||||
m_steamVRTabController.initStage1();
|
||||
m_chaperoneTabController.initStage1();
|
||||
@@ -472,11 +471,42 @@ 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() )
|
||||
return;
|
||||
|
||||
m_actions.UpdateStates();
|
||||
|
||||
processInputBindings();
|
||||
|
||||
vr::VREvent_t vrEvent;
|
||||
bool chaperoneDataAlreadyUpdated = false;
|
||||
while ( pollNextEvent( m_ulOverlayHandle, &vrEvent ) )
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
#include "tabcontrollers/UtilitiesTabController.h"
|
||||
#include "tabcontrollers/AccessibilityTabController.h"
|
||||
|
||||
#include "ivrinput/ivrinput.h"
|
||||
|
||||
// application namespace
|
||||
namespace advsettings
|
||||
{
|
||||
@@ -86,7 +88,9 @@ 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;
|
||||
|
||||
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.
|
||||
@@ -103,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 );
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
#include <openvr.h>
|
||||
#include <QMessageBox>
|
||||
#include "openvr_init.h"
|
||||
#include <easylogging++.h>
|
||||
#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,68 @@ 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.";
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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
|
||||
@@ -70,9 +135,14 @@ OpenVR_Init::OpenVR_Init()
|
||||
{
|
||||
reportVersionError( vr::IVRNotifications_Version );
|
||||
}
|
||||
else if ( !vr::VR_IsInterfaceVersionValid( vr::IVRInput_Version ) )
|
||||
{
|
||||
reportVersionError( vr::IVRInput_Version );
|
||||
}
|
||||
}
|
||||
|
||||
OpenVR_Init::~OpenVR_Init()
|
||||
{
|
||||
vr::VR_Shutdown();
|
||||
}
|
||||
} // namespace openvr_init
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"default_bindings": [
|
||||
{
|
||||
"controller_type": "vive_controller",
|
||||
"binding_url": "default_action_manifests/vive_defaults.json"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"action_sets": [
|
||||
{
|
||||
"name": "/actions/main",
|
||||
"usage": "leftright"
|
||||
}
|
||||
],
|
||||
"localization" : [
|
||||
{
|
||||
"language_tag": "en",
|
||||
|
||||
"/actions/main" : "Advanced Settings Actions",
|
||||
|
||||
"/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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
start AdvancedSettings.exe -bindingsinterface
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"alias_info" : {},
|
||||
"app_key" : "matzman666.advancedsettings",
|
||||
"bindings" : {
|
||||
"/actions/main" : {
|
||||
"sources" : []
|
||||
}
|
||||
},
|
||||
"controller_type" : "vive_controller",
|
||||
"description" : "Nothing has been bound by default.",
|
||||
"name" : "Empty Bindings (default)",
|
||||
"options" : {},
|
||||
"simulated_actions" : []
|
||||
}
|
||||
Reference in New Issue
Block a user