/*
* Copyright (c) 2010-2016, Sikuli.org, sikulix.com
* Released under the MIT License.
*
*/
package org.sikuli.script;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.util.Date;
import org.sikuli.basics.Debug;
/**
* EXPERIMENTAL --- INTERNAL USE ONLY<br>
* is not official API --- will not be in version 2
*/
public class Device {
static RunTime runTime = RunTime.get();
private static String me = "Device: ";
private static final int lvl = 3;
private static void log(int level, String message, Object... args) {
Debug.logx(level, me + message, args);
}
private Object device = null;
private String devName = "Device";
protected boolean inUse = false;
protected boolean keep = false;
protected Object owner = null;
private boolean blocked = false;
private boolean suspended = false;
protected Location lastPos = null;
protected boolean isMouse = false;
protected int MouseMovedIgnore = 0;
protected int MouseMovedShow = 1;
protected int MouseMovedPause = 2;
protected int MouseMovedAction = 3;
protected int mouseMovedResponse = MouseMovedIgnore;
protected boolean MouseMovedHighlight = true;
protected ObserverCallBack mouseMovedCallback = null;
protected ObserverCallBack callback = null;
private boolean shouldRunCallback = false;
static boolean shouldTerminate = false;
public static void setShouldTerminate() {
shouldTerminate = true;
log(lvl, "setShouldTerminate: request issued");
}
public boolean isShouldRunCallback() {
return shouldRunCallback;
}
public void setShouldRunCallback(boolean shouldRunCallback) {
this.shouldRunCallback = shouldRunCallback;
}
protected Device(Mouse m) {
device = m;
devName = "Mouse";
}
protected Device(Screen s) {
device = s;
devName = "Screen";
}
public boolean isInUse() {
return inUse;
}
public boolean isSuspended() {
return suspended;
}
public boolean isBlocked() {
return blocked;
}
public boolean isNotLocal(Object owner) {
if (owner instanceof Region) {
if (((Region) owner).isOtherScreen()) {
return true;
}
} else if (owner instanceof Location) {
if (((Location) owner).isOtherScreen()) {
return true;
}
}
return false;
}
/**
* to block the device globally <br>
* only the contained device methods without owner will be granted
*
* @return success
*/
public boolean block() {
return block(null);
}
/**
* to block the device globally for the given owner <br>
* only the contained mouse methods having the same owner will be granted
*
* @param owner Object
* @return success
*/
public boolean block(Object owner) {
if (use(owner)) {
blocked = true;
return true;
} else {
return false;
}
}
/**
* free the mouse globally after a block()
*
* @return success (false means: not blocked currently)
*/
public boolean unblock() {
return unblock(null);
}
/**
* free the mouse globally for this owner after a block(owner)
*
* @param ownerGiven Object
* @return success (false means: not blocked currently for this owner)
*/
public boolean unblock(Object ownerGiven) {
if (ownerGiven == null) {
ownerGiven = device;
} else if (isNotLocal(ownerGiven)) {
return false;
}
if (blocked && owner == ownerGiven) {
blocked = false;
let(ownerGiven);
return true;
}
return false;
}
protected boolean use() {
return use(null);
}
protected synchronized boolean use(Object owner) {
if (owner == null) {
owner = this;
} else if (isNotLocal(owner)) {
return false;
}
if ((blocked || inUse) && this.owner == owner) {
return true;
}
while (inUse) {
try {
wait();
} catch (InterruptedException e) {
}
}
if (!inUse) {
inUse = true;
try {
checkLastPos();
} catch (Exception ex) {}
checkShouldRunCallback();
if (shouldTerminate) {
shouldTerminate = false;
throw new AssertionError("aborted by unknown source");
}
keep = false;
this.owner = owner;
log(lvl + 1, "%s: use start: %s", devName, owner);
return true;
}
log(-1, "synch problem - use start: %s", owner);
return false;
}
protected synchronized boolean keep(Object ownerGiven) {
if (ownerGiven == null) {
ownerGiven = this;
} else if (isNotLocal(ownerGiven)) {
return false;
}
if (inUse && owner == ownerGiven) {
keep = true;
log(lvl + 1, "%s: use keep: %s", devName, ownerGiven);
return true;
}
return false;
}
protected boolean let() {
return let(null);
}
protected synchronized boolean let(Object owner) {
if (owner == null) {
owner = this;
} else if (isNotLocal(owner)) {
return false;
}
if (inUse && this.owner == owner) {
if (keep) {
keep = false;
return true;
}
if (isMouse) {
lastPos = getLocation();
}
inUse = false;
this.owner = null;
notify();
log(lvl + 1, "%s: use stop: %s", devName, owner);
return true;
}
return false;
}
protected Location getLocation() {
PointerInfo mp = MouseInfo.getPointerInfo();
if (mp != null) {
return new Location(MouseInfo.getPointerInfo().getLocation());
} else {
Debug.error("Mouse: not possible to get mouse position (PointerInfo == null)");
return null;
}
}
private void checkLastPos() throws UnsupportedOperationException {
if (lastPos == null) {
return;
}
Location pos = getLocation();
if (pos != null && (lastPos.x != pos.x || lastPos.y != pos.y)) {
log(lvl, "%s: moved externally: now (%d,%d) was (%d,%d) (mouseMovedResponse %d)",
devName, pos.x, pos.y, lastPos.x, lastPos.y, mouseMovedResponse);
if (mouseMovedResponse > 0) {
if (MouseMovedHighlight) {
showMousePos(pos.getPoint());
}
}
if (mouseMovedResponse == MouseMovedPause) {
while (pos.x > 0 && pos.y > 0) {
delay(500);
pos = getLocation();
if (MouseMovedHighlight) {
showMousePos(pos.getPoint());
}
}
if (pos.x < 1) {
return;
}
throw new UnsupportedOperationException("Terminating in MouseMovedResponse = Pause");
}
if (mouseMovedResponse == MouseMovedAction) {
//TODO implement 3
if (mouseMovedCallback != null) {
mouseMovedCallback.happened(new ObserveEvent("MouseMoved", ObserveEvent.Type.GENERIC,
lastPos, new Location(pos), null, (new Date()).getTime()));
if (shouldTerminate) {
shouldTerminate = false;
throw new AssertionError("aborted by Sikulix.MouseMovedCallBack");
}
}
}
}
}
private void checkShouldRunCallback() {
if (shouldRunCallback && callback != null) {
callback.happened(new ObserveEvent("DeviceGeneric", ObserveEvent.Type.GENERIC,
null, null, null, (new Date()).getTime()));
if (shouldTerminate) {
shouldTerminate = false;
throw new AssertionError("aborted by Sikulix.GenericDeviceCallBack");
}
}
}
/**
* what to do if mouse is moved outside Sikuli's mouse protection <br>
* in case of event the user provided callBack.happened is called
*
* @param givenCallBack
*/
public void setCallback(Object givenCallBack) {
if (givenCallBack != null) {
callback = new ObserverCallBack(givenCallBack, ObserveEvent.Type.GENERIC);
}
}
private static void showMousePos(Point pos) {
Location lPos = new Location(pos);
Region inner = lPos.grow(20).highlight();
delay(500);
lPos.grow(40).highlight(1);
delay(500);
inner.highlight();
}
protected static void delay(int time) {
if (time == 0) {
return;
}
if (time < 10) {
time = time * 1000;
}
try {
Thread.sleep(time);
} catch (InterruptedException e) {
}
}
}