Explorar o código

Committed patch from iwgEric that fixes a couple of issues on android touchinput http://jmonkeyengine.org/groups/android/forum/topic/error-with-nifty-and-awt/?topic_page=2#post-165433

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9226 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
rem..om %!s(int64=13) %!d(string=hai) anos
pai
achega
859123673e

+ 620 - 616
engine/src/android/com/jme3/input/android/AndroidInput.java

@@ -1,616 +1,620 @@
-package com.jme3.input.android;
-
-import android.content.Context;
-import android.opengl.GLSurfaceView;
-import android.util.AttributeSet;
-import android.view.GestureDetector;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
-import com.jme3.input.KeyInput;
-import com.jme3.input.RawInputListener;
-import com.jme3.input.TouchInput;
-import com.jme3.input.event.MouseButtonEvent;
-import com.jme3.input.event.MouseMotionEvent;
-import com.jme3.input.event.TouchEvent;
-import com.jme3.input.event.TouchEvent.Type;
-import com.jme3.math.Vector2f;
-import com.jme3.util.RingBuffer;
-import java.util.HashMap;
-import java.util.logging.Logger;
-
-/**
- * <code>AndroidInput</code> is one of the main components that connect jme with android. Is derived from GLSurfaceView and handles all Inputs
- * @author larynx
- *
- */
-public class AndroidInput extends GLSurfaceView implements 
-        TouchInput,
-        GestureDetector.OnGestureListener, 
-        GestureDetector.OnDoubleTapListener, 
-        ScaleGestureDetector.OnScaleGestureListener {
-
-    final private static int MAX_EVENTS = 1024;
-    // Custom settings
-    public boolean mouseEventsEnabled = true;
-    public boolean mouseEventsInvertX = false;
-    public boolean mouseEventsInvertY = false;
-    public boolean keyboardEventsEnabled = false;
-    public boolean dontSendHistory = false;
-    // Used to transfer events from android thread to GLThread
-    final private RingBuffer<TouchEvent> eventQueue = new RingBuffer<TouchEvent>(MAX_EVENTS);
-    final private RingBuffer<TouchEvent> eventPoolUnConsumed = new RingBuffer<TouchEvent>(MAX_EVENTS);
-    final private RingBuffer<TouchEvent> eventPool = new RingBuffer<TouchEvent>(MAX_EVENTS);
-    final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>();
-    // Internal
-    private ScaleGestureDetector scaledetector;
-    private GestureDetector detector;
-    private int lastX;
-    private int lastY;
-    private final static Logger logger = Logger.getLogger(AndroidInput.class.getName());
-    private boolean isInitialized = false;
-    private RawInputListener listener = null;
-    private static final int[] ANDROID_TO_JME = {
-        0x0, // unknown
-        0x0, // key code soft left
-        0x0, // key code soft right
-        KeyInput.KEY_HOME,
-        KeyInput.KEY_ESCAPE, // key back
-        0x0, // key call
-        0x0, // key endcall
-        KeyInput.KEY_0,
-        KeyInput.KEY_1,
-        KeyInput.KEY_2,
-        KeyInput.KEY_3,
-        KeyInput.KEY_4,
-        KeyInput.KEY_5,
-        KeyInput.KEY_6,
-        KeyInput.KEY_7,
-        KeyInput.KEY_8,
-        KeyInput.KEY_9,
-        KeyInput.KEY_MULTIPLY,
-        0x0, // key pound
-        KeyInput.KEY_UP,
-        KeyInput.KEY_DOWN,
-        KeyInput.KEY_LEFT,
-        KeyInput.KEY_RIGHT,
-        KeyInput.KEY_RETURN, // dpad center
-        0x0, // volume up
-        0x0, // volume down
-        KeyInput.KEY_POWER, // power (?)
-        0x0, // camera
-        0x0, // clear
-        KeyInput.KEY_A,
-        KeyInput.KEY_B,
-        KeyInput.KEY_C,
-        KeyInput.KEY_D,
-        KeyInput.KEY_E,
-        KeyInput.KEY_F,
-        KeyInput.KEY_G,
-        KeyInput.KEY_H,
-        KeyInput.KEY_I,
-        KeyInput.KEY_J,
-        KeyInput.KEY_K,
-        KeyInput.KEY_L,
-        KeyInput.KEY_M,
-        KeyInput.KEY_N,
-        KeyInput.KEY_O,
-        KeyInput.KEY_P,
-        KeyInput.KEY_Q,
-        KeyInput.KEY_R,
-        KeyInput.KEY_S,
-        KeyInput.KEY_T,
-        KeyInput.KEY_U,
-        KeyInput.KEY_V,
-        KeyInput.KEY_W,
-        KeyInput.KEY_X,
-        KeyInput.KEY_Y,
-        KeyInput.KEY_Z,
-        KeyInput.KEY_COMMA,
-        KeyInput.KEY_PERIOD,
-        KeyInput.KEY_LMENU,
-        KeyInput.KEY_RMENU,
-        KeyInput.KEY_LSHIFT,
-        KeyInput.KEY_RSHIFT,
-        //        0x0, // fn
-        //        0x0, // cap (?)
-
-        KeyInput.KEY_TAB,
-        KeyInput.KEY_SPACE,
-        0x0, // sym (?) symbol
-        0x0, // explorer
-        0x0, // envelope
-        KeyInput.KEY_RETURN, // newline/enter
-        KeyInput.KEY_DELETE,
-        KeyInput.KEY_GRAVE,
-        KeyInput.KEY_MINUS,
-        KeyInput.KEY_EQUALS,
-        KeyInput.KEY_LBRACKET,
-        KeyInput.KEY_RBRACKET,
-        KeyInput.KEY_BACKSLASH,
-        KeyInput.KEY_SEMICOLON,
-        KeyInput.KEY_APOSTROPHE,
-        KeyInput.KEY_SLASH,
-        KeyInput.KEY_AT, // at (@)
-        KeyInput.KEY_NUMLOCK, //0x0, // num
-        0x0, //headset hook
-        0x0, //focus
-        KeyInput.KEY_ADD,
-        KeyInput.KEY_LMETA, //menu
-        0x0,//notification
-        0x0,//search
-        0x0,//media play/pause
-        0x0,//media stop
-        0x0,//media next
-        0x0,//media previous
-        0x0,//media rewind
-        0x0,//media fastforward
-        0x0,//mute
-    };
-
-    public AndroidInput(Context ctx, AttributeSet attribs) {
-        super(ctx, attribs);
-        detector = new GestureDetector(null, this, null, false);
-        scaledetector = new ScaleGestureDetector(ctx, this);
-
-    }
-
-    public AndroidInput(Context ctx) {
-        super(ctx);
-        detector = new GestureDetector(null, this, null, false);
-        scaledetector = new ScaleGestureDetector(ctx, this);
-    }
-
-    private TouchEvent getNextFreeTouchEvent() {
-        return getNextFreeTouchEvent(false);
-    }
-
-    /**
-     * Fetches a touch event from the reuse pool
-     * @param wait if true waits for a reusable event to get available/released 
-     * by an other thread, if false returns a new one if needed.
-     * 
-     * @return a usable TouchEvent
-     */
-    private TouchEvent getNextFreeTouchEvent(boolean wait) {
-        TouchEvent evt = null;
-        synchronized (eventPoolUnConsumed) {
-            int size = eventPoolUnConsumed.size();
-            while (size > 0) {
-                evt = eventPoolUnConsumed.pop();
-                if (!evt.isConsumed()) {
-                    eventPoolUnConsumed.push(evt);
-                    evt = null;
-                } else {
-                    break;
-                }
-                size--;
-            }
-        }
-
-        if (evt == null) {
-            if (eventPool.isEmpty() && wait) {
-                logger.warning("eventPool buffer underrun");
-                boolean isEmpty;
-                do {
-                    synchronized (eventPool) {
-                        isEmpty = eventPool.isEmpty();
-                    }
-                    try {
-                        Thread.sleep(50);
-                    } catch (InterruptedException e) {
-                    }
-                } while (isEmpty);
-                synchronized (eventPool) {
-                    evt = eventPool.pop();
-                }
-            } else if (eventPool.isEmpty()) {
-                evt = new TouchEvent();
-                logger.warning("eventPool buffer underrun");
-            } else {
-                synchronized (eventPool) {
-                    evt = eventPool.pop();
-                }
-            }
-        }
-        return evt;
-    }
-
-    /**
-     * onTouchEvent gets called from android thread on touchpad events
-     */
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        boolean bWasHandled = false;
-        TouchEvent touch;
-        //    System.out.println("native : " + event.getAction());
-        int action = event.getAction() & MotionEvent.ACTION_MASK;
-        int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
-                >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-        int pointerId = event.getPointerId(pointerIndex);
-
-        // final int historySize = event.getHistorySize();
-        //final int pointerCount = event.getPointerCount();
-
-        switch (action) {
-            case MotionEvent.ACTION_POINTER_DOWN:
-            case MotionEvent.ACTION_DOWN:
-                touch = getNextFreeTouchEvent();
-                touch.set(Type.DOWN, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0);
-                touch.setPointerId(pointerId);
-                touch.setTime(event.getEventTime());
-                touch.setPressure(event.getPressure(pointerIndex));
-                processEvent(touch);
-
-                bWasHandled = true;
-                break;
-
-            case MotionEvent.ACTION_POINTER_UP:
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP:
-
-                touch = getNextFreeTouchEvent();
-                touch.set(Type.UP, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0);
-                touch.setPointerId(pointerId);
-                touch.setTime(event.getEventTime());
-                touch.setPressure(event.getPressure(pointerIndex));
-                processEvent(touch);
-
-
-                bWasHandled = true;
-                break;
-            case MotionEvent.ACTION_MOVE:
-
-
-                // Convert all pointers into events
-                for (int p = 0; p < event.getPointerCount(); p++) {
-                    Vector2f lastPos = lastPositions.get(pointerIndex);
-                    if (lastPos == null) {
-                        lastPos = new Vector2f(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));
-                        lastPositions.put(pointerId, lastPos);
-                    }
-                    touch = getNextFreeTouchEvent();
-                    touch.set(Type.MOVE, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), event.getX(pointerIndex) - lastPos.x, this.getHeight() - event.getY(pointerIndex) - lastPos.y);
-                    touch.setPointerId(pointerId);
-                    touch.setTime(event.getEventTime());
-                    touch.setPressure(event.getPressure(pointerIndex));
-                    processEvent(touch);
-                    lastPos.set(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));
-                }
-                bWasHandled = true;
-                break;
-            case MotionEvent.ACTION_OUTSIDE:
-                break;
-
-        }
-
-        // Try to detect gestures        
-        this.detector.onTouchEvent(event);
-        this.scaledetector.onTouchEvent(event);
-
-        return bWasHandled;
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        TouchEvent evt;
-        evt = getNextFreeTouchEvent();
-        evt.set(TouchEvent.Type.KEY_DOWN);
-        evt.setKeyCode(keyCode);
-        evt.setCharacters(event.getCharacters());
-        evt.setTime(event.getEventTime());
-
-        // Send the event
-        processEvent(evt);
-
-        // Handle all keys ourself except Volume Up/Down
-        if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
-            return false;
-        } else {
-            return true;
-        }
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        TouchEvent evt;
-        evt = getNextFreeTouchEvent();
-        evt.set(TouchEvent.Type.KEY_UP);
-        evt.setKeyCode(keyCode);
-        evt.setCharacters(event.getCharacters());
-        evt.setTime(event.getEventTime());
-
-        // Send the event
-        processEvent(evt);
-
-        // Handle all keys ourself except Volume Up/Down
-        if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
-            return false;
-        } else {
-            return true;
-        }
-    }
-
-    // -----------------------------------------
-    // JME3 Input interface
-    @Override
-    public void initialize() {
-        TouchEvent item;
-        for (int i = 0; i < MAX_EVENTS; i++) {
-            item = new TouchEvent();
-            eventPool.push(item);
-        }
-        isInitialized = true;
-    }
-
-    @Override
-    public void destroy() {
-        isInitialized = false;
-
-        // Clean up queues
-        while (!eventPool.isEmpty()) {
-            eventPool.pop();
-        }
-        while (!eventQueue.isEmpty()) {
-            eventQueue.pop();
-        }
-    }
-
-    @Override
-    public boolean isInitialized() {
-        return isInitialized;
-    }
-
-    @Override
-    public void setInputListener(RawInputListener listener) {
-        this.listener = listener;
-    }
-
-    @Override
-    public long getInputTimeNanos() {
-        return System.nanoTime();
-    }
-    // -----------------------------------------
-
-    private void processEvent(TouchEvent event) {
-        synchronized (eventQueue) {
-            eventQueue.push(event);
-        }
-    }
-
-    //  ---------------  INSIDE GLThread  --------------- 
-    @Override
-    public void update() {
-        generateEvents();
-    }
-
-    private void generateEvents() {
-        if (listener != null) {
-            TouchEvent event;
-            MouseButtonEvent btn;
-            int newX;
-            int newY;
-
-            while (!eventQueue.isEmpty()) {
-                synchronized (eventQueue) {
-                    event = eventQueue.pop();
-                }
-                if (event != null) {
-                    listener.onTouchEvent(event);
-
-                    if (mouseEventsEnabled) {
-                        if (mouseEventsInvertX) {
-                            newX = this.getWidth() - (int) event.getX();
-                        } else {
-                            newX = (int) event.getX();
-                        }
-
-                        if (mouseEventsInvertY) {
-                            newY = this.getHeight() - (int) event.getY();
-                        } else {
-                            newY = (int) event.getY();
-                        }
-
-                        switch (event.getType()) {
-                            case DOWN:
-                                // Handle mouse down event 
-                                btn = new MouseButtonEvent(0, true, newX, newY);
-                                btn.setTime(event.getTime());
-                                listener.onMouseButtonEvent(btn);
-                                // Store current pos
-                                lastX = -1;
-                                lastY = -1;
-                                break;
-
-                            case UP:
-                                // Handle mouse up event 
-                                btn = new MouseButtonEvent(0, false, newX, newY);
-                                btn.setTime(event.getTime());
-                                listener.onMouseButtonEvent(btn);
-                                // Store current pos
-                                lastX = -1;
-                                lastY = -1;
-                                break;
-
-                            case MOVE:
-                                int dx;
-                                int dy;
-                                if (lastX != -1) {
-                                    dx = newX - lastX;
-                                    dy = newY - lastY;
-                                } else {
-                                    dx = 0;
-                                    dy = 0;
-                                }
-                                MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0);
-                                mot.setTime(event.getTime());
-                                listener.onMouseMotionEvent(mot);
-                                lastX = newX;
-                                lastY = newY;
-                                break;
-                        }
-
-
-                    }
-                }
-
-                if (event.isConsumed() == false) {
-                    synchronized (eventPoolUnConsumed) {
-                        eventPoolUnConsumed.push(event);
-                    }
-
-                } else {
-                    synchronized (eventPool) {
-                        eventPool.push(event);
-                    }
-                }
-            }
-
-        }
-    }
-    //  --------------- ENDOF INSIDE GLThread  --------------- 
-
-    // --------------- Gesture detected callback events  --------------- 
-    public boolean onDown(MotionEvent event) {
-        return false;
-    }
-
-    public void onLongPress(MotionEvent event) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.LONGPRESSED, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
-        touch.setPointerId(0);
-        touch.setTime(event.getEventTime());
-        processEvent(touch);
-    }
-
-    public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.FLING, event.getX(), this.getHeight() - event.getY(), vx, vy);
-        touch.setPointerId(0);
-        touch.setTime(event.getEventTime());
-        processEvent(touch);
-
-        return true;
-    }
-
-    public boolean onSingleTapConfirmed(MotionEvent event) {
-        //Nothing to do here the tap has already been detected.
-        return false;
-    }
-
-    public boolean onDoubleTap(MotionEvent event) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.DOUBLETAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
-        touch.setPointerId(0);
-        touch.setTime(event.getEventTime());
-        processEvent(touch);
-        return true;
-    }
-
-    public boolean onDoubleTapEvent(MotionEvent event) {
-        return false;
-    }
-
-    public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.SCALE_START, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), 0f, 0f);
-        touch.setPointerId(0);
-        touch.setTime(scaleGestureDetector.getEventTime());
-        touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
-        touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
-        processEvent(touch);
-        //    System.out.println("scaleBegin");
-
-        return true;
-    }
-
-    public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
-        touch.setPointerId(0);
-        touch.setTime(scaleGestureDetector.getEventTime());
-        touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
-        touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
-        processEvent(touch);
-        //   System.out.println("scale");
-
-        return false;
-    }
-
-    public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
-        touch.setPointerId(0);
-        touch.setTime(scaleGestureDetector.getEventTime());
-        touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
-        touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
-        processEvent(touch);
-    }
-
-    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.SCROLL, e1.getX(), this.getHeight() - e1.getY(), distanceX, distanceY * (-1));
-        touch.setPointerId(0);
-        touch.setTime(e1.getEventTime());
-        processEvent(touch);
-        //System.out.println("scroll " + e1.getPointerCount());
-        return false;
-    }
-
-    public void onShowPress(MotionEvent event) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.SHOWPRESS, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
-        touch.setPointerId(0);
-        touch.setTime(event.getEventTime());
-        processEvent(touch);
-    }
-
-    public boolean onSingleTapUp(MotionEvent event) {
-        TouchEvent touch = getNextFreeTouchEvent();
-        touch.set(Type.TAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
-        touch.setPointerId(0);
-        touch.setTime(event.getEventTime());
-        processEvent(touch);
-        return true;
-    }
-
-    @Override
-    public void setSimulateMouse(boolean simulate) {
-        mouseEventsEnabled = simulate;
-    }
-
-    @Override
-    public void setSimulateKeyboard(boolean simulate) {
-        keyboardEventsEnabled = simulate;
-    }
-
-    @Override
-    public void setOmitHistoricEvents(boolean dontSendHistory) {
-        this.dontSendHistory = dontSendHistory;
-    }
-
-    // TODO: move to TouchInput
-    public boolean isMouseEventsEnabled() {
-        return mouseEventsEnabled;
-    }
-
-    public void setMouseEventsEnabled(boolean mouseEventsEnabled) {
-        this.mouseEventsEnabled = mouseEventsEnabled;
-    }
-
-    public boolean isMouseEventsInvertY() {
-        return mouseEventsInvertY;
-    }
-
-    public void setMouseEventsInvertY(boolean mouseEventsInvertY) {
-        this.mouseEventsInvertY = mouseEventsInvertY;
-    }
-
-    public boolean isMouseEventsInvertX() {
-        return mouseEventsInvertX;
-    }
-
-    public void setMouseEventsInvertX(boolean mouseEventsInvertX) {
-        this.mouseEventsInvertX = mouseEventsInvertX;
-    }
-}
+package com.jme3.input.android;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import com.jme3.input.KeyInput;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.TouchInput;
+import com.jme3.input.event.MouseButtonEvent;
+import com.jme3.input.event.MouseMotionEvent;
+import com.jme3.input.event.TouchEvent;
+import com.jme3.input.event.TouchEvent.Type;
+import com.jme3.math.Vector2f;
+import com.jme3.util.RingBuffer;
+import java.util.HashMap;
+import java.util.logging.Logger;
+
+/**
+ * <code>AndroidInput</code> is one of the main components that connect jme with android. Is derived from GLSurfaceView and handles all Inputs
+ * @author larynx
+ *
+ */
+public class AndroidInput extends GLSurfaceView implements 
+        TouchInput,
+        GestureDetector.OnGestureListener, 
+        GestureDetector.OnDoubleTapListener, 
+        ScaleGestureDetector.OnScaleGestureListener {
+
+    final private static int MAX_EVENTS = 1024;
+    // Custom settings
+    public boolean mouseEventsEnabled = true;
+    public boolean mouseEventsInvertX = false;
+    public boolean mouseEventsInvertY = false;
+    public boolean keyboardEventsEnabled = false;
+    public boolean dontSendHistory = false;
+    // Used to transfer events from android thread to GLThread
+    final private RingBuffer<TouchEvent> eventQueue = new RingBuffer<TouchEvent>(MAX_EVENTS);
+    final private RingBuffer<TouchEvent> eventPoolUnConsumed = new RingBuffer<TouchEvent>(MAX_EVENTS);
+    final private RingBuffer<TouchEvent> eventPool = new RingBuffer<TouchEvent>(MAX_EVENTS);
+    final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>();
+    // Internal
+    private ScaleGestureDetector scaledetector;
+    private GestureDetector detector;
+    private int lastX;
+    private int lastY;
+    private final static Logger logger = Logger.getLogger(AndroidInput.class.getName());
+    private boolean isInitialized = false;
+    private RawInputListener listener = null;
+    private static final int[] ANDROID_TO_JME = {
+        0x0, // unknown
+        0x0, // key code soft left
+        0x0, // key code soft right
+        KeyInput.KEY_HOME,
+        KeyInput.KEY_ESCAPE, // key back
+        0x0, // key call
+        0x0, // key endcall
+        KeyInput.KEY_0,
+        KeyInput.KEY_1,
+        KeyInput.KEY_2,
+        KeyInput.KEY_3,
+        KeyInput.KEY_4,
+        KeyInput.KEY_5,
+        KeyInput.KEY_6,
+        KeyInput.KEY_7,
+        KeyInput.KEY_8,
+        KeyInput.KEY_9,
+        KeyInput.KEY_MULTIPLY,
+        0x0, // key pound
+        KeyInput.KEY_UP,
+        KeyInput.KEY_DOWN,
+        KeyInput.KEY_LEFT,
+        KeyInput.KEY_RIGHT,
+        KeyInput.KEY_RETURN, // dpad center
+        0x0, // volume up
+        0x0, // volume down
+        KeyInput.KEY_POWER, // power (?)
+        0x0, // camera
+        0x0, // clear
+        KeyInput.KEY_A,
+        KeyInput.KEY_B,
+        KeyInput.KEY_C,
+        KeyInput.KEY_D,
+        KeyInput.KEY_E,
+        KeyInput.KEY_F,
+        KeyInput.KEY_G,
+        KeyInput.KEY_H,
+        KeyInput.KEY_I,
+        KeyInput.KEY_J,
+        KeyInput.KEY_K,
+        KeyInput.KEY_L,
+        KeyInput.KEY_M,
+        KeyInput.KEY_N,
+        KeyInput.KEY_O,
+        KeyInput.KEY_P,
+        KeyInput.KEY_Q,
+        KeyInput.KEY_R,
+        KeyInput.KEY_S,
+        KeyInput.KEY_T,
+        KeyInput.KEY_U,
+        KeyInput.KEY_V,
+        KeyInput.KEY_W,
+        KeyInput.KEY_X,
+        KeyInput.KEY_Y,
+        KeyInput.KEY_Z,
+        KeyInput.KEY_COMMA,
+        KeyInput.KEY_PERIOD,
+        KeyInput.KEY_LMENU,
+        KeyInput.KEY_RMENU,
+        KeyInput.KEY_LSHIFT,
+        KeyInput.KEY_RSHIFT,
+        //        0x0, // fn
+        //        0x0, // cap (?)
+
+        KeyInput.KEY_TAB,
+        KeyInput.KEY_SPACE,
+        0x0, // sym (?) symbol
+        0x0, // explorer
+        0x0, // envelope
+        KeyInput.KEY_RETURN, // newline/enter
+        KeyInput.KEY_DELETE,
+        KeyInput.KEY_GRAVE,
+        KeyInput.KEY_MINUS,
+        KeyInput.KEY_EQUALS,
+        KeyInput.KEY_LBRACKET,
+        KeyInput.KEY_RBRACKET,
+        KeyInput.KEY_BACKSLASH,
+        KeyInput.KEY_SEMICOLON,
+        KeyInput.KEY_APOSTROPHE,
+        KeyInput.KEY_SLASH,
+        KeyInput.KEY_AT, // at (@)
+        KeyInput.KEY_NUMLOCK, //0x0, // num
+        0x0, //headset hook
+        0x0, //focus
+        KeyInput.KEY_ADD,
+        KeyInput.KEY_LMETA, //menu
+        0x0,//notification
+        0x0,//search
+        0x0,//media play/pause
+        0x0,//media stop
+        0x0,//media next
+        0x0,//media previous
+        0x0,//media rewind
+        0x0,//media fastforward
+        0x0,//mute
+    };
+
+    public AndroidInput(Context ctx, AttributeSet attribs) {
+        super(ctx, attribs);
+        detector = new GestureDetector(null, this, null, false);
+        scaledetector = new ScaleGestureDetector(ctx, this);
+
+    }
+
+    public AndroidInput(Context ctx) {
+        super(ctx);
+        detector = new GestureDetector(null, this, null, false);
+        scaledetector = new ScaleGestureDetector(ctx, this);
+    }
+
+    private TouchEvent getNextFreeTouchEvent() {
+        return getNextFreeTouchEvent(false);
+    }
+
+    /**
+     * Fetches a touch event from the reuse pool
+     * @param wait if true waits for a reusable event to get available/released 
+     * by an other thread, if false returns a new one if needed.
+     * 
+     * @return a usable TouchEvent
+     */
+    private TouchEvent getNextFreeTouchEvent(boolean wait) {
+        TouchEvent evt = null;
+        synchronized (eventPoolUnConsumed) {
+            int size = eventPoolUnConsumed.size();
+            while (size > 0) {
+                evt = eventPoolUnConsumed.pop();
+                if (!evt.isConsumed()) {
+                    eventPoolUnConsumed.push(evt);
+                    evt = null;
+                } else {
+                    break;
+                }
+                size--;
+            }
+        }
+
+        if (evt == null) {
+            if (eventPool.isEmpty() && wait) {
+                logger.warning("eventPool buffer underrun");
+                boolean isEmpty;
+                do {
+                    synchronized (eventPool) {
+                        isEmpty = eventPool.isEmpty();
+                    }
+                    try {
+                        Thread.sleep(50);
+                    } catch (InterruptedException e) {
+                    }
+                } while (isEmpty);
+                synchronized (eventPool) {
+                    evt = eventPool.pop();
+                }
+            } else if (eventPool.isEmpty()) {
+                evt = new TouchEvent();
+                logger.warning("eventPool buffer underrun");
+            } else {
+                synchronized (eventPool) {
+                    evt = eventPool.pop();
+                }
+            }
+        }
+        return evt;
+    }
+
+    /**
+     * onTouchEvent gets called from android thread on touchpad events
+     */
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        boolean bWasHandled = false;
+        TouchEvent touch;
+        //    System.out.println("native : " + event.getAction());
+        int action = event.getAction() & MotionEvent.ACTION_MASK;
+        int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
+                >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+        int pointerId = event.getPointerId(pointerIndex);
+
+        // final int historySize = event.getHistorySize();
+        //final int pointerCount = event.getPointerCount();
+
+        switch (action) {
+            case MotionEvent.ACTION_POINTER_DOWN:
+            case MotionEvent.ACTION_DOWN:
+                touch = getNextFreeTouchEvent();
+                touch.set(Type.DOWN, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0);
+                touch.setPointerId(pointerId);
+                touch.setTime(event.getEventTime());
+                touch.setPressure(event.getPressure(pointerIndex));
+                processEvent(touch);
+
+                bWasHandled = true;
+                break;
+
+            case MotionEvent.ACTION_POINTER_UP:
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+
+                touch = getNextFreeTouchEvent();
+                touch.set(Type.UP, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0);
+                touch.setPointerId(pointerId);
+                touch.setTime(event.getEventTime());
+                touch.setPressure(event.getPressure(pointerIndex));
+                processEvent(touch);
+
+
+                bWasHandled = true;
+                break;
+            case MotionEvent.ACTION_MOVE:
+
+
+                // Convert all pointers into events
+                for (int p = 0; p < event.getPointerCount(); p++) {
+                    Vector2f lastPos = lastPositions.get(pointerIndex);
+                    if (lastPos == null) {
+                        lastPos = new Vector2f(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));
+                        lastPositions.put(pointerId, lastPos);
+                    }
+                    touch = getNextFreeTouchEvent();
+                    touch.set(Type.MOVE, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), event.getX(pointerIndex) - lastPos.x, this.getHeight() - event.getY(pointerIndex) - lastPos.y);
+                    touch.setPointerId(pointerId);
+                    touch.setTime(event.getEventTime());
+                    touch.setPressure(event.getPressure(pointerIndex));
+                    processEvent(touch);
+                    lastPos.set(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));
+                }
+                bWasHandled = true;
+                break;
+            case MotionEvent.ACTION_OUTSIDE:
+                break;
+
+        }
+
+        // Try to detect gestures        
+        this.detector.onTouchEvent(event);
+        this.scaledetector.onTouchEvent(event);
+
+        return bWasHandled;
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        TouchEvent evt;
+        evt = getNextFreeTouchEvent();
+        evt.set(TouchEvent.Type.KEY_DOWN);
+        evt.setKeyCode(keyCode);
+        evt.setCharacters(event.getCharacters());
+        evt.setTime(event.getEventTime());
+
+        // Send the event
+        processEvent(evt);
+
+        // Handle all keys ourself except Volume Up/Down
+        if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        TouchEvent evt;
+        evt = getNextFreeTouchEvent();
+        evt.set(TouchEvent.Type.KEY_UP);
+        evt.setKeyCode(keyCode);
+        evt.setCharacters(event.getCharacters());
+        evt.setTime(event.getEventTime());
+
+        // Send the event
+        processEvent(evt);
+
+        // Handle all keys ourself except Volume Up/Down
+        if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    // -----------------------------------------
+    // JME3 Input interface
+    @Override
+    public void initialize() {
+        TouchEvent item;
+        for (int i = 0; i < MAX_EVENTS; i++) {
+            item = new TouchEvent();
+            eventPool.push(item);
+        }
+        isInitialized = true;
+    }
+
+    @Override
+    public void destroy() {
+        isInitialized = false;
+
+        // Clean up queues
+        while (!eventPool.isEmpty()) {
+            eventPool.pop();
+        }
+        while (!eventQueue.isEmpty()) {
+            eventQueue.pop();
+        }
+    }
+
+    @Override
+    public boolean isInitialized() {
+        return isInitialized;
+    }
+
+    @Override
+    public void setInputListener(RawInputListener listener) {
+        this.listener = listener;
+    }
+
+    @Override
+    public long getInputTimeNanos() {
+        return System.nanoTime();
+    }
+    // -----------------------------------------
+
+    private void processEvent(TouchEvent event) {
+        synchronized (eventQueue) {
+            eventQueue.push(event);
+        }
+    }
+
+    //  ---------------  INSIDE GLThread  --------------- 
+    @Override
+    public void update() {
+        generateEvents();
+    }
+
+    private void generateEvents() {
+        if (listener != null) {
+            TouchEvent event;
+            MouseButtonEvent btn;
+            int newX;
+            int newY;
+
+            while (!eventQueue.isEmpty()) {
+                synchronized (eventQueue) {
+                    event = eventQueue.pop();
+                }
+                if (event != null) {
+                    listener.onTouchEvent(event);
+
+                    if (mouseEventsEnabled) {
+                        if (mouseEventsInvertX) {
+                            newX = this.getWidth() - (int) event.getX();
+                        } else {
+                            newX = (int) event.getX();
+                        }
+
+                        if (mouseEventsInvertY) {
+                            newY = this.getHeight() - (int) event.getY();
+                        } else {
+                            newY = (int) event.getY();
+                        }
+
+                        switch (event.getType()) {
+                            case DOWN:
+                                // Handle mouse down event 
+                                btn = new MouseButtonEvent(0, true, newX, newY);
+                                btn.setTime(event.getTime());
+                                listener.onMouseButtonEvent(btn);
+                                // Store current pos
+                                lastX = -1;
+                                lastY = -1;
+                                break;
+
+                            case UP:
+                                // Handle mouse up event 
+                                btn = new MouseButtonEvent(0, false, newX, newY);
+                                btn.setTime(event.getTime());
+                                listener.onMouseButtonEvent(btn);
+                                // Store current pos
+                                lastX = -1;
+                                lastY = -1;
+                                break;
+
+                            case MOVE:
+                                int dx;
+                                int dy;
+                                if (lastX != -1) {
+                                    dx = newX - lastX;
+                                    dy = newY - lastY;
+                                } else {
+                                    dx = 0;
+                                    dy = 0;
+                                }
+                                MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0);
+                                mot.setTime(event.getTime());
+                                listener.onMouseMotionEvent(mot);
+                                lastX = newX;
+                                lastY = newY;
+                                break;
+                        }
+
+
+                    }
+                }
+
+                if (event.isConsumed() == false) {
+                    synchronized (eventPoolUnConsumed) {
+                        eventPoolUnConsumed.push(event);
+                    }
+
+                } else {
+                    synchronized (eventPool) {
+                        eventPool.push(event);
+                    }
+                }
+            }
+
+        }
+    }
+    //  --------------- ENDOF INSIDE GLThread  --------------- 
+
+    // --------------- Gesture detected callback events  --------------- 
+    public boolean onDown(MotionEvent event) {
+        return false;
+    }
+
+    public void onLongPress(MotionEvent event) {
+        TouchEvent touch = getNextFreeTouchEvent();
+        touch.set(Type.LONGPRESSED, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
+        touch.setPointerId(0);
+        touch.setTime(event.getEventTime());
+        processEvent(touch);
+    }
+
+    public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy) {
+        TouchEvent touch = getNextFreeTouchEvent();
+        touch.set(Type.FLING, event.getX(), this.getHeight() - event.getY(), vx, vy);
+        touch.setPointerId(0);
+        touch.setTime(event.getEventTime());
+        processEvent(touch);
+
+        return true;
+    }
+
+    public boolean onSingleTapConfirmed(MotionEvent event) {
+        //Nothing to do here the tap has already been detected.
+        return false;
+    }
+
+    public boolean onDoubleTap(MotionEvent event) {
+        TouchEvent touch = getNextFreeTouchEvent();
+        touch.set(Type.DOUBLETAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
+        touch.setPointerId(0);
+        touch.setTime(event.getEventTime());
+        processEvent(touch);
+        return true;
+    }
+
+    public boolean onDoubleTapEvent(MotionEvent event) {
+        return false;
+    }
+
+    public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
+        TouchEvent touch = getNextFreeTouchEvent();
+        touch.set(Type.SCALE_START, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), 0f, 0f);
+        touch.setPointerId(0);
+        touch.setTime(scaleGestureDetector.getEventTime());
+        touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
+        touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
+        processEvent(touch);
+        //    System.out.println("scaleBegin");
+
+        return true;
+    }
+
+    public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
+        TouchEvent touch = getNextFreeTouchEvent();
+        touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
+        touch.setPointerId(0);
+        touch.setTime(scaleGestureDetector.getEventTime());
+        touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
+        touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
+        processEvent(touch);
+        //   System.out.println("scale");
+
+        return false;
+    }
+
+    public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
+        TouchEvent touch = getNextFreeTouchEvent();
+        touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
+        touch.setPointerId(0);
+        touch.setTime(scaleGestureDetector.getEventTime());
+        touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
+        touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
+        processEvent(touch);
+    }
+
+    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+        TouchEvent touch = getNextFreeTouchEvent();
+        touch.set(Type.SCROLL, e1.getX(), this.getHeight() - e1.getY(), distanceX, distanceY * (-1));
+        touch.setPointerId(0);
+        touch.setTime(e1.getEventTime());
+        processEvent(touch);
+        //System.out.println("scroll " + e1.getPointerCount());
+        return false;
+    }
+
+    public void onShowPress(MotionEvent event) {
+        TouchEvent touch = getNextFreeTouchEvent();
+        touch.set(Type.SHOWPRESS, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
+        touch.setPointerId(0);
+        touch.setTime(event.getEventTime());
+        processEvent(touch);
+    }
+
+    public boolean onSingleTapUp(MotionEvent event) {
+        TouchEvent touch = getNextFreeTouchEvent();
+        touch.set(Type.TAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
+        touch.setPointerId(0);
+        touch.setTime(event.getEventTime());
+        processEvent(touch);
+        return true;
+    }
+
+    @Override
+    public void setSimulateMouse(boolean simulate) {
+        mouseEventsEnabled = simulate;
+    }
+    @Override
+    public boolean getSimulateMouse() {
+        return mouseEventsEnabled;
+    }
+
+    @Override
+    public void setSimulateKeyboard(boolean simulate) {
+        keyboardEventsEnabled = simulate;
+    }
+
+    @Override
+    public void setOmitHistoricEvents(boolean dontSendHistory) {
+        this.dontSendHistory = dontSendHistory;
+    }
+
+    // TODO: move to TouchInput
+    public boolean isMouseEventsEnabled() {
+        return mouseEventsEnabled;
+    }
+
+    public void setMouseEventsEnabled(boolean mouseEventsEnabled) {
+        this.mouseEventsEnabled = mouseEventsEnabled;
+    }
+
+    public boolean isMouseEventsInvertY() {
+        return mouseEventsInvertY;
+    }
+
+    public void setMouseEventsInvertY(boolean mouseEventsInvertY) {
+        this.mouseEventsInvertY = mouseEventsInvertY;
+    }
+
+    public boolean isMouseEventsInvertX() {
+        return mouseEventsInvertX;
+    }
+
+    public void setMouseEventsInvertX(boolean mouseEventsInvertX) {
+        this.mouseEventsInvertX = mouseEventsInvertX;
+    }
+}

+ 892 - 881
engine/src/core/com/jme3/input/InputManager.java

@@ -1,881 +1,892 @@
-/*
- * Copyright (c) 2009-2012 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *   may be used to endorse or promote products derived from this software
- *   without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.input;
-
-import com.jme3.app.Application;
-import com.jme3.input.controls.*;
-import com.jme3.input.event.*;
-import com.jme3.math.FastMath;
-import com.jme3.math.Vector2f;
-import com.jme3.util.IntMap;
-import com.jme3.util.IntMap.Entry;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * The <code>InputManager</code> is responsible for converting input events
- * received from the Key, Mouse and Joy Input implementations into an
- * abstract, input device independent representation that user code can use.
- * <p>
- * By default an <code>InputManager</code> is included with every Application instance for use
- * in user code to query input, unless the Application is created as headless
- * or with input explicitly disabled.
- * <p>
- * The input manager has two concepts, a {@link Trigger} and a mapping.
- * A trigger represents a specific input trigger, such as a key button, 
- * or a mouse axis. A mapping represents a link onto one or several triggers, 
- * when the appropriate trigger is activated (e.g. a key is pressed), the 
- * mapping will be invoked. Any listeners registered to receive an event
- * from the mapping will have an event raised.
- * <p>
- * There are two types of events that {@link InputListener input listeners}
- * can receive, one is {@link ActionListener#onAction(java.lang.String, boolean, float) action}
- * events and another is {@link AnalogListener#onAnalog(java.lang.String, float, float) analog}
- * events. 
- * <p>
- * <code>onAction</code> events are raised when the specific input
- * activates or deactivates. For a digital input such as key press, the <code>onAction()</code>
- * event will be raised with the <code>isPressed</code> argument equal to true,
- * when the key is released, <code>onAction</code> is called again but this time
- * with the <code>isPressed</code> argument set to false.
- * For analog inputs, the <code>onAction</code> method will be called any time
- * the input is non-zero, however an exception to this is for joystick axis inputs,
- * which are only called when the input is above the {@link InputManager#setAxisDeadZone(float) dead zone}.
- * <p>
- * <code>onAnalog</code> events are raised every frame while the input is activated.
- * For digital inputs, every frame that the input is active will cause the
- * <code>onAnalog</code> method to be called, the argument <code>value</code>
- * argument will equal to the frame's time per frame (TPF) value but only
- * for digital inputs. For analog inputs however, the <code>value</code> argument
- * will equal the actual analog value.
- */
-public class InputManager implements RawInputListener {
-
-    private static final Logger logger = Logger.getLogger(InputManager.class.getName());
-    private final KeyInput keys;
-    private final MouseInput mouse;
-    private final JoyInput joystick;
-    private final TouchInput touch;
-    private float frameTPF;
-    private long lastLastUpdateTime = 0;
-    private long lastUpdateTime = 0;
-    private long frameDelta = 0;
-    private long firstTime = 0;
-    private boolean eventsPermitted = false;
-    private boolean mouseVisible = true;
-    private boolean safeMode = false;
-    private float axisDeadZone = 0.05f;
-    private Vector2f cursorPos = new Vector2f();
-    private Joystick[] joysticks;
-    private final IntMap<ArrayList<Mapping>> bindings = new IntMap<ArrayList<Mapping>>();
-    private final HashMap<String, Mapping> mappings = new HashMap<String, Mapping>();
-    private final IntMap<Long> pressedButtons = new IntMap<Long>();
-    private final IntMap<Float> axisValues = new IntMap<Float>();
-    private ArrayList<RawInputListener> rawListeners = new ArrayList<RawInputListener>();
-    private RawInputListener[] rawListenerArray = null;
-    private ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();
-
-    private static class Mapping {
-
-        private final String name;
-        private final ArrayList<Integer> triggers = new ArrayList<Integer>();
-        private final ArrayList<InputListener> listeners = new ArrayList<InputListener>();
-
-        public Mapping(String name) {
-            this.name = name;
-        }
-    }
-
-    /**
-     * Initializes the InputManager.
-     * 
-     * <p>This should only be called internally in {@link Application}.
-     *
-     * @param mouse
-     * @param keys
-     * @param joystick
-     * @param touch
-     * @throws IllegalArgumentException If either mouseInput or keyInput are null.
-     */
-    public InputManager(MouseInput mouse, KeyInput keys, JoyInput joystick, TouchInput touch) {
-        if (keys == null || mouse == null) {
-            throw new NullPointerException("Mouse or keyboard cannot be null");
-        }
-
-        this.keys = keys;
-        this.mouse = mouse;
-        this.joystick = joystick;
-        this.touch = touch;
-
-        keys.setInputListener(this);
-        mouse.setInputListener(this);
-        if (joystick != null) {
-            joystick.setInputListener(this);
-            joysticks = joystick.loadJoysticks(this);
-        }
-        if (touch != null) {
-            touch.setInputListener(this);
-        }
-
-        firstTime = keys.getInputTimeNanos();
-    }
-
-    private void invokeActions(int hash, boolean pressed) {
-        ArrayList<Mapping> maps = bindings.get(hash);
-        if (maps == null) {
-            return;
-        }
-
-        int size = maps.size();
-        for (int i = size - 1; i >= 0; i--) {
-            Mapping mapping = maps.get(i);
-            ArrayList<InputListener> listeners = mapping.listeners;
-            int listenerSize = listeners.size();
-            for (int j = listenerSize - 1; j >= 0; j--) {
-                InputListener listener = listeners.get(j);
-                if (listener instanceof ActionListener) {
-                    ((ActionListener) listener).onAction(mapping.name, pressed, frameTPF);
-                }
-            }
-        }
-    }
-
-    private float computeAnalogValue(long timeDelta) {
-        if (safeMode || frameDelta == 0) {
-            return 1f;
-        } else {
-            return FastMath.clamp((float) timeDelta / (float) frameDelta, 0, 1);
-        }
-    }
-
-    private void invokeTimedActions(int hash, long time, boolean pressed) {
-        if (!bindings.containsKey(hash)) {
-            return;
-        }
-
-        if (pressed) {
-            pressedButtons.put(hash, time);
-        } else {
-            Long pressTimeObj = pressedButtons.remove(hash);
-            if (pressTimeObj == null) {
-                return; // under certain circumstances it can be null, ignore
-            }                        // the event then.
-
-            long pressTime = pressTimeObj;
-            long lastUpdate = lastLastUpdateTime;
-            long releaseTime = time;
-            long timeDelta = releaseTime - Math.max(pressTime, lastUpdate);
-
-            if (timeDelta > 0) {
-                invokeAnalogs(hash, computeAnalogValue(timeDelta), false);
-            }
-        }
-    }
-
-    private void invokeUpdateActions() {
-        for (Entry<Long> pressedButton : pressedButtons) {
-            int hash = pressedButton.getKey();
-
-            long pressTime = pressedButton.getValue();
-            long timeDelta = lastUpdateTime - Math.max(lastLastUpdateTime, pressTime);
-
-            if (timeDelta > 0) {
-                invokeAnalogs(hash, computeAnalogValue(timeDelta), false);
-            }
-        }
-
-        for (Entry<Float> axisValue : axisValues) {
-            int hash = axisValue.getKey();
-            float value = axisValue.getValue();
-            invokeAnalogs(hash, value * frameTPF, true);
-        }
-    }
-
-    private void invokeAnalogs(int hash, float value, boolean isAxis) {
-        ArrayList<Mapping> maps = bindings.get(hash);
-        if (maps == null) {
-            return;
-        }
-
-        if (!isAxis) {
-            value *= frameTPF;
-        }
-
-        int size = maps.size();
-        for (int i = size - 1; i >= 0; i--) {
-            Mapping mapping = maps.get(i);
-            ArrayList<InputListener> listeners = mapping.listeners;
-            int listenerSize = listeners.size();
-            for (int j = listenerSize - 1; j >= 0; j--) {
-                InputListener listener = listeners.get(j);
-                if (listener instanceof AnalogListener) {
-                    // NOTE: multiply by TPF for any button bindings
-                    ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF);
-                }
-            }
-        }
-    }
-
-    private void invokeAnalogsAndActions(int hash, float value, boolean applyTpf) {
-        if (value < axisDeadZone) {
-            invokeAnalogs(hash, value, !applyTpf);
-            return;
-        }
-
-        ArrayList<Mapping> maps = bindings.get(hash);
-        if (maps == null) {
-            return;
-        }
-
-        boolean valueChanged = !axisValues.containsKey(hash);
-        if (applyTpf) {
-            value *= frameTPF;
-        }
-
-        int size = maps.size();
-        for (int i = size - 1; i >= 0; i--) {
-            Mapping mapping = maps.get(i);
-            ArrayList<InputListener> listeners = mapping.listeners;
-            int listenerSize = listeners.size();
-            for (int j = listenerSize - 1; j >= 0; j--) {
-                InputListener listener = listeners.get(j);
-
-                if (listener instanceof ActionListener && valueChanged) {
-                    ((ActionListener) listener).onAction(mapping.name, true, frameTPF);
-                }
-
-                if (listener instanceof AnalogListener) {
-                    ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF);
-                }
-
-            }
-        }
-    }
-
-    /**
-     * Callback from RawInputListener. Do not use.
-     */
-    public void beginInput() {
-    }
-
-    /**
-     * Callback from RawInputListener. Do not use.
-     */
-    public void endInput() {
-    }
-
-    private void onJoyAxisEventQueued(JoyAxisEvent evt) {
-//        for (int i = 0; i < rawListeners.size(); i++){
-//            rawListeners.get(i).onJoyAxisEvent(evt);
-//        }
-
-        int joyId = evt.getJoyIndex();
-        int axis = evt.getAxisIndex();
-        float value = evt.getValue();
-        if (value < axisDeadZone && value > -axisDeadZone) {
-            int hash1 = JoyAxisTrigger.joyAxisHash(joyId, axis, true);
-            int hash2 = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
-
-            Float val1 = axisValues.get(hash1);
-            Float val2 = axisValues.get(hash2);
-
-            if (val1 != null && val1.floatValue() > axisDeadZone) {
-                invokeActions(hash1, false);
-            }
-            if (val2 != null && val2.floatValue() > axisDeadZone) {
-                invokeActions(hash2, false);
-            }
-
-            axisValues.remove(hash1);
-            axisValues.remove(hash2);
-
-        } else if (value < 0) {
-            int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, true);
-            int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
-            invokeAnalogsAndActions(hash, -value, true);
-            axisValues.put(hash, -value);
-            axisValues.remove(otherHash);
-        } else {
-            int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
-            int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, true);
-            invokeAnalogsAndActions(hash, value, true);
-            axisValues.put(hash, value);
-            axisValues.remove(otherHash);
-        }
-    }
-
-    /**
-     * Callback from RawInputListener. Do not use.
-     */
-    public void onJoyAxisEvent(JoyAxisEvent evt) {
-        if (!eventsPermitted) {
-            throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time.");
-        }
-
-        inputQueue.add(evt);
-    }
-
-    private void onJoyButtonEventQueued(JoyButtonEvent evt) {
-//        for (int i = 0; i < rawListeners.size(); i++){
-//            rawListeners.get(i).onJoyButtonEvent(evt);
-//        }
-
-        int hash = JoyButtonTrigger.joyButtonHash(evt.getJoyIndex(), evt.getButtonIndex());
-        invokeActions(hash, evt.isPressed());
-        invokeTimedActions(hash, evt.getTime(), evt.isPressed());
-    }
-
-    /**
-     * Callback from RawInputListener. Do not use.
-     */
-    public void onJoyButtonEvent(JoyButtonEvent evt) {
-        if (!eventsPermitted) {
-            throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time.");
-        }
-
-        inputQueue.add(evt);
-    }
-
-    private void onMouseMotionEventQueued(MouseMotionEvent evt) {
-//        for (int i = 0; i < rawListeners.size(); i++){
-//            rawListeners.get(i).onMouseMotionEvent(evt);
-//        }
-
-        if (evt.getDX() != 0) {
-            float val = Math.abs(evt.getDX()) / 1024f;
-            invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_X, evt.getDX() < 0), val, false);
-        }
-        if (evt.getDY() != 0) {
-            float val = Math.abs(evt.getDY()) / 1024f;
-            invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_Y, evt.getDY() < 0), val, false);
-        }
-        if (evt.getDeltaWheel() != 0) {
-            float val = Math.abs(evt.getDeltaWheel()) / 100f;
-            invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_WHEEL, evt.getDeltaWheel() < 0), val, false);
-        }
-    }
-
-    /**
-     * Callback from RawInputListener. Do not use.
-     */
-    public void onMouseMotionEvent(MouseMotionEvent evt) {
-        if (!eventsPermitted) {
-            throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");
-        }
-
-        cursorPos.set(evt.getX(), evt.getY());
-        inputQueue.add(evt);
-    }
-
-    private void onMouseButtonEventQueued(MouseButtonEvent evt) {
-        int hash = MouseButtonTrigger.mouseButtonHash(evt.getButtonIndex());
-        invokeActions(hash, evt.isPressed());
-        invokeTimedActions(hash, evt.getTime(), evt.isPressed());
-    }
-
-    /**
-     * Callback from RawInputListener. Do not use.
-     */
-    public void onMouseButtonEvent(MouseButtonEvent evt) {
-        if (!eventsPermitted) {
-            throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");
-        }
-        //updating cursor pos on click, so that non android touch events can properly update cursor position.
-        cursorPos.set(evt.getX(), evt.getY());
-        inputQueue.add(evt);
-    }
-
-    private void onKeyEventQueued(KeyInputEvent evt) {
-        if (evt.isRepeating()) {
-            return; // repeat events not used for bindings
-        }
-        
-        int hash = KeyTrigger.keyHash(evt.getKeyCode());
-        invokeActions(hash, evt.isPressed());
-        invokeTimedActions(hash, evt.getTime(), evt.isPressed());
-    }
-
-    /**
-     * Callback from RawInputListener. Do not use.
-     */
-    public void onKeyEvent(KeyInputEvent evt) {
-        if (!eventsPermitted) {
-            throw new UnsupportedOperationException("KeyInput has raised an event at an illegal time.");
-        }
-
-        inputQueue.add(evt);
-    }
-
-    /**
-     * Set the deadzone for joystick axes.
-     * 
-     * <p>{@link ActionListener#onAction(java.lang.String, boolean, float) }
-     * events will only be raised if the joystick axis value is greater than
-     * the <code>deadZone</code>.
-     * 
-     * @param deadZone the deadzone for joystick axes. 
-     */
-    public void setAxisDeadZone(float deadZone) {
-        this.axisDeadZone = deadZone;
-    }
-
-    /**
-     * Returns the deadzone for joystick axes.
-     * 
-     * @return the deadzone for joystick axes.
-     */
-    public float getAxisDeadZone() {
-        return axisDeadZone;
-    }
-    
-    /**
-     * Adds a new listener to receive events on the given mappings.
-     * 
-     * <p>The given InputListener will be registered to receive events
-     * on the specified mapping names. When a mapping raises an event, the
-     * listener will have its appropriate method invoked, either
-     * {@link ActionListener#onAction(java.lang.String, boolean, float) }
-     * or {@link AnalogListener#onAnalog(java.lang.String, float, float) }
-     * depending on which interface the <code>listener</code> implements. 
-     * If the listener implements both interfaces, then it will receive the
-     * appropriate event for each method.
-     * 
-     * @param listener The listener to register to receive input events.
-     * @param mappingNames The mapping names which the listener will receive
-     * events from.
-     * 
-     * @see InputManager#removeListener(com.jme3.input.controls.InputListener) 
-     */
-    public void addListener(InputListener listener, String... mappingNames) {
-        for (String mappingName : mappingNames) {
-            Mapping mapping = mappings.get(mappingName);
-            if (mapping == null) {
-                mapping = new Mapping(mappingName);
-                mappings.put(mappingName, mapping);
-            }
-            if (!mapping.listeners.contains(listener)) {
-                mapping.listeners.add(listener);
-            }
-        }
-    }
-
-    /**
-     * Removes a listener from receiving events.
-     * 
-     * <p>This will unregister the listener from any mappings that it
-     * was previously registered with via 
-     * {@link InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) }.
-     * 
-     * @param listener The listener to unregister.
-     * 
-     * @see InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) 
-     */
-    public void removeListener(InputListener listener) {
-        for (Mapping mapping : mappings.values()) {
-            mapping.listeners.remove(listener);
-        }
-    }
-
-    /**
-     * Create a new mapping to the given triggers.
-     * 
-     * <p>
-     * The given mapping will be assigned to the given triggers, when
-     * any of the triggers given raise an event, the listeners
-     * registered to the mappings will receive appropriate events.
-     * 
-     * @param mappingName The mapping name to assign.
-     * @param triggers The triggers to which the mapping is to be registered.
-     * 
-     * @see InputManager#deleteMapping(java.lang.String) 
-     */
-    public void addMapping(String mappingName, Trigger... triggers) {
-        Mapping mapping = mappings.get(mappingName);
-        if (mapping == null) {
-            mapping = new Mapping(mappingName);
-            mappings.put(mappingName, mapping);
-        }
-
-        for (Trigger trigger : triggers) {
-            int hash = trigger.triggerHashCode();
-            ArrayList<Mapping> names = bindings.get(hash);
-            if (names == null) {
-                names = new ArrayList<Mapping>();
-                bindings.put(hash, names);
-            }
-            if (!names.contains(mapping)) {
-                names.add(mapping);
-                mapping.triggers.add(hash);
-            } else {
-                logger.log(Level.WARNING, "Attempted to add mapping \"{0}\" twice to trigger.", mappingName);
-            }
-        }
-    }
-
-    /**
-     * Returns true if this InputManager has a mapping registered
-     * for the given mappingName.
-     *
-     * @param mappingName The mapping name to check.
-     *
-     * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[]) 
-     * @see InputManager#deleteMapping(java.lang.String) 
-     */ 
-    public boolean hasMapping(String mappingName) {
-        return mappings.containsKey(mappingName);
-    }
-    
-    /**
-     * Deletes a mapping from receiving trigger events.
-     * 
-     * <p>
-     * The given mapping will no longer be assigned to receive trigger
-     * events.
-     * 
-     * @param mappingName The mapping name to unregister.
-     * 
-     * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[]) 
-     */
-    public void deleteMapping(String mappingName) {
-        Mapping mapping = mappings.remove(mappingName);
-        if (mapping == null) {
-            throw new IllegalArgumentException("Cannot find mapping: " + mappingName);
-        }
-
-        ArrayList<Integer> triggers = mapping.triggers;
-        for (int i = triggers.size() - 1; i >= 0; i--) {
-            int hash = triggers.get(i);
-            ArrayList<Mapping> maps = bindings.get(hash);
-            maps.remove(mapping);
-        }
-    }
-
-    /**
-     * Deletes a specific trigger registered to a mapping.
-     * 
-     * <p>
-     * The given mapping will no longer receive events raised by the 
-     * trigger.
-     * 
-     * @param mappingName The mapping name to cease receiving events from the 
-     * trigger.
-     * @param trigger The trigger to no longer invoke events on the mapping.
-     */
-    public void deleteTrigger(String mappingName, Trigger trigger) {
-        Mapping mapping = mappings.get(mappingName);
-        if (mapping == null) {
-            throw new IllegalArgumentException("Cannot find mapping: " + mappingName);
-        }
-
-        ArrayList<Mapping> maps = bindings.get(trigger.triggerHashCode());
-        maps.remove(mapping);
-
-    }
-
-    /**
-     * Clears all the input mappings from this InputManager. 
-     * Consequently, also clears all of the
-     * InputListeners as well.
-     */
-    public void clearMappings() {
-        mappings.clear();
-        bindings.clear();
-        reset();
-    }
-
-    /**
-     * Do not use.
-     * Called to reset pressed keys or buttons when focus is restored.
-     */
-    public void reset() {
-        pressedButtons.clear();
-        axisValues.clear();
-    }
-
-    /**
-     * Returns whether the mouse cursor is visible or not.
-     * 
-     * <p>By default the cursor is visible.
-     * 
-     * @return whether the mouse cursor is visible or not.
-     * 
-     * @see InputManager#setCursorVisible(boolean) 
-     */
-    public boolean isCursorVisible() {
-        return mouseVisible;
-    }
-
-    /**
-     * Set whether the mouse cursor should be visible or not.
-     * 
-     * @param visible whether the mouse cursor should be visible or not.
-     */
-    public void setCursorVisible(boolean visible) {
-        if (mouseVisible != visible) {
-            mouseVisible = visible;
-            mouse.setCursorVisible(mouseVisible);
-        }
-    }
-
-    /**
-     * Returns the current cursor position. The position is relative to the
-     * bottom-left of the screen and is in pixels.
-     * 
-     * @return the current cursor position
-     */
-    public Vector2f getCursorPosition() {
-        return cursorPos;
-    }
-
-    /**
-     * Returns an array of all joysticks installed on the system.
-     * 
-     * @return an array of all joysticks installed on the system.
-     */
-    public Joystick[] getJoysticks() {
-        return joysticks;
-    }
-
-    /**
-     * Adds a {@link RawInputListener} to receive raw input events.
-     * 
-     * <p>
-     * Any raw input listeners registered to this <code>InputManager</code>
-     * will receive raw input events first, before they get handled
-     * by the <code>InputManager</code> itself. The listeners are 
-     * each processed in the order they were added, e.g. FIFO.
-     * <p>
-     * If a raw input listener has handled the event and does not wish
-     * other listeners down the list to process the event, it may set the
-     * {@link InputEvent#setConsumed() consumed flag} to indicate the 
-     * event was consumed and shouldn't be processed any further.
-     * The listener may do this either at each of the event callbacks 
-     * or at the {@link RawInputListener#endInput() } method.
-     * 
-     * @param listener A listener to receive raw input events.
-     * 
-     * @see RawInputListener
-     */
-    public void addRawInputListener(RawInputListener listener) {
-        rawListeners.add(listener);
-        rawListenerArray = null;
-    }
-
-    /**
-     * Removes a {@link RawInputListener} so that it no longer
-     * receives raw input events.
-     * 
-     * @param listener The listener to cease receiving raw input events.
-     * 
-     * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener) 
-     */
-    public void removeRawInputListener(RawInputListener listener) {
-        rawListeners.remove(listener);
-        rawListenerArray = null;
-    }
-
-    /**
-     * Clears all {@link RawInputListener}s.
-     * 
-     * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener) 
-     */
-    public void clearRawInputListeners() {
-        rawListeners.clear();
-        rawListenerArray = null;
-    }
-
-    private RawInputListener[] getRawListenerArray() {
-        if (rawListenerArray == null) 
-            rawListenerArray = rawListeners.toArray(new RawInputListener[rawListeners.size()]);
-        return rawListenerArray;
-    }
-    
-    /**
-     * Enable simulation of mouse events. Used for touchscreen input only.
-     * 
-     * @param value True to enable simulation of mouse events
-     */
-    public void setSimulateMouse(boolean value) {
-        if (touch != null) {
-            touch.setSimulateMouse(value);
-        }
-    }
-    
-    /**
-     * Enable simulation of keyboard events. Used for touchscreen input only.
-     * 
-     * @param value True to enable simulation of keyboard events
-     */
-    public void setSimulateKeyboard(boolean value) {
-        if (touch != null) {
-            touch.setSimulateKeyboard(value);
-        }
-    }
-
-    private void processQueue() {
-        int queueSize = inputQueue.size();
-        RawInputListener[] array = getRawListenerArray();
- 
-        for (RawInputListener listener : array) {
-            listener.beginInput();
-
-            for (int j = 0; j < queueSize; j++) {
-                InputEvent event = inputQueue.get(j);
-                if (event.isConsumed()) {
-                    continue;
-                }
-
-                if (event instanceof MouseMotionEvent) {
-                    listener.onMouseMotionEvent((MouseMotionEvent) event);
-                } else if (event instanceof KeyInputEvent) {
-                    listener.onKeyEvent((KeyInputEvent) event);
-                } else if (event instanceof MouseButtonEvent) {
-                    listener.onMouseButtonEvent((MouseButtonEvent) event);
-                } else if (event instanceof JoyAxisEvent) {
-                    listener.onJoyAxisEvent((JoyAxisEvent) event);
-                } else if (event instanceof JoyButtonEvent) {
-                    listener.onJoyButtonEvent((JoyButtonEvent) event);
-                } else if (event instanceof TouchEvent) {
-                    listener.onTouchEvent((TouchEvent) event);
-                } else {
-                    assert false;
-                }
-            }
-
-            listener.endInput();
-        }
-
-        for (int i = 0; i < queueSize; i++) {
-            InputEvent event = inputQueue.get(i);
-            if (event.isConsumed()) {
-                continue;
-            }
-
-            if (event instanceof MouseMotionEvent) {
-                onMouseMotionEventQueued((MouseMotionEvent) event);
-            } else if (event instanceof KeyInputEvent) {
-                onKeyEventQueued((KeyInputEvent) event);
-            } else if (event instanceof MouseButtonEvent) {
-                onMouseButtonEventQueued((MouseButtonEvent) event);
-            } else if (event instanceof JoyAxisEvent) {
-                onJoyAxisEventQueued((JoyAxisEvent) event);
-            } else if (event instanceof JoyButtonEvent) {
-                onJoyButtonEventQueued((JoyButtonEvent) event);
-            } else if (event instanceof TouchEvent) {
-                onTouchEventQueued((TouchEvent) event);
-            } else {
-                assert false;
-            }
-            // larynx, 2011.06.10 - flag event as reusable because
-            // the android input uses a non-allocating ringbuffer which
-            // needs to know when the event is not anymore in inputQueue
-            // and therefor can be reused.
-            event.setConsumed();
-        }
-
-        inputQueue.clear();
-    }
-
-    /**
-     * Updates the <code>InputManager</code>. 
-     * This will query current input devices and send
-     * appropriate events to registered listeners.
-     *
-     * @param tpf Time per frame value.
-     */
-    public void update(float tpf) {
-        frameTPF = tpf;
-        
-        // Activate safemode if the TPF value is so small
-        // that rounding errors are inevitable
-        safeMode = tpf < 0.015f;
-        
-        long currentTime = keys.getInputTimeNanos();
-        frameDelta = currentTime - lastUpdateTime;
-
-        eventsPermitted = true;
-
-        keys.update();
-        mouse.update();
-        if (joystick != null) {
-            joystick.update();
-        }
-        if (touch != null) {
-            touch.update();
-        }
-
-        eventsPermitted = false;
-
-        processQueue();
-        invokeUpdateActions();
-
-        lastLastUpdateTime = lastUpdateTime;
-        lastUpdateTime = currentTime;
-    }
-
-    /**
-     * Dispatches touch events to touch listeners
-     * @param evt The touch event to be dispatched to all onTouch listeners
-     */
-    public void onTouchEventQueued(TouchEvent evt) { 
-        ArrayList<Mapping> maps = bindings.get(TouchTrigger.touchHash(evt.getKeyCode()));
-        if (maps == null) {
-            return;
-        }
-
-        int size = maps.size();
-        for (int i = size - 1; i >= 0; i--) {
-            Mapping mapping = maps.get(i);
-            ArrayList<InputListener> listeners = mapping.listeners;
-            int listenerSize = listeners.size();
-            for (int j = listenerSize - 1; j >= 0; j--) {
-                InputListener listener = listeners.get(j);
-                if (listener instanceof TouchListener) {
-                    ((TouchListener) listener).onTouch(mapping.name, evt, frameTPF); 
-                }
-            }
-        }               
-    }
-    
-    /**
-     * Callback from RawInputListener. Do not use.
-     */
-    @Override
-    public void onTouchEvent(TouchEvent evt) {
-        if (!eventsPermitted) {
-            throw new UnsupportedOperationException("TouchInput has raised an event at an illegal time.");
-        }
-        inputQueue.add(evt);         
-    }
-}
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.input;
+
+import com.jme3.app.Application;
+import com.jme3.input.controls.*;
+import com.jme3.input.event.*;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector2f;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * The <code>InputManager</code> is responsible for converting input events
+ * received from the Key, Mouse and Joy Input implementations into an
+ * abstract, input device independent representation that user code can use.
+ * <p>
+ * By default an <code>InputManager</code> is included with every Application instance for use
+ * in user code to query input, unless the Application is created as headless
+ * or with input explicitly disabled.
+ * <p>
+ * The input manager has two concepts, a {@link Trigger} and a mapping.
+ * A trigger represents a specific input trigger, such as a key button, 
+ * or a mouse axis. A mapping represents a link onto one or several triggers, 
+ * when the appropriate trigger is activated (e.g. a key is pressed), the 
+ * mapping will be invoked. Any listeners registered to receive an event
+ * from the mapping will have an event raised.
+ * <p>
+ * There are two types of events that {@link InputListener input listeners}
+ * can receive, one is {@link ActionListener#onAction(java.lang.String, boolean, float) action}
+ * events and another is {@link AnalogListener#onAnalog(java.lang.String, float, float) analog}
+ * events. 
+ * <p>
+ * <code>onAction</code> events are raised when the specific input
+ * activates or deactivates. For a digital input such as key press, the <code>onAction()</code>
+ * event will be raised with the <code>isPressed</code> argument equal to true,
+ * when the key is released, <code>onAction</code> is called again but this time
+ * with the <code>isPressed</code> argument set to false.
+ * For analog inputs, the <code>onAction</code> method will be called any time
+ * the input is non-zero, however an exception to this is for joystick axis inputs,
+ * which are only called when the input is above the {@link InputManager#setAxisDeadZone(float) dead zone}.
+ * <p>
+ * <code>onAnalog</code> events are raised every frame while the input is activated.
+ * For digital inputs, every frame that the input is active will cause the
+ * <code>onAnalog</code> method to be called, the argument <code>value</code>
+ * argument will equal to the frame's time per frame (TPF) value but only
+ * for digital inputs. For analog inputs however, the <code>value</code> argument
+ * will equal the actual analog value.
+ */
+public class InputManager implements RawInputListener {
+
+    private static final Logger logger = Logger.getLogger(InputManager.class.getName());
+    private final KeyInput keys;
+    private final MouseInput mouse;
+    private final JoyInput joystick;
+    private final TouchInput touch;
+    private float frameTPF;
+    private long lastLastUpdateTime = 0;
+    private long lastUpdateTime = 0;
+    private long frameDelta = 0;
+    private long firstTime = 0;
+    private boolean eventsPermitted = false;
+    private boolean mouseVisible = true;
+    private boolean safeMode = false;
+    private float axisDeadZone = 0.05f;
+    private Vector2f cursorPos = new Vector2f();
+    private Joystick[] joysticks;
+    private final IntMap<ArrayList<Mapping>> bindings = new IntMap<ArrayList<Mapping>>();
+    private final HashMap<String, Mapping> mappings = new HashMap<String, Mapping>();
+    private final IntMap<Long> pressedButtons = new IntMap<Long>();
+    private final IntMap<Float> axisValues = new IntMap<Float>();
+    private ArrayList<RawInputListener> rawListeners = new ArrayList<RawInputListener>();
+    private RawInputListener[] rawListenerArray = null;
+    private ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();
+
+    private static class Mapping {
+
+        private final String name;
+        private final ArrayList<Integer> triggers = new ArrayList<Integer>();
+        private final ArrayList<InputListener> listeners = new ArrayList<InputListener>();
+
+        public Mapping(String name) {
+            this.name = name;
+        }
+    }
+
+    /**
+     * Initializes the InputManager.
+     * 
+     * <p>This should only be called internally in {@link Application}.
+     *
+     * @param mouse
+     * @param keys
+     * @param joystick
+     * @param touch
+     * @throws IllegalArgumentException If either mouseInput or keyInput are null.
+     */
+    public InputManager(MouseInput mouse, KeyInput keys, JoyInput joystick, TouchInput touch) {
+        if (keys == null || mouse == null) {
+            throw new NullPointerException("Mouse or keyboard cannot be null");
+        }
+
+        this.keys = keys;
+        this.mouse = mouse;
+        this.joystick = joystick;
+        this.touch = touch;
+
+        keys.setInputListener(this);
+        mouse.setInputListener(this);
+        if (joystick != null) {
+            joystick.setInputListener(this);
+            joysticks = joystick.loadJoysticks(this);
+        }
+        if (touch != null) {
+            touch.setInputListener(this);
+        }
+
+        firstTime = keys.getInputTimeNanos();
+    }
+
+    private void invokeActions(int hash, boolean pressed) {
+        ArrayList<Mapping> maps = bindings.get(hash);
+        if (maps == null) {
+            return;
+        }
+
+        int size = maps.size();
+        for (int i = size - 1; i >= 0; i--) {
+            Mapping mapping = maps.get(i);
+            ArrayList<InputListener> listeners = mapping.listeners;
+            int listenerSize = listeners.size();
+            for (int j = listenerSize - 1; j >= 0; j--) {
+                InputListener listener = listeners.get(j);
+                if (listener instanceof ActionListener) {
+                    ((ActionListener) listener).onAction(mapping.name, pressed, frameTPF);
+                }
+            }
+        }
+    }
+
+    private float computeAnalogValue(long timeDelta) {
+        if (safeMode || frameDelta == 0) {
+            return 1f;
+        } else {
+            return FastMath.clamp((float) timeDelta / (float) frameDelta, 0, 1);
+        }
+    }
+
+    private void invokeTimedActions(int hash, long time, boolean pressed) {
+        if (!bindings.containsKey(hash)) {
+            return;
+        }
+
+        if (pressed) {
+            pressedButtons.put(hash, time);
+        } else {
+            Long pressTimeObj = pressedButtons.remove(hash);
+            if (pressTimeObj == null) {
+                return; // under certain circumstances it can be null, ignore
+            }                        // the event then.
+
+            long pressTime = pressTimeObj;
+            long lastUpdate = lastLastUpdateTime;
+            long releaseTime = time;
+            long timeDelta = releaseTime - Math.max(pressTime, lastUpdate);
+
+            if (timeDelta > 0) {
+                invokeAnalogs(hash, computeAnalogValue(timeDelta), false);
+            }
+        }
+    }
+
+    private void invokeUpdateActions() {
+        for (Entry<Long> pressedButton : pressedButtons) {
+            int hash = pressedButton.getKey();
+
+            long pressTime = pressedButton.getValue();
+            long timeDelta = lastUpdateTime - Math.max(lastLastUpdateTime, pressTime);
+
+            if (timeDelta > 0) {
+                invokeAnalogs(hash, computeAnalogValue(timeDelta), false);
+            }
+        }
+
+        for (Entry<Float> axisValue : axisValues) {
+            int hash = axisValue.getKey();
+            float value = axisValue.getValue();
+            invokeAnalogs(hash, value * frameTPF, true);
+        }
+    }
+
+    private void invokeAnalogs(int hash, float value, boolean isAxis) {
+        ArrayList<Mapping> maps = bindings.get(hash);
+        if (maps == null) {
+            return;
+        }
+
+        if (!isAxis) {
+            value *= frameTPF;
+        }
+
+        int size = maps.size();
+        for (int i = size - 1; i >= 0; i--) {
+            Mapping mapping = maps.get(i);
+            ArrayList<InputListener> listeners = mapping.listeners;
+            int listenerSize = listeners.size();
+            for (int j = listenerSize - 1; j >= 0; j--) {
+                InputListener listener = listeners.get(j);
+                if (listener instanceof AnalogListener) {
+                    // NOTE: multiply by TPF for any button bindings
+                    ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF);
+                }
+            }
+        }
+    }
+
+    private void invokeAnalogsAndActions(int hash, float value, boolean applyTpf) {
+        if (value < axisDeadZone) {
+            invokeAnalogs(hash, value, !applyTpf);
+            return;
+        }
+
+        ArrayList<Mapping> maps = bindings.get(hash);
+        if (maps == null) {
+            return;
+        }
+
+        boolean valueChanged = !axisValues.containsKey(hash);
+        if (applyTpf) {
+            value *= frameTPF;
+        }
+
+        int size = maps.size();
+        for (int i = size - 1; i >= 0; i--) {
+            Mapping mapping = maps.get(i);
+            ArrayList<InputListener> listeners = mapping.listeners;
+            int listenerSize = listeners.size();
+            for (int j = listenerSize - 1; j >= 0; j--) {
+                InputListener listener = listeners.get(j);
+
+                if (listener instanceof ActionListener && valueChanged) {
+                    ((ActionListener) listener).onAction(mapping.name, true, frameTPF);
+                }
+
+                if (listener instanceof AnalogListener) {
+                    ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF);
+                }
+
+            }
+        }
+    }
+
+    /**
+     * Callback from RawInputListener. Do not use.
+     */
+    public void beginInput() {
+    }
+
+    /**
+     * Callback from RawInputListener. Do not use.
+     */
+    public void endInput() {
+    }
+
+    private void onJoyAxisEventQueued(JoyAxisEvent evt) {
+//        for (int i = 0; i < rawListeners.size(); i++){
+//            rawListeners.get(i).onJoyAxisEvent(evt);
+//        }
+
+        int joyId = evt.getJoyIndex();
+        int axis = evt.getAxisIndex();
+        float value = evt.getValue();
+        if (value < axisDeadZone && value > -axisDeadZone) {
+            int hash1 = JoyAxisTrigger.joyAxisHash(joyId, axis, true);
+            int hash2 = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
+
+            Float val1 = axisValues.get(hash1);
+            Float val2 = axisValues.get(hash2);
+
+            if (val1 != null && val1.floatValue() > axisDeadZone) {
+                invokeActions(hash1, false);
+            }
+            if (val2 != null && val2.floatValue() > axisDeadZone) {
+                invokeActions(hash2, false);
+            }
+
+            axisValues.remove(hash1);
+            axisValues.remove(hash2);
+
+        } else if (value < 0) {
+            int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, true);
+            int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
+            invokeAnalogsAndActions(hash, -value, true);
+            axisValues.put(hash, -value);
+            axisValues.remove(otherHash);
+        } else {
+            int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
+            int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, true);
+            invokeAnalogsAndActions(hash, value, true);
+            axisValues.put(hash, value);
+            axisValues.remove(otherHash);
+        }
+    }
+
+    /**
+     * Callback from RawInputListener. Do not use.
+     */
+    public void onJoyAxisEvent(JoyAxisEvent evt) {
+        if (!eventsPermitted) {
+            throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time.");
+        }
+
+        inputQueue.add(evt);
+    }
+
+    private void onJoyButtonEventQueued(JoyButtonEvent evt) {
+//        for (int i = 0; i < rawListeners.size(); i++){
+//            rawListeners.get(i).onJoyButtonEvent(evt);
+//        }
+
+        int hash = JoyButtonTrigger.joyButtonHash(evt.getJoyIndex(), evt.getButtonIndex());
+        invokeActions(hash, evt.isPressed());
+        invokeTimedActions(hash, evt.getTime(), evt.isPressed());
+    }
+
+    /**
+     * Callback from RawInputListener. Do not use.
+     */
+    public void onJoyButtonEvent(JoyButtonEvent evt) {
+        if (!eventsPermitted) {
+            throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time.");
+        }
+
+        inputQueue.add(evt);
+    }
+
+    private void onMouseMotionEventQueued(MouseMotionEvent evt) {
+//        for (int i = 0; i < rawListeners.size(); i++){
+//            rawListeners.get(i).onMouseMotionEvent(evt);
+//        }
+
+        if (evt.getDX() != 0) {
+            float val = Math.abs(evt.getDX()) / 1024f;
+            invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_X, evt.getDX() < 0), val, false);
+        }
+        if (evt.getDY() != 0) {
+            float val = Math.abs(evt.getDY()) / 1024f;
+            invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_Y, evt.getDY() < 0), val, false);
+        }
+        if (evt.getDeltaWheel() != 0) {
+            float val = Math.abs(evt.getDeltaWheel()) / 100f;
+            invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_WHEEL, evt.getDeltaWheel() < 0), val, false);
+        }
+    }
+
+    /**
+     * Callback from RawInputListener. Do not use.
+     */
+    public void onMouseMotionEvent(MouseMotionEvent evt) {
+        if (!eventsPermitted) {
+            throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");
+        }
+
+        cursorPos.set(evt.getX(), evt.getY());
+        inputQueue.add(evt);
+    }
+
+    private void onMouseButtonEventQueued(MouseButtonEvent evt) {
+        int hash = MouseButtonTrigger.mouseButtonHash(evt.getButtonIndex());
+        invokeActions(hash, evt.isPressed());
+        invokeTimedActions(hash, evt.getTime(), evt.isPressed());
+    }
+
+    /**
+     * Callback from RawInputListener. Do not use.
+     */
+    public void onMouseButtonEvent(MouseButtonEvent evt) {
+        if (!eventsPermitted) {
+            throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");
+        }
+        //updating cursor pos on click, so that non android touch events can properly update cursor position.
+        cursorPos.set(evt.getX(), evt.getY());
+        inputQueue.add(evt);
+    }
+
+    private void onKeyEventQueued(KeyInputEvent evt) {
+        if (evt.isRepeating()) {
+            return; // repeat events not used for bindings
+        }
+        
+        int hash = KeyTrigger.keyHash(evt.getKeyCode());
+        invokeActions(hash, evt.isPressed());
+        invokeTimedActions(hash, evt.getTime(), evt.isPressed());
+    }
+
+    /**
+     * Callback from RawInputListener. Do not use.
+     */
+    public void onKeyEvent(KeyInputEvent evt) {
+        if (!eventsPermitted) {
+            throw new UnsupportedOperationException("KeyInput has raised an event at an illegal time.");
+        }
+
+        inputQueue.add(evt);
+    }
+
+    /**
+     * Set the deadzone for joystick axes.
+     * 
+     * <p>{@link ActionListener#onAction(java.lang.String, boolean, float) }
+     * events will only be raised if the joystick axis value is greater than
+     * the <code>deadZone</code>.
+     * 
+     * @param deadZone the deadzone for joystick axes. 
+     */
+    public void setAxisDeadZone(float deadZone) {
+        this.axisDeadZone = deadZone;
+    }
+
+    /**
+     * Returns the deadzone for joystick axes.
+     * 
+     * @return the deadzone for joystick axes.
+     */
+    public float getAxisDeadZone() {
+        return axisDeadZone;
+    }
+    
+    /**
+     * Adds a new listener to receive events on the given mappings.
+     * 
+     * <p>The given InputListener will be registered to receive events
+     * on the specified mapping names. When a mapping raises an event, the
+     * listener will have its appropriate method invoked, either
+     * {@link ActionListener#onAction(java.lang.String, boolean, float) }
+     * or {@link AnalogListener#onAnalog(java.lang.String, float, float) }
+     * depending on which interface the <code>listener</code> implements. 
+     * If the listener implements both interfaces, then it will receive the
+     * appropriate event for each method.
+     * 
+     * @param listener The listener to register to receive input events.
+     * @param mappingNames The mapping names which the listener will receive
+     * events from.
+     * 
+     * @see InputManager#removeListener(com.jme3.input.controls.InputListener) 
+     */
+    public void addListener(InputListener listener, String... mappingNames) {
+        for (String mappingName : mappingNames) {
+            Mapping mapping = mappings.get(mappingName);
+            if (mapping == null) {
+                mapping = new Mapping(mappingName);
+                mappings.put(mappingName, mapping);
+            }
+            if (!mapping.listeners.contains(listener)) {
+                mapping.listeners.add(listener);
+            }
+        }
+    }
+
+    /**
+     * Removes a listener from receiving events.
+     * 
+     * <p>This will unregister the listener from any mappings that it
+     * was previously registered with via 
+     * {@link InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) }.
+     * 
+     * @param listener The listener to unregister.
+     * 
+     * @see InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) 
+     */
+    public void removeListener(InputListener listener) {
+        for (Mapping mapping : mappings.values()) {
+            mapping.listeners.remove(listener);
+        }
+    }
+
+    /**
+     * Create a new mapping to the given triggers.
+     * 
+     * <p>
+     * The given mapping will be assigned to the given triggers, when
+     * any of the triggers given raise an event, the listeners
+     * registered to the mappings will receive appropriate events.
+     * 
+     * @param mappingName The mapping name to assign.
+     * @param triggers The triggers to which the mapping is to be registered.
+     * 
+     * @see InputManager#deleteMapping(java.lang.String) 
+     */
+    public void addMapping(String mappingName, Trigger... triggers) {
+        Mapping mapping = mappings.get(mappingName);
+        if (mapping == null) {
+            mapping = new Mapping(mappingName);
+            mappings.put(mappingName, mapping);
+        }
+
+        for (Trigger trigger : triggers) {
+            int hash = trigger.triggerHashCode();
+            ArrayList<Mapping> names = bindings.get(hash);
+            if (names == null) {
+                names = new ArrayList<Mapping>();
+                bindings.put(hash, names);
+            }
+            if (!names.contains(mapping)) {
+                names.add(mapping);
+                mapping.triggers.add(hash);
+            } else {
+                logger.log(Level.WARNING, "Attempted to add mapping \"{0}\" twice to trigger.", mappingName);
+            }
+        }
+    }
+
+    /**
+     * Returns true if this InputManager has a mapping registered
+     * for the given mappingName.
+     *
+     * @param mappingName The mapping name to check.
+     *
+     * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[]) 
+     * @see InputManager#deleteMapping(java.lang.String) 
+     */ 
+    public boolean hasMapping(String mappingName) {
+        return mappings.containsKey(mappingName);
+    }
+    
+    /**
+     * Deletes a mapping from receiving trigger events.
+     * 
+     * <p>
+     * The given mapping will no longer be assigned to receive trigger
+     * events.
+     * 
+     * @param mappingName The mapping name to unregister.
+     * 
+     * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[]) 
+     */
+    public void deleteMapping(String mappingName) {
+        Mapping mapping = mappings.remove(mappingName);
+        if (mapping == null) {
+            throw new IllegalArgumentException("Cannot find mapping: " + mappingName);
+        }
+
+        ArrayList<Integer> triggers = mapping.triggers;
+        for (int i = triggers.size() - 1; i >= 0; i--) {
+            int hash = triggers.get(i);
+            ArrayList<Mapping> maps = bindings.get(hash);
+            maps.remove(mapping);
+        }
+    }
+
+    /**
+     * Deletes a specific trigger registered to a mapping.
+     * 
+     * <p>
+     * The given mapping will no longer receive events raised by the 
+     * trigger.
+     * 
+     * @param mappingName The mapping name to cease receiving events from the 
+     * trigger.
+     * @param trigger The trigger to no longer invoke events on the mapping.
+     */
+    public void deleteTrigger(String mappingName, Trigger trigger) {
+        Mapping mapping = mappings.get(mappingName);
+        if (mapping == null) {
+            throw new IllegalArgumentException("Cannot find mapping: " + mappingName);
+        }
+
+        ArrayList<Mapping> maps = bindings.get(trigger.triggerHashCode());
+        maps.remove(mapping);
+
+    }
+
+    /**
+     * Clears all the input mappings from this InputManager. 
+     * Consequently, also clears all of the
+     * InputListeners as well.
+     */
+    public void clearMappings() {
+        mappings.clear();
+        bindings.clear();
+        reset();
+    }
+
+    /**
+     * Do not use.
+     * Called to reset pressed keys or buttons when focus is restored.
+     */
+    public void reset() {
+        pressedButtons.clear();
+        axisValues.clear();
+    }
+
+    /**
+     * Returns whether the mouse cursor is visible or not.
+     * 
+     * <p>By default the cursor is visible.
+     * 
+     * @return whether the mouse cursor is visible or not.
+     * 
+     * @see InputManager#setCursorVisible(boolean) 
+     */
+    public boolean isCursorVisible() {
+        return mouseVisible;
+    }
+
+    /**
+     * Set whether the mouse cursor should be visible or not.
+     * 
+     * @param visible whether the mouse cursor should be visible or not.
+     */
+    public void setCursorVisible(boolean visible) {
+        if (mouseVisible != visible) {
+            mouseVisible = visible;
+            mouse.setCursorVisible(mouseVisible);
+        }
+    }
+
+    /**
+     * Returns the current cursor position. The position is relative to the
+     * bottom-left of the screen and is in pixels.
+     * 
+     * @return the current cursor position
+     */
+    public Vector2f getCursorPosition() {
+        return cursorPos;
+    }
+
+    /**
+     * Returns an array of all joysticks installed on the system.
+     * 
+     * @return an array of all joysticks installed on the system.
+     */
+    public Joystick[] getJoysticks() {
+        return joysticks;
+    }
+
+    /**
+     * Adds a {@link RawInputListener} to receive raw input events.
+     * 
+     * <p>
+     * Any raw input listeners registered to this <code>InputManager</code>
+     * will receive raw input events first, before they get handled
+     * by the <code>InputManager</code> itself. The listeners are 
+     * each processed in the order they were added, e.g. FIFO.
+     * <p>
+     * If a raw input listener has handled the event and does not wish
+     * other listeners down the list to process the event, it may set the
+     * {@link InputEvent#setConsumed() consumed flag} to indicate the 
+     * event was consumed and shouldn't be processed any further.
+     * The listener may do this either at each of the event callbacks 
+     * or at the {@link RawInputListener#endInput() } method.
+     * 
+     * @param listener A listener to receive raw input events.
+     * 
+     * @see RawInputListener
+     */
+    public void addRawInputListener(RawInputListener listener) {
+        rawListeners.add(listener);
+        rawListenerArray = null;
+    }
+
+    /**
+     * Removes a {@link RawInputListener} so that it no longer
+     * receives raw input events.
+     * 
+     * @param listener The listener to cease receiving raw input events.
+     * 
+     * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener) 
+     */
+    public void removeRawInputListener(RawInputListener listener) {
+        rawListeners.remove(listener);
+        rawListenerArray = null;
+    }
+
+    /**
+     * Clears all {@link RawInputListener}s.
+     * 
+     * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener) 
+     */
+    public void clearRawInputListeners() {
+        rawListeners.clear();
+        rawListenerArray = null;
+    }
+
+    private RawInputListener[] getRawListenerArray() {
+        if (rawListenerArray == null) 
+            rawListenerArray = rawListeners.toArray(new RawInputListener[rawListeners.size()]);
+        return rawListenerArray;
+    }
+    
+    /**
+     * Enable simulation of mouse events. Used for touchscreen input only.
+     * 
+     * @param value True to enable simulation of mouse events
+     */
+    public void setSimulateMouse(boolean value) {
+        if (touch != null) {
+            touch.setSimulateMouse(value);
+        }
+    }
+    /**
+     * Returns state of simulation of mouse events. Used for touchscreen input only.
+     *
+     */
+    public boolean getSimulateMouse() {
+        if (touch != null) {
+            return touch.getSimulateMouse();
+        } else {
+            return false;
+        }
+    }
+    
+    /**
+     * Enable simulation of keyboard events. Used for touchscreen input only.
+     * 
+     * @param value True to enable simulation of keyboard events
+     */
+    public void setSimulateKeyboard(boolean value) {
+        if (touch != null) {
+            touch.setSimulateKeyboard(value);
+        }
+    }
+
+    private void processQueue() {
+        int queueSize = inputQueue.size();
+        RawInputListener[] array = getRawListenerArray();
+ 
+        for (RawInputListener listener : array) {
+            listener.beginInput();
+
+            for (int j = 0; j < queueSize; j++) {
+                InputEvent event = inputQueue.get(j);
+                if (event.isConsumed()) {
+                    continue;
+                }
+
+                if (event instanceof MouseMotionEvent) {
+                    listener.onMouseMotionEvent((MouseMotionEvent) event);
+                } else if (event instanceof KeyInputEvent) {
+                    listener.onKeyEvent((KeyInputEvent) event);
+                } else if (event instanceof MouseButtonEvent) {
+                    listener.onMouseButtonEvent((MouseButtonEvent) event);
+                } else if (event instanceof JoyAxisEvent) {
+                    listener.onJoyAxisEvent((JoyAxisEvent) event);
+                } else if (event instanceof JoyButtonEvent) {
+                    listener.onJoyButtonEvent((JoyButtonEvent) event);
+                } else if (event instanceof TouchEvent) {
+                    listener.onTouchEvent((TouchEvent) event);
+                } else {
+                    assert false;
+                }
+            }
+
+            listener.endInput();
+        }
+
+        for (int i = 0; i < queueSize; i++) {
+            InputEvent event = inputQueue.get(i);
+            if (event.isConsumed()) {
+                continue;
+            }
+
+            if (event instanceof MouseMotionEvent) {
+                onMouseMotionEventQueued((MouseMotionEvent) event);
+            } else if (event instanceof KeyInputEvent) {
+                onKeyEventQueued((KeyInputEvent) event);
+            } else if (event instanceof MouseButtonEvent) {
+                onMouseButtonEventQueued((MouseButtonEvent) event);
+            } else if (event instanceof JoyAxisEvent) {
+                onJoyAxisEventQueued((JoyAxisEvent) event);
+            } else if (event instanceof JoyButtonEvent) {
+                onJoyButtonEventQueued((JoyButtonEvent) event);
+            } else if (event instanceof TouchEvent) {
+                onTouchEventQueued((TouchEvent) event);
+            } else {
+                assert false;
+            }
+            // larynx, 2011.06.10 - flag event as reusable because
+            // the android input uses a non-allocating ringbuffer which
+            // needs to know when the event is not anymore in inputQueue
+            // and therefor can be reused.
+            event.setConsumed();
+        }
+
+        inputQueue.clear();
+    }
+
+    /**
+     * Updates the <code>InputManager</code>. 
+     * This will query current input devices and send
+     * appropriate events to registered listeners.
+     *
+     * @param tpf Time per frame value.
+     */
+    public void update(float tpf) {
+        frameTPF = tpf;
+        
+        // Activate safemode if the TPF value is so small
+        // that rounding errors are inevitable
+        safeMode = tpf < 0.015f;
+        
+        long currentTime = keys.getInputTimeNanos();
+        frameDelta = currentTime - lastUpdateTime;
+
+        eventsPermitted = true;
+
+        keys.update();
+        mouse.update();
+        if (joystick != null) {
+            joystick.update();
+        }
+        if (touch != null) {
+            touch.update();
+        }
+
+        eventsPermitted = false;
+
+        processQueue();
+        invokeUpdateActions();
+
+        lastLastUpdateTime = lastUpdateTime;
+        lastUpdateTime = currentTime;
+    }
+
+    /**
+     * Dispatches touch events to touch listeners
+     * @param evt The touch event to be dispatched to all onTouch listeners
+     */
+    public void onTouchEventQueued(TouchEvent evt) { 
+        ArrayList<Mapping> maps = bindings.get(TouchTrigger.touchHash(evt.getKeyCode()));
+        if (maps == null) {
+            return;
+        }
+
+        int size = maps.size();
+        for (int i = size - 1; i >= 0; i--) {
+            Mapping mapping = maps.get(i);
+            ArrayList<InputListener> listeners = mapping.listeners;
+            int listenerSize = listeners.size();
+            for (int j = listenerSize - 1; j >= 0; j--) {
+                InputListener listener = listeners.get(j);
+                if (listener instanceof TouchListener) {
+                    ((TouchListener) listener).onTouch(mapping.name, evt, frameTPF); 
+                }
+            }
+        }               
+    }
+    
+    /**
+     * Callback from RawInputListener. Do not use.
+     */
+    @Override
+    public void onTouchEvent(TouchEvent evt) {
+        if (!eventsPermitted) {
+            throw new UnsupportedOperationException("TouchInput has raised an event at an illegal time.");
+        }
+        inputQueue.add(evt);         
+    }
+}

+ 6 - 0
engine/src/core/com/jme3/input/TouchInput.java

@@ -73,6 +73,12 @@ public interface TouchInput extends Input {
      * @param simulate if mouse events should be generated
      */
     public void setSimulateMouse(boolean simulate);
+    
+    /**
+     * Get if mouse events are generated
+     *
+     */
+    public boolean getSimulateMouse();
 
     /**
      * Set if keyboard events should be generated

+ 235 - 233
engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java

@@ -1,233 +1,235 @@
-/*
- * Copyright (c) 2009-2010 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *   may be used to endorse or promote products derived from this software
- *   without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.jme3.niftygui;
-
-import com.jme3.input.InputManager;
-import com.jme3.input.KeyInput;
-import com.jme3.input.RawInputListener;
-import com.jme3.input.event.*;
-import de.lessvoid.nifty.Nifty;
-import de.lessvoid.nifty.NiftyInputConsumer;
-import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader;
-import de.lessvoid.nifty.input.keyboard.KeyboardInputEvent;
-import de.lessvoid.nifty.spi.input.InputSystem;
-import java.util.ArrayList;
-
-public class InputSystemJme implements InputSystem, RawInputListener {
-
-    private final ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();
-
-    private InputManager inputManager;
-
-    private boolean isDragging = false, niftyOwnsDragging = false;
-    private boolean pressed = false;
-    private int buttonIndex;
-    private int x, y;
-    private int height;
-
-    private boolean shiftDown = false;
-    private boolean ctrlDown  = false;
-
-    private Nifty nifty;
-
-    public InputSystemJme(InputManager inputManager){
-        this.inputManager = inputManager;
-    }
-
-    public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) {
-    }
-
-    public void setNifty(Nifty nifty) {
-        this.nifty = nifty;
-    }
-
-    /**
-     * @param height The height of the viewport. Used to convert
-     * buttom-left origin to upper-left origin.
-     */
-    public void setHeight(int height){
-        this.height = height;
-    }
-
-    public void setMousePosition(int x, int y){
-    }
-
-    public void beginInput(){
-    }
-
-    public void endInput(){
-        boolean result = nifty.update();
-    }
-
-    private void onTouchEventQueued(TouchEvent evt, NiftyInputConsumer nic) {  
-        boolean consumed = false;
-
-        x = (int) evt.getX();
-        y = (int) (height - evt.getY());
-
-        switch (evt.getType()) {
-           case DOWN:
-               consumed = nic.processMouseEvent(x, y, 0, 0, false);
-               isDragging = true;
-               niftyOwnsDragging = consumed;
-               if (consumed){
-                   evt.setConsumed();
-               }
-
-               break;
-
-           case UP:
-               if (niftyOwnsDragging){
-                   consumed = nic.processMouseEvent(x, y, 0, buttonIndex, pressed);
-                   if (consumed){
-                       evt.setConsumed();
-                   }
-               }
-
-               isDragging = false;
-               niftyOwnsDragging = false;
-               break;
-       }
-    }
-    
-    private void onMouseMotionEventQueued(MouseMotionEvent evt, NiftyInputConsumer nic) {
-        x = evt.getX();
-        y = height - evt.getY();
-        nic.processMouseEvent(x, y, evt.getDeltaWheel(), buttonIndex, pressed);
-//        if (nic.processMouseEvent(niftyEvt) /*|| nifty.getCurrentScreen().isMouseOverElement()*/){
-            // Do not consume motion events
-            //evt.setConsumed();
-//        }
-    }
-
-    private void onMouseButtonEventQueued(MouseButtonEvent evt, NiftyInputConsumer nic) {
-        boolean wasPressed = pressed;
-        boolean forwardToNifty = true;
-        
-        buttonIndex = evt.getButtonIndex();
-        pressed = evt.isPressed();
-        
-        // Mouse button raised. End dragging
-        if (wasPressed && !pressed){
-            if (!niftyOwnsDragging){
-                forwardToNifty = false;
-            }
-            isDragging = false;
-            niftyOwnsDragging = false;
-        }
-
-        boolean consumed = false;
-        if (forwardToNifty){
-            consumed = nic.processMouseEvent(x, y, 0, buttonIndex, pressed);
-            if (consumed){
-                evt.setConsumed();
-            }
-        }
-        
-        // Mouse button pressed. Begin dragging
-        if (!wasPressed && pressed){
-            isDragging = true;
-            niftyOwnsDragging = consumed;
-        }
-    }
-
-    private void onKeyEventQueued(KeyInputEvent evt, NiftyInputConsumer nic) {
-        int code = evt.getKeyCode();
-
-        if (code == KeyInput.KEY_LSHIFT || code == KeyInput.KEY_RSHIFT) {
-            shiftDown = evt.isPressed();
-        } else if (code == KeyInput.KEY_LCONTROL || code == KeyInput.KEY_RCONTROL) {
-            ctrlDown = evt.isPressed();
-        }
-        
-        KeyboardInputEvent keyEvt = new KeyboardInputEvent(code,
-                                                           evt.getKeyChar(),
-                                                           evt.isPressed(),
-                                                           shiftDown,
-                                                           ctrlDown);
-
-        if (nic.processKeyboardEvent(keyEvt)){
-            evt.setConsumed();
-        }
-    }
-    
-    public void onMouseMotionEvent(MouseMotionEvent evt) {
-        // Only forward the event if there's actual motion involved.
-        if (inputManager.isCursorVisible() && (evt.getDX() != 0 ||
-                                               evt.getDY() != 0 ||
-                                               evt.getDeltaWheel() != 0)){
-            inputQueue.add(evt);
-        }
-    }
-
-    public void onMouseButtonEvent(MouseButtonEvent evt) {
-        if (inputManager.isCursorVisible() && evt.getButtonIndex() >= 0 && evt.getButtonIndex() <= 2){
-            inputQueue.add(evt);
-        }
-    }
-    
-    public void onJoyAxisEvent(JoyAxisEvent evt) {
-    }
-
-    public void onJoyButtonEvent(JoyButtonEvent evt) {
-    }
-    
-    public void onKeyEvent(KeyInputEvent evt) {
-        inputQueue.add(evt);
-    }
-    
-    public void onTouchEvent(TouchEvent evt) {     
-        inputQueue.add(evt);
-    }
-
-    public void forwardEvents(NiftyInputConsumer nic) {
-        int queueSize = inputQueue.size();
-
-        for (int i = 0; i < queueSize; i++){
-            InputEvent evt = inputQueue.get(i);
-            if (evt instanceof MouseMotionEvent){
-                onMouseMotionEventQueued( (MouseMotionEvent)evt, nic);
-            }else if (evt instanceof MouseButtonEvent){
-                onMouseButtonEventQueued( (MouseButtonEvent)evt, nic);
-            }else if (evt instanceof KeyInputEvent){
-                onKeyEventQueued( (KeyInputEvent)evt, nic);
-            }else if (evt instanceof TouchEvent){
-                onTouchEventQueued( (TouchEvent)evt, nic);
-            }
-        }
-
-        inputQueue.clear();
-    }
-    
-    
-}
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.niftygui;
+
+import com.jme3.input.InputManager;
+import com.jme3.input.KeyInput;
+import com.jme3.input.RawInputListener;
+import com.jme3.input.event.*;
+import de.lessvoid.nifty.Nifty;
+import de.lessvoid.nifty.NiftyInputConsumer;
+import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader;
+import de.lessvoid.nifty.input.keyboard.KeyboardInputEvent;
+import de.lessvoid.nifty.spi.input.InputSystem;
+import java.util.ArrayList;
+
+public class InputSystemJme implements InputSystem, RawInputListener {
+
+    private final ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();
+
+    private InputManager inputManager;
+
+    private boolean isDragging = false, niftyOwnsDragging = false;
+    private boolean pressed = false;
+    private int buttonIndex;
+    private int x, y;
+    private int height;
+
+    private boolean shiftDown = false;
+    private boolean ctrlDown  = false;
+
+    private Nifty nifty;
+
+    public InputSystemJme(InputManager inputManager){
+        this.inputManager = inputManager;
+    }
+
+    public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) {
+    }
+
+    public void setNifty(Nifty nifty) {
+        this.nifty = nifty;
+    }
+
+    /**
+     * @param height The height of the viewport. Used to convert
+     * buttom-left origin to upper-left origin.
+     */
+    public void setHeight(int height){
+        this.height = height;
+    }
+
+    public void setMousePosition(int x, int y){
+    }
+
+    public void beginInput(){
+    }
+
+    public void endInput(){
+        boolean result = nifty.update();
+    }
+
+    private void onTouchEventQueued(TouchEvent evt, NiftyInputConsumer nic) {  
+        boolean consumed = false;
+
+        x = (int) evt.getX();
+        y = (int) (height - evt.getY());
+
+        if (!inputManager.getSimulateMouse()) {
+        switch (evt.getType()) {
+           case DOWN:
+                   consumed = nic.processMouseEvent(x, y, 0, 0, true);
+               isDragging = true;
+               niftyOwnsDragging = consumed;
+               if (consumed){
+                   evt.setConsumed();
+               }
+
+               break;
+
+           case UP:
+               if (niftyOwnsDragging){
+                       consumed = nic.processMouseEvent(x, y, 0, 0, false);
+                   if (consumed){
+                       evt.setConsumed();
+                   }
+               }
+
+               isDragging = false;
+               niftyOwnsDragging = false;
+               break;
+       }
+    }
+    }
+    
+    private void onMouseMotionEventQueued(MouseMotionEvent evt, NiftyInputConsumer nic) {
+        x = evt.getX();
+        y = height - evt.getY();
+        nic.processMouseEvent(x, y, evt.getDeltaWheel(), buttonIndex, pressed);
+//        if (nic.processMouseEvent(niftyEvt) /*|| nifty.getCurrentScreen().isMouseOverElement()*/){
+            // Do not consume motion events
+            //evt.setConsumed();
+//        }
+    }
+
+    private void onMouseButtonEventQueued(MouseButtonEvent evt, NiftyInputConsumer nic) {
+        boolean wasPressed = pressed;
+        boolean forwardToNifty = true;
+        
+        buttonIndex = evt.getButtonIndex();
+        pressed = evt.isPressed();
+        
+        // Mouse button raised. End dragging
+        if (wasPressed && !pressed){
+            if (!niftyOwnsDragging){
+                forwardToNifty = false;
+            }
+            isDragging = false;
+            niftyOwnsDragging = false;
+        }
+
+        boolean consumed = false;
+        if (forwardToNifty){
+            consumed = nic.processMouseEvent(x, y, 0, buttonIndex, pressed);
+            if (consumed){
+                evt.setConsumed();
+            }
+        }
+        
+        // Mouse button pressed. Begin dragging
+        if (!wasPressed && pressed){
+            isDragging = true;
+            niftyOwnsDragging = consumed;
+        }
+    }
+
+    private void onKeyEventQueued(KeyInputEvent evt, NiftyInputConsumer nic) {
+        int code = evt.getKeyCode();
+
+        if (code == KeyInput.KEY_LSHIFT || code == KeyInput.KEY_RSHIFT) {
+            shiftDown = evt.isPressed();
+        } else if (code == KeyInput.KEY_LCONTROL || code == KeyInput.KEY_RCONTROL) {
+            ctrlDown = evt.isPressed();
+        }
+        
+        KeyboardInputEvent keyEvt = new KeyboardInputEvent(code,
+                                                           evt.getKeyChar(),
+                                                           evt.isPressed(),
+                                                           shiftDown,
+                                                           ctrlDown);
+
+        if (nic.processKeyboardEvent(keyEvt)){
+            evt.setConsumed();
+        }
+    }
+    
+    public void onMouseMotionEvent(MouseMotionEvent evt) {
+        // Only forward the event if there's actual motion involved.
+        if (inputManager.isCursorVisible() && (evt.getDX() != 0 ||
+                                               evt.getDY() != 0 ||
+                                               evt.getDeltaWheel() != 0)){
+            inputQueue.add(evt);
+        }
+    }
+
+    public void onMouseButtonEvent(MouseButtonEvent evt) {
+        if (inputManager.isCursorVisible() && evt.getButtonIndex() >= 0 && evt.getButtonIndex() <= 2){
+            inputQueue.add(evt);
+        }
+    }
+    
+    public void onJoyAxisEvent(JoyAxisEvent evt) {
+    }
+
+    public void onJoyButtonEvent(JoyButtonEvent evt) {
+    }
+    
+    public void onKeyEvent(KeyInputEvent evt) {
+        inputQueue.add(evt);
+    }
+    
+    public void onTouchEvent(TouchEvent evt) {     
+        inputQueue.add(evt);
+    }
+
+    public void forwardEvents(NiftyInputConsumer nic) {
+        int queueSize = inputQueue.size();
+
+        for (int i = 0; i < queueSize; i++){
+            InputEvent evt = inputQueue.get(i);
+            if (evt instanceof MouseMotionEvent){
+                onMouseMotionEventQueued( (MouseMotionEvent)evt, nic);
+            }else if (evt instanceof MouseButtonEvent){
+                onMouseButtonEventQueued( (MouseButtonEvent)evt, nic);
+            }else if (evt instanceof KeyInputEvent){
+                onKeyEventQueued( (KeyInputEvent)evt, nic);
+            }else if (evt instanceof TouchEvent){
+                onTouchEventQueued( (TouchEvent)evt, nic);
+            }
+        }
+
+        inputQueue.clear();
+    }
+    
+    
+}