Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 14 additions & 37 deletions mouse/mouse_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
}

#elif defined(IS_WINDOWS)

DWORD MMMouseUpToMEventF(MMMouseButton button) {
if (button == LEFT_BUTTON) { return MOUSEEVENTF_LEFTUP; }
if (button == RIGHT_BUTTON) { return MOUSEEVENTF_RIGHTUP; }
Expand Down Expand Up @@ -99,7 +98,6 @@ void moveMouse(MMPointInt32 point){
CGPointFromMMPointInt32(point), kCGMouseButtonLeft);

calculateDeltas(&move, point);

CGEventPost(kCGHIDEventTap, move);
CFRelease(move);
CFRelease(source);
Expand Down Expand Up @@ -156,7 +154,7 @@ MMPointInt32 location() {
}

/* Press down a button, or release it. */
int toggleMouseErr(bool down, MMMouseButton button) {
int toggleMouse(bool down, MMMouseButton button) {
#if defined(IS_MACOSX)
const CGPoint currentPos = CGPointFromMMPointInt32(location());
const CGEventType mouseType = MMMouseToCGEventType(down, button);
Expand All @@ -167,20 +165,17 @@ int toggleMouseErr(bool down, MMMouseButton button) {
CFRelease(source);
return (int)kCGErrorCannotComplete;
}

CGEventPost(kCGHIDEventTap, event);
CFRelease(event);
CFRelease(source);

return 0;
#elif defined(USE_X11)
Display *display = XGetMainDisplay();
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;

mouseInput.type = INPUT_MOUSE;
Expand All @@ -195,26 +190,18 @@ int toggleMouseErr(bool down, MMMouseButton button) {
#endif
}

void toggleMouse(bool down, MMMouseButton button) {
toggleMouseErr(down, button);
}

int clickMouseErr(MMMouseButton button){
int err = toggleMouseErr(true, button);
int clickMouse(MMMouseButton button){
int err = toggleMouse(true, button);
if (err != 0) {
return err;
}

microsleep(5.0);

return toggleMouseErr(false, button);
}

void clickMouse(MMMouseButton button){
clickMouseErr(button);
return toggleMouse(false, button);
}

int doubleClickErr(MMMouseButton button){
/* Special function for sending double clicks, needed for MacOS. */
int doubleClick(MMMouseButton button, int count){
#if defined(IS_MACOSX)
/* Double click for Mac. */
const CGPoint currentPos = CGPointFromMMPointInt32(location());
Expand All @@ -223,41 +210,32 @@ int doubleClickErr(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);
CGEventSetIntegerValueField(event, kCGMouseEventClickState, count);
CGEventPost(kCGHIDEventTap, event);

CGEventSetType(event, mouseTypeUP);
CGEventPost(kCGHIDEventTap, event);

CFRelease(event);
CFRelease(source);

return 0;
#else
/* Double click for everything else. */
int err = clickMouseErr(button);
int err = clickMouse(button);
if (err != 0) {
return err;
}

microsleep(200);

return clickMouseErr(button);
return clickMouse(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)
Expand All @@ -268,7 +246,7 @@ void scrollMouseXY(int x, int y) {

#if defined(IS_MACOSX)
CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef event = CGEventCreateScrollWheelEvent(source, kCGScrollEventUnitPixel, 2, y, x);
CGEventRef event = CGEventCreateScrollWheelEvent(source, kCGScrollEventUnitPixel, 2, y, x);
CGEventPost(kCGHIDEventTap, event);

CFRelease(event);
Expand Down Expand Up @@ -351,10 +329,10 @@ bool smoothlyMoveMouse(MMPointInt32 endPoint, double lowSpeed, double highSpeed)
velo_y /= veloDistance;

pos.x += floor(velo_x + 0.5);
pos.y += floor(velo_y + 0.5);
pos.y += floor(velo_y + 0.5);

/* Make sure we are in the screen boundaries! (Strange things will happen if we are not.) */
// if (pos.x >= screenSize.w || pos.y >= screenSize.h) {
/* Make sure we are in the screen boundaries! */
// if (pos.x >= screenSize.w || pos.y >= screenSize.h) {
// return false;
// }
moveMouse(pos);
Expand All @@ -363,6 +341,5 @@ bool smoothlyMoveMouse(MMPointInt32 endPoint, double lowSpeed, double highSpeed)
microsleep(DEADBEEF_UNIFORM(lowSpeed, highSpeed));
// microsleep(DEADBEEF_UNIFORM(1.0, 3.0));
}

return true;
}
76 changes: 50 additions & 26 deletions robotgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,7 @@ func Location() (int, int) {
return x, y
}

// Click click the mouse button
// ClickV1 click the mouse button
//
// robotgo.Click(button string, double bool)
//
Expand All @@ -668,7 +668,7 @@ func Location() (int, int) {
// robotgo.Click() // default is left button
// robotgo.Click("right")
// robotgo.Click("wheelLeft")
func Click(args ...interface{}) {
func ClickV1(args ...interface{}) {
var (
button C.MMMouseButton = C.LEFT_BUTTON
double bool
Expand All @@ -685,24 +685,25 @@ func Click(args ...interface{}) {
if !double {
C.clickMouse(button)
} else {
C.doubleClick(button)
C.doubleClick(button, 2)
}

MilliSleep(MouseSleep)
}

// ClickE click the mouse button and return error
// Click click the mouse button and return error
//
// robotgo.ClickE(button string, double bool)
// robotgo.Click(button string, double bool)
//
// Examples:
//
// err := robotgo.ClickE() // default is left button
// err := robotgo.ClickE("right")
func ClickE(args ...interface{}) error {
// err := robotgo.Click() // default is left button
// err := robotgo.Click("right")
func Click(args ...interface{}) error {
Comment on lines +694 to +702
Copy link

Copilot AI Jan 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Click function signature in the documentation comment still references the old API with (button string, double bool), but the actual implementation now supports an additional count parameter at args[3]. The documentation should be updated to reflect the new signature or the count parameter implementation should be reconsidered since it appears incomplete.

Copilot uses AI. Check for mistakes.
var (
button C.MMMouseButton = C.LEFT_BUTTON
double bool
count int
)

if len(args) > 0 {
Expand All @@ -720,30 +721,54 @@ func ClickE(args ...interface{}) error {
}
double = dbl
}
if len(args) > 2 {
count = args[2].(int)
}

defer MilliSleep(MouseSleep)

if !double {
if code := C.toggleMouseErr(true, button); code != 0 {
return formatClickError(int(code), button, "down", false)
if code := C.toggleMouse(true, button); code != 0 {
return formatClickError(int(code), button, "down", count)
}

// match clickMouse timing
C.microsleep(C.double(5.0))

if code := C.toggleMouseErr(false, button); code != 0 {
return formatClickError(int(code), button, "up", false)
MilliSleep(5)
if code := C.toggleMouse(false, button); code != 0 {
return formatClickError(int(code), button, "up", count)
}
} else {
if code := C.doubleClickErr(button); code != 0 {
return formatClickError(int(code), button, "double", true)
if code := C.doubleClick(button, 2); code != 0 {
return formatClickError(int(code), button, "double", 2)
}
}
return nil
}

// MultiClick performs multiple clicks and returns error
//
// robotgo.MultiClick(button string, count int)
func MultiClick(button string, count int, click ...bool) error {
if count < 1 {
return nil
}
defer MilliSleep(MouseSleep)

if runtime.GOOS == "darwin" && len(click) <= 0 {
btn := CheckMouse(button)
code := C.doubleClick(btn, C.int(count))
return formatClickError(int(code), btn, "down", count)
}

for i := 0; i < count; i++ {
if err := Click(button, false, i+1); err != nil {
return err
}
}
return nil
}

func formatClickError(code int, button C.MMMouseButton, stage string, double bool) error {
func formatClickError(code int, button C.MMMouseButton, stage string, count int) error {
if code == 0 {
return nil
}
btnName := MouseButtonString(button)
detail := ""

Expand Down Expand Up @@ -776,10 +801,9 @@ func formatClickError(code int, button C.MMMouseButton, stage string, double boo
}

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, count=%d): %s (code=%d)", stage, btnName, count, detail, code)
}

return fmt.Errorf("click %s failed (%s, double=%v), code=%d", stage, btnName, double, code)
return fmt.Errorf("click %s failed (%s, count=%d), code=%d", stage, btnName, count, code)
}

// MoveClick move and click the mouse
Expand Down Expand Up @@ -824,12 +848,12 @@ func Toggle(key ...interface{}) error {
if len(key) > 1 && key[1].(string) == "up" {
down = false
}
C.toggleMouse(C.bool(down), button)

code := C.toggleMouse(C.bool(down), button)
if len(key) > 2 {
MilliSleep(MouseSleep)
}

return nil
return formatClickError(int(code), button, "down", 1)
}

// MouseDown send mouse down event
Expand Down
Loading