mirror of
https://github.com/go-vgo/robotgo
synced 2026-05-24 02:08:33 +00:00
Merge pull request #750 from PekingSpades/master
Add error-returning mouse click helpers (ClickE)
This commit is contained in:
commit
dafd29e5ea
2 changed files with 161 additions and 11 deletions
|
|
@ -156,20 +156,29 @@ MMPointInt32 location() {
|
|||
}
|
||||
|
||||
/* Press down a button, or release it. */
|
||||
void toggleMouse(bool down, MMMouseButton button) {
|
||||
int toggleMouseErr(bool down, MMMouseButton button) {
|
||||
#if defined(IS_MACOSX)
|
||||
const CGPoint currentPos = CGPointFromMMPointInt32(location());
|
||||
const CGEventType mouseType = MMMouseToCGEventType(down, button);
|
||||
CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||
CGEventRef event = CGEventCreateMouseEvent(source, mouseType, currentPos, (CGMouseButton)button);
|
||||
|
||||
if (event == NULL) {
|
||||
CFRelease(source);
|
||||
return (int)kCGErrorCannotComplete;
|
||||
}
|
||||
|
||||
CGEventPost(kCGHIDEventTap, event);
|
||||
CFRelease(event);
|
||||
CFRelease(source);
|
||||
|
||||
return 0;
|
||||
#elif defined(USE_X11)
|
||||
Display *display = XGetMainDisplay();
|
||||
XTestFakeButtonEvent(display, button, down ? True : False, CurrentTime);
|
||||
Status status = XTestFakeButtonEvent(display, button, down ? True : False, CurrentTime);
|
||||
XSync(display, false);
|
||||
|
||||
return status ? 0 : 1;
|
||||
#elif defined(IS_WINDOWS)
|
||||
// mouse_event(MMMouseToMEventF(down, button), 0, 0, 0, 0);
|
||||
INPUT mouseInput;
|
||||
|
|
@ -181,18 +190,31 @@ void toggleMouse(bool down, MMMouseButton button) {
|
|||
mouseInput.mi.time = 0;
|
||||
mouseInput.mi.dwExtraInfo = 0;
|
||||
mouseInput.mi.mouseData = 0;
|
||||
SendInput(1, &mouseInput, sizeof(mouseInput));
|
||||
UINT sent = SendInput(1, &mouseInput, sizeof(mouseInput));
|
||||
return sent == 1 ? 0 : (int)GetLastError();
|
||||
#endif
|
||||
}
|
||||
|
||||
void clickMouse(MMMouseButton button){
|
||||
toggleMouse(true, button);
|
||||
microsleep(5.0);
|
||||
toggleMouse(false, button);
|
||||
void toggleMouse(bool down, MMMouseButton button) {
|
||||
toggleMouseErr(down, button);
|
||||
}
|
||||
|
||||
/* Special function for sending double clicks, needed for MacOS. */
|
||||
void doubleClick(MMMouseButton button){
|
||||
int clickMouseErr(MMMouseButton button){
|
||||
int err = toggleMouseErr(true, button);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
microsleep(5.0);
|
||||
|
||||
return toggleMouseErr(false, button);
|
||||
}
|
||||
|
||||
void clickMouse(MMMouseButton button){
|
||||
clickMouseErr(button);
|
||||
}
|
||||
|
||||
int doubleClickErr(MMMouseButton button){
|
||||
#if defined(IS_MACOSX)
|
||||
/* Double click for Mac. */
|
||||
const CGPoint currentPos = CGPointFromMMPointInt32(location());
|
||||
|
|
@ -202,6 +224,11 @@ void doubleClick(MMMouseButton button){
|
|||
CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||
CGEventRef event = CGEventCreateMouseEvent(source, mouseTypeDown, currentPos, kCGMouseButtonLeft);
|
||||
|
||||
if (event == NULL) {
|
||||
CFRelease(source);
|
||||
return (int)kCGErrorCannotComplete;
|
||||
}
|
||||
|
||||
/* Set event to double click. */
|
||||
CGEventSetIntegerValueField(event, kCGMouseEventClickState, 2);
|
||||
CGEventPost(kCGHIDEventTap, event);
|
||||
|
|
@ -211,14 +238,26 @@ void doubleClick(MMMouseButton button){
|
|||
|
||||
CFRelease(event);
|
||||
CFRelease(source);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
/* Double click for everything else. */
|
||||
clickMouse(button);
|
||||
int err = clickMouseErr(button);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
microsleep(200);
|
||||
clickMouse(button);
|
||||
|
||||
return clickMouseErr(button);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Special function for sending double clicks, needed for MacOS. */
|
||||
void doubleClick(MMMouseButton button){
|
||||
doubleClickErr(button);
|
||||
}
|
||||
|
||||
/* Function used to scroll the screen in the required direction. */
|
||||
void scrollMouseXY(int x, int y) {
|
||||
#if defined(IS_WINDOWS)
|
||||
|
|
|
|||
111
robotgo.go
111
robotgo.go
|
|
@ -52,8 +52,10 @@ import "C"
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
|
|
@ -505,6 +507,24 @@ func CheckMouse(btn string) C.MMMouseButton {
|
|||
return C.LEFT_BUTTON
|
||||
}
|
||||
|
||||
// MouseButtonString converts a C.MMMouseButton to a readable name.
|
||||
func MouseButtonString(btn C.MMMouseButton) string {
|
||||
m1 := map[C.MMMouseButton]string{
|
||||
C.LEFT_BUTTON: "left",
|
||||
C.CENTER_BUTTON: "center",
|
||||
C.RIGHT_BUTTON: "right",
|
||||
C.WheelDown: "wheelDown",
|
||||
C.WheelUp: "wheelUp",
|
||||
C.WheelLeft: "wheelLeft",
|
||||
C.WheelRight: "wheelRight",
|
||||
}
|
||||
if v, ok := m1[btn]; ok {
|
||||
return v
|
||||
}
|
||||
|
||||
return fmt.Sprintf("button%d", btn)
|
||||
}
|
||||
|
||||
// MoveScale calculate the os scale factor x, y
|
||||
func MoveScale(x, y int, displayId ...int) (int, int) {
|
||||
if Scale || runtime.GOOS == "windows" {
|
||||
|
|
@ -671,6 +691,97 @@ func Click(args ...interface{}) {
|
|||
MilliSleep(MouseSleep)
|
||||
}
|
||||
|
||||
// ClickE click the mouse button and return error
|
||||
//
|
||||
// robotgo.ClickE(button string, double bool)
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// err := robotgo.ClickE() // default is left button
|
||||
// err := robotgo.ClickE("right")
|
||||
func ClickE(args ...interface{}) error {
|
||||
var (
|
||||
button C.MMMouseButton = C.LEFT_BUTTON
|
||||
double bool
|
||||
)
|
||||
|
||||
if len(args) > 0 {
|
||||
btn, ok := args[0].(string)
|
||||
if !ok {
|
||||
return errors.New("first argument must be a button string")
|
||||
}
|
||||
button = CheckMouse(btn)
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
dbl, ok := args[1].(bool)
|
||||
if !ok {
|
||||
return errors.New("second argument must be a bool indicating double click")
|
||||
}
|
||||
double = dbl
|
||||
}
|
||||
|
||||
defer MilliSleep(MouseSleep)
|
||||
|
||||
if !double {
|
||||
if code := C.toggleMouseErr(true, button); code != 0 {
|
||||
return formatClickError(int(code), button, "down", false)
|
||||
}
|
||||
|
||||
// match clickMouse timing
|
||||
C.microsleep(C.double(5.0))
|
||||
|
||||
if code := C.toggleMouseErr(false, button); code != 0 {
|
||||
return formatClickError(int(code), button, "up", false)
|
||||
}
|
||||
} else {
|
||||
if code := C.doubleClickErr(button); code != 0 {
|
||||
return formatClickError(int(code), button, "double", true)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatClickError(code int, button C.MMMouseButton, stage string, double bool) error {
|
||||
btnName := MouseButtonString(button)
|
||||
detail := ""
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
if code != 0 {
|
||||
detail = syscall.Errno(code).Error()
|
||||
}
|
||||
case "darwin":
|
||||
cgErrors := map[int]string{
|
||||
0: "kCGErrorSuccess",
|
||||
1000: "kCGErrorFailure",
|
||||
1001: "kCGErrorIllegalArgument",
|
||||
1002: "kCGErrorInvalidConnection",
|
||||
1003: "kCGErrorInvalidContext",
|
||||
1004: "kCGErrorCannotComplete",
|
||||
1005: "kCGErrorNotImplemented",
|
||||
1006: "kCGErrorRangeCheck",
|
||||
1007: "kCGErrorTypeCheck",
|
||||
1008: "kCGErrorNoCurrentPoint",
|
||||
1010: "kCGErrorInvalidOperation",
|
||||
}
|
||||
if v, ok := cgErrors[code]; ok {
|
||||
detail = v
|
||||
}
|
||||
default:
|
||||
if code == 1 {
|
||||
detail = "XTestFakeButtonEvent returned false"
|
||||
}
|
||||
}
|
||||
|
||||
if detail != "" {
|
||||
return fmt.Errorf("click %s failed (%s, double=%v): %s (code=%d)", stage, btnName, double, detail, code)
|
||||
}
|
||||
|
||||
return fmt.Errorf("click %s failed (%s, double=%v), code=%d", stage, btnName, double, code)
|
||||
}
|
||||
|
||||
// MoveClick move and click the mouse
|
||||
//
|
||||
// robotgo.MoveClick(x, y int, button string, double bool)
|
||||
|
|
|
|||
Loading…
Reference in a new issue