Merge pull request #42 from OpenVR-Advanced-Settings/media-keys

Update keyboard functions, Add media control keys to Utilities page
This commit is contained in:
username223
2019-01-12 22:24:07 +01:00
committed by GitHub
16 changed files with 363 additions and 36 deletions
+1
View File
@@ -230,6 +230,7 @@ Any changes to controller settings are immediately applied by Revive (Requires R
- **Steam Desktop Overlay Width:** Allows to increase the size of the Steam desktop overlay. Useful for multi-monitor setups.
- **Media Control Keys:** Allows controlling a media player through the media keys. This is the same as having a keyboard with media keys and then pressing them. Should support most common media players.
<a name="accessibility_page"></a>
## - Accessibility Page:
+2 -1
View File
@@ -139,4 +139,5 @@ DISTFILES += \
src/res/qml/SettingsPage.qml \
src/res/qml/StatisticsPage.qml \
src/res/qml/SteamVRPage.qml \
src/res/qml/UtilitiesPage.qml
src/res/qml/UtilitiesPage.qml \
src/res/qml/media_keys/*
Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 45 KiB

+149
View File
@@ -320,6 +320,155 @@ MyStackViewPage {
}
}
GroupBox {
id: mediaKeysGroupBox
Layout.fillWidth: true
label: MyText {
leftPadding: 10
text: "Media Control Keys"
bottomPadding: -10
}
background: Rectangle {
color: "transparent"
border.color: "#ffffff"
radius: 8
}
ColumnLayout {
anchors.fill: parent
Rectangle {
color: "#ffffff"
height: 1
Layout.fillWidth: true
Layout.bottomMargin: 5
}
RowLayout {
property bool playButtonIsPlay: false
MyPushButton2 {
id: previousSongButton
Layout.preferredHeight: 48
Layout.preferredWidth: 48
contentItem: Image {
source: "media_keys/outline_skip_previous_white_24dp.svg"
sourceSize: Qt.size( imgSP.sourceSize.width*2, imgSP.sourceSize.height*2 )
Image{
id: imgSP
source: parent.source
width:0
height:0
}
anchors.fill: parent
}
onClicked: {
UtilitiesTabController.sendMediaPreviousSong()
}
background: Rectangle {
opacity: parent.down ? 1.0 : (parent.activeFocus ? 0.5 : 0.0)
color: "#406288"
radius: 4
anchors.fill: parent
}
}
MyPushButton2 {
id: stopSongButton
Layout.preferredHeight: 48
Layout.preferredWidth: 48
contentItem: Image {
source: "media_keys/outline_stop_white_24dp.svg"
sourceSize: Qt.size( imgS.sourceSize.width*2, imgS.sourceSize.height*2 )
Image{
id: imgS
source: parent.source
width:0
height:0
}
anchors.fill: parent
}
onClicked: {
UtilitiesTabController.sendMediaStopSong()
playPauseSongButton.contentItem.source = "media_keys/outline_play_arrow_white_24dp.svg"
parent.playButtonIsPlay = true
}
background: Rectangle {
opacity: parent.down ? 1.0 : (parent.activeFocus ? 0.5 : 0.0)
color: "#406288"
radius: 4
anchors.fill: parent
}
}
MyPushButton2 {
id: playPauseSongButton
Layout.preferredHeight: 48
Layout.preferredWidth: 48
contentItem: Image {
// Assume that music is playing when people enter VR.
source: "media_keys/outline_pause_white_24dp.svg"
sourceSize: Qt.size( imgP.sourceSize.width*2, imgP.sourceSize.height*2 )
Image{
id: imgP
source: parent.source
width:0
height:0
}
anchors.fill: parent
}
onClicked: {
UtilitiesTabController.sendMediaPausePlay()
if (parent.playButtonIsPlay) {
playPauseSongButton.contentItem.source = "media_keys/outline_pause_white_24dp.svg"
parent.playButtonIsPlay = false
} else {
playPauseSongButton.contentItem.source = "media_keys/outline_play_arrow_white_24dp.svg"
parent.playButtonIsPlay = true
}
}
background: Rectangle {
opacity: parent.down ? 1.0 : (parent.activeFocus ? 0.5 : 0.0)
color: "#406288"
radius: 4
anchors.fill: parent
}
}
MyPushButton2 {
id: nextSongButton
Layout.preferredHeight: 48
Layout.preferredWidth: 48
contentItem: Image {
source: "media_keys/outline_skip_next_white_24dp.svg"
sourceSize: Qt.size( imgSN.sourceSize.width*2, imgSN.sourceSize.height*2 )
Image{
id: imgSN
source: parent.source
width:0
height:0
}
anchors.fill: parent
}
onClicked: {
UtilitiesTabController.sendMediaNextSong()
}
background: Rectangle {
opacity: parent.down ? 1.0 : (parent.activeFocus ? 0.5 : 0.0)
color: "#406288"
radius: 4
anchors.fill: parent
}
}
}
}
}
Item {
Layout.fillHeight: true
}
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="white" d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>

After

Width:  |  Height:  |  Size: 184 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="white" d="M10 8.64L15.27 12 10 15.36V8.64M8 5v14l11-7L8 5z"/></svg>

After

Width:  |  Height:  |  Size: 201 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="white" d="M6 18l8.5-6L6 6v12zm2-8.14L11.03 12 8 14.14V9.86zM16 6h2v12h-2z"/></svg>

After

Width:  |  Height:  |  Size: 216 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="white" d="M6 6h2v12H6zm3.5 6l8.5 6V6l-8.5 6zm6.5 2.14L12.97 12 16 9.86v4.28z"/></svg>

After

Width:  |  Height:  |  Size: 219 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="white" d="M16 8v8H8V8h8m2-2H6v12h12V6z"/></svg>

After

Width:  |  Height:  |  Size: 181 B

+5 -1
View File
@@ -15,8 +15,12 @@ public:
virtual void sendKeyboardInput( QString input ) = 0;
virtual void sendKeyboardEnter() = 0;
virtual void sendKeyboardBackspace( int count ) = 0;
virtual void sendKeyboardBackspace( const int count ) = 0;
virtual void sendKeyboardAltTab() = 0;
virtual void sendMediaNextSong() = 0;
virtual void sendMediaPreviousSong() = 0;
virtual void sendMediaPausePlay() = 0;
virtual void sendMediaStopSong() = 0;
};
} // namespace advsettings
+18 -1
View File
@@ -83,11 +83,28 @@ void UtilitiesTabController::sendKeyboardAltTab()
keyboardInput->sendKeyboardAltTab();
}
void UtilitiesTabController::sendKeyboardBackspace( int count )
void UtilitiesTabController::sendKeyboardBackspace( const int count )
{
keyboardInput->sendKeyboardBackspace( count );
}
void UtilitiesTabController::sendMediaNextSong()
{
keyboardInput->sendMediaNextSong();
}
void UtilitiesTabController::sendMediaPreviousSong()
{
keyboardInput->sendMediaPreviousSong();
}
void UtilitiesTabController::sendMediaPausePlay()
{
keyboardInput->sendMediaPausePlay();
}
void UtilitiesTabController::sendMediaStopSong()
{
keyboardInput->sendMediaStopSong();
}
bool UtilitiesTabController::alarmEnabled() const
{
return m_alarmEnabled;
+5 -1
View File
@@ -69,7 +69,11 @@ public slots:
void sendKeyboardInput( QString input );
void sendKeyboardEnter();
void sendKeyboardAltTab();
void sendKeyboardBackspace( int count );
void sendKeyboardBackspace( const int count );
void sendMediaNextSong();
void sendMediaPreviousSong();
void sendMediaPausePlay();
void sendMediaStopSong();
void setAlarmEnabled( bool enabled, bool notify = true );
void setAlarmIsModal( bool modal, bool notify = true );
@@ -17,7 +17,7 @@ void KeyboardInputDummy::sendKeyboardEnter()
// noop
}
void KeyboardInputDummy::sendKeyboardBackspace( int count )
void KeyboardInputDummy::sendKeyboardBackspace( const int count )
{
// noop
UNREFERENCED_PARAMETER( count );
@@ -28,4 +28,24 @@ void KeyboardInputDummy::sendKeyboardAltTab()
// noop
}
void KeyboardInputDummy::sendMediaNextSong()
{
// noop
}
void KeyboardInputDummy::sendMediaPreviousSong()
{
// noop
}
void KeyboardInputDummy::sendMediaPausePlay()
{
// noop
}
void KeyboardInputDummy::sendMediaStopSong()
{
// noop
}
} // namespace advsettings
@@ -11,8 +11,12 @@ class KeyboardInputDummy : public KeyboardInput
public:
virtual void sendKeyboardInput( QString input ) override;
virtual void sendKeyboardEnter() override;
virtual void sendKeyboardBackspace( int count ) override;
virtual void sendKeyboardBackspace( const int count ) override;
virtual void sendKeyboardAltTab() override;
virtual void sendMediaNextSong() override;
virtual void sendMediaPreviousSong() override;
virtual void sendMediaPausePlay() override;
virtual void sendMediaStopSong() override;
};
} // namespace advsettings
@@ -4,6 +4,20 @@
namespace advsettings
{
/*!
Represents the state of a keyboard button press. Can be either Up or Down.
Implemented because neither Qt nor Windows.h included a sensible key state enum.
*/
enum class KeyStatus
{
Up,
Down,
};
/*!
Fills out an INPUT struct ip with scanCode and keyup status.
*/
void fillKiStruct( INPUT& ip, WORD scanCode, bool keyup )
{
ip.type = INPUT_KEYBOARD;
@@ -21,11 +35,18 @@ void fillKiStruct( INPUT& ip, WORD scanCode, bool keyup )
ip.ki.time = 0;
};
void sendKeyboardInputRaw( int inputCount, LPINPUT input )
/*!
Calls SendInput on an INPUT struct and has associated error handling.
inputCount can't be deduced from input because of implicit conversions from
arrays to pointers when passed as arguments.
*/
void sendKeyboardInputRaw( const int inputCount, const LPINPUT input )
{
if ( ( inputCount > 0 )
&& !SendInput(
static_cast<UINT>( inputCount ), input, sizeof( INPUT ) ) )
const auto success
= SendInput( static_cast<UINT>( inputCount ), input, sizeof( INPUT ) );
if ( ( inputCount > 0 ) && !success )
{
char* err;
auto errCode = GetLastError();
@@ -50,6 +71,62 @@ void sendKeyboardInputRaw( int inputCount, LPINPUT input )
}
}
/*!
Returns an INPUT struct with the corresponding virtualKeyCode and keyStatus set.
All fields except ki.wVk and ki.dwFlags are zero.
The INPUT struct is used with the SendInput Windows function.
Official Docs:
https://docs.microsoft.com/en-us/windows/desktop/api/winuser/ns-winuser-taginput
*/
INPUT createInputStruct( const WORD virtualKeyCode, const KeyStatus keyStatus )
{
// Zero init to ensure random data doesn't muck something up.
INPUT input = {};
input.type = INPUT_KEYBOARD;
input.ki.wVk = virtualKeyCode;
if ( keyStatus == KeyStatus::Up )
{
input.ki.dwFlags = KEYEVENTF_KEYUP;
}
else if ( keyStatus == KeyStatus::Down )
{
// Struct is already zero initialized, but this is here for clarity.
// Compiler will likely sort this out, otherwise the performance hit is
// negligible.
// There is no corresponding KEYDOWN event, you just don't
// set KEYEVENTF_KEYUP.
input.ki.dwFlags = 0;
}
// The sizeof(INPUT) on MSVC is 28. Returning by value is a non-issue
// compared to the simplicity of not having to pass a ref to an already
// existing INPUT struct, and possibly forgetting to zero it out.
return input;
}
/*!
Sends a key press to Windows. The virtualKeyCode is a Windows specific define
found in <windows.h>.
Virtual Key Codes offical docs:
https://docs.microsoft.com/en-us/windows/desktop/inputdev/virtual-key-codes
*/
void sendKeyPressAndRelease( const WORD virtualKeyCode )
{
const auto press = createInputStruct( virtualKeyCode, KeyStatus::Down );
const auto release = createInputStruct( virtualKeyCode, KeyStatus::Up );
constexpr auto numberOfActions = 2;
INPUT actions[numberOfActions] = { press, release };
sendKeyboardInputRaw( numberOfActions, actions );
}
void KeyboardInputWindows::sendKeyboardInput( QString input )
{
int len = input.length();
@@ -122,44 +199,85 @@ void KeyboardInputWindows::sendKeyboardInput( QString input )
}
}
/*!
Sends a single Enter keypress to Windows.
*/
void KeyboardInputWindows::sendKeyboardEnter()
{
LPINPUT ips = new INPUT[2];
fillKiStruct( ips[0], VK_RETURN, false );
fillKiStruct( ips[1], VK_RETURN, true );
sendKeyboardInputRaw( 2, ips );
delete[] ips;
sendKeyPressAndRelease( VK_RETURN );
}
void KeyboardInputWindows::sendKeyboardBackspace( int count )
/*!
Sends count amount of backspaces to Windows. count of 0 or below will do
nothing.
*/
void KeyboardInputWindows::sendKeyboardBackspace( const int count )
{
if ( count > 0 )
// We can only send a positive amount of key presses.
if ( count <= 0 )
{
// We ensure that count is nonnegative, therefore safe cast.
LPINPUT ips = new INPUT[static_cast<unsigned int>( count ) * 2];
for ( int i = 0; i < count; i++ )
{
fillKiStruct( ips[2 * i], VK_BACK, false );
fillKiStruct( ips[2 * i + 1], VK_BACK, true );
}
return;
}
sendKeyboardInputRaw( count * 2, ips );
delete[] ips;
for ( int presses = 0; presses < count; ++presses )
{
sendKeyPressAndRelease( VK_BACK );
}
}
/*!
Sends an Alt + Tab combination to Windows.
Used for tabbing out of game windows blocking other programs on desktop while in
VR. At least one use case was tabbing out of a fullscreen game to be able to use
a music player.
*/
void KeyboardInputWindows::sendKeyboardAltTab()
{
LPINPUT ips = new INPUT[4];
// VK_MENU is alt
fillKiStruct( ips[0], VK_MENU, false );
fillKiStruct( ips[1], VK_TAB, false );
fillKiStruct( ips[2], VK_TAB, true );
fillKiStruct( ips[3], VK_MENU, true );
// 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 );
sendKeyboardInputRaw( 4, ips );
delete[] ips;
constexpr auto numberOfActions = 4;
INPUT actions[numberOfActions]
= { pressAlt, pressTab, releaseAlt, releaseTab };
sendKeyboardInputRaw( numberOfActions, actions );
}
/*!
Sends a media key Next Song button to Windows.
*/
void KeyboardInputWindows::sendMediaNextSong()
{
sendKeyPressAndRelease( VK_MEDIA_NEXT_TRACK );
}
/*!
Sends a media key Previous Song button to Windows.
*/
void KeyboardInputWindows::sendMediaPreviousSong()
{
sendKeyPressAndRelease( VK_MEDIA_PREV_TRACK );
}
/*!
Sends a media key Play/Pause button to Windows.
*/
void KeyboardInputWindows::sendMediaPausePlay()
{
sendKeyPressAndRelease( VK_MEDIA_PLAY_PAUSE );
}
/*!
Sends a media key Stop button to Windows.
*/
void KeyboardInputWindows::sendMediaStopSong()
{
sendKeyPressAndRelease( VK_MEDIA_STOP );
}
} // namespace advsettings
@@ -11,8 +11,12 @@ class KeyboardInputWindows : public KeyboardInput
public:
virtual void sendKeyboardInput( QString input ) override;
virtual void sendKeyboardEnter() override;
virtual void sendKeyboardBackspace( int count ) override;
virtual void sendKeyboardBackspace( const int count ) override;
virtual void sendKeyboardAltTab() override;
virtual void sendMediaNextSong() override;
virtual void sendMediaPreviousSong() override;
virtual void sendMediaPausePlay() override;
virtual void sendMediaStopSong() override;
};
} // namespace advsettings