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:
2026-08-28 12:47:14 +03:00
parent d48c2044bc
commit d7274f34e0
+77 -16
View File
@@ -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":