1
1
#include "../mouse.h"
2
+ #include "../deadbeef_rand.h"
2
3
#include "../microsleep.h"
4
+ #include "mouse_utils.h"
3
5
4
- #include <math.h> /* For floor() */
5
6
#include <ApplicationServices/ApplicationServices.h>
7
+ #include <math.h> /* For floor() */
6
8
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 ;
10
10
11
- #define MMMouseToCGEventType (down , button ) \
12
- (down ? MMMouseDownToCGEventType(button) : MMMouseUpToCGEventType(button))
11
+ #define MMMouseToCGEventType (down , button ) \
12
+ (down ? MMMouseDownToCGEventType(button) : MMMouseUpToCGEventType(button))
13
13
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))
18
19
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))
23
25
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))
28
31
29
32
/**
30
33
* Calculate the delta for a mouse move and add them to the event.
31
34
* @param event The mouse move event (by ref).
32
35
* @param point The new mouse x and y.
33
36
*/
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 );
52
54
}
53
55
54
56
/**
55
57
* Move the mouse to a specific point.
56
58
* @param point The coordinates to move the mouse to (x, y).
57
59
*/
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 );
89
81
}
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 );
91
90
}
92
91
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 );
99
98
100
- CGEventPost (kCGHIDEventTap , drag );
101
- CFRelease (drag );
102
- CFRelease (src );
99
+ CGEventPost (kCGHIDEventTap , drag );
100
+ CFRelease (drag );
101
+ CFRelease (src );
103
102
}
104
103
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 );
112
110
113
- return MMPointFromCGPoint (point );
111
+ return MMPointFromCGPoint (point );
114
112
}
115
113
116
114
/**
117
115
* Press down a button, or release it.
118
116
* @param down True for down, false for up.
119
117
* @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
120
126
*/
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 );
130
148
}
131
149
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 );
136
153
}
137
154
138
155
/**
139
156
* Special function for sending double clicks, needed for Mac OS X.
140
157
* @param button Button to click.
141
158
*/
142
159
void 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 );
172
168
}
173
169
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
+ */
181
176
182
- CGEventRef event ;
177
+ CGEventRef event ;
183
178
184
- event = CGEventCreateScrollWheelEvent (NULL , kCGScrollEventUnitPixel , 2 , y , x );
185
- CGEventPost (kCGHIDEventTap , event );
179
+ event = CGEventCreateScrollWheelEvent (NULL , kCGScrollEventUnitPixel , 2 , y , x );
180
+ CGEventPost (kCGHIDEventTap , event );
186
181
187
- CFRelease (event );
182
+ CFRelease (event );
188
183
}
0 commit comments