blob: a0c87884f3885a580eb7f39b379d1f867e3cdb37 [file] [log] [blame]
/*
* 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() {
if (pressedButtons.size() > 0) 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);
}
}
if (axisValues.size() > 0) 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);
}
}