From 189a1aa0acdb432872e9adc1efe3c321e31c5a02 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 31 May 2025 22:34:29 +0200 Subject: [PATCH] on screen kb: init --- .../modules/common/ConfigOptions.qml | 5 + .../onScreenKeyboard/OnScreenKeyboard.qml | 169 ++++++++++++++ .../modules/onScreenKeyboard/OskContent.qml | 47 ++++ .../modules/onScreenKeyboard/OskKey.qml | 125 ++++++++++ .../modules/onScreenKeyboard/layouts.js | 218 ++++++++++++++++++ .config/quickshell/services/Ydotool.qml | 42 ++++ .config/quickshell/shell.qml | 3 + 7 files changed, 609 insertions(+) create mode 100644 .config/quickshell/modules/onScreenKeyboard/OnScreenKeyboard.qml create mode 100644 .config/quickshell/modules/onScreenKeyboard/OskContent.qml create mode 100644 .config/quickshell/modules/onScreenKeyboard/OskKey.qml create mode 100644 .config/quickshell/modules/onScreenKeyboard/layouts.js create mode 100644 .config/quickshell/services/Ydotool.qml diff --git a/.config/quickshell/modules/common/ConfigOptions.qml b/.config/quickshell/modules/common/ConfigOptions.qml index 34709a887..5f1afaea5 100644 --- a/.config/quickshell/modules/common/ConfigOptions.qml +++ b/.config/quickshell/modules/common/ConfigOptions.qml @@ -61,6 +61,11 @@ Singleton { property int timeout: 1000 } + property QtObject osk: QtObject { + property string layout: "qwerty_full" + property bool pinnedOnStartup: false + } + property QtObject overview: QtObject { property real scale: 0.18 // Relative to screen size property real numOfRows: 2 diff --git a/.config/quickshell/modules/onScreenKeyboard/OnScreenKeyboard.qml b/.config/quickshell/modules/onScreenKeyboard/OnScreenKeyboard.qml new file mode 100644 index 000000000..39862c55f --- /dev/null +++ b/.config/quickshell/modules/onScreenKeyboard/OnScreenKeyboard.qml @@ -0,0 +1,169 @@ +import "root:/" +import "root:/services" +import "root:/modules/common" +import "root:/modules/common/widgets" +import "root:/modules/common/functions/color_utils.js" as ColorUtils +import QtQuick +import QtQuick.Controls +import QtQuick.Effects +import QtQuick.Layouts +import Quickshell.Io +import Quickshell +import Quickshell.Widgets +import Quickshell.Wayland +import Quickshell.Hyprland + +Scope { // Scope + id: root + property bool pinned: ConfigOptions?.osk.pinnedOnStartup ?? false + + component OskControlButton: GroupButton { // Pin button + baseWidth: 40 + baseHeight: 40 + clickedWidth: baseWidth + clickedHeight: baseHeight + 20 + buttonRadius: Appearance.rounding.normal + } + + Loader { + id: oskLoader + active: false + onActiveChanged: { + if (!oskLoader.active) { + Ydotool.releaseAllKeys(); + } + } + + sourceComponent: PanelWindow { // Window + id: oskRoot + visible: oskLoader.active + + anchors { + bottom: true + left: true + right: true + } + + function hide() { + oskLoader.active = false + } + exclusiveZone: root.pinned ? implicitHeight - Appearance.sizes.hyprlandGapsOut : 0 + implicitWidth: oskBackground.width + Appearance.sizes.elevationMargin * 2 + implicitHeight: oskBackground.height + Appearance.sizes.elevationMargin * 2 + WlrLayershell.namespace: "quickshell:osk" + WlrLayershell.layer: WlrLayer.Overlay + // Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab + // WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive + color: "transparent" + + mask: Region { + item: oskBackground + } + + + // Background + StyledRectangularShadow { + target: oskBackground + } + Rectangle { + id: oskBackground + anchors.centerIn: parent + color: Appearance.colors.colLayer0 + radius: Appearance.rounding.windowRounding + property real padding: 10 + implicitWidth: oskRowLayout.implicitWidth + padding * 2 + implicitHeight: oskRowLayout.implicitHeight + padding * 2 + + Keys.onPressed: (event) => { // Esc to close + if (event.key === Qt.Key_Escape) { + oskRoot.hide() + } + } + + RowLayout { + id: oskRowLayout + anchors.centerIn: parent + spacing: 5 + VerticalButtonGroup { + OskControlButton { // Pin button + toggled: root.pinned + onClicked: root.pinned = !root.pinned + contentItem: MaterialSymbol { + text: "keep" + horizontalAlignment: Text.AlignHCenter + iconSize: Appearance.font.pixelSize.larger + color: root.pinned ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer0 + } + } + OskControlButton { + onClicked: () => { + oskRoot.hide() + } + contentItem: MaterialSymbol { + horizontalAlignment: Text.AlignHCenter + text: "keyboard_hide" + iconSize: Appearance.font.pixelSize.larger + } + } + } + Rectangle { + Layout.topMargin: 20 + Layout.bottomMargin: 20 + Layout.fillHeight: true + implicitWidth: 1 + color: Appearance.m3colors.m3outlineVariant + } + OskContent { + id: oskContent + Layout.fillWidth: true + } + } + } + + } + } + + IpcHandler { + target: "osk" + + function toggle(): void { + oskLoader.active = !oskLoader.active + } + + function close(): void { + oskLoader.active = false + } + + function open(): void { + oskLoader.active = true + } + } + + GlobalShortcut { + name: "oskToggle" + description: qsTr("Toggles on screen keyboard on press") + + onPressed: { + oskLoader.active = !oskLoader.active; + } + } + + GlobalShortcut { + name: "oskOpen" + description: qsTr("Opens on screen keyboard on press") + + onPressed: { + oskLoader.active = true; + } + } + + GlobalShortcut { + name: "oskClose" + description: qsTr("Closes on screen keyboard on press") + + onPressed: { + oskLoader.active = false; + } + } + +} diff --git a/.config/quickshell/modules/onScreenKeyboard/OskContent.qml b/.config/quickshell/modules/onScreenKeyboard/OskContent.qml new file mode 100644 index 000000000..06e954adc --- /dev/null +++ b/.config/quickshell/modules/onScreenKeyboard/OskContent.qml @@ -0,0 +1,47 @@ +import "root:/" +import "root:/services" +import "root:/modules/common" +import "root:/modules/common/widgets" +import "layouts.js" as Layouts +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Widgets +import Quickshell.Hyprland + +Item { + id: root + property var activeLayoutName: ConfigOptions?.osk.layout ?? Layouts.defaultLayout + property var layouts: Layouts.byName + property var currentLayout: layouts[activeLayoutName] + + implicitWidth: keyRows.implicitWidth + implicitHeight: keyRows.implicitHeight + + ColumnLayout { + id: keyRows + anchors.fill: parent + spacing: 5 + + Repeater { + model: root.currentLayout.keys + + delegate: RowLayout { + id: keyRow + required property var modelData + spacing: 5 + + Repeater { + model: modelData + // A normal key looks like this: {label: "a", labelShift: "A", shape: "normal", keycode: 30, type: "normal"} + delegate: OskKey { + required property var modelData + keyData: modelData + } + } + } + } + } +} diff --git a/.config/quickshell/modules/onScreenKeyboard/OskKey.qml b/.config/quickshell/modules/onScreenKeyboard/OskKey.qml new file mode 100644 index 000000000..1f28a9e57 --- /dev/null +++ b/.config/quickshell/modules/onScreenKeyboard/OskKey.qml @@ -0,0 +1,125 @@ +import "root:/services" +import "root:/modules/common" +import "root:/modules/common/widgets" +import "root:/modules/common/functions/color_utils.js" as ColorUtils +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Hyprland + +RippleButton { + id: root + property var keyData + property string key: keyData.label + property string type: keyData.keytype + property var keycode: keyData.keycode + property string shape: keyData.shape + property bool isShift: Ydotool.shiftKeys.includes(keycode) + property bool isBackspace: (key.toLowerCase() == "backspace") + property bool isEnter: (key.toLowerCase() == "enter" || key.toLowerCase() == "return") + property real baseWidth: 45 + property real baseHeight: 45 + property var widthMultiplier: ({ + "normal": 1, + "fn": 1, + "tab": 1.6, + "caps": 1.9, + "shift": 2.5, + "control": 1.3 + }) + property var heightMultiplier: ({ + "normal": 1, + "fn": 0.7, + "tab": 1, + "caps": 1, + "shift": 1, + "control": 1 + }) + toggled: isShift ? Ydotool.shiftMode : false + + enabled: shape != "empty" + colBackground: shape == "empty" ? ColorUtils.transparentize(Appearance.colors.colLayer1) : Appearance.colors.colLayer1 + buttonRadius: Appearance.rounding.small + implicitWidth: baseWidth * widthMultiplier[shape] || baseWidth + implicitHeight: baseHeight * heightMultiplier[shape] || baseHeight + Layout.fillWidth: shape == "space" || shape == "expand" + + Connections { + target: Ydotool + enabled: isShift + function onShiftModeChanged() { + if (Ydotool.shiftMode == 0) { + capsLockTimer.hasStarted = false; + } + } + } + + Timer { + id: capsLockTimer + property bool hasStarted: false + property bool canCaps: false + interval: 300 + function startWaiting() { + hasStarted = true; + canCaps = true; + start(); + } + onTriggered: { + canCaps = false; + } + } + + downAction: () => { + Ydotool.press(root.keycode); + if (isShift && Ydotool.shiftMode == 0) Ydotool.shiftMode = 1; + } + releaseAction: () => { + if (root.type == "normal") { + Ydotool.release(root.keycode); + if (Ydotool.shiftMode == 1) { + Ydotool.releaseShiftKeys() + } + } else if (isShift) { + if (Ydotool.shiftMode == 1) { + if (!capsLockTimer.hasStarted) { + capsLockTimer.startWaiting(); + } else { + if (capsLockTimer.canCaps) { + Ydotool.shiftMode = 2; // Caps lock mode + } else { + Ydotool.releaseShiftKeys() + } + } + } else if (Ydotool.shiftMode == 2) { + Ydotool.releaseShiftKeys(); + } + } else if (root.type == "modkey") { + root.toggled = !root.toggled; + if (!root.toggled) { + if (isShift) { + Ydotool.releaseShiftKeys(); + } else { + Ydotool.release(root.keycode); + } + } + } + + } + + contentItem: StyledText { + id: keyText + anchors.fill: parent + font.family: (isBackspace || isEnter) ? Appearance.font.family.iconMaterial : Appearance.font.family.main + font.pixelSize: root.shape == "fn" ? Appearance.font.pixelSize.small : + (isBackspace || isEnter) ? Appearance.font.pixelSize.huge : + Appearance.font.pixelSize.large + horizontalAlignment: Text.AlignHCenter + color: root.toggled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer1 + text: root.isBackspace ? "backspace" : root.isEnter ? "subdirectory_arrow_left" : + Ydotool.shiftMode == 2 ? (root.keyData.labelCaps || root.keyData.labelShift || root.keyData.label) : + Ydotool.shiftMode == 1 ? (root.keyData.labelShift || root.keyData.label) : + root.keyData.label + } +} diff --git a/.config/quickshell/modules/onScreenKeyboard/layouts.js b/.config/quickshell/modules/onScreenKeyboard/layouts.js new file mode 100644 index 000000000..6b8b98f06 --- /dev/null +++ b/.config/quickshell/modules/onScreenKeyboard/layouts.js @@ -0,0 +1,218 @@ +// We're going to use ydotool +// See /usr/include/linux/input-event-codes.h for keycodes + +const defaultLayout = "qwerty_full"; +const byName = { + "qwerty_full": { + name: "QWERTY - Full", + name_short: "US", + comment: "Like physical keyboard", + // A key looks like this: { k: "a", ks: "A", t: "normal" } (key, key-shift, type) + // key types are: normal, tab, caps, shift, control, fn (normal w/ half height), space, expand + // keys: [ + // [{ k: "Esc", t: "fn" }, { k: "F1", t: "fn" }, { k: "F2", t: "fn" }, { k: "F3", t: "fn" }, { k: "F4", t: "fn" }, { k: "F5", t: "fn" }, { k: "F6", t: "fn" }, { k: "F7", t: "fn" }, { k: "F8", t: "fn" }, { k: "F9", t: "fn" }, { k: "F10", t: "fn" }, { k: "F11", t: "fn" }, { k: "F12", t: "fn" }, { k: "PrtSc", t: "fn" }, { k: "Del", t: "fn" }], + // [{ k: "`", ks: "~", t: "normal" }, { k: "1", ks: "!", t: "normal" }, { k: "2", ks: "@", t: "normal" }, { k: "3", ks: "#", t: "normal" }, { k: "4", ks: "$", t: "normal" }, { k: "5", ks: "%", t: "normal" }, { k: "6", ks: "^", t: "normal" }, { k: "7", ks: "&", t: "normal" }, { k: "8", ks: "*", t: "normal" }, { k: "9", ks: "(", t: "normal" }, { k: "0", ks: ")", t: "normal" }, { k: "-", ks: "_", t: "normal" }, { k: "=", ks: "+", t: "normal" }, { k: "Backspace", t: "shift" }], + // [{ k: "Tab", t: "tab" }, { k: "q", ks: "Q", t: "normal" }, { k: "w", ks: "W", t: "normal" }, { k: "e", ks: "E", t: "normal" }, { k: "r", ks: "R", t: "normal" }, { k: "t", ks: "T", t: "normal" }, { k: "y", ks: "Y", t: "normal" }, { k: "u", ks: "U", t: "normal" }, { k: "i", ks: "I", t: "normal" }, { k: "o", ks: "O", t: "normal" }, { k: "p", ks: "P", t: "normal" }, { k: "[", ks: "{", t: "normal" }, { k: "]", ks: "}", t: "normal" }, { k: "\\", ks: "|", t: "expand" }], + // [{ k: "Caps", t: "caps" }, { k: "a", ks: "A", t: "normal" }, { k: "s", ks: "S", t: "normal" }, { k: "d", ks: "D", t: "normal" }, { k: "f", ks: "F", t: "normal" }, { k: "g", ks: "G", t: "normal" }, { k: "h", ks: "H", t: "normal" }, { k: "j", ks: "J", t: "normal" }, { k: "k", ks: "K", t: "normal" }, { k: "l", ks: "L", t: "normal" }, { k: ";", ks: ":", t: "normal" }, { k: "'", ks: '"', t: "normal" }, { k: "Enter", t: "expand" }], + // [{ k: "Shift", t: "shift" }, { k: "z", ks: "Z", t: "normal" }, { k: "x", ks: "X", t: "normal" }, { k: "c", ks: "C", t: "normal" }, { k: "v", ks: "V", t: "normal" }, { k: "b", ks: "B", t: "normal" }, { k: "n", ks: "N", t: "normal" }, { k: "m", ks: "M", t: "normal" }, { k: ",", ks: "<", t: "normal" }, { k: ".", ks: ">", t: "normal" }, { k: "/", ks: "?", t: "normal" }, { k: "Shift", t: "expand" }], + // [{ k: "Ctrl", t: "control" }, { k: "Fn", t: "normal" }, { k: "Win", t: "normal" }, { k: "Alt", t: "normal" }, { k: "Space", t: "space" }, { k: "Alt", t: "normal" }, { k: "Menu", t: "normal" }, { k: "Ctrl", t: "control" }] + // ] + // A normal key looks like this: {label: "a", labelShift: "A", shape: "normal", keycode: 30, type: "normal"} + // A modkey looks like this: {label: "Ctrl", shape: "control", keycode: 29, type: "modkey"} + // key types are: normal, tab, caps, shift, control, fn (normal w/ half height), space, expand + keys: [ + [ + { keytype: "normal", label: "Esc", shape: "fn", keycode: 1 }, + { keytype: "normal", label: "F1", shape: "fn", keycode: 59 }, + { keytype: "normal", label: "F2", shape: "fn", keycode: 60 }, + { keytype: "normal", label: "F3", shape: "fn", keycode: 61 }, + { keytype: "normal", label: "F4", shape: "fn", keycode: 62 }, + { keytype: "normal", label: "F5", shape: "fn", keycode: 63 }, + { keytype: "normal", label: "F6", shape: "fn", keycode: 64 }, + { keytype: "normal", label: "F7", shape: "fn", keycode: 65 }, + { keytype: "normal", label: "F8", shape: "fn", keycode: 66 }, + { keytype: "normal", label: "F9", shape: "fn", keycode: 67 }, + { keytype: "normal", label: "F10", shape: "fn", keycode: 68 }, + { keytype: "normal", label: "F11", shape: "fn", keycode: 87 }, + { keytype: "normal", label: "F12", shape: "fn", keycode: 88 }, + { keytype: "normal", label: "PrtSc", shape: "fn", keycode: 99 }, + { keytype: "normal", label: "Del", shape: "fn", keycode: 111 } + ], + [ + { keytype: "normal", label: "`", labelShift: "~", shape: "normal", keycode: 41 }, + { keytype: "normal", label: "1", labelShift: "!", shape: "normal", keycode: 2 }, + { keytype: "normal", label: "2", labelShift: "@", shape: "normal", keycode: 3 }, + { keytype: "normal", label: "3", labelShift: "#", shape: "normal", keycode: 4 }, + { keytype: "normal", label: "4", labelShift: "$", shape: "normal", keycode: 5 }, + { keytype: "normal", label: "5", labelShift: "%", shape: "normal", keycode: 6 }, + { keytype: "normal", label: "6", labelShift: "^", shape: "normal", keycode: 7 }, + { keytype: "normal", label: "7", labelShift: "&", shape: "normal", keycode: 8 }, + { keytype: "normal", label: "8", labelShift: "*", shape: "normal", keycode: 9 }, + { keytype: "normal", label: "9", labelShift: "(", shape: "normal", keycode: 10 }, + { keytype: "normal", label: "0", labelShift: ")", shape: "normal", keycode: 11 }, + { keytype: "normal", label: "-", labelShift: "_", shape: "normal", keycode: 12 }, + { keytype: "normal", label: "=", labelShift: "+", shape: "normal", keycode: 13 }, + { keytype: "normal", label: "Backspace", shape: "expand", keycode: 14 } + ], + [ + { keytype: "normal", label: "Tab", shape: "tab", keycode: 15 }, + { keytype: "normal", label: "q", labelShift: "Q", shape: "normal", keycode: 16 }, + { keytype: "normal", label: "w", labelShift: "W", shape: "normal", keycode: 17 }, + { keytype: "normal", label: "e", labelShift: "E", shape: "normal", keycode: 18 }, + { keytype: "normal", label: "r", labelShift: "R", shape: "normal", keycode: 19 }, + { keytype: "normal", label: "t", labelShift: "T", shape: "normal", keycode: 20 }, + { keytype: "normal", label: "y", labelShift: "Y", shape: "normal", keycode: 21 }, + { keytype: "normal", label: "u", labelShift: "U", shape: "normal", keycode: 22 }, + { keytype: "normal", label: "i", labelShift: "I", shape: "normal", keycode: 23 }, + { keytype: "normal", label: "o", labelShift: "O", shape: "normal", keycode: 24 }, + { keytype: "normal", label: "p", labelShift: "P", shape: "normal", keycode: 25 }, + { keytype: "normal", label: "[", labelShift: "{", shape: "normal", keycode: 26 }, + { keytype: "normal", label: "]", labelShift: "}", shape: "normal", keycode: 27 }, + { keytype: "normal", label: "\\", labelShift: "|", shape: "expand", keycode: 43 } + ], + [ + //{ keytype: "normal", label: "Caps", shape: "caps", keycode: 58 }, // not needed as double-pressing shift does that + { keytype: "spacer", label: "", shape: "empty" }, + { keytype: "spacer", label: "", shape: "empty" }, + { keytype: "normal", label: "a", labelShift: "A", shape: "normal", keycode: 30 }, + { keytype: "normal", label: "s", labelShift: "S", shape: "normal", keycode: 31 }, + { keytype: "normal", label: "d", labelShift: "D", shape: "normal", keycode: 32 }, + { keytype: "normal", label: "f", labelShift: "F", shape: "normal", keycode: 33 }, + { keytype: "normal", label: "g", labelShift: "G", shape: "normal", keycode: 34 }, + { keytype: "normal", label: "h", labelShift: "H", shape: "normal", keycode: 35 }, + { keytype: "normal", label: "j", labelShift: "J", shape: "normal", keycode: 36 }, + { keytype: "normal", label: "k", labelShift: "K", shape: "normal", keycode: 37 }, + { keytype: "normal", label: "l", labelShift: "L", shape: "normal", keycode: 38 }, + { keytype: "normal", label: ";", labelShift: ":", shape: "normal", keycode: 39 }, + { keytype: "normal", label: "'", labelShift: '"', shape: "normal", keycode: 40 }, + { keytype: "normal", label: "Enter", shape: "expand", keycode: 28 } + ], + [ + { keytype: "modkey", label: "Shift", labelShift: "Shift", labelCaps: "Caps", shape: "shift", keycode: 42 }, + { keytype: "normal", label: "z", labelShift: "Z", shape: "normal", keycode: 44 }, + { keytype: "normal", label: "x", labelShift: "X", shape: "normal", keycode: 45 }, + { keytype: "normal", label: "c", labelShift: "C", shape: "normal", keycode: 46 }, + { keytype: "normal", label: "v", labelShift: "V", shape: "normal", keycode: 47 }, + { keytype: "normal", label: "b", labelShift: "B", shape: "normal", keycode: 48 }, + { keytype: "normal", label: "n", labelShift: "N", shape: "normal", keycode: 49 }, + { keytype: "normal", label: "m", labelShift: "M", shape: "normal", keycode: 50 }, + { keytype: "normal", label: ",", labelShift: "<", shape: "normal", keycode: 51 }, + { keytype: "normal", label: ".", labelShift: ">", shape: "normal", keycode: 52 }, + { keytype: "normal", label: "/", labelShift: "?", shape: "normal", keycode: 53 }, + { keytype: "modkey", label: "Shift", labelShift: "Shift", labelCaps: "Caps", shape: "expand", keycode: 54 } // optional + ], + [ + { keytype: "modkey", label: "Ctrl", shape: "control", keycode: 29 }, + // { label: "Super", shape: "normal", keycode: 125 }, // dangerous + { keytype: "modkey", label: "Alt", shape: "normal", keycode: 56 }, + { keytype: "normal", label: "Space", shape: "space", keycode: 57 }, + { keytype: "modkey", label: "Alt", shape: "normal", keycode: 100 }, + // { label: "Super", shape: "normal", keycode: 126 }, // dangerous + { keytype: "normal", label: "Menu", shape: "normal", keycode: 139 }, + { keytype: "modkey", label: "Ctrl", shape: "control", keycode: 97 } + ] + ] + }, + "qwertz_full": { + name: "QWERTZ - Full", + name_short: "DE", + comment: "Keyboard layout commonly used in German-speaking countries", + keys: [ + [ + { keytype: "normal", label: "Esc", shape: "fn", keycode: 1 }, + { keytype: "normal", label: "F1", shape: "fn", keycode: 59 }, + { keytype: "normal", label: "F2", shape: "fn", keycode: 60 }, + { keytype: "normal", label: "F3", shape: "fn", keycode: 61 }, + { keytype: "normal", label: "F4", shape: "fn", keycode: 62 }, + { keytype: "normal", label: "F5", shape: "fn", keycode: 63 }, + { keytype: "normal", label: "F6", shape: "fn", keycode: 64 }, + { keytype: "normal", label: "F7", shape: "fn", keycode: 65 }, + { keytype: "normal", label: "F8", shape: "fn", keycode: 66 }, + { keytype: "normal", label: "F9", shape: "fn", keycode: 67 }, + { keytype: "normal", label: "F10", shape: "fn", keycode: 68 }, + { keytype: "normal", label: "F11", shape: "fn", keycode: 87 }, + { keytype: "normal", label: "F12", shape: "fn", keycode: 88 }, + { keytype: "normal", label: "Druck", shape: "fn", keycode: 99 }, + { keytype: "normal", label: "Entf", shape: "fn", keycode: 111 } + ], + [ + { keytype: "normal", label: "^", labelShift: "°", labelAlt: "′", shape: "normal", keycode: 41 }, + { keytype: "normal", label: "1", labelShift: "!", labelAlt: "¹", shape: "normal", keycode: 2 }, + { keytype: "normal", label: "2", labelShift: "\"", labelAlt: "²", shape: "normal", keycode: 3 }, + { keytype: "normal", label: "3", labelShift: "§", labelAlt: "³", shape: "normal", keycode: 4 }, + { keytype: "normal", label: "4", labelShift: "$", labelAlt: "¼", shape: "normal", keycode: 5 }, + { keytype: "normal", label: "5", labelShift: "%", labelAlt: "½", shape: "normal", keycode: 6 }, + { keytype: "normal", label: "6", labelShift: "&", labelAlt: "¬", shape: "normal", keycode: 7 }, + { keytype: "normal", label: "7", labelShift: "/", labelAlt: "{", shape: "normal", keycode: 8 }, + { keytype: "normal", label: "8", labelShift: "(", labelAlt: "[", shape: "normal", keycode: 9 }, + { keytype: "normal", label: "9", labelShift: ")", labelAlt: "]", shape: "normal", keycode: 10 }, + { keytype: "normal", label: "0", labelShift: "=", labelAlt: "}", shape: "normal", keycode: 11 }, + { keytype: "normal", label: "ß", labelShift: "?", labelAlt: "\\", shape: "normal", keycode: 12 }, + { keytype: "normal", label: "´", labelShift: "`", labelAlt: "¸", shape: "normal", keycode: 13 }, + { keytype: "normal", label: "⟵", shape: "expand", keycode: 14 } + ], + [ + { keytype: "normal", label: "Tab ⇆", shape: "tab", keycode: 15 }, + { keytype: "normal", label: "q", labelShift: "Q", labelAlt: "@", shape: "normal", keycode: 16 }, + { keytype: "normal", label: "w", labelShift: "W", labelAlt: "ſ", shape: "normal", keycode: 17 }, + { keytype: "normal", label: "e", labelShift: "E", labelAlt: "€", shape: "normal", keycode: 18 }, + { keytype: "normal", label: "r", labelShift: "R", labelAlt: "¶", shape: "normal", keycode: 19 }, + { keytype: "normal", label: "t", labelShift: "T", labelAlt: "ŧ", shape: "normal", keycode: 20 }, + { keytype: "normal", label: "z", labelShift: "Z", labelAlt: "←", shape: "normal", keycode: 21 }, + { keytype: "normal", label: "u", labelShift: "U", labelAlt: "↓", shape: "normal", keycode: 22 }, + { keytype: "normal", label: "i", labelShift: "I", labelAlt: "→", shape: "normal", keycode: 23 }, + { keytype: "normal", label: "o", labelShift: "O", labelAlt: "ø", shape: "normal", keycode: 24 }, + { keytype: "normal", label: "p", labelShift: "P", labelAlt: "þ", shape: "normal", keycode: 25 }, + { keytype: "normal", label: "ü", labelShift: "Ü", labelAlt: "¨", shape: "normal", keycode: 26 }, + { keytype: "normal", label: "+", labelShift: "*", labelAlt: "~", shape: "normal", keycode: 27 }, + { keytype: "normal", label: "↵", shape: "expand", keycode: 28 } + ], + [ + //{ keytype: "normal", label: "Umschalt ⇩", shape: "caps", keycode: 58 }, + { keytype: "spacer", label: "", shape: "empty" }, + { keytype: "spacer", label: "", shape: "empty" }, + { keytype: "normal", label: "a", labelShift: "A", labelAlt: "æ", shape: "normal", keycode: 30 }, + { keytype: "normal", label: "s", labelShift: "S", labelAlt: "ſ", shape: "normal", keycode: 31 }, + { keytype: "normal", label: "d", labelShift: "D", labelAlt: "ð", shape: "normal", keycode: 32 }, + { keytype: "normal", label: "f", labelShift: "F", labelAlt: "đ", shape: "normal", keycode: 33 }, + { keytype: "normal", label: "g", labelShift: "G", labelAlt: "ŋ", shape: "normal", keycode: 34 }, + { keytype: "normal", label: "h", labelShift: "H", labelAlt: "ħ", shape: "normal", keycode: 35 }, + { keytype: "normal", label: "j", labelShift: "J", labelAlt: "", shape: "normal", keycode: 36 }, + { keytype: "normal", label: "k", labelShift: "K", labelAlt: "ĸ", shape: "normal", keycode: 37 }, + { keytype: "normal", label: "l", labelShift: "L", labelAlt: "ł", shape: "normal", keycode: 38 }, + { keytype: "normal", label: "ö", labelShift: "Ö", labelAlt: "˝", shape: "normal", keycode: 39 }, + { keytype: "normal", label: "ä", labelShift: 'Ä', labelAlt: "^", shape: "normal", keycode: 40 }, + { keytype: "normal", label: "#", labelShift: '\'', labelAlt: "’", shape: "normal", keycode: 43 }, + { keytype: "spacer", label: "", shape: "empty" }, + //{ keytype: "normal", label: "↵", shape: "expand", keycode: 28 } + ], + [ + { keytype: "modkey", label: "Shift", labelShift: "Shift ⇧", labelCaps: "Locked ⇩", shape: "shift", keycode: 42 }, + { keytype: "normal", label: "<", labelShift: ">", labelAlt: "|", shape: "normal", keycode: 86 }, + { keytype: "normal", label: "y", labelShift: "Y", labelAlt: "»", shape: "normal", keycode: 44 }, + { keytype: "normal", label: "x", labelShift: "X", labelAlt: "«", shape: "normal", keycode: 45 }, + { keytype: "normal", label: "c", labelShift: "C", labelAlt: "¢", shape: "normal", keycode: 46 }, + { keytype: "normal", label: "v", labelShift: "V", labelAlt: "„", shape: "normal", keycode: 47 }, + { keytype: "normal", label: "b", labelShift: "B", labelAlt: "“", shape: "normal", keycode: 48 }, + { keytype: "normal", label: "n", labelShift: "N", labelAlt: "”", shape: "normal", keycode: 49 }, + { keytype: "normal", label: "m", labelShift: "M", labelAlt: "µ", shape: "normal", keycode: 50 }, + { keytype: "normal", label: ",", labelShift: ";", labelAlt: "·", shape: "normal", keycode: 51 }, + { keytype: "normal", label: ".", labelShift: ":", labelAlt: "…", shape: "normal", keycode: 52 }, + { keytype: "normal", label: "-", labelShift: "_", labelAlt: "–", shape: "normal", keycode: 53 }, + { keytype: "modkey", label: "Shift", labelShift: "Shift ⇧", labelCaps: "Locked ⇩", shape: "expand", keycode: 54 }, // optional + ], + [ + { keytype: "modkey", label: "Strg", shape: "control", keycode: 29 }, + //{ keytype: "normal", label: "", shape: "normal", keycode: 125 }, // dangerous + { keytype: "modkey", label: "Alt", shape: "normal", keycode: 56 }, + { keytype: "normal", label: "Leertaste", shape: "space", keycode: 57 }, + { keytype: "modkey", label: "Alt Gr", shape: "normal", keycode: 100 }, + // { label: "Super", shape: "normal", keycode: 126 }, // dangerous + //{ keytype: "normal", label: "Menu", shape: "normal", keycode: 139 }, // doesn't work? + { keytype: "modkey", label: "Strg", shape: "control", keycode: 97 }, + { keytype: "normal", label: "⇦", shape: "normal", keycode: 105 }, + { keytype: "normal", label: "⇨", shape: "normal", keycode: 106 }, + ] + ] + } +} \ No newline at end of file diff --git a/.config/quickshell/services/Ydotool.qml b/.config/quickshell/services/Ydotool.qml new file mode 100644 index 000000000..7cafcbbe2 --- /dev/null +++ b/.config/quickshell/services/Ydotool.qml @@ -0,0 +1,42 @@ +pragma Singleton + +import "root:/modules/common" +import Quickshell +import Quickshell.Io +import Quickshell.Hyprland + +Singleton { + id: root + property int shiftMode: 0 // 0: off, 1: on, 2: lock + property list shiftKeys: [42, 54] // Keycodes for Shift keys (left and right) + property list altKeys: [56, 100] // Keycodes for Alt keys (left and right) + property list ctrlKeys: [29, 97] // Keycodes for Ctrl keys (left and right) + + onShiftModeChanged: { + if (shiftMode === 0) { + + } + } + + function releaseAllKeys() { + const keycodes = Array.from(Array(249).keys()); + const releaseCommand = `ydotool key --key-delay 0 ${keycodes.map(keycode => `${keycode}:0`).join(" ")}` + Hyprland.dispatch(`exec ${releaseCommand}`) + root.shiftMode = 0; // Reset shift mode + } + + function releaseShiftKeys() { + const releaseCommand = `ydotool key --key-delay 0 ${root.shiftKeys.map(keycode => `${keycode}:0`).join(" ")}` + Hyprland.dispatch(`exec ${releaseCommand}`) + root.shiftMode = 0; // Reset shift mode + } + + function press(keycode) { + Hyprland.dispatch(`exec ydotool key --key-delay 0 ${keycode}:1`); + } + + function release(keycode) { + Hyprland.dispatch(`exec ydotool key --key-delay 0 ${keycode}:0`); + } +} + diff --git a/.config/quickshell/shell.qml b/.config/quickshell/shell.qml index 29ab0f327..6c72df6db 100644 --- a/.config/quickshell/shell.qml +++ b/.config/quickshell/shell.qml @@ -9,6 +9,7 @@ import "./modules/dock/" import "./modules/mediaControls/" import "./modules/notificationPopup/" import "./modules/onScreenDisplay/" +import "./modules/onScreenKeyboard/" import "./modules/overview/" import "./modules/screenCorners/" import "./modules/session/" @@ -31,6 +32,7 @@ ShellRoot { property bool enableNotificationPopup: true property bool enableOnScreenDisplayBrightness: true property bool enableOnScreenDisplayVolume: true + property bool enableOnScreenKeyboard: true property bool enableOverview: true property bool enableReloadPopup: true property bool enableScreenCorners: true @@ -54,6 +56,7 @@ ShellRoot { Loader { active: enableNotificationPopup; sourceComponent: NotificationPopup {} } Loader { active: enableOnScreenDisplayBrightness; sourceComponent: OnScreenDisplayBrightness {} } Loader { active: enableOnScreenDisplayVolume; sourceComponent: OnScreenDisplayVolume {} } + Loader { active: enableOnScreenKeyboard; sourceComponent: OnScreenKeyboard {} } Loader { active: enableOverview; sourceComponent: Overview {} } Loader { active: enableReloadPopup; sourceComponent: ReloadPopup {} } Loader { active: enableScreenCorners; sourceComponent: ScreenCorners {} }