Add key input handling functionality to the agent. Implement API endpoint for sending key presses with support for action types (tap, down, up) and modifiers. Update web interface to allow users to send keys and toggle modifier states through a new interactive element.
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
package input
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func PressKey(key, action string, modifiers []string) error {
|
||||
vk, ok := vkCode(key)
|
||||
if !ok {
|
||||
return ErrBadKey
|
||||
}
|
||||
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
if err := attachInputDesktop(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
modVKs := make([]uint16, 0, len(modifiers))
|
||||
for _, mod := range modifiers {
|
||||
modVK, ok := vkCode(mod)
|
||||
if !ok {
|
||||
return ErrBadKey
|
||||
}
|
||||
modVKs = append(modVKs, modVK)
|
||||
}
|
||||
|
||||
for _, modVK := range modVKs {
|
||||
if err := sendKeyEvent(modVK, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
switch strings.ToLower(strings.TrimSpace(action)) {
|
||||
case "down":
|
||||
if err := sendKeyEvent(vk, false); err != nil {
|
||||
return err
|
||||
}
|
||||
case "up":
|
||||
if err := sendKeyEvent(vk, true); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
if err := sendKeyEvent(vk, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sendKeyEvent(vk, true); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := len(modVKs) - 1; i >= 0; i-- {
|
||||
if err := sendKeyEvent(modVKs[i], true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendKeyEvent(vk uint16, keyUp bool) error {
|
||||
flags := uint32(0)
|
||||
if keyUp {
|
||||
flags = keyeventfKeyup
|
||||
}
|
||||
inputs := []keybdInput{{Type: inputKeyboard, Vk: vk, Flags: flags}}
|
||||
return sendKeys(inputs)
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package input
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var ErrBadKey = errors.New("unknown key")
|
||||
|
||||
// vkNames maps API key names to Windows virtual-key codes.
|
||||
var vkNames = map[string]uint16{
|
||||
"backspace": 0x08,
|
||||
"tab": 0x09,
|
||||
"enter": 0x0D,
|
||||
"shift": 0x10,
|
||||
"ctrl": 0x11,
|
||||
"control": 0x11,
|
||||
"alt": 0x12,
|
||||
"pause": 0x13,
|
||||
"capslock": 0x14,
|
||||
"escape": 0x1B,
|
||||
"esc": 0x1B,
|
||||
"space": 0x20,
|
||||
"pageup": 0x21,
|
||||
"pagedown": 0x22,
|
||||
"end": 0x23,
|
||||
"home": 0x24,
|
||||
"left": 0x25,
|
||||
"up": 0x26,
|
||||
"right": 0x27,
|
||||
"down": 0x28,
|
||||
"insert": 0x2D,
|
||||
"delete": 0x2E,
|
||||
"del": 0x2E,
|
||||
"win": 0x5B,
|
||||
"lwin": 0x5B,
|
||||
"rwin": 0x5C,
|
||||
"0": 0x30,
|
||||
"1": 0x31,
|
||||
"2": 0x32,
|
||||
"3": 0x33,
|
||||
"4": 0x34,
|
||||
"5": 0x35,
|
||||
"6": 0x36,
|
||||
"7": 0x37,
|
||||
"8": 0x38,
|
||||
"9": 0x39,
|
||||
"a": 0x41,
|
||||
"b": 0x42,
|
||||
"c": 0x43,
|
||||
"d": 0x44,
|
||||
"e": 0x45,
|
||||
"f": 0x46,
|
||||
"g": 0x47,
|
||||
"h": 0x48,
|
||||
"i": 0x49,
|
||||
"j": 0x4A,
|
||||
"k": 0x4B,
|
||||
"l": 0x4C,
|
||||
"m": 0x4D,
|
||||
"n": 0x4E,
|
||||
"o": 0x4F,
|
||||
"p": 0x50,
|
||||
"q": 0x51,
|
||||
"r": 0x52,
|
||||
"s": 0x53,
|
||||
"t": 0x54,
|
||||
"u": 0x55,
|
||||
"v": 0x56,
|
||||
"w": 0x57,
|
||||
"x": 0x58,
|
||||
"y": 0x59,
|
||||
"z": 0x5A,
|
||||
"f1": 0x70,
|
||||
"f2": 0x71,
|
||||
"f3": 0x72,
|
||||
"f4": 0x73,
|
||||
"f5": 0x74,
|
||||
"f6": 0x75,
|
||||
"f7": 0x76,
|
||||
"f8": 0x77,
|
||||
"f9": 0x78,
|
||||
"f10": 0x79,
|
||||
"f11": 0x7A,
|
||||
"f12": 0x7B,
|
||||
";": 0xBA,
|
||||
"=": 0xBB,
|
||||
",": 0xBC,
|
||||
"-": 0xBD,
|
||||
".": 0xBE,
|
||||
"/": 0xBF,
|
||||
"`": 0xC0,
|
||||
"[": 0xDB,
|
||||
"\\": 0xDC,
|
||||
"]": 0xDD,
|
||||
"'": 0xDE,
|
||||
}
|
||||
|
||||
func vkCode(name string) (uint16, bool) {
|
||||
key := strings.ToLower(strings.TrimSpace(name))
|
||||
if len(key) == 1 {
|
||||
if vk, ok := vkNames[key]; ok {
|
||||
return vk, true
|
||||
}
|
||||
}
|
||||
vk, ok := vkNames[key]
|
||||
return vk, ok
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package input
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestVkCode(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := map[string]uint16{
|
||||
"a": 0x41,
|
||||
"A": 0x41,
|
||||
"enter": 0x0D,
|
||||
"ctrl": 0x11,
|
||||
"win": 0x5B,
|
||||
"f5": 0x74,
|
||||
}
|
||||
for name, want := range cases {
|
||||
got, ok := vkCode(name)
|
||||
if !ok || got != want {
|
||||
t.Fatalf("vkCode(%q) = (%v, %v), want %v", name, got, ok, want)
|
||||
}
|
||||
}
|
||||
if _, ok := vkCode("notakey"); ok {
|
||||
t.Fatal("expected unknown key")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user