From 4d5d9849da67f20579bfbd662b68676217a7e755 Mon Sep 17 00:00:00 2001 From: 2mal3 <56305732+2mal3@users.noreply.github.com> Date: Mon, 19 Jan 2026 18:47:53 +0100 Subject: [PATCH] feat: support sending modifier keys (#18) Co-authored-by: E44 <129310925+programmer-44@users.noreply.github.com> --- .../frontend/src/routes/ControlView.svelte | 66 +++--- control/frontend/src/routes/KeyInput.svelte | 190 ++++++++++++------ display/pkg/keyboard.go | 178 ++++++++++++++++ display/pkg/keys.go | 129 ------------ display/pkg/main.go | 42 ---- display/web/main.go | 8 +- shared/keys.json | 114 ----------- 7 files changed, 339 insertions(+), 388 deletions(-) create mode 100644 display/pkg/keyboard.go delete mode 100644 display/pkg/keys.go delete mode 100644 shared/keys.json diff --git a/control/frontend/src/routes/ControlView.svelte b/control/frontend/src/routes/ControlView.svelte index accd15a..865cb41 100644 --- a/control/frontend/src/routes/ControlView.svelte +++ b/control/frontend/src/routes/ControlView.svelte @@ -57,6 +57,7 @@ snippet: send_keys_popup, title: 'Tastatur-Eingaben Senden', title_icon: Keyboard, + window_class: 'h-full' }; }; @@ -123,17 +124,8 @@ ); } - async function send_single_key_press(key: string) { - await run_on_all_selected_displays((d) => - send_keyboard_input(d.ip, [{ key, action: 'press' }]) - ); - setTimeout( - async () => - await run_on_all_selected_displays((d) => - send_keyboard_input(d.ip, [{ key, action: 'release' }]) - ), - 10 - ); + async function send_single_key_press(key: string, action: 'press' | 'release') { + await run_on_all_selected_displays((d) => send_keyboard_input(d.ip, [{ key, action }])); } let website_url = $state(''); let website_url_valid = $state(false); @@ -192,14 +184,7 @@ {/snippet} {#snippet send_keys_popup()} -
-
- -
-
- -
-
+ {/snippet} {#snippet text_popup()} @@ -214,22 +199,37 @@
- { + await send_single_key_press('ArrowLeft', 'press'); + }} + onmouseup={async () => { + await send_single_key_press('ArrowLeft', 'release'); + }} > - + + +
+ +
+ +
diff --git a/display/pkg/keyboard.go b/display/pkg/keyboard.go new file mode 100644 index 0000000..dd5b0b4 --- /dev/null +++ b/display/pkg/keyboard.go @@ -0,0 +1,178 @@ +package pkg + +import ( + "fmt" + "log/slog" + "time" + + "github.com/micmonay/keybd_event" +) + +type KeyAction int + +const ( + KeyPress KeyAction = iota + KeyRelease +) + +type Input struct { + Key string + Action KeyAction +} + +func KeyboardInput(inputs []Input) error { + var err error + + kb, err := keybd_event.NewKeyBonding() + if err != nil { + return fmt.Errorf("failed to create key bonding: %w", err) + } + + for _, input := range inputs { + switch input.Key { + case "Shift", "ShiftLeft", "ShiftRight": + kb.HasSHIFT(true) + case "Ctrl", "Control", "ControlLeft", "ControlRight": + kb.HasCTRL(true) + case "Alt", "AltLeft", "AltRight": + kb.HasALT(true) + case "Super", "Meta", "MetaLeft", "MetaRight": + kb.HasSuper(true) + default: + keyCode, ok := KeyboardEvents[input.Key] + if !ok { + slog.Warn("Could not parse keyboard input", "key", input.Key) + continue + } + kb.SetKeys(keyCode) + } + + switch input.Action { + case KeyPress: + err = kb.Press() + case KeyRelease: + err = kb.Release() + } + + if err != nil { + return fmt.Errorf("failed to run key event: %w", err) + } + + time.Sleep(time.Microsecond * 10) + } + + return nil +} + +var KeyboardEvents = map[string]int{ + "Escape": keybd_event.VK_ESC, + "Digit1": keybd_event.VK_1, + "Digit2": keybd_event.VK_2, + "Digit3": keybd_event.VK_3, + "Digit4": keybd_event.VK_4, + "Digit5": keybd_event.VK_5, + "Digit6": keybd_event.VK_6, + "Digit7": keybd_event.VK_7, + "Digit8": keybd_event.VK_8, + "Digit9": keybd_event.VK_9, + "Digit0": keybd_event.VK_0, + "KeyQ": keybd_event.VK_Q, + "KeyW": keybd_event.VK_W, + "KeyE": keybd_event.VK_E, + "KeyR": keybd_event.VK_R, + "KeyT": keybd_event.VK_T, + "KeyY": keybd_event.VK_Y, + "KeyU": keybd_event.VK_U, + "KeyI": keybd_event.VK_I, + "KeyO": keybd_event.VK_O, + "KeyP": keybd_event.VK_P, + "KeyA": keybd_event.VK_A, + "KeyS": keybd_event.VK_S, + "KeyD": keybd_event.VK_D, + "KeyF": keybd_event.VK_F, + "KeyG": keybd_event.VK_G, + "KeyH": keybd_event.VK_H, + "KeyJ": keybd_event.VK_J, + "KeyK": keybd_event.VK_K, + "KeyL": keybd_event.VK_L, + "KeyZ": keybd_event.VK_Z, + "KeyX": keybd_event.VK_X, + "KeyC": keybd_event.VK_C, + "KeyV": keybd_event.VK_V, + "KeyB": keybd_event.VK_B, + "KeyN": keybd_event.VK_N, + "KeyM": keybd_event.VK_M, + "F1": keybd_event.VK_F1, + "F2": keybd_event.VK_F2, + "F3": keybd_event.VK_F3, + "F4": keybd_event.VK_F4, + "F5": keybd_event.VK_F5, + "F6": keybd_event.VK_F6, + "F7": keybd_event.VK_F7, + "F8": keybd_event.VK_F8, + "F9": keybd_event.VK_F9, + "F10": keybd_event.VK_F10, + "F11": keybd_event.VK_F11, + "F12": keybd_event.VK_F12, + "F13": keybd_event.VK_F13, + "F14": keybd_event.VK_F14, + "F15": keybd_event.VK_F15, + "F16": keybd_event.VK_F16, + "F17": keybd_event.VK_F17, + "F18": keybd_event.VK_F18, + "F19": keybd_event.VK_F19, + "F20": keybd_event.VK_F20, + "F21": keybd_event.VK_F21, + "F22": keybd_event.VK_F22, + "F23": keybd_event.VK_F23, + "F24": keybd_event.VK_F24, + "NumLock": keybd_event.VK_NUMLOCK, + "ScrollLock": keybd_event.VK_SCROLLLOCK, + "CapsLock": keybd_event.VK_CAPSLOCK, + "Minus": keybd_event.VK_SP2, + "Equal": keybd_event.VK_SP3, + "Backspace": keybd_event.VK_BACKSPACE, + "Tab": keybd_event.VK_TAB, + "BracketLeft": keybd_event.VK_SP4, + "BracketRight": keybd_event.VK_SP5, + "Enter": keybd_event.VK_ENTER, + "Semicolon": keybd_event.VK_SP6, + "Quote": keybd_event.VK_SP7, + "Backquote": keybd_event.VK_SP1, + "Backslash": keybd_event.VK_SP8, + "Comma": keybd_event.VK_SP9, + "Period": keybd_event.VK_SP10, + "Slash": keybd_event.VK_SP11, + "IntlBackslash": keybd_event.VK_SP12, + "NumpadMultiply": keybd_event.VK_KPASTERISK, + "NumpadAdd": keybd_event.VK_KPPLUS, + "NumpadSubtract": keybd_event.VK_KPMINUS, + "NumpadDecimal": keybd_event.VK_KPDOT, + "Space": keybd_event.VK_SPACE, + "Numpad0": keybd_event.VK_KP0, + "Numpad1": keybd_event.VK_KP1, + "Numpad2": keybd_event.VK_KP2, + "Numpad3": keybd_event.VK_KP3, + "Numpad4": keybd_event.VK_KP4, + "Numpad5": keybd_event.VK_KP5, + "Numpad6": keybd_event.VK_KP6, + "Numpad7": keybd_event.VK_KP7, + "Numpad8": keybd_event.VK_KP8, + "Numpad9": keybd_event.VK_KP9, + "PageUp": keybd_event.VK_PAGEUP, + "PageDown": keybd_event.VK_PAGEDOWN, + "End": keybd_event.VK_END, + "Home": keybd_event.VK_HOME, + "ArrowLeft": keybd_event.VK_LEFT, + "ArrowUp": keybd_event.VK_UP, + "ArrowRight": keybd_event.VK_RIGHT, + "ArrowDown": keybd_event.VK_DOWN, + "PrintScreen": keybd_event.VK_PRINT, + "Insert": keybd_event.VK_INSERT, + "Delete": keybd_event.VK_DELETE, + "Help": keybd_event.VK_HELP, + "BrowserBack": keybd_event.VK_BACK, + "Pause": keybd_event.VK_PAUSE, + "Lang1": keybd_event.VK_HANGUEL, + "Lang2": keybd_event.VK_HANJA, +} diff --git a/display/pkg/keys.go b/display/pkg/keys.go deleted file mode 100644 index abaf8a5..0000000 --- a/display/pkg/keys.go +++ /dev/null @@ -1,129 +0,0 @@ -package pkg - -import "github.com/micmonay/keybd_event" - -var KeyboardEvents = map[string]int{ - "VK_SP1": keybd_event.VK_SP1, - "VK_SP2": keybd_event.VK_SP2, - "VK_SP3": keybd_event.VK_SP3, - "VK_SP4": keybd_event.VK_SP4, - "VK_SP5": keybd_event.VK_SP5, - "VK_SP6": keybd_event.VK_SP6, - "VK_SP7": keybd_event.VK_SP7, - "VK_SP8": keybd_event.VK_SP8, - "VK_SP9": keybd_event.VK_SP9, - "VK_SP10": keybd_event.VK_SP10, - "VK_SP11": keybd_event.VK_SP11, - "VK_SP12": keybd_event.VK_SP12, - "VK_ESC": keybd_event.VK_ESC, - "VK_1": keybd_event.VK_1, - "VK_2": keybd_event.VK_2, - "VK_3": keybd_event.VK_3, - "VK_4": keybd_event.VK_4, - "VK_5": keybd_event.VK_5, - "VK_6": keybd_event.VK_6, - "VK_7": keybd_event.VK_7, - "VK_8": keybd_event.VK_8, - "VK_9": keybd_event.VK_9, - "VK_0": keybd_event.VK_0, - "VK_Q": keybd_event.VK_Q, - "VK_W": keybd_event.VK_W, - "VK_E": keybd_event.VK_E, - "VK_R": keybd_event.VK_R, - "VK_T": keybd_event.VK_T, - "VK_Y": keybd_event.VK_Y, - "VK_U": keybd_event.VK_U, - "VK_I": keybd_event.VK_I, - "VK_O": keybd_event.VK_O, - "VK_P": keybd_event.VK_P, - "VK_A": keybd_event.VK_A, - "VK_S": keybd_event.VK_S, - "VK_D": keybd_event.VK_D, - "VK_F": keybd_event.VK_F, - "VK_G": keybd_event.VK_G, - "VK_H": keybd_event.VK_H, - "VK_J": keybd_event.VK_J, - "VK_K": keybd_event.VK_K, - "VK_L": keybd_event.VK_L, - "VK_Z": keybd_event.VK_Z, - "VK_X": keybd_event.VK_X, - "VK_C": keybd_event.VK_C, - "VK_V": keybd_event.VK_V, - "VK_B": keybd_event.VK_B, - "VK_N": keybd_event.VK_N, - "VK_M": keybd_event.VK_M, - "VK_F1": keybd_event.VK_F1, - "VK_F2": keybd_event.VK_F2, - "VK_F3": keybd_event.VK_F3, - "VK_F4": keybd_event.VK_F4, - "VK_F5": keybd_event.VK_F5, - "VK_F6": keybd_event.VK_F6, - "VK_F7": keybd_event.VK_F7, - "VK_F8": keybd_event.VK_F8, - "VK_F9": keybd_event.VK_F9, - "VK_F10": keybd_event.VK_F10, - "VK_F11": keybd_event.VK_F11, - "VK_F12": keybd_event.VK_F12, - "VK_F13": keybd_event.VK_F13, - "VK_F14": keybd_event.VK_F14, - "VK_F15": keybd_event.VK_F15, - "VK_F16": keybd_event.VK_F16, - "VK_F17": keybd_event.VK_F17, - "VK_F18": keybd_event.VK_F18, - "VK_F19": keybd_event.VK_F19, - "VK_F20": keybd_event.VK_F20, - "VK_F21": keybd_event.VK_F21, - "VK_F22": keybd_event.VK_F22, - "VK_F23": keybd_event.VK_F23, - "VK_F24": keybd_event.VK_F24, - "VK_NUMLOCK": keybd_event.VK_NUMLOCK, - "VK_SCROLLLOCK": keybd_event.VK_SCROLLLOCK, - "VK_RESERVED": keybd_event.VK_RESERVED, - "VK_MINUS": keybd_event.VK_MINUS, - "VK_EQUAL": keybd_event.VK_EQUAL, - "VK_BACKSPACE": keybd_event.VK_BACKSPACE, - "VK_TAB": keybd_event.VK_TAB, - "VK_LEFTBRACE": keybd_event.VK_LEFTBRACE, - "VK_RIGHTBRACE": keybd_event.VK_RIGHTBRACE, - "VK_ENTER": keybd_event.VK_ENTER, - "VK_SEMICOLON": keybd_event.VK_SEMICOLON, - "VK_APOSTROPHE": keybd_event.VK_APOSTROPHE, - "VK_GRAVE": keybd_event.VK_GRAVE, - "VK_BACKSLASH": keybd_event.VK_BACKSLASH, - "VK_COMMA": keybd_event.VK_COMMA, - "VK_DOT": keybd_event.VK_DOT, - "VK_SLASH": keybd_event.VK_SLASH, - "VK_KPASTERISK": keybd_event.VK_KPASTERISK, - "VK_SPACE": keybd_event.VK_SPACE, - "VK_CAPSLOCK": keybd_event.VK_CAPSLOCK, - "VK_KP0": keybd_event.VK_KP0, - "VK_KP1": keybd_event.VK_KP1, - "VK_KP2": keybd_event.VK_KP2, - "VK_KP3": keybd_event.VK_KP3, - "VK_KP4": keybd_event.VK_KP4, - "VK_KP5": keybd_event.VK_KP5, - "VK_KP6": keybd_event.VK_KP6, - "VK_KP7": keybd_event.VK_KP7, - "VK_KP8": keybd_event.VK_KP8, - "VK_KP9": keybd_event.VK_KP9, - "VK_KPMINUS": keybd_event.VK_KPMINUS, - "VK_KPPLUS": keybd_event.VK_KPPLUS, - "VK_KPDOT": keybd_event.VK_KPDOT, - "VK_CANCEL": keybd_event.VK_CANCEL, - "VK_BACK": keybd_event.VK_BACK, - "VK_PAUSE": keybd_event.VK_PAUSE, - "VK_HANGUEL": keybd_event.VK_HANGUEL, - "VK_HANJA": keybd_event.VK_HANJA, - "VK_PAGEUP": keybd_event.VK_PAGEUP, - "VK_PAGEDOWN": keybd_event.VK_PAGEDOWN, - "VK_END": keybd_event.VK_END, - "VK_HOME": keybd_event.VK_HOME, - "VK_LEFT": keybd_event.VK_LEFT, - "VK_UP": keybd_event.VK_UP, - "VK_RIGHT": keybd_event.VK_RIGHT, - "VK_DOWN": keybd_event.VK_DOWN, - "VK_PRINT": keybd_event.VK_PRINT, - "VK_INSERT": keybd_event.VK_INSERT, - "VK_DELETE": keybd_event.VK_DELETE, - "VK_HELP": keybd_event.VK_HELP, -} diff --git a/display/pkg/main.go b/display/pkg/main.go index cf58926..3e01841 100644 --- a/display/pkg/main.go +++ b/display/pkg/main.go @@ -11,8 +11,6 @@ import ( "time" "plg-mudics/shared" - - "github.com/micmonay/keybd_event" ) func GetDeviceIp() (string, error) { @@ -46,46 +44,6 @@ func GetDeviceMac() (string, error) { return "", fmt.Errorf("no suitable MAC address found") } -type KeyAction int - -const ( - KeyPress KeyAction = iota - KeyRelease -) - -type Input struct { - Key int - Action KeyAction -} - -func KeyboardInput(inputs []Input) error { - var err error - - kb, err := keybd_event.NewKeyBonding() - if err != nil { - return fmt.Errorf("failed to create key bonding: %w", err) - } - - for _, input := range inputs { - kb.SetKeys(input.Key) - - switch input.Action { - case KeyPress: - err = kb.Press() - case KeyRelease: - err = kb.Release() - } - - if err != nil { - return fmt.Errorf("failed to run key event: %w", err) - } - - time.Sleep(time.Microsecond * 100) - } - - return nil -} - func TakeScreenshot() (string, error) { tempFilePath := filepath.Join(os.TempDir(), fmt.Sprintf("screenshot_%d.png", time.Now().Unix())) diff --git a/display/web/main.go b/display/web/main.go index c7e4fb9..c62d46c 100644 --- a/display/web/main.go +++ b/display/web/main.go @@ -216,12 +216,6 @@ func keyboardInputRoute(ctx echo.Context) error { return ctx.JSON(http.StatusBadRequest, shared.ErrorResponse{Description: fmt.Sprintf("Invalid action: %s", input.Action)}) } - code, ok := pkg.KeyboardEvents[input.Key] - if !ok { - slog.Error("Unsupported key", "key", input.Key) - return ctx.JSON(http.StatusBadRequest, shared.ErrorResponse{Description: fmt.Sprintf("Unsupported key: %s", input.Key)}) - } - var action pkg.KeyAction if input.Action == "press" { action = pkg.KeyPress @@ -231,7 +225,7 @@ func keyboardInputRoute(ctx echo.Context) error { } inputs = append(inputs, pkg.Input{ - Key: code, + Key: input.Key, Action: action, }) } diff --git a/shared/keys.json b/shared/keys.json deleted file mode 100644 index cbb1fa0..0000000 --- a/shared/keys.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "Escape": "VK_ESC", - "Digit1": "VK_1", - "Digit2": "VK_2", - "Digit3": "VK_3", - "Digit4": "VK_4", - "Digit5": "VK_5", - "Digit6": "VK_6", - "Digit7": "VK_7", - "Digit8": "VK_8", - "Digit9": "VK_9", - "Digit0": "VK_0", - "KeyQ": "VK_Q", - "KeyW": "VK_W", - "KeyE": "VK_E", - "KeyR": "VK_R", - "KeyT": "VK_T", - "KeyY": "VK_Y", - "KeyU": "VK_U", - "KeyI": "VK_I", - "KeyO": "VK_O", - "KeyP": "VK_P", - "KeyA": "VK_A", - "KeyS": "VK_S", - "KeyD": "VK_D", - "KeyF": "VK_F", - "KeyG": "VK_G", - "KeyH": "VK_H", - "KeyJ": "VK_J", - "KeyK": "VK_K", - "KeyL": "VK_L", - "KeyZ": "VK_Z", - "KeyX": "VK_X", - "KeyC": "VK_C", - "KeyV": "VK_V", - "KeyB": "VK_B", - "KeyN": "VK_N", - "KeyM": "VK_M", - "F1": "VK_F1", - "F2": "VK_F2", - "F3": "VK_F3", - "F4": "VK_F4", - "F5": "VK_F5", - "F6": "VK_F6", - "F7": "VK_F7", - "F8": "VK_F8", - "F9": "VK_F9", - "F10": "VK_F10", - "F11": "VK_F11", - "F12": "VK_F12", - "F13": "VK_F13", - "F14": "VK_F14", - "F15": "VK_F15", - "F16": "VK_F16", - "F17": "VK_F17", - "F18": "VK_F18", - "F19": "VK_F19", - "F20": "VK_F20", - "F21": "VK_F21", - "F22": "VK_F22", - "F23": "VK_F23", - "F24": "VK_F24", - "NumLock": "VK_NUMLOCK", - "ScrollLock": "VK_SCROLLLOCK", - "CapsLock": "VK_CAPSLOCK", - "Minus": "VK_SP2", - "Equal": "VK_SP3", - "Backspace": "VK_BACKSPACE", - "Tab": "VK_TAB", - "BracketLeft": "VK_SP4", - "BracketRight": "VK_SP5", - "Enter": "VK_ENTER", - "Semicolon": "VK_SP6", - "Quote": "VK_SP7", - "Backquote": "VK_SP1", - "Backslash": "VK_SP8", - "Comma": "VK_SP9", - "Period": "VK_SP10", - "Slash": "VK_SP11", - "IntlBackslash": "VK_SP12", - "NumpadMultiply": "VK_KPASTERISK", - "NumpadDivide": "VK_KPSLASH", - "NumpadAdd": "VK_KPPLUS", - "NumpadSubtract": "VK_KPMINUS", - "NumpadDecimal": "VK_KPDOT", - "NumpadEnter": "VK_KPENTER", - "Space": "VK_SPACE", - "Numpad0": "VK_KP0", - "Numpad1": "VK_KP1", - "Numpad2": "VK_KP2", - "Numpad3": "VK_KP3", - "Numpad4": "VK_KP4", - "Numpad5": "VK_KP5", - "Numpad6": "VK_KP6", - "Numpad7": "VK_KP7", - "Numpad8": "VK_KP8", - "Numpad9": "VK_KP9", - "PageUp": "VK_PAGEUP", - "PageDown": "VK_PAGEDOWN", - "End": "VK_END", - "Home": "VK_HOME", - "ArrowLeft": "VK_LEFT", - "ArrowUp": "VK_UP", - "ArrowRight": "VK_RIGHT", - "ArrowDown": "VK_DOWN", - "PrintScreen": "VK_PRINT", - "Insert": "VK_INSERT", - "Delete": "VK_DELETE", - "Help": "VK_HELP", - "BrowserBack": "VK_BACK", - "Pause": "VK_PAUSE", - "Lang1": "VK_HANGUEL", - "Lang2": "VK_HANJA" -} \ No newline at end of file