1 /** 2 * Control, screen-scrape, and send input to other 3 * graphical programs. 4 * 5 * License: 6 * This Source Code Form is subject to the terms of 7 * the Mozilla Public License, v. 2.0. If a copy of 8 * the MPL was not distributed with this file, You 9 * can obtain one at http://mozilla.org/MPL/2.0/. 10 * 11 * Authors: 12 * Vladimir Panteleev <vladimir@thecybershadow.net> 13 */ 14 15 module ae.sys.sendinput; 16 17 import core.thread; 18 import core.time; 19 20 import std.exception; 21 import std.random; 22 import std.string; 23 24 import ae.utils.graphics.color; 25 import ae.utils.graphics.image; 26 import ae.utils.geometry : Rect; 27 import ae.utils.math; 28 29 version (linux) 30 { 31 enum haveX11 = is(typeof({ import deimos.X11.X; })); 32 //version = HAVE_X11; 33 34 static if (haveX11) 35 { 36 pragma(lib, "X11"); 37 38 import deimos.X11.X; 39 import deimos.X11.Xlib; 40 } 41 42 import std.conv; 43 import std.process; 44 45 private Display* getDisplay() 46 { 47 static Display* dpy; 48 if (!dpy) 49 dpy = XOpenDisplay(null); 50 enforce(dpy, "Can't open display!"); 51 return dpy; 52 } 53 54 void setMousePos(int x, int y) 55 { 56 static if (haveX11) 57 { 58 auto dpy = getDisplay(); 59 auto rootWindow = XRootWindow(dpy, 0); 60 XSelectInput(dpy, rootWindow, KeyReleaseMask); 61 XWarpPointer(dpy, None, rootWindow, 0, 0, 0, 0, x, y); 62 XFlush(dpy); 63 } 64 else 65 enforce(spawnProcess(["xdotool", "mousemove", text(windowX + x), text(windowY + y)]).wait() == 0, "xdotool failed"); 66 } 67 68 alias Window = deimos.X11.X.Window; 69 70 Image!BGR captureWindow(Window window) 71 { 72 // TODO haveX11 73 auto result = execute(["import", "-window", text(window), "bmp:-"]); 74 enforce(result.status == 0, "ImageMagick import failed"); 75 return result.output.parseBMP!BGR(); 76 } 77 78 Window findWindowByName(string name) 79 { 80 // TODO haveX11 81 auto result = execute(["xdotool", "search", "--name", name]); 82 enforce(result.status == 0, "xdotool failed"); 83 return result.output.chomp.to!Window; 84 } 85 86 Rect!int getWindowGeometry(Window window) 87 { 88 auto dpy = getDisplay(); 89 Window child; 90 XWindowAttributes xwa; 91 XGetWindowAttributes(dpy, window, &xwa); 92 XTranslateCoordinates(dpy, window, XRootWindow(dpy, 0), xwa.x, xwa.y, &xwa.x, &xwa.y, &child); 93 return Rect!int(xwa.x, xwa.y, xwa.x + xwa.width, xwa.y + xwa.height); 94 } 95 96 float ease(float t, float speed) 97 { 98 speed = 0.3f + speed * 0.4f; 99 t = t * 2 - 1; 100 t = (1-pow(1-abs(t), 1/speed)) * sign(t); 101 t = (t + 1) / 2; 102 return t; 103 } 104 105 void easeMousePos(int x0, int y0, int x1, int y1, Duration duration) 106 { 107 auto xSpeed = uniform01!float; 108 auto ySpeed = uniform01!float; 109 110 auto start = MonoTime.currTime(); 111 auto end = start + duration; 112 while (true) 113 { 114 auto now = MonoTime.currTime(); 115 if (now >= end) 116 break; 117 float t = 1f * (now - start).total!"hnsecs" / duration.total!"hnsecs"; 118 setMousePos( 119 x0 + cast(int)(ease(t, xSpeed) * (x1 - x0)), 120 y0 + cast(int)(ease(t, ySpeed) * (y1 - y0)), 121 ); 122 Thread.sleep(1.msecs); 123 } 124 x0 = x1; 125 y0 = y1; 126 127 setMousePos(x1, y1); 128 } 129 130 void mouseButton(int button, bool down) 131 { 132 // TODO haveX11 133 enforce(spawnProcess(["xdotool", down ? "mousedown" : "mouseup", text(button)]).wait() == 0, "xdotool failed"); 134 } 135 } 136 137 version (Windows) 138 { 139 // TODO 140 }