11#include "../mouse.h"
2+ #include "../deadbeef_rand.h"
23#include "../microsleep.h"
4+ #include "mouse_utils.h"
35
4- #include <math.h> /* For floor() */
56#include <ApplicationServices/ApplicationServices.h>
7+ #include <math.h> /* For floor() */
68
7- #if !defined(M_SQRT2 )
8- #define M_SQRT2 1.4142135623730950488016887 /* Fix for MSVC. */
9- #endif
9+ static int32_t DEFAULT_DOUBLE_CLICK_INTERVAL_MS = 200 ;
1010
11- #define MMMouseToCGEventType (down , button ) \
12- (down ? MMMouseDownToCGEventType(button) : MMMouseUpToCGEventType(button))
11+ #define MMMouseToCGEventType (down , button ) \
12+ (down ? MMMouseDownToCGEventType(button) : MMMouseUpToCGEventType(button))
1313
14- #define MMMouseDownToCGEventType (button ) \
15- ((button) == (LEFT_BUTTON) ? kCGEventLeftMouseDown \
16- : ((button) == RIGHT_BUTTON ? kCGEventRightMouseDown \
17- : kCGEventOtherMouseDown))
14+ #define MMMouseDownToCGEventType (button ) \
15+ ((button) == (LEFT_BUTTON) \
16+ ? kCGEventLeftMouseDown \
17+ : ((button) == RIGHT_BUTTON ? kCGEventRightMouseDown \
18+ : kCGEventOtherMouseDown))
1819
19- #define MMMouseUpToCGEventType (button ) \
20- ((button) == LEFT_BUTTON ? kCGEventLeftMouseUp \
21- : ((button) == RIGHT_BUTTON ? kCGEventRightMouseUp \
22- : kCGEventOtherMouseUp))
20+ #define MMMouseUpToCGEventType (button ) \
21+ ((button) == LEFT_BUTTON \
22+ ? kCGEventLeftMouseUp \
23+ : ((button) == RIGHT_BUTTON ? kCGEventRightMouseUp \
24+ : kCGEventOtherMouseUp))
2325
24- #define MMMouseDragToCGEventType (button ) \
25- ((button) == LEFT_BUTTON ? kCGEventLeftMouseDragged \
26- : ((button) == RIGHT_BUTTON ? kCGEventRightMouseDragged \
27- : kCGEventOtherMouseDragged))
26+ #define MMMouseDragToCGEventType (button ) \
27+ ((button) == LEFT_BUTTON \
28+ ? kCGEventLeftMouseDragged \
29+ : ((button) == RIGHT_BUTTON ? kCGEventRightMouseDragged \
30+ : kCGEventOtherMouseDragged))
2831
2932/**
3033 * Calculate the delta for a mouse move and add them to the event.
3134 * @param event The mouse move event (by ref).
3235 * @param point The new mouse x and y.
3336 */
34- void calculateDeltas (CGEventRef * event , MMPoint point )
35- {
36- /**
37- * The next few lines are a workaround for games not detecting mouse moves.
38- * See this issue for more information:
39- * https://github.com/octalmage/robotjs/issues/159
40- */
41- CGEventRef get = CGEventCreate (NULL );
42- CGPoint mouse = CGEventGetLocation (get );
43-
44- // Calculate the deltas.
45- int64_t deltaX = point .x - mouse .x ;
46- int64_t deltaY = point .y - mouse .y ;
47-
48- CGEventSetIntegerValueField (* event , kCGMouseEventDeltaX , deltaX );
49- CGEventSetIntegerValueField (* event , kCGMouseEventDeltaY , deltaY );
50-
51- CFRelease (get );
37+ void calculateDeltas (CGEventRef * event , MMPoint point ) {
38+ /**
39+ * The next few lines are a workaround for games not detecting mouse moves.
40+ * See this issue for more information:
41+ * https://github.com/octalmage/robotjs/issues/159
42+ */
43+ CGEventRef get = CGEventCreate (NULL );
44+ CGPoint mouse = CGEventGetLocation (get );
45+
46+ // Calculate the deltas.
47+ int64_t deltaX = point .x - mouse .x ;
48+ int64_t deltaY = point .y - mouse .y ;
49+
50+ CGEventSetIntegerValueField (* event , kCGMouseEventDeltaX , deltaX );
51+ CGEventSetIntegerValueField (* event , kCGMouseEventDeltaY , deltaY );
52+
53+ CFRelease (get );
5254}
5355
5456/**
5557 * Move the mouse to a specific point.
5658 * @param point The coordinates to move the mouse to (x, y).
5759 */
58- void moveMouse (MMPoint point )
59- {
60- CGPoint position = CGPointMake (point .x , point .y );
61- // Create an HID hardware event source
62- CGEventSourceRef src = CGEventSourceCreate (kCGEventSourceStateHIDSystemState );
63-
64- CGEventRef evt = NULL ;
65- if (CGEventSourceButtonState (kCGEventSourceStateHIDSystemState , kCGMouseButtonLeft ))
66- {
67- // Create a left button drag
68- evt = CGEventCreateMouseEvent (src , kCGEventLeftMouseDragged , position , kCGMouseButtonLeft );
69- }
70- else
71- {
72- if (CGEventSourceButtonState (kCGEventSourceStateHIDSystemState , kCGMouseButtonRight ))
73- {
74- // Create a right button drag
75- evt = CGEventCreateMouseEvent (src , kCGEventRightMouseDragged , position , kCGMouseButtonLeft );
76- }
77- else
78- {
79- // Create a mouse move event
80- evt = CGEventCreateMouseEvent (src , kCGEventMouseMoved , position , kCGMouseButtonLeft );
81- }
82- }
83-
84- // Post mouse event and release
85- CGEventPost (kCGHIDEventTap , evt );
86- if (evt != NULL )
87- {
88- CFRelease (evt );
60+ void moveMouse (MMPoint point ) {
61+ CGPoint position = CGPointMake (point .x , point .y );
62+ // Create an HID hardware event source
63+ CGEventSourceRef src = CGEventSourceCreate (kCGEventSourceStateHIDSystemState );
64+
65+ CGEventRef evt = NULL ;
66+ if (CGEventSourceButtonState (kCGEventSourceStateHIDSystemState ,
67+ kCGMouseButtonLeft )) {
68+ // Create a left button drag
69+ evt = CGEventCreateMouseEvent (src , kCGEventLeftMouseDragged , position ,
70+ kCGMouseButtonLeft );
71+ } else {
72+ if (CGEventSourceButtonState (kCGEventSourceStateHIDSystemState ,
73+ kCGMouseButtonRight )) {
74+ // Create a right button drag
75+ evt = CGEventCreateMouseEvent (src , kCGEventRightMouseDragged , position ,
76+ kCGMouseButtonLeft );
77+ } else {
78+ // Create a mouse move event
79+ evt = CGEventCreateMouseEvent (src , kCGEventMouseMoved , position ,
80+ kCGMouseButtonLeft );
8981 }
90- CFRelease (src );
82+ }
83+
84+ // Post mouse event and release
85+ CGEventPost (kCGHIDEventTap , evt );
86+ if (evt != NULL ) {
87+ CFRelease (evt );
88+ }
89+ CFRelease (src );
9190}
9291
93- void dragMouse (MMPoint point , const MMMouseButton button )
94- {
95- CGEventSourceRef src = CGEventSourceCreate ( kCGEventSourceStateHIDSystemState );
96- const CGEventType dragType = MMMouseDragToCGEventType ( button );
97- CGEventRef drag = CGEventCreateMouseEvent ( src , dragType , CGPointFromMMPoint (point ), (CGMouseButton )button );
98- calculateDeltas (& drag , point );
92+ void dragMouse (MMPoint point , const MMMouseButton button ) {
93+ CGEventSourceRef src = CGEventSourceCreate ( kCGEventSourceStateHIDSystemState );
94+ const CGEventType dragType = MMMouseDragToCGEventType ( button );
95+ CGEventRef drag = CGEventCreateMouseEvent (
96+ src , dragType , CGPointFromMMPoint (point ), (CGMouseButton )button );
97+ calculateDeltas (& drag , point );
9998
100- CGEventPost (kCGHIDEventTap , drag );
101- CFRelease (drag );
102- CFRelease (src );
99+ CGEventPost (kCGHIDEventTap , drag );
100+ CFRelease (drag );
101+ CFRelease (src );
103102}
104103
105- MMPoint getMousePos ()
106- {
107- CGEventSourceRef src = CGEventSourceCreate (kCGEventSourceStateHIDSystemState );
108- CGEventRef event = CGEventCreate (src );
109- CGPoint point = CGEventGetLocation (event );
110- CFRelease (event );
111- CFRelease (src );
104+ MMPoint getMousePos () {
105+ CGEventSourceRef src = CGEventSourceCreate (kCGEventSourceStateHIDSystemState );
106+ CGEventRef event = CGEventCreate (src );
107+ CGPoint point = CGEventGetLocation (event );
108+ CFRelease (event );
109+ CFRelease (src );
112110
113- return MMPointFromCGPoint (point );
111+ return MMPointFromCGPoint (point );
114112}
115113
116114/**
117115 * Press down a button, or release it.
118116 * @param down True for down, false for up.
119117 * @param button The button to press down or release.
118+ *
119+ * This function ships a manual implementation to handle double clicks by tracking the time interval between mouse events.
120+ * Reason for this is the fact that https://developer.apple.com/documentation/coregraphics/1408790-cgeventsourcesecondssincelasteve?language=objc
121+ * has a bit of latency and will stop working correctly if the time between two consecutive clicks is not long enough.
122+ *
123+ * This implementation captures the current timestamp for up/down events on each of left/middle/right mouse buttons.
124+ * If the interval between two clicks is lower than https://developer.apple.com/documentation/appkit/nsevent/1528384-doubleclickinterval?language=objc
125+ * and both clicks happen at the same position, we alter the mouse event to trigger a double click by setting kCGMouseEventClickState = 2 on the event
120126 */
121- void toggleMouse (bool down , MMMouseButton button )
122- {
123- const CGPoint currentPos = CGPointFromMMPoint (getMousePos ());
124- const CGEventType mouseType = MMMouseToCGEventType (down , button );
125- CGEventSourceRef src = CGEventSourceCreate (kCGEventSourceStateHIDSystemState );
126- CGEventRef event = CGEventCreateMouseEvent (src , mouseType , currentPos , (CGMouseButton )button );
127- CGEventPost (kCGHIDEventTap , event );
128- CFRelease (event );
129- CFRelease (src );
127+ void toggleMouse (bool down , MMMouseButton button ) {
128+ static ClickTimer clickTimer = {{0 , 0 }, {0 , 0 }, {0 , 0 }, {0 , 0 }};
129+
130+ MMPoint currentMMPoint = getMousePos ();
131+
132+ clock_t intervalSinceLastClick = timeSinceLastClick (& clickTimer , down , button , clock ());
133+
134+ const CGPoint currentPos = CGPointFromMMPoint (currentMMPoint );
135+ const CGEventType mouseType = MMMouseToCGEventType (down , button );
136+ CGEventSourceRef src = CGEventSourceCreate (kCGEventSourceStateHIDSystemState );
137+ CGEventRef event = CGEventCreateMouseEvent (src , mouseType , currentPos ,
138+ (CGMouseButton )button );
139+ double maxInterval = GetDoubleClickTime ();
140+ if (intervalSinceLastClick > 0 && intervalSinceLastClick <= maxInterval &&
141+ areSamePoint (currentMMPoint , clickTimer .clickLocation )) {
142+ CGEventSetIntegerValueField (event , kCGMouseEventClickState , 2 );
143+ }
144+ CGEventPost (kCGHIDEventTap , event );
145+ CFRelease (event );
146+ CFRelease (src );
147+ recordClickTime (& clickTimer , down , button , currentMMPoint );
130148}
131149
132- void clickMouse (MMMouseButton button )
133- {
134- toggleMouse (true, button );
135- toggleMouse (false, button );
150+ void clickMouse (MMMouseButton button ) {
151+ toggleMouse (true, button );
152+ toggleMouse (false, button );
136153}
137154
138155/**
139156 * Special function for sending double clicks, needed for Mac OS X.
140157 * @param button Button to click.
141158 */
142159void doubleClick (MMMouseButton button ) {
143- /* Double click for Mac. */
144- const CGPoint currentPos = CGPointFromMMPoint (getMousePos ());
145- const CGEventType mouseTypeDown = MMMouseToCGEventType (true, button );
146- const CGEventType mouseTypeUp = MMMouseToCGEventType (false, button );
147-
148- CGEventSourceRef src = CGEventSourceCreate (kCGEventSourceStateHIDSystemState );
149- CGEventRef event = CGEventCreateMouseEvent (src , mouseTypeDown , currentPos ,
150- button );
151-
152- // First down
153- CGEventPost (kCGHIDEventTap , event );
154-
155- // First up
156- CGEventSetType (event , mouseTypeUp );
157- CGEventPost (kCGHIDEventTap , event );
158-
159- /* Set event to double click. */
160- CGEventSetIntegerValueField (event , kCGMouseEventClickState , 2 );
161-
162- // Second down
163- CGEventSetType (event , mouseTypeDown );
164- CGEventPost (kCGHIDEventTap , event );
165-
166- // Second up
167- CGEventSetType (event , mouseTypeUp );
168- CGEventPost (kCGHIDEventTap , event );
169-
170- CFRelease (event );
171- CFRelease (src );
160+ double maxDoubleClickTime = GetDoubleClickTime ();
161+ clickMouse (button );
162+ if (maxDoubleClickTime > DEFAULT_DOUBLE_CLICK_INTERVAL_MS ) {
163+ microsleep (DEFAULT_DOUBLE_CLICK_INTERVAL_MS );
164+ } else {
165+ microsleep (DEADBEEF_RANDRANGE (1 , maxDoubleClickTime ));
166+ }
167+ clickMouse (button );
172168}
173169
174- void scrollMouse (int x , int y )
175- {
176- /*
177- * Direction should only be considered based on the scrollDirection.
178- * This should not interfere.
179- * Set up the OS specific solution
180- */
170+ void scrollMouse (int x , int y ) {
171+ /*
172+ * Direction should only be considered based on the scrollDirection.
173+ * This should not interfere.
174+ * Set up the OS specific solution
175+ */
181176
182- CGEventRef event ;
177+ CGEventRef event ;
183178
184- event = CGEventCreateScrollWheelEvent (NULL , kCGScrollEventUnitPixel , 2 , y , x );
185- CGEventPost (kCGHIDEventTap , event );
179+ event = CGEventCreateScrollWheelEvent (NULL , kCGScrollEventUnitPixel , 2 , y , x );
180+ CGEventPost (kCGHIDEventTap , event );
186181
187- CFRelease (event );
182+ CFRelease (event );
188183}
0 commit comments