//go:build windows package handlers import ( "syscall" "unsafe" ) var ( user32 = syscall.NewLazyDLL("user32.dll") procSetCursorPos = user32.NewProc("SetCursorPos") procSendInput = user32.NewProc("SendInput") procMapVirtualKeyW = user32.NewProc("MapVirtualKeyW") procGetSystemMetrics = user32.NewProc("GetSystemMetrics") ) const ( MOUSEEVENTF_MOVE = 0x0001 MOUSEEVENTF_LEFTDOWN = 0x0002 MOUSEEVENTF_LEFTUP = 0x0004 MOUSEEVENTF_RIGHTDOWN = 0x0008 MOUSEEVENTF_RIGHTUP = 0x0010 MOUSEEVENTF_MIDDLEDOWN = 0x0020 MOUSEEVENTF_MIDDLEUP = 0x0040 MOUSEEVENTF_ABSOLUTE = 0x8000 MOUSEEVENTF_VIRTUALDESK = 0x4000 INPUT_MOUSE = 0 INPUT_KEYBOARD = 1 KEYEVENTF_EXTENDEDKEY = 0x0001 KEYEVENTF_KEYUP = 0x0002 KEYEVENTF_UNICODE = 0x0004 KEYEVENTF_SCANCODE = 0x0008 SM_XVIRTUALSCREEN = 76 SM_YVIRTUALSCREEN = 77 SM_CXVIRTUALSCREEN = 78 SM_CYVIRTUALSCREEN = 79 ) type mouseInput struct { dx int32 dy int32 mouseData uint32 dwFlags uint32 time uint32 dwExtraInfo uintptr } type keybdInput struct { wVk uint16 wScan uint16 dwFlags uint32 time uint32 dwExtraInfo uintptr } type hardwareInput struct { uMsg uint32 wParamL uint16 wParamH uint16 } type input struct { inputType uint32 _ uint32 union [32]byte } func setCursorPos(x, y int32) { if !sendMouseMoveAbsolute(x, y) { _, _, _ = procSetCursorPos.Call(uintptr(x), uintptr(y)) } } func sendMouseDown(button int) { switch button { case 0: sendMouseInput(MOUSEEVENTF_LEFTDOWN, 0) case 2: sendMouseInput(MOUSEEVENTF_RIGHTDOWN, 0) case 1: sendMouseInput(MOUSEEVENTF_MIDDLEDOWN, 0) } } func sendMouseUp(button int) { switch button { case 0: sendMouseInput(MOUSEEVENTF_LEFTUP, 0) case 2: sendMouseInput(MOUSEEVENTF_RIGHTUP, 0) case 1: sendMouseInput(MOUSEEVENTF_MIDDLEUP, 0) } } func sendMouseInput(flags uint32, mouseData uint32) { //garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2 var inp input inp.inputType = INPUT_MOUSE mi := (*mouseInput)(unsafe.Pointer(&inp.union[0])) mi.dx = 0 mi.dy = 0 mi.mouseData = mouseData mi.dwFlags = flags mi.time = 0 mi.dwExtraInfo = 0 _, _, _ = procSendInput.Call(1, uintptr(unsafe.Pointer(&inp)), unsafe.Sizeof(inp)) } func sendMouseMoveAbsolute(x, y int32) bool { left := getSystemMetrics(SM_XVIRTUALSCREEN) top := getSystemMetrics(SM_YVIRTUALSCREEN) width := getSystemMetrics(SM_CXVIRTUALSCREEN) height := getSystemMetrics(SM_CYVIRTUALSCREEN) if width <= 1 || height <= 1 { return false } const maxCoord = 65535 relX := int64(x) - int64(left) relY := int64(y) - int64(top) if relX < 0 { relX = 0 } if relY < 0 { relY = 0 } if relX > int64(width-1) { relX = int64(width - 1) } if relY > int64(height-1) { relY = int64(height - 1) } normX := int32(relX * maxCoord / int64(width-1)) normY := int32(relY * maxCoord / int64(height-1)) var inp input inp.inputType = INPUT_MOUSE mi := (*mouseInput)(unsafe.Pointer(&inp.union[0])) mi.dx = normX mi.dy = normY mi.mouseData = 0 mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK mi.time = 0 mi.dwExtraInfo = 0 r, _, _ := procSendInput.Call(1, uintptr(unsafe.Pointer(&inp)), unsafe.Sizeof(inp)) return r != 0 } func getSystemMetrics(index int32) int32 { r, _, _ := procGetSystemMetrics.Call(uintptr(index)) return int32(r) } func sendKeyDown(vk uint16) { sendKeyInput(vk, false) } func sendKeyUp(vk uint16) { sendKeyInput(vk, true) } func sendKeyInput(vk uint16, keyUp bool) { //garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2 var inp input inp.inputType = INPUT_KEYBOARD ki := (*keybdInput)(unsafe.Pointer(&inp.union[0])) ki.wVk = vk ki.wScan = uint16(mapVirtualKey(vk)) flags := uint32(0) if keyUp { flags |= KEYEVENTF_KEYUP } if isExtendedKey(vk) { flags |= KEYEVENTF_EXTENDEDKEY } ki.dwFlags = flags ki.time = 0 ki.dwExtraInfo = 0 _, _, _ = procSendInput.Call(1, uintptr(unsafe.Pointer(&inp)), unsafe.Sizeof(inp)) } func isExtendedKey(vk uint16) bool { switch vk { case 0xA3, // VK_RCONTROL 0xA5, // VK_RMENU 0x21, // VK_PRIOR 0x22, // VK_NEXT 0x23, // VK_END 0x24, // VK_HOME 0x25, // VK_LEFT 0x26, // VK_UP 0x27, // VK_RIGHT 0x28, // VK_DOWN 0x2D, // VK_INSERT 0x2E, // VK_DELETE 0x6F, // VK_DIVIDE 0x90: // VK_NUMLOCK return true default: return false } } func mapVirtualKey(vk uint16) uint32 { ret, _, _ := procMapVirtualKeyW.Call(uintptr(vk), 0) return uint32(ret) } func sendTextInput(text string) { for _, r := range text { switch r { case '\r', '\n': sendKeyDown(0x0D) sendKeyUp(0x0D) case '\t': sendKeyDown(0x09) sendKeyUp(0x09) case '\b': sendKeyDown(0x08) sendKeyUp(0x08) default: sendUnicodeRune(r) } } } func sendUnicodeRune(r rune) { var inp input inp.inputType = INPUT_KEYBOARD ki := (*keybdInput)(unsafe.Pointer(&inp.union[0])) ki.wVk = 0 ki.wScan = uint16(r) ki.dwFlags = KEYEVENTF_UNICODE _, _, _ = procSendInput.Call(1, uintptr(unsafe.Pointer(&inp)), unsafe.Sizeof(inp)) var inpUp input inpUp.inputType = INPUT_KEYBOARD kiUp := (*keybdInput)(unsafe.Pointer(&inpUp.union[0])) kiUp.wVk = 0 kiUp.wScan = uint16(r) kiUp.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP _, _, _ = procSendInput.Call(1, uintptr(unsafe.Pointer(&inpUp)), unsafe.Sizeof(inpUp)) } func keyCodeToVK(code string) uint16 { vkMap := map[string]uint16{ "KeyA": 0x41, "KeyB": 0x42, "KeyC": 0x43, "KeyD": 0x44, "KeyE": 0x45, "KeyF": 0x46, "KeyG": 0x47, "KeyH": 0x48, "KeyI": 0x49, "KeyJ": 0x4A, "KeyK": 0x4B, "KeyL": 0x4C, "KeyM": 0x4D, "KeyN": 0x4E, "KeyO": 0x4F, "KeyP": 0x50, "KeyQ": 0x51, "KeyR": 0x52, "KeyS": 0x53, "KeyT": 0x54, "KeyU": 0x55, "KeyV": 0x56, "KeyW": 0x57, "KeyX": 0x58, "KeyY": 0x59, "KeyZ": 0x5A, "Digit0": 0x30, "Digit1": 0x31, "Digit2": 0x32, "Digit3": 0x33, "Digit4": 0x34, "Digit5": 0x35, "Digit6": 0x36, "Digit7": 0x37, "Digit8": 0x38, "Digit9": 0x39, "Enter": 0x0D, "Space": 0x20, "Backspace": 0x08, "Tab": 0x09, "Escape": 0x1B, "ShiftLeft": 0xA0, "ShiftRight": 0xA1, "ControlLeft": 0xA2, "ControlRight": 0xA3, "AltLeft": 0xA4, "AltRight": 0xA5, "MetaLeft": 0x5B, "MetaRight": 0x5C, "ArrowLeft": 0x25, "ArrowUp": 0x26, "ArrowRight": 0x27, "ArrowDown": 0x28, "Delete": 0x2E, "Home": 0x24, "End": 0x23, "PageUp": 0x21, "PageDown": 0x22, "F1": 0x70, "F2": 0x71, "F3": 0x72, "F4": 0x73, "F5": 0x74, "F6": 0x75, "F7": 0x76, "F8": 0x77, "F9": 0x78, "F10": 0x79, "F11": 0x7A, "F12": 0x7B, "Slash": 0xBF, "Backquote": 0xC0, "Minus": 0xBD, "Equal": 0xBB, "BracketLeft": 0xDB, "BracketRight": 0xDD, "Backslash": 0xDC, "IntlBackslash": 0xDC, "Semicolon": 0xBA, "Quote": 0xDE, "Comma": 0xBC, "Period": 0xBE, } if vk, ok := vkMap[code]; ok { return vk } return 0 }