Added keyboard input utilities. Allows to send text input as emulated key strokes to the active application.
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
}*/
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,11 @@ Rectangle {
|
||||
visible: false
|
||||
}
|
||||
|
||||
property UtilitiesPage utilitiesPage: UtilitiesPage {
|
||||
stackView: mainView
|
||||
visible: false
|
||||
}
|
||||
|
||||
StackView {
|
||||
id: mainView
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user