Merge branch 'master' into alpha-kung
This commit is contained in:
@@ -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();
|
||||
@@ -475,15 +474,47 @@ 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()
|
||||
{
|
||||
vr::VRSystem()->GetTimeSinceLastVsync( nullptr, &m_currentFrame );
|
||||
|
||||
|
||||
if ( m_currentFrame > m_lastFrame )
|
||||
{
|
||||
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
|
||||
{
|
||||
@@ -89,7 +91,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.
|
||||
@@ -106,6 +110,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" : []
|
||||
}
|
||||
@@ -42,10 +42,11 @@ MyStackViewPage {
|
||||
|
||||
RowLayout {
|
||||
spacing: 18
|
||||
Layout.fillWidth: true
|
||||
|
||||
MyPushButton {
|
||||
text: "Keyboard Input"
|
||||
Layout.preferredWidth: 200
|
||||
Layout.fillWidth: true
|
||||
onClicked: {
|
||||
OverlayController.showKeyboard("", 601)
|
||||
}
|
||||
@@ -61,7 +62,7 @@ MyStackViewPage {
|
||||
|
||||
MyPushButton {
|
||||
text: "Enter"
|
||||
Layout.preferredWidth: 200
|
||||
Layout.fillWidth: true
|
||||
onClicked: {
|
||||
UtilitiesTabController.sendKeyboardEnter()
|
||||
}
|
||||
@@ -69,7 +70,7 @@ MyStackViewPage {
|
||||
|
||||
MyPushButton {
|
||||
text: "Alt-Tab"
|
||||
Layout.preferredWidth: 200
|
||||
Layout.fillWidth: true
|
||||
onClicked: {
|
||||
UtilitiesTabController.sendKeyboardAltTab()
|
||||
}
|
||||
@@ -77,7 +78,7 @@ MyStackViewPage {
|
||||
|
||||
MyPushButton {
|
||||
text: "Backspace"
|
||||
Layout.preferredWidth: 200
|
||||
Layout.fillWidth: true
|
||||
onClicked: {
|
||||
UtilitiesTabController.sendKeyboardBackspace(1)
|
||||
}
|
||||
@@ -85,11 +86,19 @@ MyStackViewPage {
|
||||
|
||||
MyPushButton {
|
||||
text: "10x Backspace"
|
||||
Layout.preferredWidth: 200
|
||||
Layout.fillWidth: true
|
||||
onClicked: {
|
||||
UtilitiesTabController.sendKeyboardBackspace(10)
|
||||
}
|
||||
}
|
||||
|
||||
MyPushButton {
|
||||
text: "Alt-Enter"
|
||||
Layout.fillWidth: true
|
||||
onClicked: {
|
||||
UtilitiesTabController.sendKeyboardAltEnter()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ public:
|
||||
virtual void sendKeyboardEnter() = 0;
|
||||
virtual void sendKeyboardBackspace( const int count ) = 0;
|
||||
virtual void sendKeyboardAltTab() = 0;
|
||||
virtual void sendKeyboardAltEnter() = 0;
|
||||
virtual void sendMediaNextSong() = 0;
|
||||
virtual void sendMediaPreviousSong() = 0;
|
||||
virtual void sendMediaPausePlay() = 0;
|
||||
|
||||
@@ -78,6 +78,11 @@ void UtilitiesTabController::sendKeyboardEnter()
|
||||
keyboardInput->sendKeyboardEnter();
|
||||
}
|
||||
|
||||
void UtilitiesTabController::sendKeyboardAltEnter()
|
||||
{
|
||||
keyboardInput->sendKeyboardAltEnter();
|
||||
}
|
||||
|
||||
void UtilitiesTabController::sendKeyboardAltTab()
|
||||
{
|
||||
keyboardInput->sendKeyboardAltTab();
|
||||
|
||||
@@ -69,6 +69,7 @@ public slots:
|
||||
void sendKeyboardInput( QString input );
|
||||
void sendKeyboardEnter();
|
||||
void sendKeyboardAltTab();
|
||||
void sendKeyboardAltEnter();
|
||||
void sendKeyboardBackspace( const int count );
|
||||
void sendMediaNextSong();
|
||||
void sendMediaPreviousSong();
|
||||
|
||||
@@ -28,6 +28,11 @@ void KeyboardInputDummy::sendKeyboardAltTab()
|
||||
// noop
|
||||
}
|
||||
|
||||
void KeyboardInputDummy::sendKeyboardAltEnter()
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
void KeyboardInputDummy::sendMediaNextSong()
|
||||
{
|
||||
// noop
|
||||
|
||||
@@ -13,6 +13,7 @@ public:
|
||||
virtual void sendKeyboardEnter() override;
|
||||
virtual void sendKeyboardBackspace( const int count ) override;
|
||||
virtual void sendKeyboardAltTab() override;
|
||||
virtual void sendKeyboardAltEnter() override;
|
||||
virtual void sendMediaNextSong() override;
|
||||
virtual void sendMediaPreviousSong() override;
|
||||
virtual void sendMediaPausePlay() override;
|
||||
|
||||
@@ -234,11 +234,13 @@ a music player.
|
||||
*/
|
||||
void KeyboardInputWindows::sendKeyboardAltTab()
|
||||
{
|
||||
// VK_MENU is Alt.
|
||||
const auto pressAlt = createInputStruct( VK_MENU, KeyStatus::Down );
|
||||
const auto pressTab = createInputStruct( VK_TAB, KeyStatus::Down );
|
||||
const auto releaseAlt = createInputStruct( VK_MENU, KeyStatus::Up );
|
||||
const auto releaseTab = createInputStruct( VK_TAB, KeyStatus::Up );
|
||||
constexpr auto altKey = VK_MENU;
|
||||
constexpr auto tabKey = VK_TAB;
|
||||
|
||||
const auto pressAlt = createInputStruct( altKey, KeyStatus::Down );
|
||||
const auto pressTab = createInputStruct( tabKey, KeyStatus::Down );
|
||||
const auto releaseAlt = createInputStruct( altKey, KeyStatus::Up );
|
||||
const auto releaseTab = createInputStruct( tabKey, KeyStatus::Up );
|
||||
|
||||
constexpr auto numberOfActions = 4;
|
||||
|
||||
@@ -248,6 +250,30 @@ void KeyboardInputWindows::sendKeyboardAltTab()
|
||||
sendKeyboardInputRaw( numberOfActions, actions );
|
||||
}
|
||||
|
||||
/*!
|
||||
Sends an Alt + Tab combination to Windows.
|
||||
|
||||
Used for toggling full screen on games and various applications that appear on
|
||||
the desktop.
|
||||
*/
|
||||
void KeyboardInputWindows::sendKeyboardAltEnter()
|
||||
{
|
||||
constexpr auto altKey = VK_MENU;
|
||||
constexpr auto enterKey = VK_RETURN;
|
||||
|
||||
const auto pressAlt = createInputStruct( altKey, KeyStatus::Down );
|
||||
const auto pressEnter = createInputStruct( enterKey, KeyStatus::Down );
|
||||
const auto releaseAlt = createInputStruct( altKey, KeyStatus::Up );
|
||||
const auto releaseEnter = createInputStruct( enterKey, KeyStatus::Up );
|
||||
|
||||
constexpr auto numberOfActions = 4;
|
||||
|
||||
INPUT actions[numberOfActions]
|
||||
= { pressAlt, pressEnter, releaseAlt, releaseEnter };
|
||||
|
||||
sendKeyboardInputRaw( numberOfActions, actions );
|
||||
}
|
||||
|
||||
/*!
|
||||
Sends a media key Next Song button to Windows.
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,7 @@ public:
|
||||
virtual void sendKeyboardEnter() override;
|
||||
virtual void sendKeyboardBackspace( const int count ) override;
|
||||
virtual void sendKeyboardAltTab() override;
|
||||
virtual void sendKeyboardAltEnter() override;
|
||||
virtual void sendMediaNextSong() override;
|
||||
virtual void sendMediaPreviousSong() override;
|
||||
virtual void sendMediaPausePlay() override;
|
||||
|
||||
Reference in New Issue
Block a user