sclass MouseMover { Pt cur, last; Robot robot; java.util.Timer timer; int interferenceMinDistance = 100; int mouseCheckInterval = 50; int blockTimeAfterInterference = 5000; // ms bool endAfterInterference = true; volatile bool interference; bool forceActive; long blockUntil; new HashSet buttonsToRelease; sbool listenToGlobalKeyPresses; // TODO: fix this logic... synchronized bool enabled() { ret timer != null; } synchronized bool hasInterference() { ret interference; } bool active() { ret enabled() && (forceActive || !hasInterference()); } bool active2() { ret enabled() && !blocked(); } synchronized MouseMover enable() ctex { if (robot == null) robot = new Robot; if (timer == null) { interference = false; cur = new Pt(mouseLocation()); timer = new java.util.Timer("MouseMover"); timer.scheduleAtFixedRate(new TimerTask { run { checkMouse(); } }, mouseCheckInterval, mouseCheckInterval); if (listenToGlobalKeyPresses) onGlobalKeyPress(r { haveInterference() }); // TODO: on global mouse click print("Mouse automation started"); } ret this; } bool farAway(Pt a, Pt b) { ret pointDistance(a, b) >= interferenceMinDistance; } synchronized void checkMouse() { if (!enabled()) ret; Pt p = new Pt(mouseLocation()); if (farAway(p, cur) && neq(p, last)) haveInterference(); } void haveInterference() { if (!enabled()) ret; interference = true; if (endAfterInterference) { print("User interference detected, stopping automation"); disable(); } else { bool blockedAlready = blocked(); blockUntil = now()+blockTimeAfterInterference; if (!blockedAlready) print("Mouse automation blocked for at least " + blockTimeAfterInterference + " ms"); releaseButtons(); cur = new Pt(mouseLocation()); } } synchronized void disable() { releaseButtons(); if (timer != null) { timer.cancel(); timer = null; /*if (!interference) print("Mouse automation ended without interference");*/ } } synchronized void moveMouseImmediate(Pt p) { checkMouse(); if (!enabled() || blocked()) ret; p = normalizePointToScreen(p); if (neq(cur, p)) { if (neq(new Pt(mouseLocation()), last)) last = cur; cur = p; robot.mouseMove(p.x, p.y); } } synchronized bool blocked() { if (!enabled() || blockUntil == 0) false; if (now() < blockUntil) true; blockUntil = 0; cur = new Pt(mouseLocation()); print("Mouse automation resumes"); false; } void moveMouse(Pt pdest) { if (pdest == null) ret; print("Moving mouse to " + pdest); Point p = getMouseLocation(); //int steps = 20, delay = 40; // slow //int steps = 20, delay = 20; // fast int steps = 20, delay = 30; // medium for (int i = 1; i <= steps; i++) { Point p2 = blendPoints(p, pdest.getPoint(), ((double) i) / steps); moveMouseImmediate(new Pt(p2)); if (i != steps) sleep(delay); } } void click() { if (active()) mouseClick(); // TODO } void pressButton() { robot.mousePress(addAndReturn(buttonsToRelease, InputEvent.BUTTON1_DOWN_MASK)); } void releaseButton() { robot.mouseRelease(removeAndReturn(buttonsToRelease, InputEvent.BUTTON1_DOWN_MASK)); } void click(Pt p) { moveMouse(p); click(); } void click(int x, int y) { click(new Pt(x, y)); } void cleanMeUp() { disable(); releaseButtons(); } void releaseButtons() { for (int btn : getAndClearList(buttonsToRelease)) robot.mouseRelease(btn); } }