diff --git a/WayOSK/Indicator.qml b/WayOSK/Indicator.qml new file mode 100644 index 0000000..d58721c --- /dev/null +++ b/WayOSK/Indicator.qml @@ -0,0 +1,45 @@ +import Quickshell +import QtQuick + +PopupWindow { + id: indicator + + anchor.window: root + implicitWidth: 50 + implicitHeight: 50 + color: "transparent" + visible: true + + MouseArea { + anchors.fill: parent + drag.target: null + + property real dragOffsetX: 0 + property real dragOffsetY: 0 + property real startX: 0 + property real startY: 0 + + onPressed: { + startX = indicator.anchor.rect.x + startY = indicator.anchor.rect.y + dragOffsetX = mouse.x + dragOffsetY = mouse.y + } + onPositionChanged: { + indicator.anchor.rect.x = startX + (mouse.x - dragOffsetX) + indicator.anchor.rect.y = startY + (mouse.y - dragOffsetY) + } + onReleased: { + if (startX === indicator.anchor.rect.x && startY === indicator.anchor.rect.y) { + keyboard.visible = !keyboard.visible + } + } + } + + Rectangle { + anchors.fill: parent + color: "gray" + radius: 50 + opacity: 0.5 + } +} diff --git a/WayOSK/Keyboard.qml b/WayOSK/Keyboard.qml new file mode 100644 index 0000000..d87830f --- /dev/null +++ b/WayOSK/Keyboard.qml @@ -0,0 +1,52 @@ +import Quickshell +import Quickshell.Io +import QtQuick + +PanelWindow { + id: keyboard + exclusionMode: ExclusionMode.Ignore + color: "transparent" + implicitHeight: 400 + + anchors { + bottom: true + left: true + 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"] + ] + + Socket { + id: keyboardSocket + path: "/tmp/wayosk.sock" + } + + Rectangle { + anchors.fill: parent + color: "gray" + opacity: 0.75 + + 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/KeyboardKey.qml b/WayOSK/KeyboardKey.qml new file mode 100644 index 0000000..efd53fc --- /dev/null +++ b/WayOSK/KeyboardKey.qml @@ -0,0 +1,29 @@ +import Quickshell.Io +import QtQuick +import QtQuick.Controls + +Button { + id: keyButton + property string keyValue: "" + property Socket socketConnection: null + + text: keyValue + width: 40 + height: 40 + font.pixelSize: 20 + background: Rectangle { + color: keyButton.down ? "gray" : "lightgray" + border.color: "gray" + radius: 5 + } + + onClicked: { + if (socketConnection) { + socketConnection.connected = true + socketConnection.write(keyValue) + socketConnection.flush() + } else { + console.error("Socket is not connected") + } + } +} diff --git a/WayOSK/keyboard_server.py b/WayOSK/keyboard_server.py new file mode 100644 index 0000000..f9e4ecb --- /dev/null +++ b/WayOSK/keyboard_server.py @@ -0,0 +1,38 @@ +import WayKey as wk +import threading +import socket +import os + +class KeyboardServer: + def __init__(self, socket_path: str): + self.socket_path = socket_path + self.server = None + self.thread = None + self.start_server() + + def start_server(self): + if os.path.exists(self.socket_path): + os.unlink(self.socket_path) + + self.server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.server.bind(self.socket_path) + self.server.listen(1) + + self.thread = threading.Thread(target=self.listen_for_keys, daemon=True) + self.thread.start() + + def listen_for_keys(self): + while True: + conn, _ = self.server.accept() + with conn: + data = conn.recv(1024).decode("utf-8") + if not data: + continue + + wk.click(f"KEY_{data}") + + def cleanup(self): + if self.server: + self.server.close() + if os.path.exists(self.socket_path): + os.unlink(self.socket_path) diff --git a/WayOSK/main.py b/WayOSK/main.py index e69de29..517d5fd 100644 --- a/WayOSK/main.py +++ b/WayOSK/main.py @@ -0,0 +1,17 @@ +from keyboard_server import KeyboardServer +import subprocess +import shutil +import sys +import os + +SOCKET_PATH = "/tmp/wayosk.sock" + +if shutil.which("quickshell") is None: + sys.exit("Error: 'quickshell' is not installed. Please install it: 'https://quickshell.org/'") + +server = KeyboardServer(SOCKET_PATH) + +try: + subprocess.run(["quickshell", "-p", f"{os.path.dirname(os.path.abspath(__file__))}/main.qml"]) +finally: + server.cleanup() diff --git a/WayOSK/main.qml b/WayOSK/main.qml new file mode 100644 index 0000000..451912f --- /dev/null +++ b/WayOSK/main.qml @@ -0,0 +1,26 @@ +import Quickshell + +Scope { + PanelWindow { + id: root + exclusionMode: ExclusionMode.Ignore + aboveWindows: false + color: "transparent" + + anchors { + top: true + bottom: true + left: true + right: true + } + + Indicator { + id: indicator + } + + Keyboard { + id: keyboard + visible: false + } + } +} diff --git a/pyproject.toml b/pyproject.toml index 7c18043..736308f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,9 @@ classifiers = [ "Operating System :: POSIX :: Linux", "Intended Audience :: End Users/Desktop" ] -dependencies = [] +dependencies = [ + "waykey>=0.1.0", +] [project.urls] Source = "https://github.com/Nmstr/WayOsk" Issues = "https://github.com/Nmstr/WayOsk/issues" diff --git a/uv.lock b/uv.lock index 6a08dcd..a8db7e1 100644 --- a/uv.lock +++ b/uv.lock @@ -2,7 +2,85 @@ version = 1 revision = 3 requires-python = ">=3.13" +[[package]] +name = "cython" +version = "3.1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/ab/915337fb39ab4f4539a313df38fc69938df3bf14141b90d61dfd5c2919de/cython-3.1.3.tar.gz", hash = "sha256:10ee785e42328924b78f75a74f66a813cb956b4a9bc91c44816d089d5934c089", size = 3186689, upload-time = "2025-08-13T06:19:13.619Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/93/0e5dfcc6215a6c2cae509d7e40f8fb197237ba5998c936e9c19692f8eedf/cython-3.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9458d540ef0853ea4fc65b8a946587bd483ef7244b470b3d93424eb7b04edeb1", size = 2998232, upload-time = "2025-08-13T06:20:35.817Z" }, + { url = "https://files.pythonhosted.org/packages/6b/6c/01b22de45e3a9b86fbe4a18cd470146514209448cb4d3d3ba9c72390d45b/cython-3.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:32d1b22c3b231326e9f16480a7f508c6841bbf7d0615c2d6f489ebc72dd05205", size = 2830052, upload-time = "2025-08-13T06:20:37.71Z" }, + { url = "https://files.pythonhosted.org/packages/23/0e/6e535f2eedf0ddc3c84b087e5d0f04a7b88d8229ec8c27be41a142bcbbfa/cython-3.1.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62b0a9514b68391aae9784405b65738bbe19cdead3dd7b90dd9e963281db1ee3", size = 2995613, upload-time = "2025-08-13T06:20:59.408Z" }, + { url = "https://files.pythonhosted.org/packages/77/10/3c9e2abf315f608bc22f49b6f9ee66859c23e07edbf484522d5f27b61ab7/cython-3.1.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:976db373c315f342dcb24cd65b5e4c08d2c7b42f9f6ac1b3f677eb2abc9bfb0f", size = 2841282, upload-time = "2025-08-13T06:21:01.274Z" }, + { url = "https://files.pythonhosted.org/packages/56/c8/46ac27096684f33e27dab749ef43c6b0119c6a0d852971eaefb73256dc4c/cython-3.1.3-py3-none-any.whl", hash = "sha256:d13025b34f72f77bf7f65c1cd628914763e6c285f4deb934314c922b91e6be5a", size = 1225725, upload-time = "2025-08-13T06:19:09.593Z" }, +] + +[[package]] +name = "evdev" +version = "1.9.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/fe/a17c106a1f4061ce83f04d14bcedcfb2c38c7793ea56bfb906a6fadae8cb/evdev-1.9.2.tar.gz", hash = "sha256:5d3278892ce1f92a74d6bf888cc8525d9f68af85dbe336c95d1c87fb8f423069", size = 33301, upload-time = "2025-05-01T19:53:47.69Z" } + +[[package]] +name = "pyobjc-core" +version = "11.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/e9/0b85c81e2b441267bca707b5d89f56c2f02578ef8f3eafddf0e0c0b8848c/pyobjc_core-11.1.tar.gz", hash = "sha256:b63d4d90c5df7e762f34739b39cc55bc63dbcf9fb2fb3f2671e528488c7a87fe", size = 974602, upload-time = "2025-06-14T20:56:34.189Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/24/12e4e2dae5f85fd0c0b696404ed3374ea6ca398e7db886d4f1322eb30799/pyobjc_core-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:18986f83998fbd5d3f56d8a8428b2f3e0754fd15cef3ef786ca0d29619024f2c", size = 676431, upload-time = "2025-06-14T20:44:49.908Z" }, + { url = "https://files.pythonhosted.org/packages/f7/79/031492497624de4c728f1857181b06ce8c56444db4d49418fa459cba217c/pyobjc_core-11.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:8849e78cfe6595c4911fbba29683decfb0bf57a350aed8a43316976ba6f659d2", size = 719330, upload-time = "2025-06-14T20:44:51.621Z" }, + { url = "https://files.pythonhosted.org/packages/ed/7d/6169f16a0c7ec15b9381f8bf33872baf912de2ef68d96c798ca4c6ee641f/pyobjc_core-11.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8cb9ed17a8d84a312a6e8b665dd22393d48336ea1d8277e7ad20c19a38edf731", size = 667203, upload-time = "2025-06-14T20:44:53.262Z" }, + { url = "https://files.pythonhosted.org/packages/49/0f/f5ab2b0e57430a3bec9a62b6153c0e79c05a30d77b564efdb9f9446eeac5/pyobjc_core-11.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:f2455683e807f8541f0d83fbba0f5d9a46128ab0d5cc83ea208f0bec759b7f96", size = 708807, upload-time = "2025-06-14T20:44:54.851Z" }, +] + +[[package]] +name = "pyobjc-framework-cocoa" +version = "11.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyobjc-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/c5/7a866d24bc026f79239b74d05e2cf3088b03263da66d53d1b4cf5207f5ae/pyobjc_framework_cocoa-11.1.tar.gz", hash = "sha256:87df76b9b73e7ca699a828ff112564b59251bb9bbe72e610e670a4dc9940d038", size = 5565335, upload-time = "2025-06-14T20:56:59.683Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/0b/a01477cde2a040f97e226f3e15e5ffd1268fcb6d1d664885a95ba592eca9/pyobjc_framework_cocoa-11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:54e93e1d9b0fc41c032582a6f0834befe1d418d73893968f3f450281b11603da", size = 389049, upload-time = "2025-06-14T20:46:53.757Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/64cf2661f6ab7c124d0486ec6d1d01a9bb2838a0d2a46006457d8c5e6845/pyobjc_framework_cocoa-11.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:fd5245ee1997d93e78b72703be1289d75d88ff6490af94462b564892e9266350", size = 393110, upload-time = "2025-06-14T20:46:54.894Z" }, + { url = "https://files.pythonhosted.org/packages/33/87/01e35c5a3c5bbdc93d5925366421e10835fcd7b23347b6c267df1b16d0b3/pyobjc_framework_cocoa-11.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:aede53a1afc5433e1e7d66568cc52acceeb171b0a6005407a42e8e82580b4fc0", size = 392644, upload-time = "2025-06-14T20:46:56.503Z" }, + { url = "https://files.pythonhosted.org/packages/c1/7c/54afe9ffee547c41e1161691e72067a37ed27466ac71c089bfdcd07ca70d/pyobjc_framework_cocoa-11.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:1b5de4e1757bb65689d6dc1f8d8717de9ec8587eb0c4831c134f13aba29f9b71", size = 396742, upload-time = "2025-06-14T20:46:57.64Z" }, +] + +[[package]] +name = "screeninfo" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cython", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ec/bb/e69e5e628d43f118e0af4fc063c20058faa8635c95a1296764acc8167e27/screeninfo-0.8.1.tar.gz", hash = "sha256:9983076bcc7e34402a1a9e4d7dabf3729411fd2abb3f3b4be7eba73519cd2ed1", size = 10666, upload-time = "2022-09-09T11:35:23.419Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/bf/c5205d480307bef660e56544b9e3d7ff687da776abb30c9cb3f330887570/screeninfo-0.8.1-py3-none-any.whl", hash = "sha256:e97d6b173856edcfa3bd282f81deb528188aff14b11ec3e195584e7641be733c", size = 12907, upload-time = "2022-09-09T11:35:21.351Z" }, +] + +[[package]] +name = "waykey" +version = "0.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "evdev" }, + { name = "screeninfo" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/f1/99a98c6672662792afdf83a7878d3cfd7229c3cd7e5768ca0c349fdab243/waykey-0.1.0.tar.gz", hash = "sha256:f487b53cf73299063231d104f7b010671b0f95bb3a5e2bb9c20220b2f481bc6d", size = 2044, upload-time = "2025-08-23T23:38:14.588Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/8b/e049e61b2c7c23272b388924e407f3ed9ef8162b33095aa4d433e7bfa1c0/waykey-0.1.0-py3-none-any.whl", hash = "sha256:f8326feaf8ea42ec40d062d1b85e1af91fb6115a2b5b7df82396b9235dcf686e", size = 2865, upload-time = "2025-08-23T23:38:13.235Z" }, +] + [[package]] name = "wayosk" version = "0.1.0" source = { virtual = "." } +dependencies = [ + { name = "waykey" }, +] + +[package.metadata] +requires-dist = [{ name = "waykey", specifier = ">=0.1.0" }]