//go:build windows package input import ( "context" "fmt" "runtime" "unsafe" "golang.org/x/sys/windows" ) var ( user32 = windows.NewLazySystemDLL("user32.dll") kernel32 = windows.NewLazySystemDLL("kernel32.dll") procSetWindowsHookExW = user32.NewProc("SetWindowsHookExW") procUnhookWindowsHookEx = user32.NewProc("UnhookWindowsHookEx") procCallNextHookEx = user32.NewProc("CallNextHookEx") procGetMessageW = user32.NewProc("GetMessageW") procSetCursorPos = user32.NewProc("SetCursorPos") procShowCursor = user32.NewProc("ShowCursor") procGetModuleHandleW = kernel32.NewProc("GetModuleHandleW") procGetSystemMetrics = user32.NewProc("GetSystemMetrics") procSetProcessDPIAware = user32.NewProc("SetProcessDPIAware") procPostThreadMessageW = user32.NewProc("PostThreadMessageW") ) const ( WH_KEYBOARD_LL = 13 WH_MOUSE_LL = 14 SM_CXSCREEN = 0 SM_CYSCREEN = 1 WM_QUIT = 0x0012 ) func init() { procSetProcessDPIAware.Call() } type MSLLHOOKSTRUCT struct { Pt Point MouseData uint32 // Mantemos uint32 mas vamos converter no callback Flags uint32 Time uint32 DwExtraInfo uintptr } type KBDLLHOOKSTRUCT struct { VkCode uint32 ScanCode uint32 Flags uint32 Time uint32 DwExtraInfo uintptr } type windowsInputHandler struct { mouseHook uintptr keyHook uintptr tid uint32 } func NewInputHandler() InputHandler { return &windowsInputHandler{} } func (h *windowsInputHandler) Install(ctx context.Context, onMouse func(MouseEvent) bool, onKey func(KeyboardEvent) bool) error { ready := make(chan error, 1) go func() { runtime.LockOSThread() h.tid = windows.GetCurrentThreadId() hMod, _, _ := procGetModuleHandleW.Call(0) mouseCallback := windows.NewCallback(func(nCode int, wParam uintptr, lParam uintptr) uintptr { if nCode >= 0 { info := (*MSLLHOOKSTRUCT)(unsafe.Pointer(lParam)) ev := MouseEvent{ Message: uint32(wParam), Point: info.Pt, Data: info.MouseData, } // Se a engine tratar o evento, não passamos para o próximo hook if onMouse(ev) { return 1 } } ret, _, _ := procCallNextHookEx.Call(h.mouseHook, uintptr(nCode), wParam, lParam) return ret }) keyCallback := windows.NewCallback(func(nCode int, wParam uintptr, lParam uintptr) uintptr { if nCode >= 0 { info := (*KBDLLHOOKSTRUCT)(unsafe.Pointer(lParam)) ev := KeyboardEvent{ Message: uint32(wParam), VKCode: info.VkCode, ScanCode: info.ScanCode, Flags: info.Flags, } if onKey(ev) { return 1 } } ret, _, _ := procCallNextHookEx.Call(h.keyHook, uintptr(nCode), wParam, lParam) return ret }) mh, _, _ := procSetWindowsHookExW.Call(WH_MOUSE_LL, mouseCallback, hMod, 0) if mh == 0 { ready <- fmt.Errorf("failed mouse hook") return } h.mouseHook = mh kh, _, _ := procSetWindowsHookExW.Call(WH_KEYBOARD_LL, keyCallback, hMod, 0) if kh == 0 { ready <- fmt.Errorf("failed key hook") return } h.keyHook = kh ready <- nil var msg struct { Hwnd windows.Handle Message uint32 WParam uintptr LParam uintptr Time uint32 Pt Point } for { ret, _, _ := procGetMessageW.Call(uintptr(unsafe.Pointer(&msg)), 0, 0, 0) if ret == 0 || msg.Message == WM_QUIT { break } } procUnhookWindowsHookEx.Call(h.mouseHook) procUnhookWindowsHookEx.Call(h.keyHook) }() return <-ready } func (h *windowsInputHandler) Uninstall() { if h.tid != 0 { procPostThreadMessageW.Call(uintptr(h.tid), WM_QUIT, 0, 0) } } func (h *windowsInputHandler) SetCursorPos(x, y int32) bool { ret, _, _ := procSetCursorPos.Call(uintptr(x), uintptr(y)) return ret != 0 } func (h *windowsInputHandler) ShowCursor(show bool) { s := -1 // No Windows, ShowCursor(FALSE) decrementa um contador if show { s = 1 } // Vamos usar uma abordagem mais direta se necessário, mas por enquanto: procShowCursor.Call(uintptr(s)) } func (h *windowsInputHandler) GetScreenResolution() (int32, int32) { w, _, _ := procGetSystemMetrics.Call(SM_CXSCREEN) h_res, _, _ := procGetSystemMetrics.Call(SM_CYSCREEN) return int32(w), int32(h_res) }