/**
* Copyright 2008 - 2015 The Loon Game Engine Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loon
* @author cping
* @email:javachenpeng@yahoo.com
* @version 0.5
*/
package loon.html5.gwt;
import loon.LObject;
import loon.event.Event;
import loon.event.InputMake;
import loon.event.KeyMake;
import loon.event.MouseMake;
import loon.event.SysKey;
import loon.event.SysTouch;
import loon.event.TouchMake;
import loon.geom.Vector2f;
import loon.jni.EventHandler;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.shared.HandlerRegistration;
public class GWTInputMake extends InputMake {
private final GWTGame game;
private final Element rootElement;
private final Vector2f lastMousePt = new Vector2f();
private boolean inDragSequence = false;
private boolean isRequestingMouseLock;
private boolean inTouchSequence = false;
private float touchDX = -1, touchDY = -1;
public float getTouchDX() {
return touchDX;
}
public float getTouchDY() {
return touchDY;
}
public GWTInputMake(GWTGame gwtgame, Element root) {
this.game = gwtgame;
this.rootElement = root;
capturePageEvent("keydown", new EventHandler() {
@Override
public void handleEvent(NativeEvent nevent) {
int key = keyForCode(nevent.getKeyCode());
char ch = (char) nevent.getCharCode();
dispatch(new KeyMake.KeyEvent(0, game.time(), ch, key, true),
nevent);
}
});
capturePageEvent("keypress", new EventHandler() {
@Override
public void handleEvent(NativeEvent nevent) {
int key = keyForCode(nevent.getKeyCode());
char ch = (char) nevent.getCharCode();
dispatch(new KeyMake.KeyEvent(0, game.time(), ch, key, true),
nevent);
}
});
capturePageEvent("keyup", new EventHandler() {
@Override
public void handleEvent(NativeEvent nevent) {
int key = keyForCode(nevent.getKeyCode());
char ch = (char) nevent.getCharCode();
dispatch(new KeyMake.KeyEvent(0, game.time(), ch, key, false),
nevent);
}
});
abstract class XYEventHandler implements EventHandler {
public void handleEvent(NativeEvent ev) {
handleEvent(ev, getRelativeX(ev, rootElement),
getRelativeY(ev, rootElement));
}
public abstract void handleEvent(NativeEvent ev, float x, float y);
}
abstract class MoveEventHandler extends XYEventHandler {
private float lastX = -1, lastY = -1;
@Override
public void handleEvent(NativeEvent ev, float x, float y) {
if (lastX == -1) {
lastX = x;
lastY = y;
}
if (inDragSequence == wantDragSequence()) {
if (isMouseLocked()) {
touchDX = getMovementX(ev);
touchDY = getMovementY(ev);
} else {
touchDX = x - lastX;
touchDY = y - lastY;
}
}
dispatch(new MouseMake.ButtonEvent(0, game.time(), x, y, -1,
false), ev);
lastX = x;
lastY = y;
lastMousePt.set(x, y);
}
protected abstract boolean wantDragSequence();
}
addEventListener(Document.get(), "contextmenu", new EventHandler() {
@Override
public void handleEvent(NativeEvent evt) {
evt.preventDefault();
evt.stopPropagation();
}
}, false);
captureEvent(rootElement, "mousedown", new XYEventHandler() {
@Override
public void handleEvent(NativeEvent ev, float x, float y) {
inDragSequence = true;
int btn = getMouseButton(ev);
if (btn != -1) {
dispatch(new MouseMake.ButtonEvent(0, game.time(), x, y,
btn, true), ev);
}
}
});
capturePageEvent("mouseup", new XYEventHandler() {
@Override
public void handleEvent(NativeEvent ev, float x, float y) {
if (inDragSequence) {
inDragSequence = false;
int btn = getMouseButton(ev);
if (btn != -1) {
dispatch(new MouseMake.ButtonEvent(0, game.time(), x,
y, btn, false), ev);
}
}
handleRequestsInUserEventContext();
}
});
capturePageEvent("mousemove", new MoveEventHandler() {
@Override
protected boolean wantDragSequence() {
return true;
}
});
captureEvent(rootElement, "mousemove", new MoveEventHandler() {
@Override
protected boolean wantDragSequence() {
return false;
}
});
captureEvent(rootElement, getMouseWheelEvent(), new EventHandler() {
@Override
public void handleEvent(NativeEvent ev) {
dispatch(new MouseMake.ButtonEvent(0, game.time(),
lastMousePt.x, lastMousePt.y, ev.getButton(), true), ev);
}
});
captureEvent(rootElement, "touchstart", new EventHandler() {
@Override
public void handleEvent(NativeEvent nevent) {
inTouchSequence = true;
dispatch(toTouchEvents(TouchMake.Event.Kind.START, nevent),
nevent);
}
});
capturePageEvent("touchmove", new EventHandler() {
@Override
public void handleEvent(NativeEvent nevent) {
if (inTouchSequence)
dispatch(toTouchEvents(TouchMake.Event.Kind.MOVE, nevent),
nevent);
}
});
capturePageEvent("touchend", new EventHandler() {
@Override
public void handleEvent(NativeEvent nevent) {
if (inTouchSequence) {
dispatch(toTouchEvents(TouchMake.Event.Kind.END, nevent),
nevent);
if (nevent.getTouches().length() == 0) {
inTouchSequence = false;
}
}
}
});
}
@Override
public boolean hasHardwareKeyboard() {
return true;
}
@Override
public native boolean hasTouch() /*-{
return ('ontouchstart' in $doc.documentElement)
|| ($wnd.navigator.userAgent.match(/ipad|iphone|android/i) != null);
}-*/;
@Override
public native boolean hasMouse() /*-{
return ('onmousedown' in $doc.documentElement)
&& ($wnd.navigator.userAgent.match(/ipad|iphone|android/i) == null);
}-*/;
@Override
public native boolean hasMouseLock() /*-{
return !!($doc.body.requestPointerLock
|| $doc.body.webkitRequestPointerLock || $doc.body.mozRequestPointerLock);
}-*/;
void emitFakeMouseUp() {
mouseEvents.emit(new MouseMake.ButtonEvent(0, game.time(), 0, 0,
SysTouch.LEFT, false));
}
@Override
public native boolean isMouseLocked() /*-{
return !!($doc.pointerLockElement || $doc.webkitPointerLockElement || $doc.mozPointerLockElement);
}-*/;
@Override
public void setMouseLocked(boolean locked) {
if (locked) {
if (hasMouseLock()) {
isRequestingMouseLock = true;
game.log().debug("Requesting mouse lock (supported)");
} else {
game.log().debug("Requesting mouse lock -- but unsupported");
}
} else {
game.log().debug("Requesting mouse unlock");
if (hasMouseLock()) {
isRequestingMouseLock = false;
unlockImpl();
}
}
}
static class EventCloseHandler implements HandlerRegistration {
private final JavaScriptObject target;
private final String name;
private final boolean capture;
private JavaScriptObject listener;
EventCloseHandler(JavaScriptObject target, String name,
EventHandler eventHandler, boolean capture) {
this.target = target;
this.name = name;
this.capture = capture;
addEventListener(this, target, name, eventHandler, capture);
}
void setListener(JavaScriptObject listener) {
this.listener = listener;
}
@Override
public void removeHandler() {
removeEventListener(target, name, listener, capture);
}
private native void addEventListener(EventCloseHandler closeHandler,
JavaScriptObject target, String name, EventHandler handler,
boolean capture) /*-{
var listener = function(e) {
handler.@loon.jni.EventHandler::handleEvent(Lcom/google/gwt/dom/client/NativeEvent;)(e);
};
target.addEventListener(name, listener, capture);
closeHandler.@loon.html5.gwt.GWTInputMake.EventCloseHandler::setListener(Lcom/google/gwt/core/client/JavaScriptObject;)(listener);
}-*/;
private native void removeEventListener(JavaScriptObject target,
String name, JavaScriptObject listener, boolean capture)/*-{
target.removeEventListener(name, listener, capture);
}-*/;
}
static HandlerRegistration addEventListener(JavaScriptObject target,
String name, EventHandler handler, boolean capture) {
return new EventCloseHandler(target, name, handler, capture);
};
static HandlerRegistration capturePageEvent(String name,
EventHandler handler) {
return addEventListener(Document.get(), name, handler, true);
}
static HandlerRegistration captureEvent(Element target, String name,
EventHandler handler) {
return addEventListener(target, name, handler, true);
}
static float getRelativeX(NativeEvent e, Element target) {
return (e.getClientX() - target.getAbsoluteLeft()
+ target.getScrollLeft() + target.getOwnerDocument()
.getScrollLeft()) / GWTGraphics.experimentalScale;
}
static float getRelativeY(NativeEvent e, Element target) {
return (e.getClientY() - target.getAbsoluteTop()
+ target.getScrollTop() + target.getOwnerDocument()
.getScrollTop()) / GWTGraphics.experimentalScale;
}
void handleRequestsInUserEventContext() {
if (isRequestingMouseLock && !isMouseLocked()) {
requestMouseLockImpl(rootElement);
}
}
private int mods(NativeEvent event) {
return modifierFlags(event.getAltKey(), event.getCtrlKey(),
event.getMetaKey(), event.getShiftKey());
}
private void dispatch(KeyMake.Event event, NativeEvent nevent) {
try {
event.setFlag(mods(nevent));
game.dispatchEvent(keyboardEvents, event);
} finally {
if (event.isSet(Event.F_PREVENT_DEFAULT)) {
nevent.preventDefault();
}
}
}
private void dispatch(MouseMake.Event event, NativeEvent nevent) {
try {
event.setFlag(mods(nevent));
game.dispatchEvent(mouseEvents, event);
} finally {
if (event.isSet(Event.F_PREVENT_DEFAULT)) {
nevent.preventDefault();
}
}
}
private void dispatch(TouchMake.Event[] events, NativeEvent nevent) {
try {
game.dispatchEvent(touchEvents, events);
} finally {
for (TouchMake.Event event : events) {
if (event.isSet(Event.F_PREVENT_DEFAULT))
nevent.preventDefault();
}
}
}
private native int getMovementX(NativeEvent nevent) /*-{
return nevent.webkitMovementX;
}-*/;
private native int getMovementY(NativeEvent nevent) /*-{
return nevent.webkitMovementY;
}-*/;
native void requestMouseLockImpl(Element element) /*-{
element.requestPointerLock = (element.requestPointerLock
|| element.webkitRequestPointerLock || element.mozRequestPointerLock);
if (element.requestPointerLock)
element.requestPointerLock();
}-*/;
private static native float getMouseWheelVelocity(NativeEvent evt) /*-{
var delta = 0.0;
var agentInfo = @loon.html5.gwt.GWTGame::agentInfo;
if (agentInfo.isFirefox) {
if (agentInfo.isMacOS) {
delta = 1.0 * evt.detail;
} else {
delta = 1.0 * evt.detail / 3;
}
} else if (agentInfo.isOpera) {
if (agentInfo.isLinux) {
delta = -1.0 * evt.wheelDelta / 80;
} else {
// on mac
delta = -1.0 * evt.wheelDelta / 40;
}
} else if (agentInfo.isChrome || agentInfo.isSafari || agentInfo.isIE) {
delta = -1.0 * evt.wheelDelta / 120;
// handle touchpad for chrome
if (Math.abs(delta) < 1) {
if (agentInfo.isWindows) {
delta = -1.0 * evt.wheelDelta;
} else if (agentInfo.isMacOS) {
delta = -1.0 * evt.wheelDelta / 3;
}
}
}
return delta;
}-*/;
protected static native String getMouseWheelEvent() /*-{
if (navigator.userAgent.toLowerCase().indexOf('firefox') != -1) {
return "DOMMouseScroll";
} else {
return "mousewheel";
}
}-*/;
protected static int getMouseButton(NativeEvent evt) {
switch (evt.getButton()) {
case (NativeEvent.BUTTON_LEFT):
return SysTouch.LEFT;
case (NativeEvent.BUTTON_MIDDLE):
return SysTouch.MIDDLE;
case (NativeEvent.BUTTON_RIGHT):
return SysTouch.RIGHT;
default:
return -1;
}
}
private native void unlockImpl() /*-{
$doc.exitPointerLock = $doc.exitPointerLock
|| $doc.webkitExitPointerLock || $doc.mozExitPointerLock;
$doc.exitPointerLock && $doc.exitPointerLock();
}-*/;
private TouchMake.Event[] toTouchEvents(TouchMake.Event.Kind kind,
NativeEvent nevent) {
JsArray<com.google.gwt.dom.client.Touch> nativeTouches = nevent
.getChangedTouches();
int nativeTouchesLen = nativeTouches.length();
TouchMake.Event[] touches = new TouchMake.Event[nativeTouchesLen];
double time = game.time();
for (int t = 0; t < nativeTouchesLen; t++) {
com.google.gwt.dom.client.Touch touch = nativeTouches.get(t);
float x = touch.getRelativeX(rootElement);
float y = touch.getRelativeY(rootElement);
int id = getTouchIdentifier(nevent, t);
touches[t] = new TouchMake.Event(0, time, x, y, kind, id);
}
return touches;
}
private static native int getTouchIdentifier(NativeEvent evt, int index) /*-{
return evt.changedTouches[index].identifier || 0;
}-*/;
private static int keyForCode(int keyCode) {
switch (keyCode) {
case KeyCodes.KEY_ALT:
return SysKey.ALT_LEFT;
case KeyCodes.KEY_BACKSPACE:
return SysKey.BACKSPACE;
case KeyCodes.KEY_DELETE:
return SysKey.DEL;
case KeyCodes.KEY_DOWN:
return SysKey.DOWN;
case KeyCodes.KEY_END:
return SysKey.END;
case KeyCodes.KEY_ENTER:
return SysKey.ENTER;
case KeyCodes.KEY_ESCAPE:
return SysKey.ESCAPE;
case KeyCodes.KEY_HOME:
return SysKey.HOME;
case KeyCodes.KEY_LEFT:
return SysKey.LEFT;
case KeyCodes.KEY_PAGEDOWN:
return SysKey.PAGE_DOWN;
case KeyCodes.KEY_PAGEUP:
return SysKey.PAGE_UP;
case KeyCodes.KEY_RIGHT:
return SysKey.RIGHT;
case KeyCodes.KEY_SHIFT:
return SysKey.SHIFT_LEFT;
case KeyCodes.KEY_TAB:
return SysKey.TAB;
case KeyCodes.KEY_UP:
return SysKey.UP;
case KEY_SPACE:
return SysKey.SPACE;
case KEY_INSERT:
return SysKey.INSERT;
case KEY_0:
return SysKey.NUM_0;
case KEY_1:
return SysKey.NUM_1;
case KEY_2:
return SysKey.NUM_2;
case KEY_3:
return SysKey.NUM_3;
case KEY_4:
return SysKey.NUM_4;
case KEY_5:
return SysKey.NUM_5;
case KEY_6:
return SysKey.NUM_6;
case KEY_7:
return SysKey.NUM_7;
case KEY_8:
return SysKey.NUM_8;
case KEY_9:
return SysKey.NUM_9;
case KEY_A:
return SysKey.A;
case KEY_B:
return SysKey.B;
case KEY_C:
return SysKey.C;
case KEY_D:
return SysKey.D;
case KEY_E:
return SysKey.E;
case KEY_F:
return SysKey.F;
case KEY_G:
return SysKey.G;
case KEY_H:
return SysKey.H;
case KEY_I:
return SysKey.I;
case KEY_J:
return SysKey.J;
case KEY_K:
return SysKey.K;
case KEY_L:
return SysKey.L;
case KEY_M:
return SysKey.M;
case KEY_N:
return SysKey.N;
case KEY_O:
return SysKey.O;
case KEY_P:
return SysKey.P;
case KEY_Q:
return SysKey.Q;
case KEY_R:
return SysKey.R;
case KEY_S:
return SysKey.S;
case KEY_T:
return SysKey.T;
case KEY_U:
return SysKey.U;
case KEY_V:
return SysKey.V;
case KEY_W:
return SysKey.W;
case KEY_X:
return SysKey.X;
case KEY_Y:
return SysKey.Y;
case KEY_Z:
return SysKey.Z;
case KEY_NUMPAD0:
return SysKey.NUM_0;
case KEY_NUMPAD1:
return SysKey.NUM_1;
case KEY_NUMPAD2:
return SysKey.NUM_2;
case KEY_NUMPAD3:
return SysKey.NUM_3;
case KEY_NUMPAD4:
return SysKey.NUM_4;
case KEY_NUMPAD5:
return SysKey.NUM_5;
case KEY_NUMPAD6:
return SysKey.NUM_6;
case KEY_NUMPAD7:
return SysKey.NUM_7;
case KEY_NUMPAD8:
return SysKey.NUM_8;
case KEY_NUMPAD9:
return SysKey.NUM_9;
case KEY_ADD:
return SysKey.PLUS;
case KEY_F1:
return SysKey.NUM_1;
case KEY_F2:
return SysKey.NUM_2;
case KEY_F3:
return SysKey.NUM_3;
case KEY_F4:
return SysKey.NUM_4;
case KEY_F5:
return SysKey.NUM_5;
case KEY_F6:
return SysKey.NUM_6;
case KEY_F7:
return SysKey.NUM_7;
case KEY_F8:
return SysKey.NUM_8;
case KEY_F9:
return SysKey.NUM_9;
case KEY_EQUALS:
return SysKey.EQUALS;
case KEY_COMMA:
return SysKey.COMMA;
case KEY_DASH:
return SysKey.MINUS;
case KEY_PERIOD:
return SysKey.PERIOD;
case KEY_FORWARD_SLASH:
return SysKey.SLASH;
case KEY_GRAVE_ACCENT:
return SysKey.GRAVE;
case KEY_OPEN_BRACKET:
return SysKey.LEFT_BRACKET;
case KEY_BACKSLASH:
return SysKey.BACKSLASH;
case KEY_CLOSE_BRACKET:
return SysKey.RIGHT_BRACKET;
default:
return SysKey.UNKNOWN;
}
}
private static final int KEY_PAUSE = 19;
private static final int KEY_CAPS_LOCK = 20;
private static final int KEY_SPACE = 32;
private static final int KEY_INSERT = 45;
private static final int KEY_0 = 48;
private static final int KEY_1 = 49;
private static final int KEY_2 = 50;
private static final int KEY_3 = 51;
private static final int KEY_4 = 52;
private static final int KEY_5 = 53;
private static final int KEY_6 = 54;
private static final int KEY_7 = 55;
private static final int KEY_8 = 56;
private static final int KEY_9 = 57;
private static final int KEY_A = 65;
private static final int KEY_B = 66;
private static final int KEY_C = 67;
private static final int KEY_D = 68;
private static final int KEY_E = 69;
private static final int KEY_F = 70;
private static final int KEY_G = 71;
private static final int KEY_H = 72;
private static final int KEY_I = 73;
private static final int KEY_J = 74;
private static final int KEY_K = 75;
private static final int KEY_L = 76;
private static final int KEY_M = 77;
private static final int KEY_N = 78;
private static final int KEY_O = 79;
private static final int KEY_P = 80;
private static final int KEY_Q = 81;
private static final int KEY_R = 82;
private static final int KEY_S = 83;
private static final int KEY_T = 84;
private static final int KEY_U = 85;
private static final int KEY_V = 86;
private static final int KEY_W = 87;
private static final int KEY_X = 88;
private static final int KEY_Y = 89;
private static final int KEY_Z = 90;
private static final int KEY_LEFT_WINDOW_KEY = 91;
private static final int KEY_RIGHT_WINDOW_KEY = 92;
private static final int KEY_NUMPAD0 = 96;
private static final int KEY_NUMPAD1 = 97;
private static final int KEY_NUMPAD2 = 98;
private static final int KEY_NUMPAD3 = 99;
private static final int KEY_NUMPAD4 = 100;
private static final int KEY_NUMPAD5 = 101;
private static final int KEY_NUMPAD6 = 102;
private static final int KEY_NUMPAD7 = 103;
private static final int KEY_NUMPAD8 = 104;
private static final int KEY_NUMPAD9 = 105;
private static final int KEY_MULTIPLY = 106;
private static final int KEY_ADD = 107;
private static final int KEY_SUBTRACT = 109;
private static final int KEY_DECIMAL_POINT_KEY = 110;
private static final int KEY_DIVIDE = 111;
private static final int KEY_F1 = 112;
private static final int KEY_F2 = 113;
private static final int KEY_F3 = 114;
private static final int KEY_F4 = 115;
private static final int KEY_F5 = 116;
private static final int KEY_F6 = 117;
private static final int KEY_F7 = 118;
private static final int KEY_F8 = 119;
private static final int KEY_F9 = 120;
private static final int KEY_F10 = 121;
private static final int KEY_F11 = 122;
private static final int KEY_F12 = 123;
private static final int KEY_NUM_LOCK = 144;
private static final int KEY_SCROLL_LOCK = 145;
private static final int KEY_SEMICOLON = 186;
private static final int KEY_EQUALS = 187;
private static final int KEY_COMMA = 188;
private static final int KEY_DASH = 189;
private static final int KEY_PERIOD = 190;
private static final int KEY_FORWARD_SLASH = 191;
private static final int KEY_GRAVE_ACCENT = 192;
private static final int KEY_OPEN_BRACKET = 219;
private static final int KEY_BACKSLASH = 220;
private static final int KEY_CLOSE_BRACKET = 221;
private static final int KEY_SINGLE_QUOTE = 222;
@Override
public void callback(LObject<?> o) {
}
}