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:
+68
-7
@@ -2,6 +2,7 @@ package input
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
@@ -12,13 +13,20 @@ const (
|
|||||||
inputMouse = 0
|
inputMouse = 0
|
||||||
inputKeyboard = 1
|
inputKeyboard = 1
|
||||||
|
|
||||||
|
mouseMove = 0x0001
|
||||||
mouseLeftDown = 0x0002
|
mouseLeftDown = 0x0002
|
||||||
mouseLeftUp = 0x0004
|
mouseLeftUp = 0x0004
|
||||||
mouseRightDown = 0x0008
|
mouseRightDown = 0x0008
|
||||||
mouseRightUp = 0x0010
|
mouseRightUp = 0x0010
|
||||||
|
mouseAbsolute = 0x8000
|
||||||
|
mouseVirtualDesk = 0x4000
|
||||||
|
desktopAllAccess = 0x01FF
|
||||||
keyeventfKeyup = 0x0002
|
keyeventfKeyup = 0x0002
|
||||||
keyeventfUnicode = 0x0004
|
keyeventfUnicode = 0x0004
|
||||||
|
smXVirtualScreen = 76
|
||||||
|
smYVirtualScreen = 77
|
||||||
|
smCXVirtualScreen = 78
|
||||||
|
smCYVirtualScreen = 79
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -29,8 +37,13 @@ var (
|
|||||||
procSetCursorPos = user32.NewProc("SetCursorPos")
|
procSetCursorPos = user32.NewProc("SetCursorPos")
|
||||||
procSendInput = user32.NewProc("SendInput")
|
procSendInput = user32.NewProc("SendInput")
|
||||||
procSetProcessDPIAware = user32.NewProc("SetProcessDPIAware")
|
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 mouseInput struct {
|
||||||
Type uint32
|
Type uint32
|
||||||
_ uint32
|
_ uint32
|
||||||
@@ -39,6 +52,7 @@ type mouseInput struct {
|
|||||||
MouseData uint32
|
MouseData uint32
|
||||||
Flags uint32
|
Flags uint32
|
||||||
Time uint32
|
Time uint32
|
||||||
|
_ uint32
|
||||||
ExtraInfo uintptr
|
ExtraInfo uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,8 +63,8 @@ type keybdInput struct {
|
|||||||
Scan uint16
|
Scan uint16
|
||||||
Flags uint32
|
Flags uint32
|
||||||
Time uint32
|
Time uint32
|
||||||
|
_ uint32
|
||||||
ExtraInfo uintptr
|
ExtraInfo uintptr
|
||||||
_ [8]byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func EnableDPIAwareness() {
|
func EnableDPIAwareness() {
|
||||||
@@ -58,25 +72,43 @@ func EnableDPIAwareness() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Click(x, y int, button string) error {
|
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)
|
down, up, err := mouseFlags(button)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ok, _, callErr := procSetCursorPos.Call(uintptr(x), uintptr(y))
|
|
||||||
if ok == 0 {
|
absX, absY := absoluteCoords(x, y)
|
||||||
return callErr
|
|
||||||
}
|
|
||||||
inputs := []mouseInput{
|
inputs := []mouseInput{
|
||||||
|
{Type: inputMouse, Dx: absX, Dy: absY, Flags: mouseMove | mouseAbsolute | mouseVirtualDesk},
|
||||||
{Type: inputMouse, Flags: down},
|
{Type: inputMouse, Flags: down},
|
||||||
{Type: inputMouse, Flags: up},
|
{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 {
|
func TypeText(text string) error {
|
||||||
if text == "" {
|
if text == "" {
|
||||||
return ErrEmptyText
|
return ErrEmptyText
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
if err := attachInputDesktop(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
units := utf16.Encode([]rune(text))
|
units := utf16.Encode([]rune(text))
|
||||||
inputs := make([]keybdInput, 0, len(units)*2)
|
inputs := make([]keybdInput, 0, len(units)*2)
|
||||||
for _, unit := range units {
|
for _, unit := range units {
|
||||||
@@ -88,6 +120,35 @@ func TypeText(text string) error {
|
|||||||
return sendKeys(inputs)
|
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) {
|
func mouseFlags(button string) (down, up uint32, err error) {
|
||||||
switch strings.ToLower(strings.TrimSpace(button)) {
|
switch strings.ToLower(strings.TrimSpace(button)) {
|
||||||
case "", "left":
|
case "", "left":
|
||||||
|
|||||||
Reference in New Issue
Block a user