Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix multi-monitor not working in windows with moveMouseSmooth pipeline fixed #611

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7b1c5fb
Fix Multiple monitor support for mouseSmoothMove. NOTE: not the most …
fjlj Dec 21, 2019
f42a6fc
update readme for fork
fjlj Dec 21, 2019
fa62495
Revert "Fix Multiple monitor support for mouseSmoothMove. NOTE: not t…
fjlj Dec 21, 2019
a23f8ee
cleanup solution for moveMouseSmooth/smoothlyMoveMouse multi-monitor …
fjlj Dec 21, 2019
3804e69
update readme to reflect multimonitor support
fjlj Dec 25, 2019
3f35d8b
Merge pull request #1 from octalmage/master
Programmnix Oct 2, 2020
16db595
Fix linux build
Programmnix Oct 2, 2020
1e44ce9
Merge branch 'master' of https://github.com/Programmnix/robotjs
Programmnix Oct 2, 2020
0e56257
fix build for mac
Programmnix Oct 2, 2020
0227899
fix build for mac
Programmnix Oct 2, 2020
c82079e
Dirty hack to allow mouth move on multiple screens on mac
Programmnix Oct 5, 2020
9c4cb1c
migrate smoothlyMoveMouse to MMSignedPoint
Programmnix Oct 6, 2020
d9e0808
Use MMSingedPoint for smoothlyMoveMouse
Programmnix Oct 6, 2020
6bd6f6b
Resolve merge conflictS
Programmnix Oct 6, 2020
8b6e2d3
Resolve build conflicts
Programmnix Oct 6, 2020
00d53b3
Fix min version for xcode
Oct 9, 2020
1372ef5
Upgrade dependencies
programminx-askui Feb 1, 2022
952bfa2
ignore vscode settings in source control
Mar 14, 2022
b3ad6ec
allow negative coords as origin of screen
Mar 14, 2022
eb13e5c
add multi-screen support for captureScreen on mac
Mar 14, 2022
82c68e5
make code changes more consistent to rest of code
Mar 14, 2022
e1e024f
Merge pull request #2 from adi-wan/feat-mac-multi-screen-support
Programmnix Mar 21, 2022
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
build/
node_modules/
prebuilds/
/.idea
/.idea
/.vscode
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
This properly fixes the multi-monitor support for windows(tested only on 10, assuming 7+ will work)<br><br>
Also supports user defined speed of smooth mouse movement (original randomness updated to alter a bit based on distance)<br><br>
=================Original ReadMe To Follow==================
<p align="center"><img src="https://cldup.com/1ATDf2JMtv.png"></p>

<p align="center"><a href="https://travis-ci.org/octalmage/robotjs"><img src="https://api.travis-ci.org/octalmage/robotjs.svg?branch=master"></a> <a href="https://ci.appveyor.com/project/octalmage/robotjs"><img src="https://ci.appveyor.com/api/projects/status/qh2eqb37j7ap6x36?svg=true"></a> <a href="https://www.npmjs.com/package/robotjs"><img src="https://img.shields.io/npm/v/robotjs.svg"></a> <a href="https://gitter.im/octalmage/robotjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"><img src="https://img.shields.io/badge/gitter-join%20chat-blue.svg"></a> <a href="http://waffle.io/octalmage/robotjs"><img src="https://img.shields.io/waffle/label/octalmage/robotjs/ready.svg?maxAge=3600"></a></p>
Expand Down Expand Up @@ -153,8 +156,7 @@ Soon! This is a bit more complicated than the rest of the features, so I saved i
We've been implementing keys as we need them. Feel free to create an issue or submit a pull request!

#### How about multi-monitor support?

The library doesn't have explicit multi-monitor support, so anything that works is kind of on accident. Subscribe to [#88](https://github.com/octalmage/robotjs/issues/88) for updates.
Multiple monitor support working. Tested in windows only, need additional feedback for mac/nix.

For any other questions please [submit an issue](https://github.com/octalmage/robotjs/issues/new).

Expand Down
3 changes: 3 additions & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
'System/Library/Frameworks/ApplicationServices.framework/Headers',
'System/Library/Frameworks/OpenGL.framework/Headers',
],
'xcode_settings': {
'MACOSX_DEPLOYMENT_TARGET': '10.12',
},
'link_settings': {
'libraries': [
'-framework Carbon',
Expand Down
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ export function scrollMouse(x: number, y: number) : void
export function getMousePos(): { x: number, y: number }
export function getPixelColor(x: number, y: number): string
export function getScreenSize(): { width: number, height: number }
export function getScreenRects(): Array<{ left: number, top: number, right: number, bottom: number }>

export var screen: Screen
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@
},
"homepage": "https://github.com/octalmage/robotjs",
"dependencies": {
"nan": "^2.14.0",
"prebuild-install": "^5.3.3"
"nan": "^2.15.0",
"prebuild-install": "^7.0.1"
},
"devDependencies": {
"tape": "^4.8.0",
"jasmine": "^2.99.0",
"tape": "^5.5.0",
"jasmine": "^4.0.2",
"prebuild": "^9.1.1",
"run-script-os": "^1.0.3",
"run-script-os": "^1.0.6",
"targetpractice": "0.0.7"
}
}
41 changes: 25 additions & 16 deletions src/mouse.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ void moveMouse(MMSignedPoint point)
//Mouse motion is now done using SendInput with MOUSEINPUT. We use Absolute mouse positioning
#define MOUSE_COORD_TO_ABS(coord, width_or_height) ((65536 * (coord) / width_or_height) + ((coord) < 0 ? -1 : 1))

size_t x = MOUSE_COORD_TO_ABS(point.x-vscreenMinX, vscreenWidth);
size_t y = MOUSE_COORD_TO_ABS(point.y-vscreenMinY, vscreenHeight);
int32_t x = MOUSE_COORD_TO_ABS(point.x - vscreenMinX, vscreenWidth);
int32_t y = MOUSE_COORD_TO_ABS(point.y - vscreenMinY, vscreenHeight);

INPUT mouseInput = {0};
mouseInput.type = INPUT_MOUSE;
Expand Down Expand Up @@ -162,14 +162,14 @@ void dragMouse(MMSignedPoint point, const MMMouseButton button)
#endif
}

MMPoint getMousePos()
MMSignedPoint getMousePos()
{
#if defined(IS_MACOSX)
CGEventRef event = CGEventCreate(NULL);
CGPoint point = CGEventGetLocation(event);
CFRelease(event);

return MMPointFromCGPoint(point);
return MMSignedPointFromCGPoint(point);
#elif defined(USE_X11)
int x, y; /* This is all we care about. Seriously. */
Window garb1, garb2; /* Why you can't specify NULL as a parameter */
Expand All @@ -180,12 +180,12 @@ MMPoint getMousePos()
XQueryPointer(display, XDefaultRootWindow(display), &garb1, &garb2,
&x, &y, &garb_x, &garb_y, &more_garbage);

return MMPointMake(x, y);
return MMSignedPointMake(x, y);
#elif defined(IS_WINDOWS)
POINT point;
GetCursorPos(&point);

return MMPointFromPOINT(point);
return MMSignedPointFromPOINT(point);
#endif
}

Expand Down Expand Up @@ -371,15 +371,20 @@ static double crude_hypot(double x, double y)
return ((M_SQRT2 - 1.0) * small) + big;
}

bool smoothlyMoveMouse(MMPoint endPoint,double speed)
bool smoothlyMoveMouse(MMSignedPoint endPoint, double speed)
{
MMPoint pos = getMousePos();
MMSize screenSize = getMainDisplaySize();
MMSignedPoint pos = getMousePos();
MMSignedSize screenSize = getMainDisplaySize();
double velo_x = 0.0, velo_y = 0.0;
double distance;


#if defined(IS_WINDOWS)
if (vscreenWidth < 0 || vscreenHeight < 0)
updateScreenMetrics();
#endif
double bdist = (distance = crude_hypot((double)pos.x - endPoint.x,(double)pos.y - endPoint.y));
while ((distance = crude_hypot((double)pos.x - endPoint.x,
(double)pos.y - endPoint.y)) > 1.0) {
(double)pos.y - endPoint.y)) > 1.0) {
double gravity = DEADBEEF_UNIFORM(5.0, 500.0);
double veloDistance;
velo_x += (gravity * ((double)endPoint.x - pos.x)) / distance;
Expand All @@ -393,16 +398,20 @@ bool smoothlyMoveMouse(MMPoint endPoint,double speed)
pos.x += floor(velo_x + 0.5);
pos.y += floor(velo_y + 0.5);


/**
* Dirty hack for move mouse smooth on all displays with multiple displays
*/
/* Make sure we are in the screen boundaries!
* (Strange things will happen if we are not.) */
if (pos.x >= screenSize.width || pos.y >= screenSize.height) {
return false;
}

moveMouse(MMSignedPointMake((int32_t)pos.x, (int32_t)pos.y));
//if (pos.x >= screenSize.width || pos.y >= screenSize.height) {
// return false;
//}
moveMouse(MMSignedPointMake(pos.x, pos.y));

/* Wait 1 - (speed) milliseconds. */
microsleep(DEADBEEF_UNIFORM(0.7, speed));
microsleep(DEADBEEF_UNIFORM((fmin(0.7,speed) + (1-(distance / ((bdist + 0.0001) * 2)))), (fmax(0.7, speed) - (distance / ((bdist + 0.0001) * 1.5)))));
}

return true;
Expand Down
4 changes: 2 additions & 2 deletions src/mouse.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@ void dragMouse(MMSignedPoint point, const MMMouseButton button);
*
* Returns false if unsuccessful (i.e. a point was hit that is outside of the
* screen boundaries), or true if successful. */
bool smoothlyMoveMouse(MMPoint point,double speed);
bool smoothlyMoveMouse(MMSignedPoint point,double speed);

/* Returns the coordinates of the mouse on the current screen. */
MMPoint getMousePos(void);
MMSignedPoint getMousePos(void);

/* Holds down or releases the mouse with the given button in the current
* position. */
Expand Down
71 changes: 62 additions & 9 deletions src/robotjs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,29 @@
#include "xdisplay.h"
#endif


#if defined(IS_WINDOWS)
#include <windows.h>

struct MonitorRects
{
std::vector<RECT> rcMonitors;

static BOOL CALLBACK MonitorEnum(HMONITOR hMon,HDC hdc,LPRECT lprcMonitor,LPARAM pData)
{
MonitorRects* pThis = reinterpret_cast<MonitorRects*>(pData);
pThis->rcMonitors.push_back(*lprcMonitor);
return TRUE;
}

MonitorRects()
{
EnumDisplayMonitors(0, 0, MonitorEnum, (LPARAM)this);
}
};
#endif


using namespace v8;

//Global delays.
Expand Down Expand Up @@ -122,8 +145,8 @@ NAN_METHOD(moveMouseSmooth)
size_t x = Nan::To<int32_t>(info[0]).FromJust();
size_t y = Nan::To<int32_t>(info[1]).FromJust();

MMPoint point;
point = MMPointMake(x, y);
MMSignedPoint point;
point = MMSignedPointMake(x, y);
if (info.Length() == 3)
{
size_t speed = Nan::To<int32_t>(info[2]).FromJust();
Expand All @@ -140,7 +163,7 @@ NAN_METHOD(moveMouseSmooth)

NAN_METHOD(getMousePos)
{
MMPoint pos = getMousePos();
MMSignedPoint pos = getMousePos();

//Return object with .x and .y.
Local<Object> obj = Nan::New<Object>();
Expand Down Expand Up @@ -689,8 +712,8 @@ NAN_METHOD(getPixelColor)
MMBitmapRef bitmap;
MMRGBHex color;

size_t x = Nan::To<int32_t>(info[0]).FromJust();
size_t y = Nan::To<int32_t>(info[1]).FromJust();
int32_t x = Nan::To<int32_t>(info[0]).FromJust();
int32_t y = Nan::To<int32_t>(info[1]).FromJust();

if (!pointVisibleOnMainDisplay(MMPointMake(x, y)))
{
Expand All @@ -710,10 +733,11 @@ NAN_METHOD(getPixelColor)
info.GetReturnValue().Set(Nan::New(hex).ToLocalChecked());
}


NAN_METHOD(getScreenSize)
{
//Get display size.
MMSize displaySize = getMainDisplaySize();
MMSignedSize displaySize = getMainDisplaySize();

//Create our return object.
Local<Object> obj = Nan::New<Object>();
Expand All @@ -724,6 +748,32 @@ NAN_METHOD(getScreenSize)
info.GetReturnValue().Set(obj);
}



NAN_METHOD(getScreenRects)
{
#if defined(IS_WINDOWS)

MonitorRects monitors;

Local<Array> a = Nan::New<Array>(monitors.rcMonitors.size());

for (int i = 0; i < monitors.rcMonitors.size(); ++i){
Local<Object> obj = Nan::New<Object>();
Nan::Set(obj, Nan::New("left").ToLocalChecked(), Nan::New<Number>(monitors.rcMonitors[i].left));
Nan::Set(obj, Nan::New("top").ToLocalChecked(), Nan::New<Number>(monitors.rcMonitors[i].top));
Nan::Set(obj, Nan::New("right").ToLocalChecked(), Nan::New<Number>(monitors.rcMonitors[i].right));
Nan::Set(obj, Nan::New("bottom").ToLocalChecked(), Nan::New<Number>(monitors.rcMonitors[i].bottom));
Nan::Set(a, i, obj);
}

info.GetReturnValue().Set(a);
#else
Nan::ThrowError("getScreenRects is not supported on your OS");
#endif
}


NAN_METHOD(getXDisplayName)
{
#if defined(USE_X11)
Expand All @@ -747,8 +797,8 @@ NAN_METHOD(setXDisplayName)

NAN_METHOD(captureScreen)
{
size_t x;
size_t y;
int32_t x;
int32_t y;
size_t w;
size_t h;

Expand All @@ -770,7 +820,7 @@ NAN_METHOD(captureScreen)
y = 0;

//Get screen size.
MMSize displaySize = getMainDisplaySize();
MMSignedSize displaySize = getMainDisplaySize();
w = displaySize.width;
h = displaySize.height;
}
Expand Down Expand Up @@ -917,6 +967,9 @@ NAN_MODULE_INIT(InitAll)

Nan::Set(target, Nan::New("getScreenSize").ToLocalChecked(),
Nan::GetFunction(Nan::New<FunctionTemplate>(getScreenSize)).ToLocalChecked());

Nan::Set(target, Nan::New("getScreenRects").ToLocalChecked(),
Nan::GetFunction(Nan::New<FunctionTemplate>(getScreenRects)).ToLocalChecked());

Nan::Set(target, Nan::New("captureScreen").ToLocalChecked(),
Nan::GetFunction(Nan::New<FunctionTemplate>(captureScreen)).ToLocalChecked());
Expand Down
48 changes: 42 additions & 6 deletions src/screen.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,62 @@
#include "xdisplay.h"
#endif

MMSize getMainDisplaySize(void)
#if defined(IS_MACOSX)
uint32_t getIDOfDisplayWithRect(MMRect rect)
{
CGError cgErr;
CGDisplayCount displayCount;
CGDisplayCount maxDisplays = 1;
CGDirectDisplayID onlineDisplays[1];
CGRect cgRect = CGRectMake(rect.origin.x,
rect.origin.y,
rect.size.width,
rect.size.height);

// If CGGetOnlineDisplayList() is not called, CGGetDisplaysWithRect() fails
CGGetOnlineDisplayList(maxDisplays, onlineDisplays, &displayCount);
cgErr = CGGetDisplaysWithRect(cgRect, maxDisplays, onlineDisplays, &displayCount);

if (cgErr != kCGErrorSuccess)
{
fprintf(stderr, "CGGetDisplaysWithRect: error %d.\n", cgErr);
exit(1);
}
if (displayCount == 0)
{
fprintf(stderr,
"No display with rect with origin (%d, %d), width %d and height %d.\n",
(int) cgRect.origin.x,
(int) cgRect.origin.y,
(int) cgRect.size.width,
(int) cgRect.size.height);
exit(1);
}

return onlineDisplays[0];
};
#endif

MMSignedSize getMainDisplaySize(void)
{
#if defined(IS_MACOSX)
CGDirectDisplayID displayID = CGMainDisplayID();
return MMSizeMake(CGDisplayPixelsWide(displayID),
return MMSignedSizeMake(CGDisplayPixelsWide(displayID),
CGDisplayPixelsHigh(displayID));
#elif defined(USE_X11)
Display *display = XGetMainDisplay();
const int screen = DefaultScreen(display);

return MMSizeMake((size_t)DisplayWidth(display, screen),
return MMSignedSizeMake((size_t)DisplayWidth(display, screen),
(size_t)DisplayHeight(display, screen));
#elif defined(IS_WINDOWS)
return MMSizeMake((size_t)GetSystemMetrics(SM_CXSCREEN),
(size_t)GetSystemMetrics(SM_CYSCREEN));
return MMSignedSizeMake((size_t)GetSystemMetrics(SM_CXVIRTUALSCREEN),
(size_t)GetSystemMetrics(SM_CYVIRTUALSCREEN));
#endif
}

bool pointVisibleOnMainDisplay(MMPoint point)
{
MMSize displaySize = getMainDisplaySize();
MMSignedSize displaySize = getMainDisplaySize();
return point.x < displaySize.width && point.y < displaySize.height;
}
6 changes: 5 additions & 1 deletion src/screen.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ extern "C"
{
#endif

#if defined(IS_MACOSX)
uint32_t getIDOfDisplayWithRect(MMRect rect);
#endif

/* Returns the size of the main display. */
MMSize getMainDisplaySize(void);
MMSignedSize getMainDisplaySize(void);

/* Convenience function that returns whether the given point is in the bounds
* of the main screen. */
Expand Down
Loading