diff options
Diffstat (limited to 'engine/src/core/com/jme3/input/InputManager.java')
-rw-r--r-- | engine/src/core/com/jme3/input/InputManager.java | 881 |
1 files changed, 881 insertions, 0 deletions
diff --git a/engine/src/core/com/jme3/input/InputManager.java b/engine/src/core/com/jme3/input/InputManager.java new file mode 100644 index 0000000..23f2988 --- /dev/null +++ b/engine/src/core/com/jme3/input/InputManager.java @@ -0,0 +1,881 @@ +/* + * 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); + } +} |