Enhance input handling by adding support for absolute mouse coordinates and desktop attachment. Implement runtime locking for thread safety during input operations. Update mouse input flags and add new functions for desktop management.
This commit is contained in:
+77
-16
@@ -2,6 +2,7 @@ package input
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
@@ -12,25 +13,37 @@ const (
|
||||
inputMouse = 0
|
||||
inputKeyboard = 1
|
||||
|
||||
mouseLeftDown = 0x0002
|
||||
mouseLeftUp = 0x0004
|
||||
mouseRightDown = 0x0008
|
||||
mouseRightUp = 0x0010
|
||||
|
||||
keyeventfKeyup = 0x0002
|
||||
keyeventfUnicode = 0x0004
|
||||
mouseMove = 0x0001
|
||||
mouseLeftDown = 0x0002
|
||||
mouseLeftUp = 0x0004
|
||||
mouseRightDown = 0x0008
|
||||
mouseRightUp = 0x0010
|
||||
mouseAbsolute = 0x8000
|
||||
mouseVirtualDesk = 0x4000
|
||||
desktopAllAccess = 0x01FF
|
||||
keyeventfKeyup = 0x0002
|
||||
keyeventfUnicode = 0x0004
|
||||
smXVirtualScreen = 76
|
||||
smYVirtualScreen = 77
|
||||
smCXVirtualScreen = 78
|
||||
smCYVirtualScreen = 79
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBadButton = errors.New("button must be left or right")
|
||||
ErrEmptyText = errors.New("text must not be empty")
|
||||
|
||||
user32 = syscall.NewLazyDLL("user32.dll")
|
||||
procSetCursorPos = user32.NewProc("SetCursorPos")
|
||||
procSendInput = user32.NewProc("SendInput")
|
||||
user32 = syscall.NewLazyDLL("user32.dll")
|
||||
procSetCursorPos = user32.NewProc("SetCursorPos")
|
||||
procSendInput = user32.NewProc("SendInput")
|
||||
procSetProcessDPIAware = user32.NewProc("SetProcessDPIAware")
|
||||
procOpenInputDesktop = user32.NewProc("OpenInputDesktop")
|
||||
procSetThreadDesktop = user32.NewProc("SetThreadDesktop")
|
||||
procCloseDesktop = user32.NewProc("CloseDesktop")
|
||||
procGetSystemMetrics = user32.NewProc("GetSystemMetrics")
|
||||
)
|
||||
|
||||
// ponytail: INPUT must be 40 bytes on amd64; wrong size yields SendInput failures.
|
||||
type mouseInput struct {
|
||||
Type uint32
|
||||
_ uint32
|
||||
@@ -39,6 +52,7 @@ type mouseInput struct {
|
||||
MouseData uint32
|
||||
Flags uint32
|
||||
Time uint32
|
||||
_ uint32
|
||||
ExtraInfo uintptr
|
||||
}
|
||||
|
||||
@@ -49,8 +63,8 @@ type keybdInput struct {
|
||||
Scan uint16
|
||||
Flags uint32
|
||||
Time uint32
|
||||
_ uint32
|
||||
ExtraInfo uintptr
|
||||
_ [8]byte
|
||||
}
|
||||
|
||||
func EnableDPIAwareness() {
|
||||
@@ -58,25 +72,43 @@ func EnableDPIAwareness() {
|
||||
}
|
||||
|
||||
func Click(x, y int, button string) error {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
if err := attachInputDesktop(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
down, up, err := mouseFlags(button)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ok, _, callErr := procSetCursorPos.Call(uintptr(x), uintptr(y))
|
||||
if ok == 0 {
|
||||
return callErr
|
||||
}
|
||||
|
||||
absX, absY := absoluteCoords(x, y)
|
||||
inputs := []mouseInput{
|
||||
{Type: inputMouse, Dx: absX, Dy: absY, Flags: mouseMove | mouseAbsolute | mouseVirtualDesk},
|
||||
{Type: inputMouse, Flags: down},
|
||||
{Type: inputMouse, Flags: up},
|
||||
}
|
||||
return sendMouse(inputs)
|
||||
if err := sendMouse(inputs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// ponytail: best-effort cursor sync for the local user; ignore denial after SendInput click.
|
||||
_, _, _ = procSetCursorPos.Call(uintptr(x), uintptr(y))
|
||||
return nil
|
||||
}
|
||||
|
||||
func TypeText(text string) error {
|
||||
if text == "" {
|
||||
return ErrEmptyText
|
||||
}
|
||||
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
if err := attachInputDesktop(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
units := utf16.Encode([]rune(text))
|
||||
inputs := make([]keybdInput, 0, len(units)*2)
|
||||
for _, unit := range units {
|
||||
@@ -88,6 +120,35 @@ func TypeText(text string) error {
|
||||
return sendKeys(inputs)
|
||||
}
|
||||
|
||||
func attachInputDesktop() error {
|
||||
handle, _, err := procOpenInputDesktop.Call(0, 0, desktopAllAccess)
|
||||
if handle == 0 {
|
||||
return err
|
||||
}
|
||||
defer procCloseDesktop.Call(handle)
|
||||
ok, _, err := procSetThreadDesktop.Call(handle)
|
||||
if ok == 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func absoluteCoords(x, y int) (int32, int32) {
|
||||
left, _, _ := procGetSystemMetrics.Call(smXVirtualScreen)
|
||||
top, _, _ := procGetSystemMetrics.Call(smYVirtualScreen)
|
||||
width, _, _ := procGetSystemMetrics.Call(smCXVirtualScreen)
|
||||
height, _, _ := procGetSystemMetrics.Call(smCYVirtualScreen)
|
||||
if width <= 1 {
|
||||
width = 1
|
||||
}
|
||||
if height <= 1 {
|
||||
height = 1
|
||||
}
|
||||
absX := int32((int64(x-int(left)) * 65535) / int64(width-1))
|
||||
absY := int32((int64(y-int(top)) * 65535) / int64(height-1))
|
||||
return absX, absY
|
||||
}
|
||||
|
||||
func mouseFlags(button string) (down, up uint32, err error) {
|
||||
switch strings.ToLower(strings.TrimSpace(button)) {
|
||||
case "", "left":
|
||||
|
||||
Reference in New Issue
Block a user