mouse_c.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. #include "mouse.h"
  2. #include "../base/deadbeef_rand.h"
  3. #include "../base/microsleep.h"
  4. #include <math.h> /* For floor() */
  5. #if defined(IS_MACOSX)
  6. // #include </System/Library/Frameworks/ApplicationServices.framework/Headers/ApplicationServices.h>
  7. #include <ApplicationServices/ApplicationServices.h>
  8. // #include </System/Library/Frameworks/ApplicationServices.framework/Versions/A/Headers/ApplicationServices.h>
  9. #elif defined(USE_X11)
  10. #include <X11/Xlib.h>
  11. #include <X11/extensions/XTest.h>
  12. #include <stdlib.h>
  13. #endif
  14. /* Some convenience macros for converting our enums to the system API types. */
  15. #if defined(IS_MACOSX)
  16. CGEventType MMMouseDownToCGEventType(MMMouseButton button) {
  17. if (button == LEFT_BUTTON) {
  18. return kCGEventLeftMouseDown;
  19. }
  20. if (button == RIGHT_BUTTON) {
  21. return kCGEventRightMouseDown;
  22. }
  23. return kCGEventOtherMouseDown;
  24. }
  25. CGEventType MMMouseUpToCGEventType(MMMouseButton button) {
  26. if (button == LEFT_BUTTON) { return kCGEventLeftMouseUp; }
  27. if (button == RIGHT_BUTTON) { return kCGEventRightMouseUp; }
  28. return kCGEventOtherMouseUp;
  29. }
  30. CGEventType MMMouseDragToCGEventType(MMMouseButton button) {
  31. if (button == LEFT_BUTTON) { return kCGEventLeftMouseDragged; }
  32. if (button == RIGHT_BUTTON) { return kCGEventRightMouseDragged; }
  33. return kCGEventOtherMouseDragged;
  34. }
  35. CGEventType MMMouseToCGEventType(bool down, MMMouseButton button) {
  36. if (down) { return MMMouseDownToCGEventType(button); }
  37. return MMMouseUpToCGEventType(button);
  38. }
  39. #elif defined(IS_WINDOWS)
  40. DWORD MMMouseUpToMEventF(MMMouseButton button) {
  41. if (button == LEFT_BUTTON) { return MOUSEEVENTF_LEFTUP; }
  42. if (button == RIGHT_BUTTON) { return MOUSEEVENTF_RIGHTUP; }
  43. return MOUSEEVENTF_MIDDLEUP;
  44. }
  45. DWORD MMMouseDownToMEventF(MMMouseButton button) {
  46. if (button == LEFT_BUTTON) { return MOUSEEVENTF_LEFTDOWN; }
  47. if (button == RIGHT_BUTTON) { return MOUSEEVENTF_RIGHTDOWN; }
  48. return MOUSEEVENTF_MIDDLEDOWN;
  49. }
  50. DWORD MMMouseToMEventF(bool down, MMMouseButton button) {
  51. if (down) { return MMMouseDownToMEventF(button); }
  52. return MMMouseUpToMEventF(button);
  53. }
  54. #endif
  55. #if defined(IS_MACOSX)
  56. /* Calculate the delta for a mouse move and add them to the event. */
  57. void calculateDeltas(CGEventRef *event, MMPointInt32 point) {
  58. /* The next few lines are a workaround for games not detecting mouse moves. */
  59. CGEventRef get = CGEventCreate(NULL);
  60. CGPoint mouse = CGEventGetLocation(get);
  61. // Calculate the deltas.
  62. int64_t deltaX = point.x - mouse.x;
  63. int64_t deltaY = point.y - mouse.y;
  64. CGEventSetIntegerValueField(*event, kCGMouseEventDeltaX, deltaX);
  65. CGEventSetIntegerValueField(*event, kCGMouseEventDeltaY, deltaY);
  66. CFRelease(get);
  67. }
  68. #endif
  69. /* Move the mouse to a specific point. */
  70. void moveMouse(MMPointInt32 point){
  71. #if defined(IS_MACOSX)
  72. CGEventRef move = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved,
  73. CGPointFromMMPointInt32(point), kCGMouseButtonLeft);
  74. calculateDeltas(&move, point);
  75. CGEventPost(kCGSessionEventTap, move);
  76. CFRelease(move);
  77. #elif defined(USE_X11)
  78. Display *display = XGetMainDisplay();
  79. XWarpPointer(display, None, DefaultRootWindow(display), 0, 0, 0, 0, point.x, point.y);
  80. XSync(display, false);
  81. #elif defined(IS_WINDOWS)
  82. // Mouse motion is now done using SendInput with MOUSEINPUT.
  83. // We use Absolute mouse positioning
  84. #define MOUSE_COORD_TO_ABS(coord, width_or_height) ( \
  85. ((65536 * coord) / width_or_height) + (coord < 0 ? -1 : 1))
  86. MMRectInt32 rect = getScreenRect(1);
  87. int32_t x = MOUSE_COORD_TO_ABS(point.x - rect.origin.x, rect.size.w);
  88. int32_t y = MOUSE_COORD_TO_ABS(point.y - rect.origin.y, rect.size.h);
  89. INPUT mouseInput;
  90. mouseInput.type = INPUT_MOUSE;
  91. mouseInput.mi.dx = x;
  92. mouseInput.mi.dy = y;
  93. mouseInput.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE | MOUSEEVENTF_VIRTUALDESK;
  94. mouseInput.mi.time = 0; // System will provide the timestamp
  95. mouseInput.mi.dwExtraInfo = 0;
  96. mouseInput.mi.mouseData = 0;
  97. SendInput(1, &mouseInput, sizeof(mouseInput));
  98. #endif
  99. }
  100. void dragMouse(MMPointInt32 point, const MMMouseButton button){
  101. #if defined(IS_MACOSX)
  102. const CGEventType dragType = MMMouseDragToCGEventType(button);
  103. CGEventRef drag = CGEventCreateMouseEvent(NULL, dragType,
  104. CGPointFromMMPointInt32(point), (CGMouseButton)button);
  105. calculateDeltas(&drag, point);
  106. CGEventPost(kCGSessionEventTap, drag);
  107. CFRelease(drag);
  108. #else
  109. moveMouse(point);
  110. #endif
  111. }
  112. MMPointInt32 location() {
  113. #if defined(IS_MACOSX)
  114. CGEventRef event = CGEventCreate(NULL);
  115. CGPoint point = CGEventGetLocation(event);
  116. CFRelease(event);
  117. return MMPointInt32FromCGPoint(point);
  118. #elif defined(USE_X11)
  119. int x, y; /* This is all we care about. Seriously. */
  120. Window garb1, garb2; /* Why you can't specify NULL as a parameter */
  121. int garb_x, garb_y; /* is beyond me. */
  122. unsigned int more_garbage;
  123. Display *display = XGetMainDisplay();
  124. XQueryPointer(display, XDefaultRootWindow(display), &garb1, &garb2, &x, &y,
  125. &garb_x, &garb_y, &more_garbage);
  126. return MMPointInt32Make(x, y);
  127. #elif defined(IS_WINDOWS)
  128. POINT point;
  129. GetCursorPos(&point);
  130. return MMPointInt32FromPOINT(point);
  131. #endif
  132. }
  133. /* Press down a button, or release it. */
  134. void toggleMouse(bool down, MMMouseButton button) {
  135. #if defined(IS_MACOSX)
  136. const CGPoint currentPos = CGPointFromMMPointInt32(location());
  137. const CGEventType mouseType = MMMouseToCGEventType(down, button);
  138. CGEventRef event = CGEventCreateMouseEvent(NULL, mouseType, currentPos, (CGMouseButton)button);
  139. CGEventPost(kCGSessionEventTap, event);
  140. CFRelease(event);
  141. #elif defined(USE_X11)
  142. Display *display = XGetMainDisplay();
  143. XTestFakeButtonEvent(display, button, down ? True : False, CurrentTime);
  144. XSync(display, false);
  145. #elif defined(IS_WINDOWS)
  146. // mouse_event(MMMouseToMEventF(down, button), 0, 0, 0, 0);
  147. INPUT mouseInput;
  148. mouseInput.type = INPUT_MOUSE;
  149. mouseInput.mi.dx = 0;
  150. mouseInput.mi.dy = 0;
  151. mouseInput.mi.dwFlags = MMMouseToMEventF(down, button);
  152. mouseInput.mi.time = 0;
  153. mouseInput.mi.dwExtraInfo = 0;
  154. mouseInput.mi.mouseData = 0;
  155. SendInput(1, &mouseInput, sizeof(mouseInput));
  156. #endif
  157. }
  158. void clickMouse(MMMouseButton button){
  159. toggleMouse(true, button);
  160. microsleep(5.0);
  161. toggleMouse(false, button);
  162. }
  163. /* Special function for sending double clicks, needed for MacOS. */
  164. void doubleClick(MMMouseButton button){
  165. #if defined(IS_MACOSX)
  166. /* Double click for Mac. */
  167. const CGPoint currentPos = CGPointFromMMPointInt32(location());
  168. const CGEventType mouseTypeDown = MMMouseToCGEventType(true, button);
  169. const CGEventType mouseTypeUP = MMMouseToCGEventType(false, button);
  170. CGEventRef event = CGEventCreateMouseEvent(NULL, mouseTypeDown, currentPos, kCGMouseButtonLeft);
  171. /* Set event to double click. */
  172. CGEventSetIntegerValueField(event, kCGMouseEventClickState, 2);
  173. CGEventPost(kCGHIDEventTap, event);
  174. CGEventSetType(event, mouseTypeUP);
  175. CGEventPost(kCGHIDEventTap, event);
  176. CFRelease(event);
  177. #else
  178. /* Double click for everything else. */
  179. clickMouse(button);
  180. microsleep(200);
  181. clickMouse(button);
  182. #endif
  183. }
  184. /* Function used to scroll the screen in the required direction. */
  185. void scrollMouseXY(int x, int y) {
  186. #if defined(IS_WINDOWS)
  187. // Fix for #97, C89 needs variables declared on top of functions (mouseScrollInput)
  188. INPUT mouseScrollInputH;
  189. INPUT mouseScrollInputV;
  190. #endif
  191. /* Direction should only be considered based on the scrollDirection. This Should not interfere. */
  192. /* Set up the OS specific solution */
  193. #if defined(__APPLE__)
  194. CGEventRef event;
  195. event = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 2, y, x);
  196. CGEventPost(kCGHIDEventTap, event);
  197. CFRelease(event);
  198. #elif defined(USE_X11)
  199. int ydir = 4; /* Button 4 is up, 5 is down. */
  200. int xdir = 6;
  201. Display *display = XGetMainDisplay();
  202. if (y < 0) { ydir = 5; }
  203. if (x < 0) { xdir = 7; }
  204. int xi; int yi;
  205. for (xi = 0; xi < abs(x); xi++) {
  206. XTestFakeButtonEvent(display, xdir, 1, CurrentTime);
  207. XTestFakeButtonEvent(display, xdir, 0, CurrentTime);
  208. }
  209. for (yi = 0; yi < abs(y); yi++) {
  210. XTestFakeButtonEvent(display, ydir, 1, CurrentTime);
  211. XTestFakeButtonEvent(display, ydir, 0, CurrentTime);
  212. }
  213. XSync(display, false);
  214. #elif defined(IS_WINDOWS)
  215. mouseScrollInputH.type = INPUT_MOUSE;
  216. mouseScrollInputH.mi.dx = 0;
  217. mouseScrollInputH.mi.dy = 0;
  218. mouseScrollInputH.mi.dwFlags = MOUSEEVENTF_WHEEL;
  219. mouseScrollInputH.mi.time = 0;
  220. mouseScrollInputH.mi.dwExtraInfo = 0;
  221. mouseScrollInputH.mi.mouseData = WHEEL_DELTA * x;
  222. mouseScrollInputV.type = INPUT_MOUSE;
  223. mouseScrollInputV.mi.dx = 0;
  224. mouseScrollInputV.mi.dy = 0;
  225. mouseScrollInputV.mi.dwFlags = MOUSEEVENTF_WHEEL;
  226. mouseScrollInputV.mi.time = 0;
  227. mouseScrollInputV.mi.dwExtraInfo = 0;
  228. mouseScrollInputV.mi.mouseData = WHEEL_DELTA * y;
  229. SendInput(1, &mouseScrollInputH, sizeof(mouseScrollInputH));
  230. SendInput(1, &mouseScrollInputV, sizeof(mouseScrollInputV));
  231. #endif
  232. }
  233. /* A crude, fast hypot() approximation to get around the fact that hypot() is not a standard ANSI C function. */
  234. #if !defined(M_SQRT2)
  235. #define M_SQRT2 1.4142135623730950488016887 /* Fix for MSVC. */
  236. #endif
  237. static double crude_hypot(double x, double y){
  238. double big = fabs(x); /* max(|x|, |y|) */
  239. double small = fabs(y); /* min(|x|, |y|) */
  240. if (big > small) {
  241. double temp = big;
  242. big = small;
  243. small = temp;
  244. }
  245. return ((M_SQRT2 - 1.0) * small) + big;
  246. }
  247. bool smoothlyMoveMouse(MMPointInt32 endPoint, double lowSpeed, double highSpeed){
  248. MMPointInt32 pos = location();
  249. // MMSizeInt32 screenSize = getMainDisplaySize();
  250. double velo_x = 0.0, velo_y = 0.0;
  251. double distance;
  252. while ((distance =crude_hypot((double)pos.x - endPoint.x, (double)pos.y - endPoint.y)) > 1.0) {
  253. double gravity = DEADBEEF_UNIFORM(5.0, 500.0);
  254. // double gravity = DEADBEEF_UNIFORM(lowSpeed, highSpeed);
  255. double veloDistance;
  256. velo_x += (gravity * ((double)endPoint.x - pos.x)) / distance;
  257. velo_y += (gravity * ((double)endPoint.y - pos.y)) / distance;
  258. /* Normalize velocity to get a unit vector of length 1. */
  259. veloDistance = crude_hypot(velo_x, velo_y);
  260. velo_x /= veloDistance;
  261. velo_y /= veloDistance;
  262. pos.x += floor(velo_x + 0.5);
  263. pos.y += floor(velo_y + 0.5);
  264. /* Make sure we are in the screen boundaries! (Strange things will happen if we are not.) */
  265. // if (pos.x >= screenSize.w || pos.y >= screenSize.h) {
  266. // return false;
  267. // }
  268. moveMouse(pos);
  269. /* Wait 1 - 3 milliseconds. */
  270. microsleep(DEADBEEF_UNIFORM(lowSpeed, highSpeed));
  271. // microsleep(DEADBEEF_UNIFORM(1.0, 3.0));
  272. }
  273. return true;
  274. }