Skip to content

Commit 5fdcc66

Browse files
committed
(#20) Split up mouse implementation
1 parent dce87eb commit 5fdcc66

File tree

5 files changed

+400
-345
lines changed

5 files changed

+400
-345
lines changed

CMakeLists.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ set(CMAKE_CXX_STANDARD 17)
44
project(libnut)
55

66
# Source
7-
set(SOURCE_FILES "src/main.cc" "src/deadbeef_rand.c" "src/keycode.c" "src/keypress.c" "src/MMBitmap.c" "src/mouse.c" "src/screen.c" "src/screengrab.c")
7+
set(SOURCE_FILES "src/main.cc" "src/deadbeef_rand.c" "src/keycode.c" "src/keypress.c" "src/MMBitmap.c" "src/screen.c" "src/screengrab.c")
88
if (UNIX AND NOT APPLE)
9-
set(SOURCE_FILES "${SOURCE_FILES}" "src/linux/xdisplay.c" "src/linux/highlightwindow.c" "src/linux/window_manager.cc")
9+
set(SOURCE_FILES "${SOURCE_FILES}" "src/linux/mouse.c" "src/linux/xdisplay.c" "src/linux/highlightwindow.c" "src/linux/window_manager.cc")
1010
elseif (UNIX AND APPLE)
11-
set(SOURCE_FILES "${SOURCE_FILES}" "src/macos/highlightwindow.m" "src/macos/window_manager.mm")
11+
set(SOURCE_FILES "${SOURCE_FILES}" "src/macos/mouse.c" "src/macos/highlightwindow.m" "src/macos/window_manager.mm")
1212
elseif (WIN32)
13-
set(SOURCE_FILES "${SOURCE_FILES}" "src/win32/highlightwindow.c" "src/win32/window_manager.cc")
13+
set(SOURCE_FILES "${SOURCE_FILES}" "src/win32/mouse.c" "src/win32/highlightwindow.c" "src/win32/window_manager.cc")
1414
endif()
1515
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})
1616

src/linux/mouse.c

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#include "../mouse.h"
2+
#include "../microsleep.h"
3+
4+
#include <math.h> /* For floor() */
5+
6+
#include <X11/Xlib.h>
7+
#include <X11/extensions/XTest.h>
8+
#include <stdlib.h>
9+
#include "../xdisplay.h"
10+
11+
#if !defined(M_SQRT2)
12+
#define M_SQRT2 1.4142135623730950488016887 /* Fix for MSVC. */
13+
#endif
14+
15+
/**
16+
* Move the mouse to a specific point.
17+
* @param point The coordinates to move the mouse to (x, y).
18+
*/
19+
void moveMouse(MMPoint point)
20+
{
21+
Display *display = XGetMainDisplay();
22+
XWarpPointer(display, None, DefaultRootWindow(display), 0, 0, 0, 0, point.x, point.y);
23+
XSync(display, false);
24+
}
25+
26+
void dragMouse(MMPoint point, const MMMouseButton button)
27+
{
28+
moveMouse(point);
29+
}
30+
31+
MMPoint getMousePos()
32+
{
33+
int x, y; /* This is all we care about. Seriously. */
34+
Window garb1, garb2; /* Why you can't specify NULL as a parameter */
35+
int garb_x, garb_y; /* is beyond me. */
36+
unsigned int more_garbage;
37+
38+
Display *display = XGetMainDisplay();
39+
XQueryPointer(display, XDefaultRootWindow(display), &garb1, &garb2, &x, &y, &garb_x, &garb_y, &more_garbage);
40+
41+
return MMPointMake(x, y);
42+
}
43+
44+
/**
45+
* Press down a button, or release it.
46+
* @param down True for down, false for up.
47+
* @param button The button to press down or release.
48+
*/
49+
void toggleMouse(bool down, MMMouseButton button)
50+
{
51+
Display *display = XGetMainDisplay();
52+
XTestFakeButtonEvent(display, button, down ? True : False, CurrentTime);
53+
XSync(display, false);
54+
}
55+
56+
void clickMouse(MMMouseButton button)
57+
{
58+
toggleMouse(true, button);
59+
toggleMouse(false, button);
60+
}
61+
62+
/**
63+
* Special function for sending double clicks, needed for Mac OS X.
64+
* @param button Button to click.
65+
*/
66+
void doubleClick(MMMouseButton button)
67+
{
68+
clickMouse(button);
69+
microsleep(200);
70+
clickMouse(button);
71+
}
72+
73+
void scrollMouse(int x, int y)
74+
{
75+
/*
76+
X11 Mouse Button Numbering
77+
1 = left button
78+
2 = middle button (pressing the scroll wheel)
79+
3 = right button
80+
4 = turn scroll wheel up
81+
5 = turn scroll wheel down
82+
6 = push scroll wheel left
83+
7 = push scroll wheel right
84+
8 = 4th button (aka browser backward button)
85+
9 = 5th button (aka browser forward button)
86+
*/
87+
int ydir = 4; // Button 4 is up, 5 is down.
88+
int xdir = 6; // Button 6 is left, 7 is right.
89+
Display *display = XGetMainDisplay();
90+
91+
if (y < 0)
92+
{
93+
ydir = 5;
94+
}
95+
if (x < 0)
96+
{
97+
xdir = 7;
98+
}
99+
100+
int xi;
101+
int yi;
102+
for (xi = 0; xi < abs(x); xi++)
103+
{
104+
XTestFakeButtonEvent(display, xdir, 1, CurrentTime);
105+
XTestFakeButtonEvent(display, xdir, 0, CurrentTime);
106+
}
107+
for (yi = 0; yi < abs(y); yi++)
108+
{
109+
XTestFakeButtonEvent(display, ydir, 1, CurrentTime);
110+
XTestFakeButtonEvent(display, ydir, 0, CurrentTime);
111+
}
112+
113+
XSync(display, false);
114+
}

src/macos/mouse.c

+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
#include "../mouse.h"
2+
#include "../microsleep.h"
3+
4+
#include <math.h> /* For floor() */
5+
#include <ApplicationServices/ApplicationServices.h>
6+
7+
#if !defined(M_SQRT2)
8+
#define M_SQRT2 1.4142135623730950488016887 /* Fix for MSVC. */
9+
#endif
10+
11+
#define MMMouseToCGEventType(down, button) \
12+
(down ? MMMouseDownToCGEventType(button) : MMMouseUpToCGEventType(button))
13+
14+
#define MMMouseDownToCGEventType(button) \
15+
((button) == (LEFT_BUTTON) ? kCGEventLeftMouseDown \
16+
: ((button) == RIGHT_BUTTON ? kCGEventRightMouseDown \
17+
: kCGEventOtherMouseDown))
18+
19+
#define MMMouseUpToCGEventType(button) \
20+
((button) == LEFT_BUTTON ? kCGEventLeftMouseUp \
21+
: ((button) == RIGHT_BUTTON ? kCGEventRightMouseUp \
22+
: kCGEventOtherMouseUp))
23+
24+
#define MMMouseDragToCGEventType(button) \
25+
((button) == LEFT_BUTTON ? kCGEventLeftMouseDragged \
26+
: ((button) == RIGHT_BUTTON ? kCGEventRightMouseDragged \
27+
: kCGEventOtherMouseDragged))
28+
29+
/**
30+
* Calculate the delta for a mouse move and add them to the event.
31+
* @param event The mouse move event (by ref).
32+
* @param point The new mouse x and y.
33+
*/
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);
52+
}
53+
54+
/**
55+
* Move the mouse to a specific point.
56+
* @param point The coordinates to move the mouse to (x, y).
57+
*/
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);
89+
}
90+
CFRelease(src);
91+
}
92+
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);
99+
100+
CGEventPost(kCGHIDEventTap, drag);
101+
CFRelease(drag);
102+
CFRelease(src);
103+
}
104+
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);
112+
113+
return MMPointFromCGPoint(point);
114+
}
115+
116+
/**
117+
* Press down a button, or release it.
118+
* @param down True for down, false for up.
119+
* @param button The button to press down or release.
120+
*/
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);
130+
}
131+
132+
void clickMouse(MMMouseButton button)
133+
{
134+
toggleMouse(true, button);
135+
toggleMouse(false, button);
136+
}
137+
138+
/**
139+
* Special function for sending double clicks, needed for Mac OS X.
140+
* @param button Button to click.
141+
*/
142+
void doubleClick(MMMouseButton button)
143+
{
144+
/* Double click for Mac. */
145+
const CGPoint currentPos = CGPointFromMMPoint(getMousePos());
146+
const CGEventType mouseTypeDown = MMMouseToCGEventType(true, button);
147+
const CGEventType mouseTypeUP = MMMouseToCGEventType(false, button);
148+
149+
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
150+
CGEventRef event = CGEventCreateMouseEvent(src, mouseTypeDown, currentPos, kCGMouseButtonLeft);
151+
152+
/* Set event to double click. */
153+
CGEventSetIntegerValueField(event, kCGMouseEventClickState, 2);
154+
155+
CGEventPost(kCGHIDEventTap, event);
156+
157+
CGEventSetType(event, mouseTypeUP);
158+
CGEventPost(kCGHIDEventTap, event);
159+
160+
CFRelease(event);
161+
CFRelease(src);
162+
}
163+
164+
void scrollMouse(int x, int y)
165+
{
166+
/*
167+
* Direction should only be considered based on the scrollDirection.
168+
* This should not interfere.
169+
* Set up the OS specific solution
170+
*/
171+
172+
CGEventRef event;
173+
174+
event = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 2, y, x);
175+
CGEventPost(kCGHIDEventTap, event);
176+
177+
CFRelease(event);
178+
}

0 commit comments

Comments
 (0)