From 02d557d83b47db72dfdedd7d5b07e72222c0c065 Mon Sep 17 00:00:00 2001 From: Nmstr Date: Tue, 26 Aug 2025 15:37:21 +0200 Subject: [PATCH 1/4] layout now loads from file --- WayOSK/Keyboard.qml | 40 +++++++--------------- WayOSK/{ => KeyboardUtils}/KeyboardKey.qml | 0 WayOSK/KeyboardUtils/qmldir | 1 + WayOSK/config_handler.py | 16 +++++++++ WayOSK/layouts/de_de.qml | 32 +++++++++++++++++ WayOSK/main.py | 16 ++++++++- 6 files changed, 77 insertions(+), 28 deletions(-) rename WayOSK/{ => KeyboardUtils}/KeyboardKey.qml (100%) create mode 100644 WayOSK/KeyboardUtils/qmldir create mode 100644 WayOSK/config_handler.py create mode 100644 WayOSK/layouts/de_de.qml diff --git a/WayOSK/Keyboard.qml b/WayOSK/Keyboard.qml index d87830f..471d163 100644 --- a/WayOSK/Keyboard.qml +++ b/WayOSK/Keyboard.qml @@ -14,39 +14,25 @@ PanelWindow { right: true } - property var keyRows: [ - ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"], - ["A", "S", "D", "F", "G", "H", "J", "K", "L"], - ["Z", "X", "C", "V", "B", "N", "M"] - ] - + property string layoutPath: "" + Socket { id: keyboardSocket path: "/tmp/wayosk.sock" } - Rectangle { - anchors.fill: parent - color: "gray" - opacity: 0.75 - - Column { - anchors.centerIn: parent - spacing: 8 + IpcHandler { + target: "keyboard" - Repeater { - model: keyRows - delegate: Row { - spacing: 8 - Repeater { - model: modelData - delegate: KeyboardKey { - keyValue: modelData - socketConnection: keyboardSocket - } - } - } - } + function setLayoutPath(path: string): void { + keyboard.layoutPath = path } } + + Loader { + id: keyboardLoader + anchors.fill: parent + source: keyboard.layoutPath + active: keyboard.layoutPath !== "" + } } diff --git a/WayOSK/KeyboardKey.qml b/WayOSK/KeyboardUtils/KeyboardKey.qml similarity index 100% rename from WayOSK/KeyboardKey.qml rename to WayOSK/KeyboardUtils/KeyboardKey.qml diff --git a/WayOSK/KeyboardUtils/qmldir b/WayOSK/KeyboardUtils/qmldir new file mode 100644 index 0000000..9400f54 --- /dev/null +++ b/WayOSK/KeyboardUtils/qmldir @@ -0,0 +1 @@ +KeyboardKey 1.0 KeyboardKey.qml diff --git a/WayOSK/config_handler.py b/WayOSK/config_handler.py new file mode 100644 index 0000000..65674f9 --- /dev/null +++ b/WayOSK/config_handler.py @@ -0,0 +1,16 @@ +import configparser +import os + +class ConfigHandler: + def __init__(self) -> None: + self.config_file = os.path.expanduser(os.path.join("~", ".config", "WayOSK", "config.ini")) + self.config = configparser.ConfigParser() + self.config.read(self.config_file) + + def get_layout_path(self) -> str: + layout = self.config.get("Keyboard", "layout", fallback="de_de") + layout_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "layouts") + if f"{layout}.qml" not in os.listdir(layout_dir): + print("Layout not found, aborting") + return "" + return os.path.join(layout_dir, f"{layout}.qml") diff --git a/WayOSK/layouts/de_de.qml b/WayOSK/layouts/de_de.qml new file mode 100644 index 0000000..6921127 --- /dev/null +++ b/WayOSK/layouts/de_de.qml @@ -0,0 +1,32 @@ +import QtQuick +import KeyboardUtils + +Rectangle { + id: customKeyboard + color: "gray" + opacity: 0.75 + property var keyRows: [ + ["Q", "W", "E", "R", "T", "Z", "U", "I", "O", "P"], + ["A", "S", "D", "F", "G", "H", "J", "K", "L"], + ["Y", "X", "C", "V", "B", "N", "M"] + ] + + Column { + anchors.centerIn: parent + spacing: 8 + + Repeater { + model: keyRows + delegate: Row { + spacing: 8 + Repeater { + model: modelData + delegate: KeyboardKey { + keyValue: modelData + socketConnection: keyboardSocket + } + } + } + } + } +} diff --git a/WayOSK/main.py b/WayOSK/main.py index 517d5fd..d304bbb 100644 --- a/WayOSK/main.py +++ b/WayOSK/main.py @@ -1,6 +1,8 @@ from keyboard_server import KeyboardServer +from config_handler import ConfigHandler import subprocess import shutil +import time import sys import os @@ -10,8 +12,20 @@ sys.exit("Error: 'quickshell' is not installed. Please install it: 'https://quickshell.org/'") server = KeyboardServer(SOCKET_PATH) +config_handler = ConfigHandler() try: - subprocess.run(["quickshell", "-p", f"{os.path.dirname(os.path.abspath(__file__))}/main.qml"]) + utils_path = os.path.dirname(os.path.abspath(__file__)) + os.environ["QML_IMPORT_PATH"] = utils_path + + quickshell_process = subprocess.Popen(["quickshell", "-p", f"{os.path.dirname(os.path.abspath(__file__))}/main.qml"]) + + layout_path = config_handler.get_layout_path() + ipc_process = None + while ipc_process != 0: + ipc_process = subprocess.call(["quickshell", "ipc", "--pid", str(quickshell_process.pid), "call", "keyboard", "setLayoutPath", layout_path]) + time.sleep(0.1) + + quickshell_process.wait() finally: server.cleanup() From 05c1aff14bc123fb6192f68cd755c544ba125e11 Mon Sep 17 00:00:00 2001 From: Nmstr Date: Tue, 26 Aug 2025 15:44:51 +0200 Subject: [PATCH 2/4] improved KeyboardKey --- WayOSK/KeyboardUtils/KeyboardKey.qml | 7 ++++--- WayOSK/keyboard_server.py | 2 +- WayOSK/layouts/de_de.qml | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/WayOSK/KeyboardUtils/KeyboardKey.qml b/WayOSK/KeyboardUtils/KeyboardKey.qml index efd53fc..a57a7e7 100644 --- a/WayOSK/KeyboardUtils/KeyboardKey.qml +++ b/WayOSK/KeyboardUtils/KeyboardKey.qml @@ -4,10 +4,11 @@ import QtQuick.Controls Button { id: keyButton - property string keyValue: "" + property string key: "" + property string label: "" property Socket socketConnection: null - text: keyValue + text: label width: 40 height: 40 font.pixelSize: 20 @@ -20,7 +21,7 @@ Button { onClicked: { if (socketConnection) { socketConnection.connected = true - socketConnection.write(keyValue) + socketConnection.write(key) socketConnection.flush() } else { console.error("Socket is not connected") diff --git a/WayOSK/keyboard_server.py b/WayOSK/keyboard_server.py index f9e4ecb..e39e5dc 100644 --- a/WayOSK/keyboard_server.py +++ b/WayOSK/keyboard_server.py @@ -29,7 +29,7 @@ def listen_for_keys(self): if not data: continue - wk.click(f"KEY_{data}") + wk.click(data) def cleanup(self): if self.server: diff --git a/WayOSK/layouts/de_de.qml b/WayOSK/layouts/de_de.qml index 6921127..42be592 100644 --- a/WayOSK/layouts/de_de.qml +++ b/WayOSK/layouts/de_de.qml @@ -22,7 +22,8 @@ Rectangle { Repeater { model: modelData delegate: KeyboardKey { - keyValue: modelData + key: "KEY_" + modelData + label: modelData socketConnection: keyboardSocket } } From a2b283c75c232b381fdb19fe41a416ee656b20c2 Mon Sep 17 00:00:00 2001 From: Nmstr Date: Tue, 26 Aug 2025 15:51:50 +0200 Subject: [PATCH 3/4] simplified KeyboardKey usage --- WayOSK/Keyboard.qml | 1 + WayOSK/KeyboardUtils/KeyboardKey.qml | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/WayOSK/Keyboard.qml b/WayOSK/Keyboard.qml index 471d163..8fac2b5 100644 --- a/WayOSK/Keyboard.qml +++ b/WayOSK/Keyboard.qml @@ -15,6 +15,7 @@ PanelWindow { } property string layoutPath: "" + property Socket keyboardSocket: keyboardSocket Socket { id: keyboardSocket diff --git a/WayOSK/KeyboardUtils/KeyboardKey.qml b/WayOSK/KeyboardUtils/KeyboardKey.qml index a57a7e7..685c2bf 100644 --- a/WayOSK/KeyboardUtils/KeyboardKey.qml +++ b/WayOSK/KeyboardUtils/KeyboardKey.qml @@ -6,7 +6,6 @@ Button { id: keyButton property string key: "" property string label: "" - property Socket socketConnection: null text: label width: 40 @@ -19,10 +18,10 @@ Button { } onClicked: { - if (socketConnection) { - socketConnection.connected = true - socketConnection.write(key) - socketConnection.flush() + if (keyboard.keyboardSocket) { + keyboard.keyboardSocket.connected = true + keyboard.keyboardSocket.write(key) + keyboard.keyboardSocket.flush() } else { console.error("Socket is not connected") } From 76bc76283f92f77534a224f23d06d8d3416e1218 Mon Sep 17 00:00:00 2001 From: Nmstr Date: Tue, 26 Aug 2025 21:23:57 +0200 Subject: [PATCH 4/4] updated de_de layout --- WayOSK/layouts/de_de.qml | 127 ++++++++++++++++++++++++++++++++++----- 1 file changed, 112 insertions(+), 15 deletions(-) diff --git a/WayOSK/layouts/de_de.qml b/WayOSK/layouts/de_de.qml index 42be592..afcb383 100644 --- a/WayOSK/layouts/de_de.qml +++ b/WayOSK/layouts/de_de.qml @@ -1,30 +1,127 @@ import QtQuick +import QtQuick.Layouts import KeyboardUtils Rectangle { id: customKeyboard color: "gray" opacity: 0.75 - property var keyRows: [ - ["Q", "W", "E", "R", "T", "Z", "U", "I", "O", "P"], - ["A", "S", "D", "F", "G", "H", "J", "K", "L"], - ["Y", "X", "C", "V", "B", "N", "M"] + + property var keyRows : [ + [ + { key: "KEY_ESCAPE", label: "Esc" }, + { key: "KEY_F1", label: "F1" }, + { key: "KEY_F2", label: "F2" }, + { key: "KEY_F3", label: "F3" }, + { key: "KEY_F4", label: "F4" }, + { key: "KEY_F5", label: "F5" }, + { key: "KEY_F6", label: "F6" }, + { key: "KEY_F7", label: "F7" }, + { key: "KEY_F8", label: "F8" }, + { key: "KEY_F9", label: "F9" }, + { key: "KEY_F10", label: "F10" }, + { key: "KEY_F11", label: "F11" }, + { key: "KEY_F12", label: "F12" } + ], + [ + { key: "KEY_GRAVE", label: "^" }, + { key: "KEY_1", label: "1" }, + { key: "KEY_2", label: "2" }, + { key: "KEY_3", label: "3" }, + { key: "KEY_4", label: "4" }, + { key: "KEY_5", label: "5" }, + { key: "KEY_6", label: "6" }, + { key: "KEY_7", label: "7" }, + { key: "KEY_8", label: "8" }, + { key: "KEY_9", label: "9" }, + { key: "KEY_0", label: "0" }, + { key: "KEY_MINUS", label: "ß" }, + { key: "KEY_EQUAL", label: "´" }, + { key: "KEY_BACKSPACE", label: "Backspace", width: 2 } + ], + [ + { key: "KEY_TAB", label: "Tab", width: 1.25 }, + { key: "KEY_Q", label: "Q" }, + { key: "KEY_W", label: "W" }, + { key: "KEY_E", label: "E" }, + { key: "KEY_R", label: "R" }, + { key: "KEY_T", label: "T" }, + { key: "KEY_Y", label: "Z" }, + { key: "KEY_U", label: "U" }, + { key: "KEY_I", label: "I" }, + { key: "KEY_O", label: "O" }, + { key: "KEY_P", label: "P" }, + { key: "KEY_LEFTBRACE", label: "Ü" }, + { key: "KEY_RIGHTBRACE", label: "+" }, + { key: "KEY_ENTER", label: "Enter", width: 1.25 } + ], + [ + { key: "KEY_CAPSLOCK", label: "Caps", width: 1.5 }, + { key: "KEY_A", label: "A" }, + { key: "KEY_S", label: "S" }, + { key: "KEY_D", label: "D" }, + { key: "KEY_F", label: "F" }, + { key: "KEY_G", label: "G" }, + { key: "KEY_H", label: "H" }, + { key: "KEY_J", label: "J" }, + { key: "KEY_K", label: "K" }, + { key: "KEY_L", label: "L" }, + { key: "KEY_SEMICOLON", label: "Ö" }, + { key: "KEY_APOSTROPHE", label: "Ä" }, + { key: "KEY_BACKSLASH", label: "#" }, + { key: "KEY_ENTER", label: "Enter" } + ], + [ + { key: "KEY_LEFTSHIFT", label: "Shift", width: 1.25 }, + { key: "KEY_LESS", label: "<" }, + { key: "KEY_Z", label: "Y" }, + { key: "KEY_X", label: "X" }, + { key: "KEY_C", label: "C" }, + { key: "KEY_V", label: "V" }, + { key: "KEY_B", label: "B" }, + { key: "KEY_N", label: "N" }, + { key: "KEY_M", label: "M" }, + { key: "KEY_COMMA", label: "," }, + { key: "KEY_DOT", label: "." }, + { key: "KEY_SLASH", label: "-" }, + { key: "KEY_RIGHTSHIFT", label: "Shift", width: 1.25 } + ], + [ + { key: "KEY_LEFTCTRL", label: "Ctrl" }, + { key: "KEY_LEFTMETA", label: "Meta" }, + { key: "KEY_LEFTALT", label: "Alt" }, + { key: "KEY_SPACE", label: "Space", width: 6 }, + { key: "KEY_RIGHTALT", label: "AltGr" }, + { key: "KEY_RIGHTMETA", label: "Meta" }, + { key: "KEY_RIGHTCTRL", label: "Ctrl" } + ], ] - Column { - anchors.centerIn: parent - spacing: 8 + ColumnLayout { + anchors.fill: parent + anchors.margins: 10 + spacing: 5 Repeater { - model: keyRows - delegate: Row { - spacing: 8 + model: keyRows.length + + RowLayout { + Layout.fillWidth: true + Layout.fillHeight: true + spacing: 5 + + property var rowKeys: keyRows[index] + Repeater { - model: modelData - delegate: KeyboardKey { - key: "KEY_" + modelData - label: modelData - socketConnection: keyboardSocket + model: rowKeys + + KeyboardKey { + key: modelData.key + label: modelData.label + Layout.fillWidth: true + Layout.preferredWidth: (modelData.width || 1) * 40 + Layout.minimumWidth: 40 + Layout.fillHeight: true } } }