Merge branch 'master' into alpha-kung

This commit is contained in:
Kung-vr
2019-01-13 15:53:59 +09:00
23 changed files with 750 additions and 285 deletions
+3 -1
View File
@@ -126,7 +126,8 @@ If you want to try latest and greatest unreleased features, you can download lat
- **Profile**: Allows to apply/define/delete supersampling profiles that save supersampling and reprojection settings. - **Profile**: Allows to apply/define/delete supersampling profiles that save supersampling and reprojection settings.
- **Application Supersampling**: Supersampling setting for OpenVR applications/games. Application supersampling [now behaves linearly](https://steamcommunity.com/games/250820/announcements/detail/1256913672017664045) where 2.0 means twice the number of pixel. - **Application Supersampling**: Supersampling setting for OpenVR applications/games. Application supersampling [now behaves linearly](https://steamcommunity.com/games/250820/announcements/detail/1256913672017664045) where 2.0 means twice the number of pixel.
- **Note**: Manual Override for Super-Sampling must be checked in the SteamVR/OpenVR settings menu. - **Note**: Manual Supersampling Override be checked.
- **Enable Manual Supersampling Override**: Enables user control of Supersampling, instead of SteamVR auto profiles.
- **Enable Motion Smoothing**: Enables Motion Smoothing, and disables asynchronous reprojection. - **Enable Motion Smoothing**: Enables Motion Smoothing, and disables asynchronous reprojection.
- **Restart SteamVR**: Restart SteamVR (May crash the Steam overlay when SteamVR Home is running when you restart. Therefore I advice that you close SteamVR Home before restarting). - **Restart SteamVR**: Restart SteamVR (May crash the Steam overlay when SteamVR Home is running when you restart. Therefore I advice that you close SteamVR Home before restarting).
@@ -229,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. - **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> <a name="accessibility_page"></a>
## - Accessibility Page: ## - Accessibility Page:
+2 -1
View File
@@ -139,4 +139,5 @@ DISTFILES += \
src/res/qml/SettingsPage.qml \ src/res/qml/SettingsPage.qml \
src/res/qml/StatisticsPage.qml \ src/res/qml/StatisticsPage.qml \
src/res/qml/SteamVRPage.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: 57 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 45 KiB

+5 -2
View File
@@ -411,8 +411,11 @@ int main( int argc, char* argv[] )
advsettings::OverlayController controller( advsettings::OverlayController controller(
desktopMode, noSound, qmlEngine ); desktopMode, noSound, qmlEngine );
QQmlComponent component( QString path = QStandardPaths::locate(
&qmlEngine, QUrl::fromLocalFile( "res/qml/mainwidget.qml" ) ); QStandardPaths::AppDataLocation,
QStringLiteral( "res/qml/mainwidget.qml" ) );
QQmlComponent component( &qmlEngine, QUrl::fromLocalFile( path ) );
auto errors = component.errors(); auto errors = component.errors();
for ( auto& e : errors ) for ( auto& e : errors )
{ {
+10 -8
View File
@@ -85,8 +85,9 @@ OverlayController::OverlayController( bool desktopMode,
<< focusChangedSoundFile; << focusChangedSoundFile;
} }
QString alarm01SoundFile = QApplication::applicationDirPath().append( QString alarm01SoundFile
"/res/sounds/alarm01.wav" ); = QStandardPaths::locate( QStandardPaths::AppDataLocation,
QStringLiteral( "res/sounds/alarm01.wav" ) );
QFileInfo alarm01SoundFileInfo( alarm01SoundFile ); QFileInfo alarm01SoundFileInfo( alarm01SoundFile );
if ( alarm01SoundFileInfo.exists() && alarm01SoundFileInfo.isFile() ) if ( alarm01SoundFileInfo.exists() && alarm01SoundFileInfo.isFile() )
{ {
@@ -433,13 +434,14 @@ void OverlayController::SetWidget( QQuickItem* quickItem,
m_ulOverlayHandle, vr::VROverlayInputMethod_Mouse ); m_ulOverlayHandle, vr::VROverlayInputMethod_Mouse );
vr::VROverlay()->SetOverlayFlag( vr::VROverlay()->SetOverlayFlag(
m_ulOverlayHandle, vr::VROverlayFlags_SendVRScrollEvents, true ); m_ulOverlayHandle, vr::VROverlayFlags_SendVRScrollEvents, true );
std::string thumbIconPath QString thumbIconPath
= QApplication::applicationDirPath().toStdString() = QStandardPaths::locate( QStandardPaths::AppDataLocation,
+ "\\res\\thumbicon.png"; QStringLiteral( "res/thumbicon.png" ) );
if ( QFile::exists( QString::fromStdString( thumbIconPath ) ) ) if ( QFile::exists( thumbIconPath ) )
{ {
vr::VROverlay()->SetOverlayFromFile( m_ulOverlayThumbnailHandle, vr::VROverlay()->SetOverlayFromFile(
thumbIconPath.c_str() ); m_ulOverlayThumbnailHandle,
thumbIconPath.toStdString().c_str() );
} }
else else
{ {
+32 -1
View File
@@ -183,6 +183,7 @@ MyStackViewPage {
} }
MyPushButton2 { MyPushButton2 {
id: steamvrSupersampleDecrement
text: "-" text: "-"
Layout.preferredWidth: 40 Layout.preferredWidth: 40
onClicked: { onClicked: {
@@ -192,7 +193,7 @@ MyStackViewPage {
MySlider { MySlider {
id: steamvrSupersamplingSlider id: steamvrSupersamplingSlider
from: 0.1 from: 0.2
to: 5.0 to: 5.0
stepSize: 0.1 stepSize: 0.1
value: 1.0 value: 1.0
@@ -208,6 +209,7 @@ MyStackViewPage {
} }
MyPushButton2 { MyPushButton2 {
id: steamvrSupersampleIncrement
text: "+" text: "+"
Layout.preferredWidth: 40 Layout.preferredWidth: 40
onClicked: { onClicked: {
@@ -239,6 +241,24 @@ MyStackViewPage {
} }
} }
} }
MyToggleButton {
id: steamvrAllowSupersampleOverrideToggle
text: "Enable Manual Supersampling Override"
onCheckedChanged: {
SteamVRTabController.setAllowSupersampleOverride(this.checked, false)
if(!this.checked){
steamvrSupersamplingText.enabled = false
steamvrSupersamplingSlider.enabled = false
steamvrSupersampleIncrement.enabled = false
steamvrSupersampleDecrement.enabled = false
}else{
steamvrSupersamplingText.enabled = true
steamvrSupersamplingSlider.enabled = true
steamvrSupersampleIncrement.enabled = true
steamvrSupersampleDecrement.enabled = true
}
}
}
MyToggleButton { MyToggleButton {
id: steamvrAllowSupersampleFilteringToggle id: steamvrAllowSupersampleFilteringToggle
text: "Enable Advanced Supersample Filtering" text: "Enable Advanced Supersample Filtering"
@@ -286,8 +306,16 @@ MyStackViewPage {
if (s1 <= steamvrSupersamplingSlider.to) { if (s1 <= steamvrSupersamplingSlider.to) {
steamvrSupersamplingSlider.value = s1 steamvrSupersamplingSlider.value = s1
} }
steamvrAllowSupersampleOverrideToggle.checked = SteamVRTabController.allowSupersampleOverride
steamvrAllowSupersampleFilteringToggle.checked = SteamVRTabController.allowSupersampleFiltering steamvrAllowSupersampleFilteringToggle.checked = SteamVRTabController.allowSupersampleFiltering
steamvrMotionSmoothingToggle.checked = SteamVRTabController.motionSmoothing steamvrMotionSmoothingToggle.checked = SteamVRTabController.motionSmoothing
if(!steamvrAllowSupersampleOverrideToggle.checked){
steamvrSupersamplingText.enabled = false
steamvrSupersamplingSlider.enabled = false
steamvrSupersampleIncrement.enabled = false
steamvrSupersampleDecrement.enabled = false
}
reloadSteamVRProfiles() reloadSteamVRProfiles()
} }
@@ -300,6 +328,9 @@ MyStackViewPage {
} }
steamvrSupersamplingText.text = s1 steamvrSupersamplingText.text = s1
} }
onAllowSupersampleOverrideChanged: {
steamvrAllowSupersampleOverrideToggle.checked = SteamVRTabController.allowSupersampleOverride
}
onAllowSupersampleFilteringChanged: { onAllowSupersampleFilteringChanged: {
steamvrAllowSupersampleFilteringToggle.checked = SteamVRTabController.allowSupersampleFiltering steamvrAllowSupersampleFilteringToggle.checked = SteamVRTabController.allowSupersampleFiltering
} }
+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 { Item {
Layout.fillHeight: true 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

+7 -6
View File
@@ -70,13 +70,14 @@ void AudioTabController::initStage2( OverlayController* var_parent,
notifKey.c_str(), notifKey.c_str(), &m_ulNotificationOverlayHandle ); notifKey.c_str(), notifKey.c_str(), &m_ulNotificationOverlayHandle );
if ( overlayError == vr::VROverlayError_None ) if ( overlayError == vr::VROverlayError_None )
{ {
std::string notifIconPath QString notifIconPath = QStandardPaths::locate(
= QApplication::applicationDirPath().toStdString() QStandardPaths::AppDataLocation,
+ "/res/qml/ptt_notification.png"; QStringLiteral( "/res/qml/ptt_notification.png" ) );
if ( QFile::exists( QString::fromStdString( notifIconPath ) ) ) if ( QFile::exists( notifIconPath ) )
{ {
vr::VROverlay()->SetOverlayFromFile( m_ulNotificationOverlayHandle, vr::VROverlay()->SetOverlayFromFile(
notifIconPath.c_str() ); m_ulNotificationOverlayHandle,
notifIconPath.toStdString().c_str() );
vr::VROverlay()->SetOverlayWidthInMeters( vr::VROverlay()->SetOverlayWidthInMeters(
m_ulNotificationOverlayHandle, 0.02f ); m_ulNotificationOverlayHandle, 0.02f );
vr::HmdMatrix34_t notificationTransform vr::HmdMatrix34_t notificationTransform
+5 -1
View File
@@ -15,8 +15,12 @@ public:
virtual void sendKeyboardInput( QString input ) = 0; virtual void sendKeyboardInput( QString input ) = 0;
virtual void sendKeyboardEnter() = 0; virtual void sendKeyboardEnter() = 0;
virtual void sendKeyboardBackspace( int count ) = 0; virtual void sendKeyboardBackspace( const int count ) = 0;
virtual void sendKeyboardAltTab() = 0; virtual void sendKeyboardAltTab() = 0;
virtual void sendMediaNextSong() = 0;
virtual void sendMediaPreviousSong() = 0;
virtual void sendMediaPausePlay() = 0;
virtual void sendMediaStopSong() = 0;
}; };
} // namespace advsettings } // namespace advsettings
+300 -204
View File
@@ -8,6 +8,7 @@ namespace advsettings
void SteamVRTabController::initStage1() void SteamVRTabController::initStage1()
{ {
initMotionSmoothing(); initMotionSmoothing();
initSupersampleOverride();
eventLoopTick(); eventLoopTick();
reloadSteamVRProfiles(); reloadSteamVRProfiles();
} }
@@ -19,106 +20,26 @@ void SteamVRTabController::initStage2( OverlayController* var_parent,
this->widget = var_widget; this->widget = var_widget;
} }
void SteamVRTabController::reloadSteamVRProfiles()
{
steamvrProfiles.clear();
auto settings = OverlayController::appSettings();
settings->beginGroup( "steamVRSettings" );
auto profileCount = settings->beginReadArray( "steamVRProfiles" );
for ( int i = 0; i < profileCount; i++ )
{
settings->setArrayIndex( i );
steamvrProfiles.emplace_back();
auto& entry = steamvrProfiles[i];
entry.profileName
= settings->value( "profileName" ).toString().toStdString();
entry.includesSupersampling
= settings->value( "includesSupersampling", false ).toBool();
if ( entry.includesSupersampling )
{
entry.supersampling
= settings->value( "supersampling", 1.0f ).toFloat();
}
entry.includesSupersampleFiltering
= settings->value( "includesSupersampleFiltering", false ).toBool();
if ( entry.includesSupersampleFiltering )
{
entry.supersampleFiltering
= settings->value( "supersampleFiltering", true ).toBool();
}
entry.includesMotionSmoothing
= settings->value( "includesMotionSmoothing", false ).toBool();
if ( entry.includesMotionSmoothing )
{
entry.motionSmooth
= settings->value( "motionSmooth", true ).toBool();
}
}
settings->endArray();
settings->endGroup();
}
void SteamVRTabController::saveSteamVRProfiles()
{
auto settings = OverlayController::appSettings();
settings->beginGroup( "steamVRSettings" );
settings->beginWriteArray( "steamVRProfiles" );
unsigned i = 0;
for ( auto& p : steamvrProfiles )
{
settings->setArrayIndex( i );
settings->setValue( "profileName",
QString::fromStdString( p.profileName ) );
settings->setValue( "includesSupersampling", p.includesSupersampling );
if ( p.includesSupersampling )
{
settings->setValue( "supersampling", p.supersampling );
}
settings->setValue( "includesSupersampleFiltering",
p.includesSupersampleFiltering );
if ( p.includesSupersampleFiltering )
{
settings->setValue( "supersampleFiltering",
p.supersampleFiltering );
}
settings->setValue( "includesMotionSmoothing",
p.includesMotionSmoothing );
if ( p.includesMotionSmoothing )
{
settings->setValue( "motionSmooth", p.motionSmooth );
}
i++;
}
settings->endArray();
settings->endGroup();
}
Q_INVOKABLE unsigned SteamVRTabController::getSteamVRProfileCount()
{
return ( unsigned ) steamvrProfiles.size();
}
Q_INVOKABLE QString
SteamVRTabController::getSteamVRProfileName( unsigned index )
{
if ( index >= steamvrProfiles.size() )
{
return QString();
}
else
{
return QString::fromStdString( steamvrProfiles[index].profileName );
}
}
// TODO DISC does it make any sense to check these settings once every ~
// 1second. primary purpose seems to be to re-sync settings if changed via other
// means?
void SteamVRTabController::eventLoopTick() void SteamVRTabController::eventLoopTick()
{ {
if ( settingsUpdateCounter >= 50 ) if ( settingsUpdateCounter >= 50 )
{ {
vr::EVRSettingsError vrSettingsError; vr::EVRSettingsError vrSettingsError;
// checks supersampling override and resynchs if necessry
// also prints error if can't find.
auto sso = vr::VRSettings()->GetBool(
vr::k_pch_SteamVR_Section,
vr::k_pch_SteamVR_SupersampleManualOverride_Bool,
&vrSettingsError );
if ( vrSettingsError != vr::VRSettingsError_None )
{
LOG( WARNING ) << "Could not read \""
<< vr::k_pch_SteamVR_SupersampleManualOverride_Bool
<< "\" setting: "
<< vr::VRSettings()->GetSettingsErrorNameFromEnum(
vrSettingsError );
}
setAllowSupersampleOverride( sso );
// checks supersampling and re-synchs if necessary // checks supersampling and re-synchs if necessary
auto ss = vr::VRSettings()->GetFloat( auto ss = vr::VRSettings()->GetFloat(
vr::k_pch_SteamVR_Section, vr::k_pch_SteamVR_Section,
@@ -185,10 +106,192 @@ void SteamVRTabController::eventLoopTick()
} }
} }
void SteamVRTabController::setSuperSampling( float value, bool notify ) /* -----------------------------------------*/
/*------------------------------------------*/
/*Profile Logic Functions*/
void SteamVRTabController::addSteamVRProfile(
const QString name,
const bool includeSupersampling,
const bool includeSupersampleFiltering,
const bool includeMotionSmoothing )
{
SteamVRProfile* profile = nullptr;
for ( auto& p : steamvrProfiles )
{
if ( p.profileName.compare( name.toStdString() ) == 0 )
{
profile = &p;
break;
}
}
if ( !profile )
{
auto i = steamvrProfiles.size();
steamvrProfiles.emplace_back();
profile = &steamvrProfiles[i];
}
profile->profileName = name.toStdString();
profile->includesSupersampling = includeSupersampling;
if ( includeSupersampling )
{
profile->supersampleOverride = m_allowSupersampleOverride;
profile->supersampling = m_superSampling;
}
profile->includesSupersampleFiltering = includeSupersampleFiltering;
if ( includeSupersampleFiltering )
{
profile->supersampleFiltering = m_allowSupersampleFiltering;
}
profile->includesMotionSmoothing = includeMotionSmoothing;
if ( includeMotionSmoothing )
{
profile->motionSmooth = m_motionSmoothing;
}
saveSteamVRProfiles();
OverlayController::appSettings()->sync();
emit steamVRProfilesUpdated();
emit steamVRProfileAdded();
}
void SteamVRTabController::applySteamVRProfile( const unsigned index )
{
if ( index < steamvrProfiles.size() )
{
auto& profile = steamvrProfiles[index];
if ( profile.includesSupersampling )
{
setAllowSupersampleOverride( profile.supersampleOverride );
setSuperSampling( profile.supersampling );
}
if ( profile.includesSupersampleFiltering )
{
setAllowSupersampleFiltering( profile.supersampleFiltering );
}
if ( profile.includesMotionSmoothing )
{
setMotionSmoothing( profile.motionSmooth );
}
vr::VRSettings()->Sync( true );
}
}
void SteamVRTabController::deleteSteamVRProfile( const unsigned index )
{
if ( index < steamvrProfiles.size() )
{
auto pos = steamvrProfiles.begin() + index;
steamvrProfiles.erase( pos );
saveSteamVRProfiles();
OverlayController::appSettings()->sync();
emit steamVRProfilesUpdated();
}
}
void SteamVRTabController::reloadSteamVRProfiles()
{
steamvrProfiles.clear();
auto settings = OverlayController::appSettings();
settings->beginGroup( "steamVRSettings" );
auto profileCount = settings->beginReadArray( "steamVRProfiles" );
for ( int i = 0; i < profileCount; i++ )
{
settings->setArrayIndex( i );
steamvrProfiles.emplace_back();
auto& entry = steamvrProfiles[i];
entry.profileName
= settings->value( "profileName" ).toString().toStdString();
entry.includesSupersampling
= settings->value( "includesSupersampling", false ).toBool();
if ( entry.includesSupersampling )
{
entry.supersampleOverride
= settings->value( "supersamplingOverride", false ).toBool();
entry.supersampling
= settings->value( "supersampling", 1.0f ).toFloat();
}
entry.includesSupersampleFiltering
= settings->value( "includesSupersampleFiltering", false ).toBool();
if ( entry.includesSupersampleFiltering )
{
entry.supersampleFiltering
= settings->value( "supersampleFiltering", true ).toBool();
}
entry.includesMotionSmoothing
= settings->value( "includesMotionSmoothing", false ).toBool();
if ( entry.includesMotionSmoothing )
{
entry.motionSmooth
= settings->value( "motionSmooth", true ).toBool();
}
}
settings->endArray();
settings->endGroup();
}
void SteamVRTabController::saveSteamVRProfiles()
{
auto settings = OverlayController::appSettings();
settings->beginGroup( "steamVRSettings" );
settings->beginWriteArray( "steamVRProfiles" );
unsigned i = 0;
for ( auto& p : steamvrProfiles )
{
settings->setArrayIndex( i );
settings->setValue( "profileName",
QString::fromStdString( p.profileName ) );
settings->setValue( "includesSupersampling", p.includesSupersampling );
if ( p.includesSupersampling )
{
settings->setValue( "supersamplingOverride",
p.supersampleOverride );
settings->setValue( "supersampling", p.supersampling );
}
settings->setValue( "includesSupersampleFiltering",
p.includesSupersampleFiltering );
if ( p.includesSupersampleFiltering )
{
settings->setValue( "supersampleFiltering",
p.supersampleFiltering );
}
settings->setValue( "includesMotionSmoothing",
p.includesMotionSmoothing );
if ( p.includesMotionSmoothing )
{
settings->setValue( "motionSmooth", p.motionSmooth );
}
i++;
}
settings->endArray();
settings->endGroup();
}
int SteamVRTabController::getSteamVRProfileCount()
{
return static_cast<int>( steamvrProfiles.size() );
}
QString SteamVRTabController::getSteamVRProfileName( const unsigned index )
{
if ( index >= steamvrProfiles.size() )
{
return QString();
}
else
{
return QString::fromStdString( steamvrProfiles[index].profileName );
}
}
/* -----------------------------------------*/
/*------------------------------------------*/
/*SuperSample (value) functions*/
void SteamVRTabController::setSuperSampling( float value, const bool notify )
{ {
bool override = false; bool override = false;
if ( value <= 0.01f ) // Mirrors Desktop Clamp
if ( value <= 0.2f )
{ {
LOG( WARNING ) << "Encountered a supersampling value <= 0.01, setting " LOG( WARNING ) << "Encountered a supersampling value <= 0.01, setting "
"supersampling to 1.0"; "supersampling to 1.0";
@@ -215,26 +318,16 @@ float SteamVRTabController::superSampling() const
{ {
return m_superSampling; return m_superSampling;
} }
/* -----------------------------------------*/
/*------------------------------------------*/
/*Motion Smoothing boolean/toggle functions*/
bool SteamVRTabController::motionSmoothing() const bool SteamVRTabController::motionSmoothing() const
{ {
return m_motionSmoothing; return m_motionSmoothing;
} }
/* void SteamVRTabController::setMotionSmoothing( const bool value,
Name: setMotionSmoothing const bool notify )
input: bool value, bool notify
output: none
@value - value to be passed to m_motionSMoothing
@notify - whether QML is informed of change (default true)
Description:
sets value of m_motionSmoothing AND applies changes in Steam/OpenVr
*/
void SteamVRTabController::setMotionSmoothing( bool value, bool notify )
{ {
if ( m_motionSmoothing != value ) if ( m_motionSmoothing != value )
{ {
@@ -250,21 +343,6 @@ void SteamVRTabController::setMotionSmoothing( bool value, bool notify )
} }
} }
/*
Name: initMotionSmoothing
input: none
output: none
description: Gets the Value of Motion Smoothing from Steam/OpenVR.
Sets the member variable of motion smoothing for UI
Adds error to log if unable to find variable.
OnError:
default value of true for motion smoothing is initialized, if user has disabled
motion smoothing on their end, they will have to toggle the variable twice to
get it to re-sync.
*/
void SteamVRTabController::initMotionSmoothing() void SteamVRTabController::initMotionSmoothing()
{ {
bool temporary = false; bool temporary = false;
@@ -285,13 +363,17 @@ void SteamVRTabController::initMotionSmoothing()
setMotionSmoothing( temporary, true ); setMotionSmoothing( temporary, true );
} }
/* -----------------------------------------*/
/*------------------------------------------*/
/*Supersample Filtering boolean/toggle functions*/
bool SteamVRTabController::allowSupersampleFiltering() const bool SteamVRTabController::allowSupersampleFiltering() const
{ {
return m_allowSupersampleFiltering; return m_allowSupersampleFiltering;
} }
void SteamVRTabController::setAllowSupersampleFiltering( bool value, void SteamVRTabController::setAllowSupersampleFiltering( const bool value,
bool notify ) const bool notify )
{ {
if ( m_allowSupersampleFiltering != value ) if ( m_allowSupersampleFiltering != value )
{ {
@@ -309,6 +391,56 @@ void SteamVRTabController::setAllowSupersampleFiltering( bool value,
} }
} }
/* -----------------------------------------*/
/*------------------------------------------*/
/*SuperSampling Override boolean/toggle functions*/
bool SteamVRTabController::allowSupersampleOverride() const
{
return m_allowSupersampleOverride;
}
void SteamVRTabController::setAllowSupersampleOverride( const bool value,
const bool notify )
{
if ( m_allowSupersampleOverride != value )
{
m_allowSupersampleOverride = value;
vr::VRSettings()->SetBool(
vr::k_pch_SteamVR_Section,
vr::k_pch_SteamVR_SupersampleManualOverride_Bool,
m_allowSupersampleOverride );
vr::VRSettings()->Sync();
if ( notify )
{
emit allowSupersampleOverrideChanged( m_allowSupersampleOverride );
}
}
}
void SteamVRTabController::initSupersampleOverride()
{
bool temporary = false;
vr::EVRSettingsError vrSettingsError;
temporary = vr::VRSettings()->GetBool(
vr::k_pch_SteamVR_Section,
vr::k_pch_SteamVR_SupersampleManualOverride_Bool,
&vrSettingsError );
if ( vrSettingsError != vr::VRSettingsError_None )
{
LOG( WARNING ) << "Could not get SuperSampling Override State \""
<< vr::k_pch_SteamVR_SupersampleManualOverride_Bool
<< "\" setting: "
<< vr::VRSettings()->GetSettingsErrorNameFromEnum(
vrSettingsError );
}
vr::VRSettings()->Sync();
setAllowSupersampleOverride( temporary, true );
}
/*------------------------------------------*/
/* -----------------------------------------*/
void SteamVRTabController::reset() void SteamVRTabController::reset()
{ {
vr::EVRSettingsError vrSettingsError; vr::EVRSettingsError vrSettingsError;
@@ -339,6 +471,45 @@ void SteamVRTabController::reset()
vrSettingsError ); vrSettingsError );
} }
vr::VRSettings()->RemoveKeyInSection(
vr::k_pch_SteamVR_Section,
vr::k_pch_SteamVR_SupersampleManualOverride_Bool,
&vrSettingsError );
if ( vrSettingsError != vr::VRSettingsError_None )
{
LOG( WARNING ) << "Could not remove \""
<< vr::k_pch_SteamVR_SupersampleManualOverride_Bool
<< "\" setting: "
<< vr::VRSettings()->GetSettingsErrorNameFromEnum(
vrSettingsError );
}
vr::VRSettings()->RemoveKeyInSection(
vr::k_pch_SteamVR_Section,
vr::k_pch_SteamVR_SupersampleScale_Float,
&vrSettingsError );
if ( vrSettingsError != vr::VRSettingsError_None )
{
LOG( WARNING ) << "Could not remove \""
<< vr::k_pch_SteamVR_SupersampleScale_Float
<< "\" setting: "
<< vr::VRSettings()->GetSettingsErrorNameFromEnum(
vrSettingsError );
}
vr::VRSettings()->RemoveKeyInSection(
vr::k_pch_SteamVR_Section,
vr::k_pch_SteamVR_MotionSmoothing_Bool,
&vrSettingsError );
if ( vrSettingsError != vr::VRSettingsError_None )
{
LOG( WARNING ) << "Could not remove \""
<< vr::k_pch_SteamVR_MotionSmoothing_Bool
<< "\" setting: "
<< vr::VRSettings()->GetSettingsErrorNameFromEnum(
vrSettingsError );
}
vr::VRSettings()->Sync(); vr::VRSettings()->Sync();
settingsUpdateCounter = 999; // Easiest way to get default values settingsUpdateCounter = 999; // Easiest way to get default values
} }
@@ -352,79 +523,4 @@ void SteamVRTabController::restartSteamVR()
QProcess::startDetached( cmd ); QProcess::startDetached( cmd );
} }
void SteamVRTabController::addSteamVRProfile( QString name,
bool includeSupersampling,
bool includeSupersampleFiltering,
bool includeMotionSmoothing )
{
SteamVRProfile* profile = nullptr;
for ( auto& p : steamvrProfiles )
{
if ( p.profileName.compare( name.toStdString() ) == 0 )
{
profile = &p;
break;
}
}
if ( !profile )
{
auto i = steamvrProfiles.size();
steamvrProfiles.emplace_back();
profile = &steamvrProfiles[i];
}
profile->profileName = name.toStdString();
profile->includesSupersampling = includeSupersampling;
if ( includeSupersampling )
{
profile->supersampling = m_superSampling;
}
profile->includesSupersampleFiltering = includeSupersampleFiltering;
if ( includeSupersampleFiltering )
{
profile->supersampleFiltering = m_allowSupersampleFiltering;
}
profile->includesMotionSmoothing = includeMotionSmoothing;
if ( includeMotionSmoothing )
{
profile->motionSmooth = m_motionSmoothing;
}
saveSteamVRProfiles();
OverlayController::appSettings()->sync();
emit steamVRProfilesUpdated();
emit steamVRProfileAdded();
}
void SteamVRTabController::applySteamVRProfile( unsigned index )
{
if ( index < steamvrProfiles.size() )
{
auto& profile = steamvrProfiles[index];
if ( profile.includesSupersampling )
{
setSuperSampling( profile.supersampling );
}
if ( profile.includesSupersampleFiltering )
{
setAllowSupersampleFiltering( profile.supersampleFiltering );
}
if ( profile.includesMotionSmoothing )
{
setMotionSmoothing( profile.motionSmooth );
}
vr::VRSettings()->Sync( true );
}
}
void SteamVRTabController::deleteSteamVRProfile( unsigned index )
{
if ( index < steamvrProfiles.size() )
{
auto pos = steamvrProfiles.begin() + index;
steamvrProfiles.erase( pos );
saveSteamVRProfiles();
OverlayController::appSettings()->sync();
emit steamVRProfilesUpdated();
}
}
} // namespace advsettings } // namespace advsettings
+10 -9
View File
@@ -21,6 +21,7 @@ struct SteamVRProfile
bool supersampleFiltering = false; bool supersampleFiltering = false;
bool motionSmooth = false; bool motionSmooth = false;
bool includesMotionSmoothing = false; bool includesMotionSmoothing = false;
bool supersampleOverride = false;
}; };
class SteamVRTabController : public QObject class SteamVRTabController : public QObject
@@ -28,27 +29,27 @@ class SteamVRTabController : public QObject
Q_OBJECT Q_OBJECT
Q_PROPERTY( float superSampling READ superSampling WRITE setSuperSampling Q_PROPERTY( float superSampling READ superSampling WRITE setSuperSampling
NOTIFY superSamplingChanged ) NOTIFY superSamplingChanged )
/*Q_PROPERTY(
float compositorSuperSampling READ compositorSuperSampling WRITE
setCompositorSuperSampling NOTIFY compositorSuperSamplingChanged )
*/
Q_PROPERTY( bool motionSmoothing READ motionSmoothing WRITE Q_PROPERTY( bool motionSmoothing READ motionSmoothing WRITE
setMotionSmoothing NOTIFY motionSmoothingChanged ) setMotionSmoothing NOTIFY motionSmoothingChanged )
Q_PROPERTY( bool allowSupersampleFiltering READ allowSupersampleFiltering Q_PROPERTY( bool allowSupersampleFiltering READ allowSupersampleFiltering
WRITE setAllowSupersampleFiltering NOTIFY WRITE setAllowSupersampleFiltering NOTIFY
allowSupersampleFilteringChanged ) allowSupersampleFilteringChanged )
Q_PROPERTY(
bool allowSupersampleOverride READ allowSupersampleOverride WRITE
setAllowSupersampleOverride NOTIFY allowSupersampleOverrideChanged )
private: private:
OverlayController* parent; OverlayController* parent;
QQuickWindow* widget; QQuickWindow* widget;
float m_superSampling = 1.0; float m_superSampling = 1.0;
// float m_compositorSuperSampling = 1.0;
bool m_motionSmoothing = true; bool m_motionSmoothing = true;
bool m_allowSupersampleFiltering = true; bool m_allowSupersampleFiltering = true;
bool m_allowSupersampleOverride = false;
void initMotionSmoothing(); void initMotionSmoothing();
void initSupersampleOverride();
std::vector<SteamVRProfile> steamvrProfiles; std::vector<SteamVRProfile> steamvrProfiles;
@@ -61,21 +62,21 @@ public:
void eventLoopTick(); void eventLoopTick();
float superSampling() const; float superSampling() const;
// float compositorSuperSampling() const;
bool motionSmoothing() const; bool motionSmoothing() const;
bool allowSupersampleFiltering() const; bool allowSupersampleFiltering() const;
bool allowSupersampleOverride() const;
void reloadSteamVRProfiles(); void reloadSteamVRProfiles();
void saveSteamVRProfiles(); void saveSteamVRProfiles();
Q_INVOKABLE unsigned getSteamVRProfileCount(); Q_INVOKABLE int getSteamVRProfileCount();
Q_INVOKABLE QString getSteamVRProfileName( unsigned index ); Q_INVOKABLE QString getSteamVRProfileName( unsigned index );
public slots: public slots:
void setSuperSampling( float value, bool notify = true ); void setSuperSampling( float value, bool notify = true );
// void setCompositorSuperSampling( float value, bool notify = true );
void setMotionSmoothing( bool value, bool notify = true ); void setMotionSmoothing( bool value, bool notify = true );
void setAllowSupersampleFiltering( bool value, bool notify = true ); void setAllowSupersampleFiltering( bool value, bool notify = true );
void setAllowSupersampleOverride( bool value, bool notify = true );
void addSteamVRProfile( QString name, void addSteamVRProfile( QString name,
bool includeSupersampling, bool includeSupersampling,
@@ -89,9 +90,9 @@ public slots:
signals: signals:
void superSamplingChanged( float value ); void superSamplingChanged( float value );
// void compositorSuperSamplingChanged( float value );
void motionSmoothingChanged( bool value ); void motionSmoothingChanged( bool value );
void allowSupersampleFilteringChanged( bool value ); void allowSupersampleFilteringChanged( bool value );
void allowSupersampleOverrideChanged( bool value );
void steamVRProfilesUpdated(); void steamVRProfilesUpdated();
void steamVRProfileAdded(); void steamVRProfileAdded();
+37 -17
View File
@@ -83,11 +83,28 @@ void UtilitiesTabController::sendKeyboardAltTab()
keyboardInput->sendKeyboardAltTab(); keyboardInput->sendKeyboardAltTab();
} }
void UtilitiesTabController::sendKeyboardBackspace( int count ) void UtilitiesTabController::sendKeyboardBackspace( const int count )
{ {
keyboardInput->sendKeyboardBackspace( 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 bool UtilitiesTabController::alarmEnabled() const
{ {
return m_alarmEnabled; return m_alarmEnabled;
@@ -250,6 +267,14 @@ void UtilitiesTabController::modAlarmTimeMinute( int value, bool notify )
} }
} }
QString getBatteryIconPath( int batteryState )
{
QString batteryPrefix = "/res/battery_";
return QStandardPaths::locate(
QStandardPaths::AppDataLocation,
batteryPrefix + QString::number( batteryState ) + ".png" );
}
vr::VROverlayHandle_t createBatteryOverlay( vr::TrackedDeviceIndex_t index ) vr::VROverlayHandle_t createBatteryOverlay( vr::TrackedDeviceIndex_t index )
{ {
vr::VROverlayHandle_t handle = vr::k_ulOverlayHandleInvalid; vr::VROverlayHandle_t handle = vr::k_ulOverlayHandleInvalid;
@@ -259,13 +284,11 @@ vr::VROverlayHandle_t createBatteryOverlay( vr::TrackedDeviceIndex_t index )
batteryKey.c_str(), batteryKey.c_str(), &handle ); batteryKey.c_str(), batteryKey.c_str(), &handle );
if ( overlayError == vr::VROverlayError_None ) if ( overlayError == vr::VROverlayError_None )
{ {
std::string batteryIconPath QString batteryIconPath = getBatteryIconPath( 0 );
= QApplication::applicationDirPath().toStdString() if ( QFile::exists( batteryIconPath ) )
+ "/res/battery_0.png";
if ( QFile::exists( QString::fromStdString( batteryIconPath ) ) )
{ {
vr::VROverlay()->SetOverlayFromFile( handle, vr::VROverlay()->SetOverlayFromFile(
batteryIconPath.c_str() ); handle, batteryIconPath.toStdString().c_str() );
vr::VROverlay()->SetOverlayWidthInMeters( handle, 0.05f ); vr::VROverlay()->SetOverlayWidthInMeters( handle, 0.05f );
vr::HmdMatrix34_t notificationTransform vr::HmdMatrix34_t notificationTransform
= { { { 1.0f, 0.0f, 0.0f, 0.00f }, = { { { 1.0f, 0.0f, 0.0f, 0.00f },
@@ -469,8 +492,7 @@ void UtilitiesTabController::eventLoopTick()
if ( deviceClass if ( deviceClass
== vr::ETrackedDeviceClass::TrackedDeviceClass_GenericTracker ) == vr::ETrackedDeviceClass::TrackedDeviceClass_GenericTracker )
{ {
if ( m_batteryOverlayHandles[i] if ( m_batteryOverlayHandles[i] == 0 )
== vr::k_ulOverlayHandleInvalid )
{ {
LOG( INFO ) << "Creating battery overlay for device " << i; LOG( INFO ) << "Creating battery overlay for device " << i;
m_batteryOverlayHandles[i] = createBatteryOverlay( i ); m_batteryOverlayHandles[i] = createBatteryOverlay( i );
@@ -512,17 +534,15 @@ void UtilitiesTabController::eventLoopTick()
{ {
LOG( INFO ) LOG( INFO )
<< "Updating battery overlay for device " << i << "Updating battery overlay for device " << i
<< " to " << batteryState << "(" << battery << ""; << " to " << batteryState << "(" << battery << ")"
std::string batteryIconPath << QString::number( m_batteryOverlayHandles[i] );
= QApplication::applicationDirPath().toStdString() QString batteryIconPath
+ "/res/battery_" + std::to_string( batteryState ) = getBatteryIconPath( batteryState );
+ ".png"; if ( QFile::exists( batteryIconPath ) )
if ( QFile::exists(
QString::fromStdString( batteryIconPath ) ) )
{ {
vr::VROverlay()->SetOverlayFromFile( vr::VROverlay()->SetOverlayFromFile(
m_batteryOverlayHandles[i], m_batteryOverlayHandles[i],
batteryIconPath.c_str() ); batteryIconPath.toStdString().c_str() );
} }
else else
{ {
+7 -3
View File
@@ -44,8 +44,8 @@ private:
QTime m_alarmLastCheckTime; QTime m_alarmLastCheckTime;
bool m_steamDesktopOverlayAvailable = false; bool m_steamDesktopOverlayAvailable = false;
float m_steamDesktopOverlayWidth = 4.0f; float m_steamDesktopOverlayWidth = 4.0f;
vr::VROverlayHandle_t vr::VROverlayHandle_t m_batteryOverlayHandles[vr::k_unMaxTrackedDeviceCount]
m_batteryOverlayHandles[vr::k_unMaxTrackedDeviceCount]; = { 0 };
int m_batteryState[vr::k_unMaxTrackedDeviceCount]; int m_batteryState[vr::k_unMaxTrackedDeviceCount];
bool m_batteryVisible[vr::k_unMaxTrackedDeviceCount]; bool m_batteryVisible[vr::k_unMaxTrackedDeviceCount];
@@ -69,7 +69,11 @@ public slots:
void sendKeyboardInput( QString input ); void sendKeyboardInput( QString input );
void sendKeyboardEnter(); void sendKeyboardEnter();
void sendKeyboardAltTab(); 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 setAlarmEnabled( bool enabled, bool notify = true );
void setAlarmIsModal( bool modal, bool notify = true ); void setAlarmIsModal( bool modal, bool notify = true );
@@ -17,7 +17,7 @@ void KeyboardInputDummy::sendKeyboardEnter()
// noop // noop
} }
void KeyboardInputDummy::sendKeyboardBackspace( int count ) void KeyboardInputDummy::sendKeyboardBackspace( const int count )
{ {
// noop // noop
UNREFERENCED_PARAMETER( count ); UNREFERENCED_PARAMETER( count );
@@ -28,4 +28,24 @@ void KeyboardInputDummy::sendKeyboardAltTab()
// noop // noop
} }
void KeyboardInputDummy::sendMediaNextSong()
{
// noop
}
void KeyboardInputDummy::sendMediaPreviousSong()
{
// noop
}
void KeyboardInputDummy::sendMediaPausePlay()
{
// noop
}
void KeyboardInputDummy::sendMediaStopSong()
{
// noop
}
} // namespace advsettings } // namespace advsettings
@@ -11,8 +11,12 @@ class KeyboardInputDummy : public KeyboardInput
public: public:
virtual void sendKeyboardInput( QString input ) override; virtual void sendKeyboardInput( QString input ) override;
virtual void sendKeyboardEnter() override; virtual void sendKeyboardEnter() override;
virtual void sendKeyboardBackspace( int count ) override; virtual void sendKeyboardBackspace( const int count ) override;
virtual void sendKeyboardAltTab() override; virtual void sendKeyboardAltTab() override;
virtual void sendMediaNextSong() override;
virtual void sendMediaPreviousSong() override;
virtual void sendMediaPausePlay() override;
virtual void sendMediaStopSong() override;
}; };
} // namespace advsettings } // namespace advsettings
@@ -4,6 +4,20 @@
namespace advsettings 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 ) void fillKiStruct( INPUT& ip, WORD scanCode, bool keyup )
{ {
ip.type = INPUT_KEYBOARD; ip.type = INPUT_KEYBOARD;
@@ -21,11 +35,18 @@ void fillKiStruct( INPUT& ip, WORD scanCode, bool keyup )
ip.ki.time = 0; 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 ) const auto success
&& !SendInput( = SendInput( static_cast<UINT>( inputCount ), input, sizeof( INPUT ) );
static_cast<UINT>( inputCount ), input, sizeof( INPUT ) ) )
if ( ( inputCount > 0 ) && !success )
{ {
char* err; char* err;
auto errCode = GetLastError(); 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 ) void KeyboardInputWindows::sendKeyboardInput( QString input )
{ {
int len = input.length(); int len = input.length();
@@ -122,44 +199,85 @@ void KeyboardInputWindows::sendKeyboardInput( QString input )
} }
} }
/*!
Sends a single Enter keypress to Windows.
*/
void KeyboardInputWindows::sendKeyboardEnter() void KeyboardInputWindows::sendKeyboardEnter()
{ {
LPINPUT ips = new INPUT[2]; sendKeyPressAndRelease( VK_RETURN );
fillKiStruct( ips[0], VK_RETURN, false );
fillKiStruct( ips[1], VK_RETURN, true );
sendKeyboardInputRaw( 2, ips );
delete[] ips;
} }
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. return;
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 );
}
sendKeyboardInputRaw( count * 2, ips ); for ( int presses = 0; presses < count; ++presses )
delete[] ips; {
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() void KeyboardInputWindows::sendKeyboardAltTab()
{ {
LPINPUT ips = new INPUT[4]; // VK_MENU is Alt.
// VK_MENU is alt const auto pressAlt = createInputStruct( VK_MENU, KeyStatus::Down );
fillKiStruct( ips[0], VK_MENU, false ); const auto pressTab = createInputStruct( VK_TAB, KeyStatus::Down );
fillKiStruct( ips[1], VK_TAB, false ); const auto releaseAlt = createInputStruct( VK_MENU, KeyStatus::Up );
fillKiStruct( ips[2], VK_TAB, true ); const auto releaseTab = createInputStruct( VK_TAB, KeyStatus::Up );
fillKiStruct( ips[3], VK_MENU, true );
sendKeyboardInputRaw( 4, ips ); constexpr auto numberOfActions = 4;
delete[] ips;
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 } // namespace advsettings
@@ -11,8 +11,12 @@ class KeyboardInputWindows : public KeyboardInput
public: public:
virtual void sendKeyboardInput( QString input ) override; virtual void sendKeyboardInput( QString input ) override;
virtual void sendKeyboardEnter() override; virtual void sendKeyboardEnter() override;
virtual void sendKeyboardBackspace( int count ) override; virtual void sendKeyboardBackspace( const int count ) override;
virtual void sendKeyboardAltTab() override; virtual void sendKeyboardAltTab() override;
virtual void sendMediaNextSong() override;
virtual void sendMediaPreviousSong() override;
virtual void sendMediaPausePlay() override;
virtual void sendMediaStopSong() override;
}; };
} // namespace advsettings } // namespace advsettings