From 3f1a07198cd571a5f1ef7d4563e864dfefdaa020 Mon Sep 17 00:00:00 2001 From: khyryra <122237202+khyryra@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:15:37 -0700 Subject: [PATCH 1/5] Add virtual keyboard support --- .gitignore | 8 ++++ resources/qml/appload.qml | 1 + resources/qml/window.qml | 64 +++++++++++++++++++++++++++ shim/src/input-shim.cpp | 93 ++++++++++++++++++++++++++++++++++++++- shim/src/shim.cpp | 17 ++++++- src/AppLibrary.h | 9 +++- src/library.h | 2 + src/libraryexternals.cpp | 7 ++- src/qtfb/FBController.cpp | 14 ++++-- src/qtfb/FBController.h | 2 + src/qtfb/common.h | 3 ++ xovi/make.sh | 0 12 files changed, 209 insertions(+), 11 deletions(-) mode change 100644 => 100755 xovi/make.sh diff --git a/.gitignore b/.gitignore index 09172ff..622b687 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,11 @@ **/*.qmd-pure /applications_root/ /testing_extensions/ +/.vscode +/shim/build +/xovi/* +!/xovi/template +!/xovi/make.sh +.qmake.stash +appload +Makefile diff --git a/resources/qml/appload.qml b/resources/qml/appload.qml index 5dffbe8..a5213ba 100644 --- a/resources/qml/appload.qml +++ b/resources/qml/appload.qml @@ -179,6 +179,7 @@ Rectangle { win.appName = modelData.name; win.supportsScaling = modelData.supportsScaling; + win.supportsVirtualKeyboard = modelData.supportsVirtualKeyboard; win.disablesWindowedMode = modelData.disablesWindowedMode; win.globalWidth = Qt.binding(function() { return _appLoadView.width; }) diff --git a/resources/qml/window.qml b/resources/qml/window.qml index 73de1b7..4f158fd 100644 --- a/resources/qml/window.qml +++ b/resources/qml/window.qml @@ -24,6 +24,7 @@ FocusScope { property alias appName: _appName.text property bool supportsScaling: false + property bool supportsVirtualKeyboard: false property var qtfbKey: -1 property int appPid: -1 property bool minimized: false @@ -288,6 +289,69 @@ FocusScope { } } + Rectangle { + id: virtualKeyboardButton + width: parent.height + height: parent.height + anchors.left: parent.left + border.width: 2 + border.color: "black" + color: parent.color + visible: supportsVirtualKeyboard + + TextArea { + anchors.fill: parent + color: "white" + text: " " + + function sendVirtualKeyCode(code) { + windowCanvas.virtualKeyboardKeyDown(code); + + // Hold each key press for 100 milliseconds + (function(c) { + var t = Qt.createQmlObject('import QtQuick 2.5; Timer { interval: 100; repeat: false }', virtualKeyboardButton); + + t.triggered.connect(function() { + windowCanvas.virtualKeyboardKeyUp(c); + + t.destroy(); + }); + + t.start(); + })(code); + } + + onFocusChanged: { + if (focus) { + cursorPosition = 1; + sendVirtualKeyCode(0xf001) // Arbitrary unused keycode + } else { + sendVirtualKeyCode(0xf000) + } + } + + onTextChanged: { + if (text.length == 0) { // Backspace pressed + sendVirtualKeyCode(8) + } else if (text.indexOf('\n') != -1) { // Enter pressed + sendVirtualKeyCode(13) + } else if (text.length == 2) { // Regular key pressed + var lastChar = text.charAt(text.length - 1) + + if (text[0] != ' ') { + lastChar = text[0] + } + + sendVirtualKeyCode(lastChar.charCodeAt(0)) + } + + text = " "; + + cursorPosition = 1; + } + } + } + Rectangle { width: parent.width height: 2 diff --git a/shim/src/input-shim.cpp b/shim/src/input-shim.cpp index 3e46e33..1197098 100644 --- a/shim/src/input-shim.cpp +++ b/shim/src/input-shim.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "qtfb-client/qtfb-client.h" @@ -54,7 +55,7 @@ extern qtfb::ClientConnection *clientConnection; extern int shimInputType; -extern std::set *identDigitizer, *identTouchScreen, *identButtons, *identNull; +extern std::set *identDigitizer, *identTouchScreen, *identButtons, *identVirtualKeyboard, *identNull; struct TouchSlotState { int x, y; @@ -65,7 +66,8 @@ std::map touchStates; #define QUEUE_TOUCH 1 #define QUEUE_PEN 2 #define QUEUE_BUTTONS 3 -#define QUEUE_NULL 4 +#define QUEUE_VIRTUALKEYBOARD 4 +#define QUEUE_NULL 5 struct PIDEventQueue *pidEventQueue; @@ -98,6 +100,50 @@ static int mapKey(int x) { return 0; } +const bool isShifted(int ascii) { + if (ascii >= '!' && ascii <= '&') return true; + if (ascii >= '(' && ascii <= '+') return true; + if (ascii >= ':' && ascii <= ':') return true; + if (ascii >= '<' && ascii <= '<') return true; + if (ascii >= '>' && ascii <= 'Z') return true; + if (ascii >= '^' && ascii <= '_') return true; + if (ascii >= '{' && ascii <= '~') return true; + + if (ascii >= 0xA2 && ascii <= 0xA3) return true; // ¢ to £ + if (ascii >= 0xA6 && ascii <= 0xA8) return true; // ¦ to ¨ + if (ascii >= 0xAF && ascii <= 0xB0) return true; // ¯ to ° + if (ascii >= 0xB9 && ascii <= 0xB0) return true; // ¹ to ° + if (ascii >= 0xC0 && ascii <= 0xD6) return true; // À to Ö + if (ascii >= 0xD8 && ascii <= 0xDE) return true; // Ø to Þ + + return false; +} + +std::tuple mapAsciiToX11Key(int ascii) { + const bool shifted = isShifted(ascii); + + // All ASCII printable characters + if (ascii >= ' ' && ascii <= '~') { + return {ascii, shifted}; + } + + // All ASCII printable extended characters + if (ascii >= 0x00a0 && ascii <= 0x00ff) { + return {ascii, shifted}; + } + + // ASCII unprintable characters + switch (ascii) { + case 8: return {0xff08, false}; // Backspace + case 9: return {0xff09, false}; // Tab + case 13: return {0xff0d, false}; // Enter/Return + case 27: return {0xff1b, false}; // Escape + case 127: return {0xffff, false}; // Delete + } + + return {0x0000, false}; +} + static void pushToAll(int queueType, struct input_event evt) { struct PIDEventQueue *current = pidEventQueue; while(current != NULL) { @@ -217,6 +263,7 @@ static void pollInputUpdates() { pushToAll(QUEUE_PEN, evt(EV_ABS, ABS_PRESSURE, dTranslate)); pushToAll(QUEUE_PEN, evt(EV_SYN, SYN_REPORT, 0)); break; + case INPUT_BTN_PRESS: pushToAll(QUEUE_BUTTONS, evt(EV_KEY, mapKey(message.userInput.x), 1)); pushToAll(QUEUE_BUTTONS, evt(EV_SYN, SYN_REPORT, 0)); @@ -225,6 +272,42 @@ static void pollInputUpdates() { pushToAll(QUEUE_BUTTONS, evt(EV_KEY, mapKey(message.userInput.x), 0)); pushToAll(QUEUE_BUTTONS, evt(EV_SYN, SYN_REPORT, 0)); break; + + case INPUT_VKB_PRESS: { + auto [keySym, isShifted] = mapAsciiToX11Key(message.userInput.x); + + if (keySym != 0x0000) { + if (isShifted) { + pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_KEY, 0xffe1, 1)); + pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_SYN, SYN_REPORT, 0)); + } + + usleep(10000); + + pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_KEY, keySym, 1)); + pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_SYN, SYN_REPORT, 0)); + } + + break; + } + case INPUT_VKB_RELEASE: { + auto [keySym, isShifted] = mapAsciiToX11Key(message.userInput.x); + + if (keySym != 0x0000) { + pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_KEY, keySym, 0)); + pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_SYN, SYN_REPORT, 0)); + + usleep(10000); + + if (isShifted) { + pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_KEY, 0xffe1, 0)); + pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_SYN, SYN_REPORT, 0)); + } + } + + break; + } + default: break; } } @@ -267,6 +350,7 @@ int inputShimOpen(fileident_t identity, int flags, mode_t mode) { e("dig", *identDigitizer); e("tch", *identTouchScreen); e("btn", *identButtons); + e("vkb", *identVirtualKeyboard); e("null", *identNull); #undef e if(identDigitizer->find(identity) != identDigitizer->end()) { @@ -285,6 +369,11 @@ int inputShimOpen(fileident_t identity, int flags, mode_t mode) { CERR << "Open buttons " << fd << std::endl; return fd; } + if(identVirtualKeyboard->find(identity) != identVirtualKeyboard->end()) { + int fd = createInEventMap(QUEUE_VIRTUALKEYBOARD, flags); + CERR << "Open virtual keyboard " << fd << std::endl; + return fd; + } if(identNull->find(identity) != identNull->end()) { int fd = createInEventMap(QUEUE_NULL, flags); CERR << "Open null " << fd << std::endl; diff --git a/shim/src/shim.cpp b/shim/src/shim.cpp index 372adec..b6a5d4d 100644 --- a/shim/src/shim.cpp +++ b/shim/src/shim.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "shim.h" #include "fb-shim.h" #include "input-shim.h" @@ -40,7 +41,7 @@ bool shimModel; bool shimInput; bool shimFramebuffer; int shimInputType = SHIM_INPUT_RM1; -std::set *identDigitizer, *identTouchScreen, *identButtons, *identNull; +std::set *identDigitizer, *identTouchScreen, *identButtons, *identVirtualKeyboard, *identNull; int realDeviceType; void readRealDeviceType() { @@ -108,6 +109,7 @@ void __attribute__((constructor)) __construct () { identDigitizer = new std::set(); identTouchScreen = new std::set(); identButtons = new std::set(); + identVirtualKeyboard = new std::set(); identNull = new std::set(); readRealDeviceType(); @@ -192,7 +194,10 @@ void __attribute__((constructor)) __construct () { CERR << "Configured FB type to " << shimType << ", input to " << shimInputType << std::endl; - const char *pathDigitizer, *pathTouchScreen, *pathButtons, *pathNull; + const char *pathDigitizer, *pathTouchScreen, *pathButtons, *pathVirtualKeyboard, *pathNull; + + pathVirtualKeyboard = "/dev/input/virtual_keyboard"; + std::ofstream(pathVirtualKeyboard).close(); switch(shimInputType) { case SHIM_INPUT_RM1: @@ -233,6 +238,11 @@ void __attribute__((constructor)) __construct () { } iterStringCollectToIdentities(identButtons, temp); + if((temp = getenv("QTFB_SHIM_INPUT_PATH_KEYS")) == NULL) { + temp = pathVirtualKeyboard; + } + iterStringCollectToIdentities(identVirtualKeyboard, temp); + if((temp = getenv("QTFB_SHIM_INPUT_PATH_NULL")) == NULL) { temp = pathNull; } @@ -247,6 +257,9 @@ void __attribute__((constructor)) __construct () { for(const auto e : *identButtons) { CERR << "Ident btn: " << e << std::endl; } + for(const auto e : *identVirtualKeyboard) { + CERR << "Ident vkb: " << e << std::endl; + } for(const auto e : *identNull) { CERR << "Ident null: " << e << std::endl; } diff --git a/src/AppLibrary.h b/src/AppLibrary.h index 5470927..4c65eca 100644 --- a/src/AppLibrary.h +++ b/src/AppLibrary.h @@ -24,6 +24,7 @@ class AppLoadApplication : public QObject { Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QString icon READ icon CONSTANT) Q_PROPERTY(bool supportsScaling READ supportsScaling) + Q_PROPERTY(bool supportsVirtualKeyboard READ supportsVirtualKeyboard) Q_PROPERTY(bool canHaveMultipleFrontends READ canHaveMultipleFrontends) Q_PROPERTY(int externalType READ externalType) // 0 - not external, 1 - external (non-graphics), 2 - external (qtfb) Q_PROPERTY(QString aspectRatio READ aspectRatio CONSTANT) @@ -32,14 +33,15 @@ class AppLoadApplication : public QObject { public: explicit AppLoadApplication(QObject *parent = nullptr) : QObject(parent) {} - AppLoadApplication(const QString &id, const QString &name, const QString &icon, bool supportsScaling, bool canHaveMultipleFrontends, int externalType, appload::library::AspectRatio aspectRatio, bool disablesWindowedMode, QObject *parent = nullptr) - : QObject(parent), _id(id), _name(name), _icon(icon), _supportsScaling(supportsScaling), _canHaveMultipleFrontends(canHaveMultipleFrontends), _externalType(externalType), _aspectRatio(aspectRatio), _disablesWindowedMode(disablesWindowedMode) {} + AppLoadApplication(const QString &id, const QString &name, const QString &icon, bool supportsScaling, bool supportsVirtualKeyboard, bool canHaveMultipleFrontends, int externalType, appload::library::AspectRatio aspectRatio, bool disablesWindowedMode, QObject *parent = nullptr) + : QObject(parent), _id(id), _name(name), _icon(icon), _supportsScaling(supportsScaling), _supportsVirtualKeyboard(supportsVirtualKeyboard), _canHaveMultipleFrontends(canHaveMultipleFrontends), _externalType(externalType), _aspectRatio(aspectRatio), _disablesWindowedMode(disablesWindowedMode) {} QString id() const { return _id; } QString name() const { return _name; } QString icon() const { return _icon; } QString aspectRatio() const { return appload::library::aspectRatioToString(_aspectRatio); } bool supportsScaling() const { return _supportsScaling; } + bool supportsVirtualKeyboard() const { return _supportsVirtualKeyboard; } bool canHaveMultipleFrontends() const { return _canHaveMultipleFrontends; } int externalType() const { return _externalType; } bool disablesWindowedMode() const { return _disablesWindowedMode; } @@ -49,6 +51,7 @@ class AppLoadApplication : public QObject { QString _name; QString _icon; bool _supportsScaling; + bool _supportsVirtualKeyboard; bool _canHaveMultipleFrontends; int _externalType; appload::library::AspectRatio _aspectRatio; @@ -107,6 +110,7 @@ class AppLoadLibrary : public QObject { entry.second->getAppName(), entry.second->getIconPath(), entry.second->supportsScaling(), + false, entry.second->canHaveMultipleFrontends(), INTERNAL, appload::library::AspectRatio::AUTO, @@ -118,6 +122,7 @@ class AppLoadLibrary : public QObject { entry.second->getAppName(), entry.second->getIconPath(), false, + entry.second->supportsVirtualKeyboard(), true, entry.second->isQTFB() ? EXTERNAL_QTFB : EXTERNAL_NOGUI, entry.second->getAspectRatio(), diff --git a/src/library.h b/src/library.h index c81736f..17c9c2c 100644 --- a/src/library.h +++ b/src/library.h @@ -48,6 +48,7 @@ namespace appload::library { bool isQTFB() const; AspectRatio getAspectRatio() const; bool disablesWindowedMode() const; + bool supportsVirtualKeyboard() const; bool valid = false; @@ -62,6 +63,7 @@ namespace appload::library { std::map environment; bool _isQTFB; bool _disablesWindowedMode; + bool _supportsVirtualKeyboard; AspectRatio aspectRatio; void parseManifest(); diff --git a/src/libraryexternals.cpp b/src/libraryexternals.cpp index 55b1a95..36d5485 100644 --- a/src/libraryexternals.cpp +++ b/src/libraryexternals.cpp @@ -28,8 +28,6 @@ void appload::library::removeGlobalLibraryHandle(AppLoadLibrary *ptr) { } } - - void appload::library::ExternalApplication::parseManifest() { QString filePath = root + "/external.manifest.json"; QFile file(filePath); @@ -55,6 +53,7 @@ void appload::library::ExternalApplication::parseManifest() { // Optional: _isQTFB = jsonObject.value("qtfb").toBool(false); _disablesWindowedMode = jsonObject.value("disablesWindowedMode").toBool(false); + _supportsVirtualKeyboard = jsonObject.value("supportsVirtualKeyboard").toBool(false); workingDirectory = jsonObject.value("workingDirectory").toString(root); args = jsonObject.value("args").toVariant().toStringList(); QJsonObject env = jsonObject.value("environment").toObject(); @@ -137,6 +136,10 @@ bool appload::library::ExternalApplication::disablesWindowedMode() const { return _disablesWindowedMode; } +bool appload::library::ExternalApplication::supportsVirtualKeyboard() const { + return _supportsVirtualKeyboard; +} + void appload::library::terminateExternal(qint64 pid) { kill(pid, SIGTERM); sendPidDiedMessage(pid); diff --git a/src/qtfb/FBController.cpp b/src/qtfb/FBController.cpp index 8653892..f022c33 100644 --- a/src/qtfb/FBController.cpp +++ b/src/qtfb/FBController.cpp @@ -137,7 +137,7 @@ void FBController::mouseMoveEvent(QMouseEvent *me) { me->accept(); } -static inline void sendSpecialKey(int key, int pkt, qtfb::FBKey _framebufferID) { +static inline void sendKeyEvent(int key, int pkt, qtfb::FBKey _framebufferID) { if(_framebufferID != -1) { qtfb::UserInputContents packet { .inputType = pkt, @@ -150,12 +150,20 @@ static inline void sendSpecialKey(int key, int pkt, qtfb::FBKey _framebufferID) } } +void FBController::virtualKeyboardKeyDown(int key) { + sendKeyEvent(key, INPUT_VKB_PRESS, _framebufferID); +} + +void FBController::virtualKeyboardKeyUp(int key) { + sendKeyEvent(key, INPUT_VKB_RELEASE, _framebufferID); +} + void FBController::specialKeyDown(int key) { - sendSpecialKey(key, INPUT_BTN_PRESS, _framebufferID); + sendKeyEvent(key, INPUT_BTN_PRESS, _framebufferID); } void FBController::specialKeyUp(int key) { - sendSpecialKey(key, INPUT_BTN_RELEASE, _framebufferID); + sendKeyEvent(key, INPUT_BTN_RELEASE, _framebufferID); } void FBController::mouseReleaseEvent(QMouseEvent *me) { diff --git a/src/qtfb/FBController.h b/src/qtfb/FBController.h index 54f3560..aa46b73 100644 --- a/src/qtfb/FBController.h +++ b/src/qtfb/FBController.h @@ -52,6 +52,8 @@ class FBController : public QQuickPaintedItem virtual void keyPressEvent(QKeyEvent *ke) override; virtual void keyReleaseEvent(QKeyEvent *ke) override; + Q_INVOKABLE void virtualKeyboardKeyDown(int key); + Q_INVOKABLE void virtualKeyboardKeyUp(int key); Q_INVOKABLE void specialKeyDown(int key); Q_INVOKABLE void specialKeyUp(int key); diff --git a/src/qtfb/common.h b/src/qtfb/common.h index 72ac6f1..cc1bb64 100644 --- a/src/qtfb/common.h +++ b/src/qtfb/common.h @@ -50,6 +50,9 @@ #define INPUT_BTN_PRESS 0x30 #define INPUT_BTN_RELEASE 0x31 +#define INPUT_VKB_PRESS 0x40 +#define INPUT_VKB_RELEASE 0x41 + #define INPUT_BTN_X_LEFT 0 #define INPUT_BTN_X_HOME 1 #define INPUT_BTN_X_RIGHT 2 diff --git a/xovi/make.sh b/xovi/make.sh old mode 100644 new mode 100755 From de6dede4faabb25cff20b060adcc73d3adbebec6 Mon Sep 17 00:00:00 2001 From: asivery Date: Sat, 24 Jan 2026 00:27:51 +0100 Subject: [PATCH 2/5] Make building the xovi variant faster --- xovi/make.sh | 13 ++++++++----- xovi/template/appload.pro | 13 +++---------- xovi/template/{ => src}/config.h | 0 xovi/template/{ => src}/main.cpp | 0 4 files changed, 11 insertions(+), 15 deletions(-) rename xovi/template/{ => src}/config.h (100%) rename xovi/template/{ => src}/main.cpp (100%) diff --git a/xovi/make.sh b/xovi/make.sh index 4e1f537..578a4e6 100755 --- a/xovi/make.sh +++ b/xovi/make.sh @@ -1,10 +1,13 @@ -cp -rv template/* . rm -rf temporary -mkdir -p temporary - +mkdir temporary cp -rv ../src temporary +cp -rv template/* temporary/ + rcc --no-compress -g cpp ../resources/resources.qrc | sed '/#ifdef _MSC_VER/,/#endif/d' | sed -n '/#ifdef/q;p' > temporary/resources.cpp -cp main.cpp config.h temporary/src/ +cd temporary +python3 $XOVI_REPO/util/xovigen.py -o xovi.cpp -H xovi.h appload.xovi qmake6 . -make +make -j`nproc` +cp appload.so ../ +cd .. diff --git a/xovi/template/appload.pro b/xovi/template/appload.pro index 7508cb2..7b23e9b 100644 --- a/xovi/template/appload.pro +++ b/xovi/template/appload.pro @@ -4,14 +4,7 @@ TARGET = appload TEMPLATE = lib CONFIG += shared plugin no_plugin_name_prefix -xoviextension.target = xovi.cpp -xoviextension.commands = python3 $$(XOVI_REPO)/util/xovigen.py -o xovi.cpp -H xovi.h appload.xovi -xoviextension.depends = appload.xovi - -QMAKE_EXTRA_TARGETS += xoviextension -PRE_TARGETDEPS += xovi.cpp - -SOURCES += temporary/src/main.cpp xovi.cpp temporary/src/management.cpp temporary/src/AppLoad.cpp temporary/src/AppLoadCoordinator.cpp temporary/src/library.cpp temporary/src/libraryexternals.cpp temporary/src/qtfb/fbmanagement.cpp temporary/src/qtfb/FBController.cpp -HEADERS += temporary/src/AppLoad.h temporary/src/AppLoadCoordinator.h temporary/src/library.h temporary/src/AppLibrary.h \ - temporary/src/qtfb/FBController.h temporary/src/qtfb/fbmanagement.h +SOURCES += src/main.cpp xovi.cpp src/management.cpp src/AppLoad.cpp src/AppLoadCoordinator.cpp src/library.cpp src/libraryexternals.cpp src/qtfb/fbmanagement.cpp src/qtfb/FBController.cpp src/keyboard/layout.cpp +HEADERS += src/AppLoad.h src/AppLoadCoordinator.h src/library.h src/AppLibrary.h \ + src/qtfb/FBController.h src/qtfb/fbmanagement.h src/keyboard/layout.h diff --git a/xovi/template/config.h b/xovi/template/src/config.h similarity index 100% rename from xovi/template/config.h rename to xovi/template/src/config.h diff --git a/xovi/template/main.cpp b/xovi/template/src/main.cpp similarity index 100% rename from xovi/template/main.cpp rename to xovi/template/src/main.cpp From cb9aa842a2fd5c75044c3c8cff5ea5fa1fab92be Mon Sep 17 00:00:00 2001 From: asivery Date: Sat, 24 Jan 2026 00:51:12 +0100 Subject: [PATCH 3/5] Add a built in virtual keyboard instead of depending on input fields --- .gitignore | 5 +- _start.qml | 21 +- appload.pro | 6 +- resources/icons/keyboard.svg | 1 + resources/keyboard/backspace.png | Bin 0 -> 536 bytes resources/keyboard/default.layout.json | 78 ++++++++ resources/keyboard/down.png | Bin 0 -> 379 bytes resources/keyboard/enter.png | Bin 0 -> 347 bytes resources/keyboard/left.png | Bin 0 -> 463 bytes resources/keyboard/menu.png | Bin 0 -> 116 bytes resources/keyboard/right.png | Bin 0 -> 436 bytes resources/keyboard/shift.png | Bin 0 -> 574 bytes resources/keyboard/tab.png | Bin 0 -> 414 bytes resources/keyboard/up.png | Bin 0 -> 370 bytes resources/qml/appload.qml | 6 +- resources/qml/virtualKeyboard/Key.qml | 211 +++++++++++++++++++++ resources/qml/virtualKeyboard/Keyboard.qml | 149 +++++++++++++++ resources/qml/window.qml | 73 +++---- resources/resources.qrc | 14 ++ shim/src/input-shim.cpp | 102 ++++------ src/AppLibrary.h | 37 +++- src/keyboard/layout.cpp | 55 ++++++ src/keyboard/layout.h | 69 +++++++ src/library.cpp | 8 + src/library.h | 5 +- src/libraryexternals.cpp | 9 +- src/qtfb/common.h | 13 ++ xovi/template/appload.qmd | 27 ++- xovi/template/src/main.cpp | 5 +- 29 files changed, 759 insertions(+), 135 deletions(-) create mode 100644 resources/icons/keyboard.svg create mode 100644 resources/keyboard/backspace.png create mode 100644 resources/keyboard/default.layout.json create mode 100644 resources/keyboard/down.png create mode 100644 resources/keyboard/enter.png create mode 100644 resources/keyboard/left.png create mode 100644 resources/keyboard/menu.png create mode 100644 resources/keyboard/right.png create mode 100644 resources/keyboard/shift.png create mode 100644 resources/keyboard/tab.png create mode 100644 resources/keyboard/up.png create mode 100644 resources/qml/virtualKeyboard/Key.qml create mode 100644 resources/qml/virtualKeyboard/Keyboard.qml create mode 100644 src/keyboard/layout.cpp create mode 100644 src/keyboard/layout.h diff --git a/.gitignore b/.gitignore index 622b687..faa1c28 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,8 @@ /testing_extensions/ /.vscode /shim/build -/xovi/* -!/xovi/template -!/xovi/make.sh +/xovi/temporary/ .qmake.stash appload Makefile +**/*.so diff --git a/_start.qml b/_start.qml index 6b53ab9..121f608 100644 --- a/_start.qml +++ b/_start.qml @@ -2,6 +2,8 @@ import QtQuick 2.5 import QtQuick.Window 2.2 import QtQuick.Controls 2.5 +import net.asivery.AppLoad 1.0 + Window { visible: true title: qsTr("AppLoad - PC emulator") @@ -30,7 +32,24 @@ Window { anchors.centerIn: parent - onLoaded: loader.item.visible = true + onLoaded: { + loader.item.visible = true; + loader.item.virtualKeyboardRef = keyboardLoader; + } + } + } + Loader { + property var layout: null + property var config: null + id: keyboardLoader + source: "qrc:/appload/qml/virtualKeyboard/Keyboard.qml" + active: false + width: parent.width + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + + onLoaded: () => { + keyboardLoader.item.rebuildKeyboard(keyboardLoader.layout, keyboardLoader.config); } } } diff --git a/appload.pro b/appload.pro index 975e2f7..8da23f1 100644 --- a/appload.pro +++ b/appload.pro @@ -4,9 +4,11 @@ TARGET = appload TEMPLATE = app SOURCES += src/main.cpp src/management.cpp src/AppLoad.cpp src/AppLoadCoordinator.cpp src/library.cpp src/libraryexternals.cpp \ - src/qtfb/fbmanagement.cpp src/qtfb/FBController.cpp + src/qtfb/fbmanagement.cpp src/qtfb/FBController.cpp \ + src/keyboard/layout.cpp HEADERS += src/AppLoad.h src/AppLoadCoordinator.h src/library.h src/AppLibrary.h \ - src/qtfb/FBController.h src/qtfb/fbmanagement.h + src/qtfb/FBController.h src/qtfb/fbmanagement.h \ + src/keyboard/layout.h RESOURCES += resources/resources.qrc diff --git a/resources/icons/keyboard.svg b/resources/icons/keyboard.svg new file mode 100644 index 0000000..08dcada --- /dev/null +++ b/resources/icons/keyboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/keyboard/backspace.png b/resources/keyboard/backspace.png new file mode 100644 index 0000000000000000000000000000000000000000..2bc3dc32f6ae8b801d8126cdcac87ad891eb26ad GIT binary patch literal 536 zcmV+z0_XjSP)%g!3(+57 zBx0qli#AIN6HT%|`QGM)etxt%1*2-p^p>ovKJBS1uM0=vKuRh_HR^}iP&B3FSO zV5q9+_FDtI1fKuN2k-(|jxIn%E{EU;olfWUn052Ou87R<^K2pH%$=wLRP`e83;5!k zD-T)^JOehKhyjiUDiW||ZkgCpw z?ejLz^XbFbI_FLT8^A~4J@C5U?{}h?QB}PZ5_m1kvSX9j6h$!wtOMV?_b0sfGr#~? zX`~N?61ok1p0En70$XXCo~cx!SzuE{o;K3RLVe!>z8=sA?uI>oZA_22(o`p23}eK& zPP_~(jrluEjdbFQh+AQhYu_wX5pl0FjV`oKKoIeuwda9~h{vrxk^L8EV#6>D!!Qh^ a8GZw&*Tc~VWd{HN0000", 62, "<", 60], + ["|", 124, "&", 38], + ["!", 33, "?", 63], + ["\"", 34, "$", 36], + ["'", 39, "`", 96], + ["_", 95, "@", 64], + ["=", 61, "~", 126], + ["/", 47, "\\", 92], + ["*", 42, "del", 127], + [":backspace", 128, "", 0] + ], + [ + [":tab", 9, "", 0], + ["1", 49, "+", 43], + ["2", 50, "^", 94], + ["3", 51, "#", 35], + ["4", 52, "%", 37], + ["5", 53, "(", 40], + ["6", 54, ")", 41], + ["7", 55, "[", 91], + ["8", 56, "]", 93], + ["9", 57, "{", 123], + ["0", 48, "}", 125] + ], + [ + ["q", 81, "", 0], + ["w", 87, "", 0], + ["e", 69, "", 0], + ["r", 82, "", 0], + ["t", 84, "", 0], + ["y", 89, "", 0], + ["u", 85, "", 0], + ["i", 73, "", 0], + ["o", 79, "", 0], + ["p", 80, "", 0], + [":enter", 13, "", 0] + ], + [ + ["a", 65, "", 0], + ["s", 83, "", 0], + ["d", 68, "", 0], + ["f", 70, "", 0], + ["g", 71, "", 0], + ["h", 72, "", 0], + ["j", 74, "", 0], + ["k", 75, "", 0], + ["l", 76, "", 0], + [":", 58, "", 0], + ["pgup", 129, "home", 135] + ], + [ + [":shift", 1048576, "", 0], + ["z", 90, "", 0], + ["x", 88, "", 0], + ["c", 67, "", 0], + ["v", 86, "", 0], + ["b", 66, "", 0], + ["n", 78, "", 0], + ["m", 77, "", 0], + [";", 59, "", 0], + [":up", 132, "", 0], + ["pgdn", 130, "end", 136] + ], + [ + ["ctrl", 2097152, "", 0], + ["alt", 4194304, "", 0], + ["-", 45, "", 0], + [",", 44, "", 0], + [" ", 32, "", 0, 3], + [".", 46, "", 0], + [":left", 133, "", 0], + [":down", 131, "", 0], + [":right", 134, "", 0] + ] +] \ No newline at end of file diff --git a/resources/keyboard/down.png b/resources/keyboard/down.png new file mode 100644 index 0000000000000000000000000000000000000000..8ac3a8b6deae941f9c7c9f9b1ac93035335f5c41 GIT binary patch literal 379 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezr3(Fgkg#&1J+jIWb z$;D|x7c$mYIED)p7{)e47mDxOArvkUnB^TEa)tFtWPniouP3a+v&}f?@ITY#`EPD) znSM;dHBiJg({M$R(fQBkdJX3)-9J`XGgIZH`{pp_3gcr>T-`)m*G+QG{w)~9C>nr- zR`mO2CZCsnURk&8T}|e!IEIS<4}A?!ZhDiId@NDo9fL=gUCb#ShT!bn^2fKuSPtqn z%Ls#iUk{Xn;zDmQ5g^|T5#?ykEW?@15kXH;I9QI)gyEz=A!p8qd?|8smitE=M+!{PRf^)aLTXgh{GQ)M3k}MIBpMRcH?>oEp V+WAK;vcN!P@O1TaS?83{1OS?0q>2Cl literal 0 HcmV?d00001 diff --git a/resources/keyboard/enter.png b/resources/keyboard/enter.png new file mode 100644 index 0000000000000000000000000000000000000000..7e96698484f31ceae595773c05f81a6ac58196d4 GIT binary patch literal 347 zcmV-h0i^zkP)`$O)ppoyC?`29>R9cU}0&yHXg%55EMZKyKOvyC~Ohd zCJT!MA)3t^c|VvgB=1c!lO)STL_|bH)`dTqY2GBG_5BX0fC{L93ea}|Gve<1 zz+IMQbKv%e?}6p9dMPE`{Q$UMzIf9DDJ42BPz4_Wk2M!=Tp-W$txgJz$73_IL*S_{ zc+&zgMh_wE)ZD@MR)fbF-OP@G=kJ2IHGrEogP#B|T?AhYcOSK~jZUZ2P#yc$*=I6b zlx2C{`iVmbyTAmPt>BTF0S{(&y7Czs3;>23Zfzig1EQ7g*!LCg|FbsXYetq^#!_AQSeg5%f4wQ0pYlr zg^LQciEReX2eV6N{x5T8G9v`TFbu;m3{x2OdVN|-c{r}|WRabPVHov#y%rJeIVTW? zVHC%4i->l|IpdWgI;quaGjSYW5Yf)KOI8*TezxE5Urr)??imPzV9ti$p46rxbA+Ey zlB7M!@FU~<{sMq&Ap{L-0KNdEd76~+_+LuM_x(j<%q0r5fZP!Y`+3dR3(w^=y~31mSt@sS{v{e zJ&U5~Bu}tLqmgzxov(kj*IKUtxF2v9I(6bcV2pY5Jg;db+&WJGka)KecTNgei4U!H z6Tq#L0zwFpB*~}N`T)R!c literal 0 HcmV?d00001 diff --git a/resources/keyboard/right.png b/resources/keyboard/right.png new file mode 100644 index 0000000000000000000000000000000000000000..d320c63d92dad8532bd69b3bf9b68f97f8394202 GIT binary patch literal 436 zcmV;l0ZaagP)Oou2^9rbxqt~C!iC`0!*~SmV!VK&h@v6{M8zqabazz>q21I< z6of?0gZGEtHP!W_2l@%YaU92S95?(WNwRCL9Uu6*e+4MZa-%4ULu1SYNkBxbJLe9K zF_R<#5wYf+YiX^gNCF~a)qCI4T2GS%M8rx&%=EKP-9>z-B$fvsMG0;et}r_ z-XAHY8s7W6-~0sb2_eegIsn`%rD~)ep-*>WvDfRhq?B_c0THnfLO7CA&XWX0#9Txy zk_1FVErigLQZA7MM8s?e;eadvz>QLBha>>NwNh%+Id@ME0N^Ta{(;;}4B%30z3H5L z9Qdz<5aJy`ug^VJ%Ca2!X7d+X>t<0DPeVJA0oecMH{!gXNiF$j)VVRHk>~j{IRJn& zYi%RX^A~dS0Dx0#Z8OWVS8@XlfRi*$n^~5S->P)dlPWN1l}a12$Um4uD{ZvWMjLIk(I$-| zXpusT6bcIZ7X&4Q5c0J)H?W1qY<4!;#rwi7X6`xfd0y_o%oyY4<>loysS1MNJn%LM zf^6#G!!SGtd^g7Qfp1|L_NE3tilSNIt1+g3uz)X76wOXNd>qFq@YzBC5RoO|m%IKH z$8kEb@a=Xx0oEMs&sM8-!5Cw#wU>Z(cYm$Z>71Q7_#{c%z()tUZmqpIxNohU12)|L zha^eb69b>7X$-77xD9LV+@I=H^$M`*>Q>V0&hL3ymO3{0 zs;UHd=7#Ny$Wjx&B61zrcfL=ns*=Xx>$(PbG5~+0Nq-T!Iq=!>xUOp&4FEhk7$73I z#&NDO<9L(G)R2k-%W03W~y@Bw@PA7BJE{S)2yC-A=o@bdEV@)}9M0e1VKVZRM7sQ>@~ M07*qoM6N<$f3oZbT!#=M@a|54s-6Jz5JFVdOIHF^^$ZxN>c6^bArTn> zi}dp!X4Y~cz|7jf^XeP06p>!zhq$imL*NmZX7^DRp8-QN>v$OA{@M_~ zR8@84#YWqijV?sw*6SjhsOq`rC~@j8O1KYmg+ifFC=@pE2?S2S%z=+&Qvd(}07*qo IM6N<$f@GSt*Z=?k literal 0 HcmV?d00001 diff --git a/resources/keyboard/up.png b/resources/keyboard/up.png new file mode 100644 index 0000000000000000000000000000000000000000..e1ec790355f06a3981bc2a1dcf093021f357fd66 GIT binary patch literal 370 zcmV-&0ge8NP)2*MC~baP(CYl67F~NL*KRzE$M6#+xQp_K0NzBz0az%dMqQH60KAHb`x*e|N~vKNj8k-f8@Q0+c=K5o0RVZnARchMcgzJ zVT|bknA9onMO-IIvQ^ayRTc+9aN?XhUNN=loI5LZAn+j~=ReF{6-9B;_?t-6)K<9Z zWu0dSUH|nmfB_6(00S7n00uCC0qbL win.destroy()); - } if(modelData.externalType == 0 /* INTERNAL */) { win.loadApplication(modelData.id); diff --git a/resources/qml/virtualKeyboard/Key.qml b/resources/qml/virtualKeyboard/Key.qml new file mode 100644 index 0000000..95ea898 --- /dev/null +++ b/resources/qml/virtualKeyboard/Key.qml @@ -0,0 +1,211 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This work is free software. you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This work is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this work. If not, see . +*/ + +import QtQuick 2.0 + +Rectangle { + id: key + required property var vkb + + property string label + property string label_alt + property int code + property int code_alt + property int currentCode: (shiftActive && label_alt != '') ? code_alt : code + property string currentLabel: (shiftActive && label_alt != '') ? label_alt : label + property bool sticky // can key be stickied? + property bool becomesSticky // will this become sticky after release? + property int stickiness // current stickiness status + property real labelOpacity: 1.0 + + // mouse input handling + property int clickThreshold: 20 + property bool isClick + property int pressMouseY + property int pressMouseX + property bool shiftActive: (vkb.keyModifiers & 0x100000) && !sticky + + width: vkb.keyWidth // some default + height: vkb.keyHeight + color: label=="" ? "transparent" : vkb.keyBgColor + border.color: label=="" ? "transparent" : vkb.keyBorderColor + border.width: 1 + radius: 2 + + Image { + id: keyImage + anchors.centerIn: parent + opacity: key.labelOpacity + source: { if(key.label.length>1 && key.label.charAt(0)==':') return "qrc:/appload/keyboard/"+key.label.substring(1)+".png"; else return ""; } + } + + Column { + visible: keyImage.source == "" + anchors.centerIn: parent + spacing: -17 + + Text { + id: keyAltLabel + property bool highlighted: key.shiftActive + + anchors.horizontalCenter: parent.horizontalCenter + + text: key.label_alt + color: vkb.keyFgColor + + opacity: key.labelOpacity * (highlighted ? 1.0 : 0.2) + Behavior on opacity { NumberAnimation { duration: 100 } } + + font.pointSize: 24 * (text.length > 1 ? 0.5 : 1.0) + Behavior on font.pointSize { NumberAnimation { duration: 100 } } + } + + Text { + id: keyLabel + property bool highlighted: key.label_alt == '' || !key.shiftActive + + anchors.horizontalCenter: parent.horizontalCenter + + text: { + if (key.label.length == 1 && key.label_alt == '') { + if (key.shiftActive) { + return key.label.toUpperCase(); + } else { + return key.label.toLowerCase(); + } + } + + return key.label; + } + + color: vkb.keyFgColor + + opacity: key.labelOpacity * (highlighted ? 1.0 : 0.2) + Behavior on opacity { NumberAnimation { duration: 100 } } + + font.pointSize: 24 * (text.length > 1 ? 0.5 : 1.0) + Behavior on font.pointSize { NumberAnimation { duration: 100 } } + } + } + + Rectangle { + id: stickIndicator + visible: sticky && stickiness>0 + color: vkb.keyHilightBgColor + anchors.fill: parent + radius: key.radius + opacity: 0.5 + anchors.topMargin: key.height/2 + } + + function handlePress(touchArea, x, y) { + console.log("Press " + label) + isClick = true; + pressMouseX = x; + pressMouseY = y; + + key.color = vkb.keyHilightBgColor + + vkb.config.keyDown(currentCode | vkb.keyModifiers); + + if (sticky) { + vkb.keyModifiers |= code; + key.becomesSticky = true; + vkb.currentStickyPressed = key; + } else { + if (vkb.currentStickyPressed != null) { + // Pressing a non-sticky key while a sticky key is pressed: + // the sticky key will not become sticky when released + vkb.currentStickyPressed.becomesSticky = false; + } + } + } + + function handleMove(touchArea, x, y) { + var mappedPoint = key.mapFromItem(touchArea, x, y) + if (!key.contains(Qt.point(mappedPoint.x, mappedPoint.y))) { + key.handleRelease(touchArea, x, y); + return false; + } + + if (key.isClick) { + if (Math.abs(x - key.pressMouseX) > key.clickThreshold + || Math.abs(y - key.pressMouseY) > key.clickThreshold) { + key.isClick = false + } + } + + return true; + } + + function handleRelease(touchArea, x, y) { + key.color = vkb.keyBgColor + + if (sticky && !becomesSticky) { + vkb.keyModifiers &= ~code + vkb.currentStickyPressed = null + } + if(!sticky) { + vkb.config.keyUp(currentCode | vkb.keyModifiers); + } + + if (vkb.keyAt(x, y) == key) { + if (key.sticky && key.becomesSticky) { + setStickiness(-1) + } + + // first non-sticky press will cause the sticky to be released + if (!sticky && vkb.resetSticky && vkb.resetSticky !== key) { + resetSticky.setStickiness(0) + } + } + } + + function setStickiness(val) + { + if(sticky) { + if( vkb.resetSticky && vkb.resetSticky !== key ) { + resetSticky.setStickiness(0) + } + + if(val===-1) + stickiness = (stickiness+1) % 3 + else + stickiness = val + + // stickiness == 0 -> not pressed + // stickiness == 1 -> release after next keypress + // stickiness == 2 -> keep pressed + + if(stickiness>0) { + vkb.keyModifiers |= code + } else { + vkb.config.keyUp(currentCode); + vkb.keyModifiers &= ~code + } + + vkb.resetSticky = null + + if(stickiness==1) { + stickIndicator.anchors.topMargin = key.height/2 + vkb.resetSticky = key + } else if(stickiness==2) { + stickIndicator.anchors.topMargin = 0 + } + } + } +} diff --git a/resources/qml/virtualKeyboard/Keyboard.qml b/resources/qml/virtualKeyboard/Keyboard.qml new file mode 100644 index 0000000..4861c30 --- /dev/null +++ b/resources/qml/virtualKeyboard/Keyboard.qml @@ -0,0 +1,149 @@ +/* + Copyright 2011-2012 Heikki Holstila + + This work is free software. you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This work is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this work. If not, see . +*/ + +import QtQuick 2.0 + +Rectangle { + id: keyboard + + property int keyModifiers + property Key resetSticky + property Key currentStickyPressed + property string keyFgColor: "#000000" + property string keyBgColor: "#ffffff" + property string keyHilightBgColor: "#000000" + property string keyBorderColor: "#000000" + + + property int outmargins: 2 + property int keyspacing: 6 + property int keysPerRow: layout?.columns || 0 + property real keywidth: (keyboard.width - keyspacing*keysPerRow - outmargins*2)/keysPerRow; + property int keyWidth: width / (layout.columns + 1) + property int keyHeight: window.height / 14 < 55 ? window.height / 14 : 55 + property var config: null + + property var layout: null + + width: parent.width + height: keyboard.outmargins + keyboard.keyspacing * (layout?.rows ?? 0 - 1) + keyboard.keyHeight * (layout?.rows ?? 0) + + Component { + id: keyboardContents + Column { + id: col + + x: (keyboard.width-width)/2 + spacing: keyboard.keyspacing + + Repeater { + id: rowRepeater + + model: layout?.rows || 0 + delegate: Row { + spacing: keyboard.keyspacing + Repeater { + id: colRepeater + property int rowIndex: index + + model: layout.columnsInRow(index) + delegate: Key { + required property int index + property var keydata: layout.key(index, colRepeater.rowIndex) + vkb: keyboard + + label: keydata.label + code: keydata.code + label_alt: keydata.altLabel + code_alt: keydata.altCode + width: keyboard.keywidth * keydata.width + ((keydata.width-1)*keyboard.keyspacing) + 1 + sticky: keydata.isModifier && (keyboard.config?.enableStickyness ?? true) + } + } + } + } + } + } + + Loader { + id: keyboardLoader + anchors.fill: parent + } + + function rebuildKeyboard(layoutData, config) { + keyboard.config = config; + keyboard.layout = layoutData; + keyboardLoader.sourceComponent = undefined; + keyboardLoader.sourceComponent = keyboardContents; + } + + //borrowed from nemo-keyboard + //Parameters: (x, y) in view coordinates + function keyAt(x, y) { + var item = keyboardLoader.item + x -= keyboard.x + y -= keyboard.y + + while ((item = item.childAt(x, y)) != null) { + //return the first "Key" element we find + if (typeof item.currentCode !== 'undefined') { + return item + } + + // Cheaper mapToItem, assuming we're not using anything fancy. + x -= item.x + y -= item.y + } + + return null + } + + MultiPointTouchArea { + id: multiTouchArea + anchors.fill: parent + property var pressedKeys: ({}) + + onPressed: (touchPoints) => { + touchPoints.forEach(function (touchPoint) { + var key = keyboard.keyAt(touchPoint.x, touchPoint.y) + if (key != null) { + key.handlePress(multiTouchArea, touchPoint.x, touchPoint.y) + } + multiTouchArea.pressedKeys[touchPoint.pointId] = key + }) + } + onUpdated: (touchPoints) => { + touchPoints.forEach(function (touchPoint) { + var key = multiTouchArea.pressedKeys[touchPoint.pointId] + if (key != null) { + if (!key.handleMove(multiTouchArea, touchPoint.x, touchPoint.y)) { + delete multiTouchArea.pressedKeys[touchPoint.pointId]; + } + } + }) + } + onReleased: (touchPoints) => { + touchPoints.forEach(function (touchPoint) { + var key = multiTouchArea.pressedKeys[touchPoint.pointId] + if (key != null) { + key.handleRelease(multiTouchArea, touchPoint.x, touchPoint.y) + } + delete multiTouchArea.pressedKeys[touchPoint.pointId] + }) + } + } +} diff --git a/resources/qml/window.qml b/resources/qml/window.qml index 4f158fd..e8b9c92 100644 --- a/resources/qml/window.qml +++ b/resources/qml/window.qml @@ -24,7 +24,6 @@ FocusScope { property alias appName: _appName.text property bool supportsScaling: false - property bool supportsVirtualKeyboard: false property var qtfbKey: -1 property int appPid: -1 property bool minimized: false @@ -34,6 +33,15 @@ FocusScope { property var _height: root.globalHeight / 3 property var beforeFullscreenData: null + // Keyboard handling: + property var virtualKeyboardLayout: null + property var virtualKeyboardRef: null + property var keyboardConfig: ({ + enableStickyness: true, + keyUp: code => windowCanvas.virtualKeyboardKeyUp(code), + keyDown: code => windowCanvas.virtualKeyboardKeyDown(code), + }) + // External I/O from this component: signal closed function loadApplication(appId) { @@ -297,58 +305,28 @@ FocusScope { border.width: 2 border.color: "black" color: parent.color - visible: supportsVirtualKeyboard + visible: virtualKeyboardLayout !== null - TextArea { + Image { + source: "qrc:/appload/icons/keyboard" + sourceSize.width: 120 + sourceSize.height: 120 anchors.fill: parent - color: "white" - text: " " - - function sendVirtualKeyCode(code) { - windowCanvas.virtualKeyboardKeyDown(code); - - // Hold each key press for 100 milliseconds - (function(c) { - var t = Qt.createQmlObject('import QtQuick 2.5; Timer { interval: 100; repeat: false }', virtualKeyboardButton); - - t.triggered.connect(function() { - windowCanvas.virtualKeyboardKeyUp(c); - - t.destroy(); - }); - - t.start(); - })(code); - } + anchors.margins: 10 + } - onFocusChanged: { - if (focus) { - cursorPosition = 1; - sendVirtualKeyCode(0xf001) // Arbitrary unused keycode + MouseArea { + anchors.fill: parent + onClicked: () => { + if(root.virtualKeyboardRef.active && root.virtualKeyboardRef.config === root.keyboardConfig) { + root.virtualKeyboardRef.active = false; } else { - sendVirtualKeyCode(0xf000) + root.virtualKeyboardRef.config = root.keyboardConfig; + root.virtualKeyboardRef.layout = root.virtualKeyboardLayout; + root.virtualKeyboardRef.active = false; + root.virtualKeyboardRef.active = true; } } - - onTextChanged: { - if (text.length == 0) { // Backspace pressed - sendVirtualKeyCode(8) - } else if (text.indexOf('\n') != -1) { // Enter pressed - sendVirtualKeyCode(13) - } else if (text.length == 2) { // Regular key pressed - var lastChar = text.charAt(text.length - 1) - - if (text[0] != ' ') { - lastChar = text[0] - } - - sendVirtualKeyCode(lastChar.charCodeAt(0)) - } - - text = " "; - - cursorPosition = 1; - } } } @@ -438,6 +416,7 @@ FocusScope { unloadingFunction = loaderScaled.item?.unloading; } if(unloadingFunction) unloadingFunction(); + root.virtualKeyboardRef.active = false; root.closed(); } } diff --git a/resources/resources.qrc b/resources/resources.qrc index 1db00f3..41af136 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -3,8 +3,22 @@ icons/appload.svg icons/reload.svg icons/exit.svg + icons/keyboard.svg qml/appload.qml qml/window.qml + qml/virtualKeyboard/Keyboard.qml + qml/virtualKeyboard/Key.qml + + keyboard/default.layout.json + keyboard/backspace.png + keyboard/down.png + keyboard/enter.png + keyboard/left.png + keyboard/menu.png + keyboard/right.png + keyboard/shift.png + keyboard/tab.png + keyboard/up.png diff --git a/shim/src/input-shim.cpp b/shim/src/input-shim.cpp index 1197098..6630949 100644 --- a/shim/src/input-shim.cpp +++ b/shim/src/input-shim.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include "qtfb-client/qtfb-client.h" @@ -100,48 +99,40 @@ static int mapKey(int x) { return 0; } -const bool isShifted(int ascii) { - if (ascii >= '!' && ascii <= '&') return true; - if (ascii >= '(' && ascii <= '+') return true; - if (ascii >= ':' && ascii <= ':') return true; - if (ascii >= '<' && ascii <= '<') return true; - if (ascii >= '>' && ascii <= 'Z') return true; - if (ascii >= '^' && ascii <= '_') return true; - if (ascii >= '{' && ascii <= '~') return true; - - if (ascii >= 0xA2 && ascii <= 0xA3) return true; // ¢ to £ - if (ascii >= 0xA6 && ascii <= 0xA8) return true; // ¦ to ¨ - if (ascii >= 0xAF && ascii <= 0xB0) return true; // ¯ to ° - if (ascii >= 0xB9 && ascii <= 0xB0) return true; // ¹ to ° - if (ascii >= 0xC0 && ascii <= 0xD6) return true; // À to Ö - if (ascii >= 0xD8 && ascii <= 0xDE) return true; // Ø to Þ - - return false; -} - -std::tuple mapAsciiToX11Key(int ascii) { - const bool shifted = isShifted(ascii); - - // All ASCII printable characters - if (ascii >= ' ' && ascii <= '~') { - return {ascii, shifted}; +int mapAsciiToX11Key(int ascii) { + // Pure modifiers: + switch(ascii){ + case INPUT_VKB_SHIFTMOD: return 0xffe1; + case INPUT_VKB_CTRLMOD: return 0xffe3; + case INPUT_VKB_ALTMOD: return 0xffe9; } - - // All ASCII printable extended characters - if (ascii >= 0x00a0 && ascii <= 0x00ff) { - return {ascii, shifted}; + // So it's not a pure modifier. + // ASCII unprintable characters + switch (ascii & 0xFF) { + case 8: return 0xff08; // Backspace + case 9: return 0xff09; // Tab + case 13: return 0xff0d; // Enter/Return + case 27: return 0xff1b; // Escape + case 127: return 0xffff; // Delete + case INPUT_VKB_LEFT: return 0xff51; + case INPUT_VKB_UP: return 0xff52; + case INPUT_VKB_RIGHT: return 0xff53; + case INPUT_VKB_DOWN: return 0xff54; + case INPUT_VKB_HOME: return 0xff50; + case INPUT_VKB_END: return 0xff57; + case INPUT_VKB_PGUP: return 0xff55; + case INPUT_VKB_PGDOWN: return 0xff56; } - // ASCII unprintable characters - switch (ascii) { - case 8: return {0xff08, false}; // Backspace - case 9: return {0xff09, false}; // Tab - case 13: return {0xff0d, false}; // Enter/Return - case 27: return {0xff1b, false}; // Escape - case 127: return {0xffff, false}; // Delete + + // If shift key pressed, uppercase the ascii + if((ascii & INPUT_VKB_SHIFTMOD) && (ascii >= 'a') && (ascii <= 'z')) { + ascii -= ' '; } + // Mask the pure ascii: + ascii &= 0xFF; - return {0x0000, false}; + return 0; } static void pushToAll(int queueType, struct input_event evt) { @@ -263,7 +254,6 @@ static void pollInputUpdates() { pushToAll(QUEUE_PEN, evt(EV_ABS, ABS_PRESSURE, dTranslate)); pushToAll(QUEUE_PEN, evt(EV_SYN, SYN_REPORT, 0)); break; - case INPUT_BTN_PRESS: pushToAll(QUEUE_BUTTONS, evt(EV_KEY, mapKey(message.userInput.x), 1)); pushToAll(QUEUE_BUTTONS, evt(EV_SYN, SYN_REPORT, 0)); @@ -274,37 +264,15 @@ static void pollInputUpdates() { break; case INPUT_VKB_PRESS: { - auto [keySym, isShifted] = mapAsciiToX11Key(message.userInput.x); - - if (keySym != 0x0000) { - if (isShifted) { - pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_KEY, 0xffe1, 1)); - pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_SYN, SYN_REPORT, 0)); - } - - usleep(10000); - - pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_KEY, keySym, 1)); - pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_SYN, SYN_REPORT, 0)); - } - + int code = mapAsciiToX11Key(message.userInput.x); + pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_KEY, code, 1)); + pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_SYN, SYN_REPORT, 0)); break; } case INPUT_VKB_RELEASE: { - auto [keySym, isShifted] = mapAsciiToX11Key(message.userInput.x); - - if (keySym != 0x0000) { - pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_KEY, keySym, 0)); - pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_SYN, SYN_REPORT, 0)); - - usleep(10000); - - if (isShifted) { - pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_KEY, 0xffe1, 0)); - pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_SYN, SYN_REPORT, 0)); - } - } - + int code = mapAsciiToX11Key(message.userInput.x); + pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_KEY, code, 0)); + pushToAll(QUEUE_VIRTUALKEYBOARD, evt(EV_SYN, SYN_REPORT, 0)); break; } diff --git a/src/AppLibrary.h b/src/AppLibrary.h index 4c65eca..8edf706 100644 --- a/src/AppLibrary.h +++ b/src/AppLibrary.h @@ -12,6 +12,7 @@ #include #include "library.h" +#include "keyboard/layout.h" #define INTERNAL 0 #define EXTERNAL_NOGUI 1 @@ -24,24 +25,44 @@ class AppLoadApplication : public QObject { Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QString icon READ icon CONSTANT) Q_PROPERTY(bool supportsScaling READ supportsScaling) - Q_PROPERTY(bool supportsVirtualKeyboard READ supportsVirtualKeyboard) Q_PROPERTY(bool canHaveMultipleFrontends READ canHaveMultipleFrontends) Q_PROPERTY(int externalType READ externalType) // 0 - not external, 1 - external (non-graphics), 2 - external (qtfb) Q_PROPERTY(QString aspectRatio READ aspectRatio CONSTANT) Q_PROPERTY(bool disablesWindowedMode READ disablesWindowedMode CONSTANT) + Q_PROPERTY(const appload::vk::Layout *const virtualKeyboardLayout READ virtualKeyboardLayout CONSTANT) public: explicit AppLoadApplication(QObject *parent = nullptr) : QObject(parent) {} - AppLoadApplication(const QString &id, const QString &name, const QString &icon, bool supportsScaling, bool supportsVirtualKeyboard, bool canHaveMultipleFrontends, int externalType, appload::library::AspectRatio aspectRatio, bool disablesWindowedMode, QObject *parent = nullptr) - : QObject(parent), _id(id), _name(name), _icon(icon), _supportsScaling(supportsScaling), _supportsVirtualKeyboard(supportsVirtualKeyboard), _canHaveMultipleFrontends(canHaveMultipleFrontends), _externalType(externalType), _aspectRatio(aspectRatio), _disablesWindowedMode(disablesWindowedMode) {} + AppLoadApplication( + const QString &id, + const QString &name, + const QString &icon, + bool supportsScaling, + bool canHaveMultipleFrontends, + int externalType, + appload::library::AspectRatio aspectRatio, + bool disablesWindowedMode, + const appload::vk::Layout *vkLayout, + QObject *parent = nullptr + ): + QObject(parent), + _id(id), + _name(name), + _icon(icon), + _supportsScaling(supportsScaling), + _canHaveMultipleFrontends(canHaveMultipleFrontends), + _externalType(externalType), + _aspectRatio(aspectRatio), + _disablesWindowedMode(disablesWindowedMode), + _virtualKeyboardLayout(vkLayout) {} QString id() const { return _id; } QString name() const { return _name; } QString icon() const { return _icon; } QString aspectRatio() const { return appload::library::aspectRatioToString(_aspectRatio); } + const appload::vk::Layout *virtualKeyboardLayout() const { return _virtualKeyboardLayout; } bool supportsScaling() const { return _supportsScaling; } - bool supportsVirtualKeyboard() const { return _supportsVirtualKeyboard; } bool canHaveMultipleFrontends() const { return _canHaveMultipleFrontends; } int externalType() const { return _externalType; } bool disablesWindowedMode() const { return _disablesWindowedMode; } @@ -56,11 +77,13 @@ class AppLoadApplication : public QObject { int _externalType; appload::library::AspectRatio _aspectRatio; bool _disablesWindowedMode; + appload::vk::Layout const *_virtualKeyboardLayout; }; class AppLoadLibrary : public QObject { Q_OBJECT Q_PROPERTY(QQmlListProperty applications READ applications NOTIFY applicationsChanged) + Q_PROPERTY(const appload::vk::Layout *defaultLayout READ defaultLayout CONSTANT) public: explicit AppLoadLibrary(QObject *parent = nullptr) : QObject(parent) { appload::library::addGlobalLibraryHandle(this); } @@ -103,6 +126,8 @@ class AppLoadLibrary : public QObject { appload::library::terminateExternal(pid); } + const appload::vk::Layout *defaultLayout() const { return appload::library::defaultLayout; } + void loadList() { clearApplications(); for (const auto &entry : appload::library::getRef()) { @@ -110,11 +135,11 @@ class AppLoadLibrary : public QObject { entry.second->getAppName(), entry.second->getIconPath(), entry.second->supportsScaling(), - false, entry.second->canHaveMultipleFrontends(), INTERNAL, appload::library::AspectRatio::AUTO, false, + NULL, this)); } for (const auto &entry : appload::library::getExternals()) { @@ -122,11 +147,11 @@ class AppLoadLibrary : public QObject { entry.second->getAppName(), entry.second->getIconPath(), false, - entry.second->supportsVirtualKeyboard(), true, entry.second->isQTFB() ? EXTERNAL_QTFB : EXTERNAL_NOGUI, entry.second->getAspectRatio(), entry.second->disablesWindowedMode(), + entry.second->getVirtualKeyboardLayout(), this)); } } diff --git a/src/keyboard/layout.cpp b/src/keyboard/layout.cpp new file mode 100644 index 0000000..525c065 --- /dev/null +++ b/src/keyboard/layout.cpp @@ -0,0 +1,55 @@ +#include "layout.h" +#include "../log.h" + +#include + +// Row 0 must not have any non-1 width keys! +int appload::vk::Layout::rows() const { return _layout.size(); } +int appload::vk::Layout::columns() const { return _layout.empty() ? 0 : _layout[0].size(); } +int appload::vk::Layout::columnsInRow(int r) const { return (int) _layout.size() <= r ? 0 : _layout[r].size(); } + +const appload::vk::KeyData *appload::vk::Layout::key(int coln, int rown) const { + if((int) _layout.size() <= rown) return NULL; + const auto &row = _layout[rown]; + return (int) row.size() <= coln ? NULL : &row[coln]; +} + +void appload::vk::Layout::load(const QByteArray &src) { + QJsonParseError err; + QJsonDocument doc = QJsonDocument::fromJson(src, &err); + if(err.error != QJsonParseError::NoError) { + QDEBUG << "Failed to load keyboard layout" << err.errorString() << "@" << err.offset; + return; + } + if(!doc.isArray()) { + QDEBUG << "Expected an array as the root object of keyboard layout"; + return; + } + QJsonArray jsonRows = doc.array(); + for(const auto &row : jsonRows) { + if(!row.isArray()) { + QDEBUG << "Expected the row to be an array of keys"; + _layout.erase(_layout.begin(), _layout.end()); + return; + } + auto &newRow = _layout.emplace_back(); + for(const auto &key : row.toArray()) { + if(!key.isArray()) { + QDEBUG << "Expected the key definition tso be an array"; + _layout.erase(_layout.begin(), _layout.end()); + return; + } + const auto &keyArr = key.toArray(); + int keyCode = keyArr[1].toInt(); + newRow.emplace_back( + keyArr[0].toString(), + keyCode, + keyArr[2].toString(), + keyArr[3].toInt(), + keyArr[4].toInt(1), + keyCode >= 0x100000, + this + ); + } + } +} diff --git a/src/keyboard/layout.h b/src/keyboard/layout.h new file mode 100644 index 0000000..1126c54 --- /dev/null +++ b/src/keyboard/layout.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +namespace appload::vk { +class KeyData : public QObject{ + Q_OBJECT + Q_PROPERTY(QString label READ label CONSTANT) + Q_PROPERTY(int code READ code CONSTANT) + Q_PROPERTY(QString altLabel READ altLabel CONSTANT) + Q_PROPERTY(int altCode READ altCode CONSTANT) + Q_PROPERTY(int width READ width CONSTANT) + Q_PROPERTY(bool isModifier READ isModifier CONSTANT) +public: + QString label() const { return _label; } + QString altLabel() const { return _altLabel; } + int code() const { return _code; } + int altCode() const { return _altCode; } + int width() const { return _width; } + bool isModifier() const { return _isModifier; } + explicit KeyData(QObject *parent = nullptr): QObject(parent) {} + + KeyData(QString label, int code, QString altLabel, int altCode, int width, int isModifier, QObject *parent): + QObject(parent), + _label(label), + _code(code), + _altLabel(altLabel), + _altCode(altCode), + _width(width), + _isModifier(isModifier) {} + + KeyData(const appload::vk::KeyData& copy): + QObject(copy.parent()), + _label(copy._label), + _code(copy._code), + _altLabel(copy._altLabel), + _altCode(copy._altCode), + _width(copy._width), + _isModifier(copy._isModifier) {} + + +private: + QString _label; + int _code; + QString _altLabel; + int _altCode; + int _width; + bool _isModifier; +}; + +class Layout : public QObject{ + Q_OBJECT + // No `NOTIFY'. Once initialized, the Layout object is static. + Q_PROPERTY(int columns READ columns CONSTANT) + Q_PROPERTY(int rows READ rows CONSTANT) +public: + explicit Layout(QObject *parent = 0): QObject(parent) {}; + void load(const QByteArray &source); + + Q_INVOKABLE const KeyData *key(int col, int row) const; + Q_INVOKABLE int columnsInRow(int row) const; + int columns() const; + int rows() const; +private: + std::vector> _layout; +}; + +} \ No newline at end of file diff --git a/src/library.cpp b/src/library.cpp index 63ebf86..56001bc 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -152,9 +152,17 @@ appload::library::LoadedApplication::~LoadedApplication() { } } static std::mutex loadingMutex; +appload::vk::Layout *appload::library::defaultLayout = NULL; int appload::library::loadApplications() { loadingMutex.lock(); + defaultLayout = new appload::vk::Layout(NULL); + QResource defaultKeyboardLayout(QString(":/appload/keyboard/default.layout.json")); + if(!defaultKeyboardLayout.isValid()) { + QDEBUG << "Invalid data in default layout!"; + } else { + defaultLayout->load(defaultKeyboardLayout.uncompressedData()); + } // Make sure all apps are unloaded: for(auto entry : appload::library::applications) { if(entry.second->isFrontendRunning()) { diff --git a/src/library.h b/src/library.h index 17c9c2c..42f5d2e 100644 --- a/src/library.h +++ b/src/library.h @@ -30,6 +30,7 @@ #include #include #include "config.h" +#include "keyboard/layout.h" class AppLoadLibrary; @@ -38,6 +39,7 @@ namespace appload::library { ORIGINAL, MOVE, AUTO, }; QString aspectRatioToString(AspectRatio ratio); + extern appload::vk::Layout *defaultLayout; class ExternalApplication { public: @@ -49,6 +51,7 @@ namespace appload::library { AspectRatio getAspectRatio() const; bool disablesWindowedMode() const; bool supportsVirtualKeyboard() const; + const appload::vk::Layout *getVirtualKeyboardLayout() const; bool valid = false; @@ -63,7 +66,7 @@ namespace appload::library { std::map environment; bool _isQTFB; bool _disablesWindowedMode; - bool _supportsVirtualKeyboard; + const appload::vk::Layout *_virtualKeyboardLayout; AspectRatio aspectRatio; void parseManifest(); diff --git a/src/libraryexternals.cpp b/src/libraryexternals.cpp index 36d5485..58e1893 100644 --- a/src/libraryexternals.cpp +++ b/src/libraryexternals.cpp @@ -53,7 +53,10 @@ void appload::library::ExternalApplication::parseManifest() { // Optional: _isQTFB = jsonObject.value("qtfb").toBool(false); _disablesWindowedMode = jsonObject.value("disablesWindowedMode").toBool(false); - _supportsVirtualKeyboard = jsonObject.value("supportsVirtualKeyboard").toBool(false); + bool supportsVirtualKeyboard = jsonObject.value("supportsVirtualKeyboard").toBool(false); + if(supportsVirtualKeyboard) { + this->_virtualKeyboardLayout = appload::library::defaultLayout; + } workingDirectory = jsonObject.value("workingDirectory").toString(root); args = jsonObject.value("args").toVariant().toStringList(); QJsonObject env = jsonObject.value("environment").toObject(); @@ -136,8 +139,8 @@ bool appload::library::ExternalApplication::disablesWindowedMode() const { return _disablesWindowedMode; } -bool appload::library::ExternalApplication::supportsVirtualKeyboard() const { - return _supportsVirtualKeyboard; +const appload::vk::Layout *appload::library::ExternalApplication::getVirtualKeyboardLayout() const { + return _virtualKeyboardLayout; } void appload::library::terminateExternal(qint64 pid) { diff --git a/src/qtfb/common.h b/src/qtfb/common.h index cc1bb64..4f4a2ba 100644 --- a/src/qtfb/common.h +++ b/src/qtfb/common.h @@ -57,6 +57,19 @@ #define INPUT_BTN_X_HOME 1 #define INPUT_BTN_X_RIGHT 2 +#define INPUT_VKB_SHIFTMOD 0x100000 +#define INPUT_VKB_CTRLMOD 0x200000 +#define INPUT_VKB_ALTMOD 0x400000 +#define INPUT_VKB_DEL 0x7f +#define INPUT_VKB_PGUP 0x80 +#define INPUT_VKB_PGDOWN 0x81 +#define INPUT_VKB_DOWN 0x82 +#define INPUT_VKB_UP 0x83 +#define INPUT_VKB_LEFT 0x84 +#define INPUT_VKB_RIGHT 0x85 +#define INPUT_VKB_HOME 0x86 +#define INPUT_VKB_END 0x87 + namespace qtfb { typedef int FBKey; diff --git a/xovi/template/appload.qmd b/xovi/template/appload.qmd index bd0df1c..40a5ac3 100644 --- a/xovi/template/appload.qmd +++ b/xovi/template/appload.qmd @@ -25,7 +25,8 @@ AFFECT [[8850026134298937527]] ~&7083172477658&~: "qrc:/appload/qml/appload.qml" ~&7713401454751279&~: { - _appLoadView.~&6503936152&~.absoluteRoot = ~&6504254477&~; + _appLoadView.~&6503936152&~.absoluteRoot = windowParent; + _appLoadView.item.virtualKeyboardRef = _apploadVirtualKeyboard; } } @@ -37,6 +38,30 @@ AFFECT [[8850026134298937527]] } } } + LOCATE AFTER ALL + INSERT { + Item { + id: windowParent + anchors.fill: parent + } + Loader { + id: _apploadVirtualKeyboard + + property var layout: null + property var config: null + width: parent.width + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + active: false + visible: active + source: "qrc:/appload/qml/virtualKeyboard/Keyboard.qml" + + onLoaded: () => { + console.log("Rebuild keyboard data: ", _apploadVirtualKeyboard.layout, _apploadVirtualKeyboard.config); + _apploadVirtualKeyboard.item.rebuildKeyboard(_apploadVirtualKeyboard.layout, _apploadVirtualKeyboard.config); + } + } + } END TRAVERSE END AFFECT diff --git a/xovi/template/src/main.cpp b/xovi/template/src/main.cpp index 50ca839..27116c6 100644 --- a/xovi/template/src/main.cpp +++ b/xovi/template/src/main.cpp @@ -19,8 +19,6 @@ extern "C" { static const char *applicationRoot; void _xovi_construct() { applicationRoot = Environment->getExtensionDirectory("appload"); - appload::library::loadApplications(); - qtfb::management::start(); qmlRegisterType("net.asivery.AppLoad", 1, 0, "AppLoad"); qmlRegisterType("net.asivery.AppLoad", 1, 0, "AppLoadCoordinator"); @@ -38,6 +36,9 @@ extern "C" { qt_resource_rebuilder$qmldiff_disable_slots_while_processing(); qRegisterResourceData(3, qt_resource_struct, qt_resource_name, qt_resource_data); qt_resource_rebuilder$qmldiff_enable_slots_while_processing(); + + appload::library::loadApplications(); + qtfb::management::start(); } } From b3e7fdcc9a33b709284bebe6783ae22ec9782345 Mon Sep 17 00:00:00 2001 From: asivery Date: Sat, 7 Feb 2026 22:12:43 +0100 Subject: [PATCH 4/5] Add cargo.toml to example, fix .gitignore --- .gitignore | 5 ++++- examples/appload/full/backend/Cargo.lock | 3 ++- examples/appload/full/backend/Cargo.toml | 10 ++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 examples/appload/full/backend/Cargo.toml diff --git a/.gitignore b/.gitignore index faa1c28..f29ced0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ /shim/build /xovi/temporary/ .qmake.stash -appload +/appload Makefile **/*.so +examples/appload/full/backend/target/ +examples/appload/full/output/ +examples/appload/full/output-rmpp/ diff --git a/examples/appload/full/backend/Cargo.lock b/examples/appload/full/backend/Cargo.lock index 287d561..bfa7d10 100644 --- a/examples/appload/full/backend/Cargo.lock +++ b/examples/appload/full/backend/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -30,6 +30,7 @@ dependencies = [ "anyhow", "async-trait", "libc", + "tokio", ] [[package]] diff --git a/examples/appload/full/backend/Cargo.toml b/examples/appload/full/backend/Cargo.toml new file mode 100644 index 0000000..b04cbb2 --- /dev/null +++ b/examples/appload/full/backend/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "backend" +version = "0.1.0" +edition = "2021" + +[dependencies] +appload-client = { path = "../../../../backends/appload-clients/rust-backend" } +async-trait = "0.1.83" +tokio = { version = "1.42.0", features = ["macros", "rt", "rt-multi-thread"] } + From 6aa6368023834bc902c0ef50059b1ab73ea3f8a9 Mon Sep 17 00:00:00 2001 From: asivery Date: Mon, 9 Feb 2026 03:43:41 +0100 Subject: [PATCH 5/5] Do not duplicate extra args --- src/libraryexternals.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraryexternals.cpp b/src/libraryexternals.cpp index 7788b10..1888f9d 100644 --- a/src/libraryexternals.cpp +++ b/src/libraryexternals.cpp @@ -99,7 +99,6 @@ qint64 appload::library::ExternalApplication::launch(int qtfbKey, QStringList ex process->setWorkingDirectory(workingDirectory); process->setProcessChannelMode(QProcess::ForwardedChannels); QStringList finalArgs = args + extraArgs; - for(const auto &arg : extraArgs) finalArgs.append(arg); process->start(execPath, finalArgs); if (!process->waitForStarted()) {