Skip to content

Commit

Permalink
(#20) Split up mouse implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
s1hofmann committed Sep 3, 2020
1 parent dce87eb commit 5fdcc66
Show file tree
Hide file tree
Showing 5 changed files with 400 additions and 345 deletions.
8 changes: 4 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ set(CMAKE_CXX_STANDARD 17)
project(libnut)

# Source
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")
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")
if (UNIX AND NOT APPLE)
set(SOURCE_FILES "${SOURCE_FILES}" "src/linux/xdisplay.c" "src/linux/highlightwindow.c" "src/linux/window_manager.cc")
set(SOURCE_FILES "${SOURCE_FILES}" "src/linux/mouse.c" "src/linux/xdisplay.c" "src/linux/highlightwindow.c" "src/linux/window_manager.cc")
elseif (UNIX AND APPLE)
set(SOURCE_FILES "${SOURCE_FILES}" "src/macos/highlightwindow.m" "src/macos/window_manager.mm")
set(SOURCE_FILES "${SOURCE_FILES}" "src/macos/mouse.c" "src/macos/highlightwindow.m" "src/macos/window_manager.mm")
elseif (WIN32)
set(SOURCE_FILES "${SOURCE_FILES}" "src/win32/highlightwindow.c" "src/win32/window_manager.cc")
set(SOURCE_FILES "${SOURCE_FILES}" "src/win32/mouse.c" "src/win32/highlightwindow.c" "src/win32/window_manager.cc")
endif()
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})

Expand Down
114 changes: 114 additions & 0 deletions src/linux/mouse.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#include "../mouse.h"
#include "../microsleep.h"

#include <math.h> /* For floor() */

#include <X11/Xlib.h>
#include <X11/extensions/XTest.h>
#include <stdlib.h>
#include "../xdisplay.h"

#if !defined(M_SQRT2)
#define M_SQRT2 1.4142135623730950488016887 /* Fix for MSVC. */
#endif

/**
* Move the mouse to a specific point.
* @param point The coordinates to move the mouse to (x, y).
*/
void moveMouse(MMPoint point)
{
Display *display = XGetMainDisplay();
XWarpPointer(display, None, DefaultRootWindow(display), 0, 0, 0, 0, point.x, point.y);
XSync(display, false);
}

void dragMouse(MMPoint point, const MMMouseButton button)
{
moveMouse(point);
}

MMPoint getMousePos()
{
int x, y; /* This is all we care about. Seriously. */
Window garb1, garb2; /* Why you can't specify NULL as a parameter */
int garb_x, garb_y; /* is beyond me. */
unsigned int more_garbage;

Display *display = XGetMainDisplay();
XQueryPointer(display, XDefaultRootWindow(display), &garb1, &garb2, &x, &y, &garb_x, &garb_y, &more_garbage);

return MMPointMake(x, y);
}

/**
* Press down a button, or release it.
* @param down True for down, false for up.
* @param button The button to press down or release.
*/
void toggleMouse(bool down, MMMouseButton button)
{
Display *display = XGetMainDisplay();
XTestFakeButtonEvent(display, button, down ? True : False, CurrentTime);
XSync(display, false);
}

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

/**
* Special function for sending double clicks, needed for Mac OS X.
* @param button Button to click.
*/
void doubleClick(MMMouseButton button)
{
clickMouse(button);
microsleep(200);
clickMouse(button);
}

void scrollMouse(int x, int y)
{
/*
X11 Mouse Button Numbering
1 = left button
2 = middle button (pressing the scroll wheel)
3 = right button
4 = turn scroll wheel up
5 = turn scroll wheel down
6 = push scroll wheel left
7 = push scroll wheel right
8 = 4th button (aka browser backward button)
9 = 5th button (aka browser forward button)
*/
int ydir = 4; // Button 4 is up, 5 is down.
int xdir = 6; // Button 6 is left, 7 is right.
Display *display = XGetMainDisplay();

if (y < 0)
{
ydir = 5;
}
if (x < 0)
{
xdir = 7;
}

int xi;
int yi;
for (xi = 0; xi < abs(x); xi++)
{
XTestFakeButtonEvent(display, xdir, 1, CurrentTime);
XTestFakeButtonEvent(display, xdir, 0, CurrentTime);
}
for (yi = 0; yi < abs(y); yi++)
{
XTestFakeButtonEvent(display, ydir, 1, CurrentTime);
XTestFakeButtonEvent(display, ydir, 0, CurrentTime);
}

XSync(display, false);
}
178 changes: 178 additions & 0 deletions src/macos/mouse.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#include "../mouse.h"
#include "../microsleep.h"

#include <math.h> /* For floor() */
#include <ApplicationServices/ApplicationServices.h>

#if !defined(M_SQRT2)
#define M_SQRT2 1.4142135623730950488016887 /* Fix for MSVC. */
#endif

#define MMMouseToCGEventType(down, button) \
(down ? MMMouseDownToCGEventType(button) : MMMouseUpToCGEventType(button))

#define MMMouseDownToCGEventType(button) \
((button) == (LEFT_BUTTON) ? kCGEventLeftMouseDown \
: ((button) == RIGHT_BUTTON ? kCGEventRightMouseDown \
: kCGEventOtherMouseDown))

#define MMMouseUpToCGEventType(button) \
((button) == LEFT_BUTTON ? kCGEventLeftMouseUp \
: ((button) == RIGHT_BUTTON ? kCGEventRightMouseUp \
: kCGEventOtherMouseUp))

#define MMMouseDragToCGEventType(button) \
((button) == LEFT_BUTTON ? kCGEventLeftMouseDragged \
: ((button) == RIGHT_BUTTON ? kCGEventRightMouseDragged \
: kCGEventOtherMouseDragged))

/**
* Calculate the delta for a mouse move and add them to the event.
* @param event The mouse move event (by ref).
* @param point The new mouse x and y.
*/
void calculateDeltas(CGEventRef *event, MMPoint point)
{
/**
* The next few lines are a workaround for games not detecting mouse moves.
* See this issue for more information:
* https://github.com/octalmage/robotjs/issues/159
*/
CGEventRef get = CGEventCreate(NULL);
CGPoint mouse = CGEventGetLocation(get);

// Calculate the deltas.
int64_t deltaX = point.x - mouse.x;
int64_t deltaY = point.y - mouse.y;

CGEventSetIntegerValueField(*event, kCGMouseEventDeltaX, deltaX);
CGEventSetIntegerValueField(*event, kCGMouseEventDeltaY, deltaY);

CFRelease(get);
}

/**
* Move the mouse to a specific point.
* @param point The coordinates to move the mouse to (x, y).
*/
void moveMouse(MMPoint point)
{
CGPoint position = CGPointMake(point.x, point.y);
// Create an HID hardware event source
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);

CGEventRef evt = NULL;
if (CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, kCGMouseButtonLeft))
{
// Create a left button drag
evt = CGEventCreateMouseEvent(src, kCGEventLeftMouseDragged, position, kCGMouseButtonLeft);
}
else
{
if (CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, kCGMouseButtonRight))
{
// Create a right button drag
evt = CGEventCreateMouseEvent(src, kCGEventRightMouseDragged, position, kCGMouseButtonLeft);
}
else
{
// Create a mouse move event
evt = CGEventCreateMouseEvent(src, kCGEventMouseMoved, position, kCGMouseButtonLeft);
}
}

// Post mouse event and release
CGEventPost(kCGHIDEventTap, evt);
if (evt != NULL)
{
CFRelease(evt);
}
CFRelease(src);
}

void dragMouse(MMPoint point, const MMMouseButton button)
{
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
const CGEventType dragType = MMMouseDragToCGEventType(button);
CGEventRef drag = CGEventCreateMouseEvent(src, dragType, CGPointFromMMPoint(point), (CGMouseButton)button);
calculateDeltas(&drag, point);

CGEventPost(kCGHIDEventTap, drag);
CFRelease(drag);
CFRelease(src);
}

MMPoint getMousePos()
{
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef event = CGEventCreate(src);
CGPoint point = CGEventGetLocation(event);
CFRelease(event);
CFRelease(src);

return MMPointFromCGPoint(point);
}

/**
* Press down a button, or release it.
* @param down True for down, false for up.
* @param button The button to press down or release.
*/
void toggleMouse(bool down, MMMouseButton button)
{
const CGPoint currentPos = CGPointFromMMPoint(getMousePos());
const CGEventType mouseType = MMMouseToCGEventType(down, button);
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef event = CGEventCreateMouseEvent(src, mouseType, currentPos, (CGMouseButton)button);
CGEventPost(kCGHIDEventTap, event);
CFRelease(event);
CFRelease(src);
}

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

/**
* Special function for sending double clicks, needed for Mac OS X.
* @param button Button to click.
*/
void doubleClick(MMMouseButton button)
{
/* Double click for Mac. */
const CGPoint currentPos = CGPointFromMMPoint(getMousePos());
const CGEventType mouseTypeDown = MMMouseToCGEventType(true, button);
const CGEventType mouseTypeUP = MMMouseToCGEventType(false, button);

CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef event = CGEventCreateMouseEvent(src, mouseTypeDown, currentPos, kCGMouseButtonLeft);

/* Set event to double click. */
CGEventSetIntegerValueField(event, kCGMouseEventClickState, 2);

CGEventPost(kCGHIDEventTap, event);

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

CFRelease(event);
CFRelease(src);
}

void scrollMouse(int x, int y)
{
/*
* Direction should only be considered based on the scrollDirection.
* This should not interfere.
* Set up the OS specific solution
*/

CGEventRef event;

event = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 2, y, x);
CGEventPost(kCGHIDEventTap, event);

CFRelease(event);
}
Loading

0 comments on commit 5fdcc66

Please sign in to comment.