172 lines
4.1 KiB
Go
172 lines
4.1 KiB
Go
//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)
|
|
}
|