Added keyboard input utilities. Allows to send text input as emulated key strokes to the active application.

This commit is contained in:
2016-12-29 16:02:17 +01:00
parent 950ebf5b08
commit 93484d2bdc
8 changed files with 307 additions and 0 deletions
+2
View File
@@ -22,6 +22,7 @@ SOURCES += src/main.cpp\
src/tabcontrollers/StatisticsTabController.cpp \
src/tabcontrollers/SteamVRTabController.cpp \
src/tabcontrollers/ReviveTabController.cpp \
src/tabcontrollers/UtilitiesTabController.cpp \
src/tabcontrollers/audiomanager/AudioManagerWindows.cpp
HEADERS += src/overlaycontroller.h \
@@ -34,6 +35,7 @@ HEADERS += src/overlaycontroller.h \
src/tabcontrollers/StatisticsTabController.h \
src/tabcontrollers/SteamVRTabController.h \
src/tabcontrollers/ReviveTabController.h \
src/tabcontrollers/UtilitiesTabController.h \
src/tabcontrollers/AudioManager.h \
src/tabcontrollers/audiomanager/AudioManagerWindows.h
+11
View File
@@ -89,6 +89,17 @@ MyStackViewPage {
}
}
MyPushButton {
id: utilitiesButton
activationSoundEnabled: false
text: "Utilities"
Layout.fillWidth: true
onClicked: {
MyResources.playFocusChangedSound()
mainView.push(utilitiesPage)
}
}
MyPushButton {
id: statisticsButton
activationSoundEnabled: false
+98
View File
@@ -0,0 +1,98 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import matzman666.advsettings 1.0
MyStackViewPage {
headerText: "Utilities"
content: ColumnLayout {
spacing: 18
GroupBox {
Layout.fillWidth: true
label: MyText {
leftPadding: 10
text: "Keyboard Utilities"
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
}
MyText {
text: "Send to active application: "
}
RowLayout {
spacing: 18
MyPushButton {
text: "VR Keyboard Input"
Layout.preferredWidth: 260
onClicked: {
OverlayController.showKeyboard("", 601)
}
Connections {
target: OverlayController
onKeyBoardInputSignal: {
if (userValue == 601) {
UtilitiesTabController.sendKeyboardInput(input)
}
}
}
}
MyPushButton {
text: "Enter"
Layout.preferredWidth: 260
onClicked: {
UtilitiesTabController.sendKeyboardEnter()
}
}
MyPushButton {
text: "Backspace"
Layout.preferredWidth: 260
onClicked: {
UtilitiesTabController.sendKeyboardBackspace(1)
}
}
MyPushButton {
text: "10x Backspace"
Layout.preferredWidth: 260
onClicked: {
UtilitiesTabController.sendKeyboardBackspace(10)
}
}
}
}
}
Item {
Layout.fillHeight: true
}
/*Component.onCompleted: {
}
Connections {
}*/
}
}
+5
View File
@@ -53,6 +53,11 @@ Rectangle {
visible: false
}
property UtilitiesPage utilitiesPage: UtilitiesPage {
stackView: mainView
visible: false
}
StackView {
id: mainView
anchors.fill: parent
+7
View File
@@ -86,6 +86,7 @@ void OverlayController::Init(QQmlEngine* qmlEngine) {
statisticsTabController.initStage1();
settingsTabController.initStage1();
reviveTabController.initStage1();
utilitiesTabController.initStage1();
// Set qml context
qmlEngine->rootContext()->setContextProperty("applicationVersion", getVersionString());
@@ -137,6 +138,11 @@ void OverlayController::Init(QQmlEngine* qmlEngine) {
QQmlEngine::setObjectOwnership(obj, QQmlEngine::CppOwnership);
return obj;
});
qmlRegisterSingletonType<SteamVRTabController>("matzman666.advsettings", 1, 0, "UtilitiesTabController", [](QQmlEngine*, QJSEngine*) {
QObject* obj = &getInstance()->utilitiesTabController;
QQmlEngine::setObjectOwnership(obj, QQmlEngine::CppOwnership);
return obj;
});
}
void OverlayController::Shutdown() {
@@ -221,6 +227,7 @@ void OverlayController::SetWidget(QQuickItem* quickItem, const std::string& name
statisticsTabController.initStage2(this, m_pWindow.get());
settingsTabController.initStage2(this, m_pWindow.get());
reviveTabController.initStage2(this, m_pWindow.get());
utilitiesTabController.initStage2(this, m_pWindow.get());
}
+2
View File
@@ -30,6 +30,7 @@
#include "tabcontrollers/StatisticsTabController.h"
#include "tabcontrollers/SettingsTabController.h"
#include "tabcontrollers/ReviveTabController.h"
#include "tabcontrollers/UtilitiesTabController.h"
@@ -76,6 +77,7 @@ public: // I know it's an ugly hack to make them public to enable external acces
StatisticsTabController statisticsTabController;
SettingsTabController settingsTabController;
ReviveTabController reviveTabController;
UtilitiesTabController utilitiesTabController;
private:
OverlayController(bool desktopMode, bool noSound) : QObject(), desktopMode(desktopMode), noSound(noSound) {}
@@ -0,0 +1,149 @@
#include "UtilitiesTabController.h"
#include <QQuickWindow>
#include "../overlaycontroller.h"
#include <Windows.h>
// application namespace
namespace advsettings {
void UtilitiesTabController::initStage1() {
}
void UtilitiesTabController::initStage2(OverlayController * parent, QQuickWindow * widget) {
this->parent = parent;
this->widget = widget;
}
void fillKiStruct(INPUT& ip, WORD scanCode, bool keyup) {
ip.type = INPUT_KEYBOARD;
ip.ki.wVk = scanCode;
if (keyup) {
ip.ki.dwFlags = KEYEVENTF_KEYUP;
} else {
ip.ki.dwFlags = 0;
}
ip.ki.wScan = 0;
ip.ki.dwExtraInfo = 0;
ip.ki.time = 0;
};
void UtilitiesTabController::sendKeyboardInput(QString input) {
int len = input.length();
if (len > 0) {
LPINPUT ips = new INPUT[len * 5 + 3];
unsigned ii = 0;
bool shiftPressed = false;
bool ctrlPressed = false;
bool altPressed = false;
for (int i = 0; i < len; i++) {
short tmp = VkKeyScan(input.at(i).toLatin1());
WORD c = tmp & 0xFF;
short shiftState = tmp >> 8;
bool isShift = shiftState & 1;
bool isCtrl = shiftState & 2;
bool isAlt = shiftState & 4;
if (isShift && !shiftPressed) {
fillKiStruct(ips[ii++], VK_SHIFT, false);
shiftPressed = true;
} else if (!isShift && shiftPressed) {
fillKiStruct(ips[ii++], VK_SHIFT, true);
shiftPressed = false;
}
if (isCtrl && !ctrlPressed) {
fillKiStruct(ips[ii++], VK_CONTROL, false);
ctrlPressed = true;
} else if (!isCtrl && ctrlPressed) {
fillKiStruct(ips[ii++], VK_CONTROL, true);
ctrlPressed = false;
}
if (isAlt && !altPressed) {
fillKiStruct(ips[ii++], VK_MENU, false);
altPressed = true;
} else if (!isAlt && altPressed) {
fillKiStruct(ips[ii++], VK_MENU, true);
altPressed = false;
}
fillKiStruct(ips[ii++], c, false);
fillKiStruct(ips[ii++], c, true);
}
if (shiftPressed) {
fillKiStruct(ips[ii++], VK_SHIFT, true);
shiftPressed = false;
}
if (ctrlPressed) {
fillKiStruct(ips[ii++], VK_CONTROL, true);
ctrlPressed = false;
}
if (altPressed) {
fillKiStruct(ips[ii++], VK_MENU, true);
altPressed = false;
}
if (!SendInput(ii, ips, sizeof(INPUT))) {
char *err;
auto errCode = GetLastError();
if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
errCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
(LPTSTR)&err,
0,
NULL)){
LOG(ERROR) << "Error calling SendInput(): Could not get error message (" << errCode << ")";
} else {
LOG(ERROR) << "Error calling SendInput(): " << err;
}
}
delete ips;
}
}
void UtilitiesTabController::sendKeyboardEnter() {
LPINPUT ips = new INPUT[2];
fillKiStruct(ips[0], VK_RETURN, false);
fillKiStruct(ips[1], VK_RETURN, true);
if (!SendInput(2, ips, sizeof(INPUT))) {
char *err;
auto errCode = GetLastError();
if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
errCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
(LPTSTR)&err,
0,
NULL)) {
LOG(ERROR) << "Error calling SendInput(): Could not get error message (" << errCode << ")";
} else {
LOG(ERROR) << "Error calling SendInput(): " << err;
}
}
delete ips;
}
void UtilitiesTabController::sendKeyboardBackspace(int count) {
if (count > 0) {
LPINPUT ips = new INPUT[count * 2];
for (int i = 0; i < count; i++) {
fillKiStruct(ips[2 * i], VK_BACK, false);
fillKiStruct(ips[2 * i + 1], VK_BACK, true);
}
if (!SendInput(2 * count, ips, sizeof(INPUT))) {
char *err;
auto errCode = GetLastError();
if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
errCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
(LPTSTR)&err,
0,
NULL)) {
LOG(ERROR) << "Error calling SendInput(): Could not get error message (" << errCode << ")";
} else {
LOG(ERROR) << "Error calling SendInput(): " << err;
}
}
delete ips;
}
}
} // namespace advconfig
@@ -0,0 +1,33 @@
#pragma once
#include <QObject>
class QQuickWindow;
// application namespace
namespace advsettings {
// forward declaration
class OverlayController;
class UtilitiesTabController : public QObject {
Q_OBJECT
private:
OverlayController* parent;
QQuickWindow* widget;
public:
void initStage1();
void initStage2(OverlayController* parent, QQuickWindow* widget);
public slots:
void sendKeyboardInput(QString input);
void sendKeyboardEnter();
void sendKeyboardBackspace(int count);
signals:
};
} // namespace advsettings