com.sidereal.dolphinoes.architecture.core.input.Input.java Source code

Java tutorial

Introduction

Here is the source code for com.sidereal.dolphinoes.architecture.core.input.Input.java

Source

/******************************************************************************* Copyright 2014 See AUTHORS file.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License. ******************************************************************************/
package com.sidereal.dolphinoes.architecture.core.input;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.ObjectMap.Entry;
import com.sidereal.dolphinoes.architecture.AbstractEvent;
import com.sidereal.dolphinoes.architecture.DolphinOES;
import com.sidereal.dolphinoes.architecture.Module;
import com.sidereal.dolphinoes.architecture.core.Configurable;
import com.sidereal.dolphinoes.architecture.core.DolphinOESConfiguration;
import com.sidereal.dolphinoes.util.DolphinOESException;

/** Configurable {@link Module} that is added to the entity system by default. Use {@link DolphinOES#input} to access it.
 * <p>
 * The values that the Module uses from {@link DolphinOESConfiguration} are only the strings found in the
 * {@link DolphinOESConfiguration#inputProcessorNames} array, which if not set or if the configuration is not passed,
 * the value "Default" will be used. All of the processor names found in the class can be retrieved using
 * {@link #getInputProcessorNames()}.
 * 
 * The whole system is based around the use of the values inside the {@link InputAction} class. Every single key
 * designates either a keyboard key, a mouse button, a finger pressed against a screen that can handle touch, or special
 * cases. Currently, the only special case is {@link InputAction#ANY_ACTION}, where all buttons, keys and fingers are
 * handled.
 * <p>
 * The methods to use for adding events to a processor are the following :
 * {@link #addActionEvent(String, int, ActionEvent, InputEventType)}, {@link #addKeyTypedEvent(String, KeyTypedEvent)},
 * {@link #addScrollEvent(String, ScrollEvent)} and {@link #addTouchEvent(String, int, TouchEvent, InputEventType)}.
 * 
 * @author Claudiu Bele */
public class Input implements Configurable {
    // region external

    /** Represents the key code for individual buttons, keys, fingers and special cases (such as {@link #ANY_ACTION}.
     * 
     * @author Claudiu Bele */
    public static class InputAction {
        /** Is used whenever all other actions are used */
        public static final int ANY_ACTION = -1;
        public static final int NO_ACTION = -2;

        public static final int FINGER_1 = -20;
        public static final int FINGER_2 = -19;
        public static final int FINGER_3 = -18;
        public static final int FINGER_4 = -17;
        public static final int FINGER_5 = -16;
        public static final int FINGER_6 = -15;
        public static final int FINGER_7 = -14;
        public static final int FINGER_8 = -13;
        public static final int FINGER_9 = -12;
        public static final int FINGER_10 = -11;

        public static final int MOUSE_LEFT = -10;
        public static final int MOUSE_RIGHT = -9;
        public static final int MOUSE_MIDDLE = -8;
        public static final int MOUSE_BACK = -7;
        public static final int MOUSE_FORWARD = -6;

        public static final int KEY_NUM_0 = 7;
        public static final int KEY_NUM_1 = 8;
        public static final int KEY_NUM_2 = 9;
        public static final int KEY_NUM_3 = 10;
        public static final int KEY_NUM_4 = 11;
        public static final int KEY_NUM_5 = 12;
        public static final int KEY_NUM_6 = 13;
        public static final int KEY_NUM_7 = 14;
        public static final int KEY_NUM_8 = 15;
        public static final int KEY_NUM_9 = 16;
        public static final int KEY_A = 29;
        public static final int KEY_ALT_LEFT = 57;
        public static final int KEY_ALT_RIGHT = 58;
        public static final int KEY_APOSTROPHE = 75;
        public static final int KEY_AT = 77;
        public static final int KEY_B = 30;
        public static final int KEY_BACK = 4;
        public static final int KEY_BACKSLASH = 73;
        public static final int KEY_C = 31;
        public static final int KEY_CALL = 5;
        public static final int KEY_CAMERA = 27;
        public static final int KEY_CLEAR = 28;
        public static final int KEY_COMMA = 55;
        public static final int KEY_D = 32;
        public static final int KEY_DEL = 67;
        public static final int KEY_BACKSPACE = 67;
        public static final int KEY_FORWARD_DEL = 112;
        public static final int KEY_DPAD_CENTER = 23;
        public static final int KEY_DPAD_DOWN = 20;
        public static final int KEY_DPAD_LEFT = 21;
        public static final int KEY_DPAD_RIGHT = 22;
        public static final int KEY_DPAD_UP = 19;
        public static final int KEY_CENTER = 23;
        public static final int KEY_DOWN = 20;
        public static final int KEY_LEFT = 21;
        public static final int KEY_RIGHT = 22;
        public static final int KEY_UP = 19;
        public static final int KEY_E = 33;
        public static final int KEY_ENDCALL = 6;
        public static final int KEY_ENTER = 66;
        public static final int KEY_ENVELOPE = 65;
        public static final int KEY_EQUALS = 70;
        public static final int KEY_EXPLORER = 64;
        public static final int KEY_F = 34;
        public static final int KEY_FOCUS = 80;
        public static final int KEY_G = 35;
        public static final int KEY_GRAVE = 68;
        public static final int KEY_H = 36;
        public static final int KEY_HEADSETHOOK = 79;
        public static final int KEY_HOME = 3;
        public static final int KEY_I = 37;
        public static final int KEY_J = 38;
        public static final int KEY_K = 39;
        public static final int KEY_L = 40;
        public static final int KEY_LEFT_BRACKET = 71;
        public static final int KEY_M = 41;
        public static final int KEY_MEDIA_FAST_FORWARD = 90;
        public static final int KEY_MEDIA_NEXT = 87;
        public static final int KEY_MEDIA_PLAY_PAUSE = 85;
        public static final int KEY_MEDIA_PREVIOUS = 88;
        public static final int KEY_MEDIA_REWIND = 89;
        public static final int KEY_MEDIA_STOP = 86;
        public static final int KEY_MENU = 82;
        public static final int KEY_MINUS = 69;
        public static final int KEY_MUTE = 91;
        public static final int KEY_N = 42;
        public static final int KEY_NOTIFICATION = 83;
        public static final int KEY_NUM = 78;
        public static final int KEY_O = 43;
        public static final int KEY_P = 44;
        public static final int KEY_PERIOD = 56;
        public static final int KEY_PLUS = 81;
        public static final int KEY_POUND = 18;
        public static final int KEY_POWER = 26;
        public static final int KEY_Q = 45;
        public static final int KEY_R = 46;
        public static final int KEY_RIGHT_BRACKET = 72;
        public static final int KEY_S = 47;
        public static final int KEY_SEARCH = 84;
        public static final int KEY_SEMICOLON = 74;
        public static final int KEY_SHIFT_LEFT = 59;
        public static final int KEY_SHIFT_RIGHT = 60;
        public static final int KEY_SLASH = 76;
        public static final int KEY_SOFT_LEFT = 1;
        public static final int KEY_SOFT_RIGHT = 2;
        public static final int KEY_SPACE = 62;
        public static final int KEY_STAR = 17;
        public static final int KEY_SYM = 63;
        public static final int KEY_T = 48;
        public static final int KEY_TAB = 61;
        public static final int KEY_U = 49;
        public static final int KEY_UNKNOWN = 0;
        public static final int KEY_V = 50;
        public static final int KEY_VOLUME_DOWN = 25;
        public static final int KEY_VOLUME_UP = 24;
        public static final int KEY_W = 51;
        public static final int KEY_X = 52;
        public static final int KEY_Y = 53;
        public static final int KEY_Z = 54;
        public static final int KEY_META_ALT_LEFT_ON = 16;
        public static final int KEY_META_ALT_ON = 2;
        public static final int KEY_META_ALT_RIGHT_ON = 32;
        public static final int KEY_META_SHIFT_LEFT_ON = 64;
        public static final int KEY_META_SHIFT_ON = 1;
        public static final int KEY_META_SHIFT_RIGHT_ON = 128;
        public static final int KEY_META_SYM_ON = 4;
        public static final int KEY_CONTROL_LEFT = 129;
        public static final int KEY_CONTROL_RIGHT = 130;
        public static final int KEY_ESCAPE = 131;
        public static final int KEY_END = 132;
        public static final int KEY_INSERT = 133;
        public static final int KEY_PAGE_UP = 92;
        public static final int KEY_PAGE_DOWN = 93;
        public static final int KEY_PICTSYMBOLS = 94;
        public static final int KEY_SWITCH_CHARSET = 95;
        public static final int KEY_BUTTON_CIRCLE = 255;
        public static final int KEY_BUTTON_A = 96;
        public static final int KEY_BUTTON_B = 97;
        public static final int KEY_BUTTON_C = 98;
        public static final int KEY_BUTTON_X = 99;
        public static final int KEY_BUTTON_Y = 100;
        public static final int KEY_BUTTON_Z = 101;
        public static final int KEY_BUTTON_L1 = 102;
        public static final int KEY_BUTTON_R1 = 103;
        public static final int KEY_BUTTON_L2 = 104;
        public static final int KEY_BUTTON_R2 = 105;
        public static final int KEY_BUTTON_THUMBL = 106;
        public static final int KEY_BUTTON_THUMBR = 107;
        public static final int KEY_BUTTON_START = 108;
        public static final int KEY_BUTTON_SELECT = 109;
        public static final int KEY_BUTTON_MODE = 110;
        public static final int KEY_NUMPAD_0 = 144;
        public static final int KEY_NUMPAD_1 = 145;
        public static final int KEY_NUMPAD_2 = 146;
        public static final int KEY_NUMPAD_3 = 147;
        public static final int KEY_NUMPAD_4 = 148;
        public static final int KEY_NUMPAD_5 = 149;
        public static final int KEY_NUMPAD_6 = 150;
        public static final int KEY_NUMPAD_7 = 151;
        public static final int KEY_NUMPAD_8 = 152;
        public static final int KEY_NUMPAD_9 = 153;
        public static final int KEY_COLON = 243;
        public static final int KEY_F1 = 244;
        public static final int KEY_F2 = 245;
        public static final int KEY_F3 = 246;
        public static final int KEY_F4 = 247;
        public static final int KEY_F5 = 248;
        public static final int KEY_F6 = 249;
        public static final int KEY_F7 = 250;
        public static final int KEY_F8 = 251;
        public static final int KEY_F9 = 252;
        public static final int KEY_F10 = 253;
        public static final int KEY_F11 = 254;
        public static final int KEY_F12 = 255;

        public static String toString(int code) {

            String toReturn = "Unknown";
            if (code >= -20 && code <= -11)
                switch (code) {
                case InputAction.FINGER_1:
                    return "Finger 1";
                case InputAction.FINGER_2:
                    return "Finger 2";
                case InputAction.FINGER_3:
                    return "Finger 3";
                case InputAction.FINGER_4:
                    return "Finger 4";
                case InputAction.FINGER_5:
                    return "Finger 5";
                case InputAction.FINGER_6:
                    return "Finger 6";
                case InputAction.FINGER_7:
                    return "Finger 7";
                case InputAction.FINGER_8:
                    return "Finger 8";
                case InputAction.FINGER_9:
                    return "Finger 9";
                case InputAction.FINGER_10:
                    return "Finger 10";
                }
            else if (code >= -10 && code <= -6)
                switch (code) {
                case InputAction.MOUSE_LEFT:
                    toReturn = "Left Mouse button";
                    break;
                case InputAction.MOUSE_RIGHT:
                    toReturn = "Right Mouse button";
                    break;
                case InputAction.MOUSE_MIDDLE:
                    toReturn = "Middle Mouse button";
                    break;
                case InputAction.MOUSE_BACK:
                    toReturn = "Back Mouse button";
                    break;
                case InputAction.MOUSE_FORWARD:
                    toReturn = "Forward Mouse button";
                    break;

                }
            else if (code >= 0 && code <= 255)
                toReturn = com.badlogic.gdx.Input.Keys.toString(code);
            else
                toReturn = "Unknown";

            if (toReturn == null)
                toReturn = "Unknown";

            if (code == InputAction.ANY_ACTION)
                toReturn = "Any action";
            if (code == InputAction.NO_ACTION)
                toReturn = "No action";

            return toReturn;
        }
    }

    /** Represents the status of the interraction with a type of input during the last frame.
     * 
     * @author Claudiu Bele */
    public static enum InputStatus {
        /** There is no interaction with the input type */
        None,

        /** The input has just been pressed. Set in {@link InputProcessor#touchDown(int, int, int, int)},
         * {@link InputProcessor#keyDown(int)} if the current status of any input is {@link #None} */
        Down,

        /** The input is being held down. Set in {@link InputProcessor#touchDown(int, int, int, int)},
         * {@link InputProcessor#keyDown(int)} if the current status of any input is {@link #Down} */
        Hold,

        /** The input is being released. Set in {@link InputProcessor#touchUp(int, int, int, int)} and
         * {@link InputProcessor#keyUp(int)}. */
        Up
    }

    /** The type that listeners can have. Used when adding/removing listeners in
     * {@link Input#addListener(InputEventType, AbstractEvent)} and
     * {@link Input#removeListener(InputEventType, AbstractEvent)}.
     * <p>
     * {@link Input#listeners} has values of type {@link InputEventType} as keys for lists of events to run based on
     * certain {@link InputProcessor} events such as touch down ( {@link InputProcessor#touchDown(int, int, int, int)}
     * ).
     * 
     * @author Claudiu */
    public static enum InputEventType {
        /** The events of this type are called whenever a button or finger or key is pressed, from
         * {@link InputProcessor#touchDown(int, int, int, int)} . */
        Down,

        /** The events of this type are called whenever a button or finger or key is pressed, from
         * {@link InputProcessor#touchDown(int, int, int, int)} . */
        Hold,

        /** The events of this type are called whenever a button or finger or key is released, from
         * {@link InputProcessor#touchDown(int, int, int, int)}. */
        Up,

    }

    // endregion external

    // region fields

    /** Map containing Input processors that can be retrieved by name. An input processor is added for each entry in
     * {@link DolphinOESConfiguration#inputProcessorNames}, in {@link #create()}.
     * <p>
     * Input processors can be retrieved using {@link #getInputProcessor(String)}, whereas the parameter to pass can be
     * selected from the array returned from {@link #getInputProcessorNames()}. */
    private final ObjectMap<String, InputProcessor> inputProcessors;

    /** Events to run when a particular key / button / finger is pressed. The actual events are found in the
     * {@link ActionEventWrapper#event} value from the {@link ActionEventWrapper} wrapper created in
     * {@link #addActionEvent(String, int, ActionEvent, InputEventType)} */
    private final ObjectMap<InputProcessor, Array<ActionEventWrapper>> actionEvents;

    /** Events to run when a particular finger is pressed. "sorted" by InputProcessor */
    private final ObjectMap<InputProcessor, Array<TouchEventWrapper>> touchEvents;

    /** Events to run when the mouse wheel is scrolled, "sorted" by InputProcessor */
    private final ObjectMap<InputProcessor, Array<ScrollEvent>> scrollEvents;

    /** Events to run when a key is typed, "sorted" by InputProcessor */
    private final ObjectMap<InputProcessor, Array<KeyTypedEvent>> keyTypedEvents;

    /** Holds all of the InputProcessors generated by name using {@link DolphinOESConfiguration#inputProcessorNames} in
     * {@link #create()}. The order in which InputProcessor instances are added is the same order as the values in the
     * array. */
    private final InputMultiplexer inputMultiplexer;

    /** Stores data for individual actions. If an {@link InputData} action does not exist but is currently handled in one
     * of the methods of the inputProcessor, a new instance will be created for the given action code. When calling
     * {@link #getInputData(int)}, if the input data can't be found with the given actioncode, a new one will be added. */
    private final ObjectMap<Integer, ActionData> inputData;

    /** Stores data for individual touchActions. If an {@link TouchInputData} action does not exist but is currently
     * handled in one of the methods of the inputProcessor, a new instance will be created for the given action code.
     * When calling {@link #getTouchInputData(int)}, if the input data can't be found with the given touchActioncode, a
     * new one will be added. */
    private final ObjectMap<Integer, TouchData> touchInputData;

    private String[] processorNames;

    // endregion fields

    // region constructors

    /** Constructor for input, does not have any connection to the LibGDX backend yet. Is called in
     * {@link DolphinOES#DolphinOES(com.sidereal.dolphinoes.architecture.GameScene, DolphinOESConfiguration)}. */
    public Input() {

        inputData = new ObjectMap<Integer, ActionData>();
        touchInputData = new ObjectMap<Integer, TouchData>();

        inputMultiplexer = new InputMultiplexer();
        inputProcessors = new ObjectMap<String, InputProcessor>();

        actionEvents = new ObjectMap<InputProcessor, Array<ActionEventWrapper>>();
        touchEvents = new ObjectMap<InputProcessor, Array<TouchEventWrapper>>();
        scrollEvents = new ObjectMap<InputProcessor, Array<ScrollEvent>>();
        keyTypedEvents = new ObjectMap<InputProcessor, Array<KeyTypedEvent>>();

        // contains all of the Events on down, up or release

    }

    /** Method for loading the configuration passed in
     * {@link DolphinOES#DolphinOES(com.sidereal.dolphinoes.architecture.GameScene, DolphinOESConfiguration)}. All of
     * the game preferences will affect the way the system works */
    @Override
    public void configure(DolphinOESConfiguration cfg) {

        if (cfg.inputProcessorNames == null || cfg.inputProcessorNames.length == 0)
            this.processorNames = new String[] { "Default" };
        else
            this.processorNames = cfg.inputProcessorNames;
    }

    /** Method for creating the Core Module, is called after the LibGDX backend has been created, thus LibGDX constructs
     * can be called in this method without errors. Is called in {@link DolphinOES#create()}. */
    @Override
    public void create() {

        for (int i = 0; i < this.processorNames.length; i++) {

            // create new input processor
            final InputProcessor inputProcessor = new InputProcessor() {

                @Override
                public boolean touchUp(int screenX, int screenY, int pointer, int button) {

                    // update internal input data
                    // update button data
                    getInputData(button - 10).update(InputStatus.Up);

                    // update touch data
                    getTouchInputData(pointer - 20).update(InputStatus.Up, screenX, screenY);

                    // run listener events

                    boolean handled = false;
                    // go over all action events and run the ones on up if
                    // applicable
                    for (int i = 0; i < actionEvents.get(this).size; i++) {
                        ActionEventWrapper currEvent = actionEvents.get(this).get(i);
                        // curr event runs when releasing
                        if (currEvent.inputType.equals(InputEventType.Up)) {

                            if (currEvent.code == pointer - 20 ||
                            // if the event runs for a particular touch
                            currEvent.code == button - 10 ||
                            // or the event runs on a particular button
                            currEvent.code == Keys.ANY_KEY)
                            // or the event runs on every key
                            {
                                handled = handled || currEvent.event.run(getInputData(button - 10));
                            }
                        }
                    }

                    // go over all touch event and run the ones on up
                    for (int i = 0; i < touchEvents.get(this).size; i++) {
                        TouchEventWrapper currEvent = touchEvents.get(this).get(i);
                        // curr event runs when releasing
                        if (currEvent.inputType.equals(InputEventType.Up)) {
                            if (currEvent.code == pointer - 20 ||
                            // if the event runs for a particular touch
                            currEvent.code == Keys.ANY_KEY)
                            // or the event runs on every key
                            {
                                handled = handled || currEvent.event.run(getTouchInputData(pointer - 20));
                            }
                        }
                    }

                    return handled;
                }

                @Override
                public boolean touchDragged(int screenX, int screenY, int pointer) {

                    getTouchInputData(pointer - 20).update(InputStatus.Hold, screenX, screenY);

                    boolean handled = false;
                    // go over all touch event and run the ones on up

                    for (int i = 0; i < touchEvents.get(this).size; i++) {

                        TouchEventWrapper currEvent = touchEvents.get(this).get(i);
                        // curr event runs when releasing
                        if ((currEvent.inputType.equals(InputEventType.Down)
                                && getTouchInputData(pointer - 20).getInputStatus() == InputStatus.Down)
                                || (currEvent.inputType.equals(InputEventType.Hold)
                                        && getTouchInputData(pointer - 20).getInputStatus() == InputStatus.Hold)) {
                            if (currEvent.code == pointer - 20 ||
                            // if an event runs for a particular touch
                            currEvent.code == Keys.ANY_KEY)
                            // if an event runs for all touches
                            {
                                handled = handled || currEvent.event.run(getTouchInputData(pointer - 20));
                            }
                        }
                    }

                    return true;
                }

                @Override
                public boolean touchDown(int screenX, int screenY, int pointer, int button) {

                    // update button data
                    if (getInputData(button - 10).getInputStatus().equals(InputStatus.None))
                        getInputData(button - 10).update(InputStatus.Down);

                    if (getInputData(pointer - 20).getInputStatus().equals(InputStatus.None))
                        getInputData(pointer - 20).update(InputStatus.Down);

                    // update pointer data
                    InputStatus targetInputStatus = getTouchInputData(pointer - 20).getInputStatus();
                    if (getTouchInputData(pointer - 20).getInputStatus().equals(InputStatus.None))
                        targetInputStatus = InputStatus.Down;
                    getTouchInputData(pointer - 20).update(targetInputStatus, screenX, screenY);

                    boolean handled = false;
                    // update internal input data
                    // ...

                    // run listener events
                    // go over all action events and run the ones on up if
                    // applicable
                    for (int i = 0; i < actionEvents.get(this).size; i++) {
                        ActionEventWrapper currEvent = actionEvents.get(this).get(i);
                        // curr event runs when releasing
                        if ((currEvent.inputType.equals(InputEventType.Down)
                                && getInputData(button - 10).getInputStatus() == InputStatus.Down)
                                || (currEvent.inputType.equals(InputEventType.Hold)
                                        && getInputData(button - 10).getInputStatus() == InputStatus.Hold)) {

                            if (currEvent.code == pointer - 20 ||
                            // if the event runs for a particular touch
                            currEvent.code == button - 10 ||
                            // or the event runs for a particular button
                            currEvent.code == Keys.ANY_KEY)
                            // or any key is to be handled
                            {
                                handled = handled || currEvent.event.run(getInputData(button - 10));
                            }
                        }
                    }

                    // go over all touch event and run the ones on up
                    for (int i = 0; i < touchEvents.get(this).size; i++) {

                        TouchEventWrapper currEvent = touchEvents.get(this).get(i);
                        // curr event runs when releasing

                        if ((currEvent.inputType.equals(InputEventType.Down)
                                && getTouchInputData(pointer - 20).getInputStatus() == InputStatus.Down)
                                || (currEvent.inputType.equals(InputEventType.Hold)
                                        && getTouchInputData(pointer - 20).getInputStatus() == InputStatus.Hold)) {
                            if (currEvent.code == pointer - 20 ||
                            // if the event runs for a particular touch
                            currEvent.code == Keys.ANY_KEY)
                            // or any key is to be handled

                            {
                                handled = handled || currEvent.event.run(getTouchInputData(pointer - 20));
                            }
                        }
                    }

                    return handled;
                }

                @Override
                public boolean scrolled(int amount) {

                    // run listener events
                    boolean handled = false;
                    for (int i = 0; i < scrollEvents.get(this).size; i++) {
                        handled = handled || scrollEvents.get(this).get(i).run(amount);
                    }
                    return handled;
                }

                // ignore, can use Gdx.input for mouse position
                @Override
                public boolean mouseMoved(int screenX, int screenY) {

                    return false;
                }

                @Override
                public boolean keyUp(int keycode) {

                    // update key press data
                    getInputData(keycode).update(InputStatus.Up);

                    // run listener events
                    boolean handled = false;
                    // go over all action events and run the ones on up if
                    // applicable
                    for (int i = 0; i < actionEvents.get(this).size; i++) {
                        ActionEventWrapper currEvent = actionEvents.get(this).get(i);
                        // curr event runs when releasing
                        if (currEvent.inputType.equals(InputEventType.Up)) {

                            if (currEvent.code == keycode ||
                            // or the event runs on a particular button
                            currEvent.code == InputAction.ANY_ACTION)
                            // or the event runs on every key
                            {
                                handled = handled || currEvent.event.run(getInputData(keycode));
                            }
                        }
                    }
                    return handled;
                }

                @Override
                public boolean keyTyped(char character) {

                    // run listener events
                    boolean handled = false;
                    for (int i = 0; i < keyTypedEvents.get(this).size; i++) {
                        handled = handled || keyTypedEvents.get(this).get(i).run(character);
                    }
                    return handled;
                }

                @Override
                public boolean keyDown(int keycode) {

                    // update key press data
                    if (getInputData(keycode).getInputStatus().equals(InputStatus.None))
                        getInputData(keycode).update(InputStatus.Down);

                    // run listener events
                    boolean handled = false;

                    // go over all action events and run the ones on up if
                    // applicable
                    for (int i = 0; i < actionEvents.get(this).size; i++) {
                        ActionEventWrapper currEvent = actionEvents.get(this).get(i);
                        // curr event runs when releasing
                        if ((currEvent.inputType.equals(InputEventType.Down)
                                && getInputData(keycode).getInputStatus() == InputStatus.Down)
                                || (currEvent.inputType.equals(InputEventType.Hold)
                                        && getInputData(keycode).getInputStatus() == InputStatus.Hold)) {

                            if (currEvent.code == keycode ||
                            // the event runs for a particular key
                            currEvent.code == InputAction.ANY_ACTION)
                            // or any key is to be handled
                            {
                                handled = handled || currEvent.event.run(getInputData(keycode));
                            }
                        }
                    }
                    return handled;
                }
            };
            inputProcessors.put(processorNames[i], inputProcessor);
            actionEvents.put(inputProcessor, new Array<ActionEventWrapper>());
            touchEvents.put(inputProcessor, new Array<TouchEventWrapper>());
            scrollEvents.put(inputProcessor, new Array<ScrollEvent>());
            keyTypedEvents.put(inputProcessor, new Array<KeyTypedEvent>());
            inputMultiplexer.addProcessor(inputProcessor);

        }
        // set LibGDX input processor to the one that we are creating
        Gdx.input.setInputProcessor(inputMultiplexer);
    }

    // endregion constructors

    // region methods

    /** Updates all of the {@link InputData} and {@link TouchData} instances. If any key/button/finger's status is down,
     * it will become hold. If it is Up, it will become None. if it is hold, it will run keyDown or touchDragged based
     * on the type of action. Is called in {@link DolphinOES#render()}. */
    @Override
    public void update() {

        // go over all keys, updating their status if necessary
        for (Entry<Integer, ActionData> entry : inputData.entries()) {
            if (entry.value.inputStatus == InputStatus.Down)
                entry.value.inputStatus = InputStatus.Hold;

            if (entry.value.inputStatus == InputStatus.Up) {
                entry.value.inputStatus = InputStatus.None;
            }

            if (entry.value.inputStatus == InputStatus.Hold)
                inputMultiplexer.keyDown(entry.value.code);

        }

        // go over all touch data, updating their status if necessary
        for (Entry<Integer, TouchData> entry : touchInputData.entries()) {
            if (entry.value.inputStatus == InputStatus.Down)
                entry.value.inputStatus = InputStatus.Hold;

            if (entry.value.inputStatus == InputStatus.Up) {
                entry.value.inputStatus = InputStatus.None;
            }

            if (entry.value.inputStatus == InputStatus.Hold) {
                inputMultiplexer.touchDragged((int) entry.value.getPosition().x, (int) entry.value.getPosition().y,
                        entry.value.code + 20);
            }

        }

    }

    /** Returns a input data for the requested code. This is for querying action data without having to create events (if
     * it is desired). codes for fingers can be retrieved from {@link InputAction}.
     * 
     * @throws DolphinOESException
     *             if <code>actioncode</code> is not linked to any action.
     * 
     * @param actionCode
     *            The code for which to get the InputData. Must be the value of one of the fields in {@link InputAction}
     *            .
     * @return */
    public ActionData getInputData(int actionCode) {

        if (InputAction.toString(actionCode).equals("Unknown") && actionCode < 0 || actionCode > 255)
            throw new DolphinOESException(
                    "Input requested for an actionCode that is not an action (" + actionCode + ")");

        if (inputData.get(actionCode) == null) {
            ActionData newData = new ActionData(actionCode);
            inputData.put(actionCode, newData);
        }
        return inputData.get(actionCode);

    }

    /** Returns a touch input data for the requested code. This is for querying action data without having to create
     * events (if it is desired). codes for fingers can be retrieved from {@link InputAction}.
     * 
     * @throws DolphinOESException
     *             if <code>actioncode</code> is not linked to a finger.
     * 
     * @param actionCode
     *            The code for which to get the TouchInputData. Must be between {@link InputAction#FINGER_1} and
     *            {@link InputAction#FINGER_10}
     * @return */
    public TouchData getTouchInputData(int touchActionCode) {

        if (InputAction.toString(touchActionCode).contains("finger"))
            throw new DolphinOESException("Touch input requested for a touchActionCode that is not a finger");

        if (touchInputData.get(touchActionCode) == null) {
            TouchData newData = new TouchData(touchActionCode);
            touchInputData.put(touchActionCode, newData);
        }
        return touchInputData.get(touchActionCode);
    }

    /** Adds an event to run when a key is typed. The only data that the user can process is the char value for the key
     * value passed in {@link KeyTypedEvent}.
     * 
     * 
     * @throws NullPointerException
     *             if no processor is found by the requested name, or the event passed is null.
     * 
     * 
     * @param inputProcessorName
     *            the name of the input processor to assign the event to. The input processor can be taken by name using
     *            {@link #getInputProcessorNames()}. The available names are from the array set in
     *            {@link DolphinOESConfiguration#inputProcessorNames}, or if it has not been set, only "Default" can be
     *            found. If null value is provided, the first entry in the array is entered. The only moment in which an
     *            error is thrown regarding the input processor is if the name can't be found in the map of input
     *            processors.
     * 
     * 
     * @param event
     *            the event to run when a key is typed. */
    public void addKeyTypedEvent(String inputProcessorName, KeyTypedEvent event) {

        if (event == null)
            throw new NullPointerException("Input.addKeyTypedEvent parameter event of type KeyTypedEvent is null");
        if (inputProcessors.get(inputProcessorName) == null)
            throw new NullPointerException(
                    "Unable to find  input processor with the given name " + inputProcessorName);

        keyTypedEvents.get(inputProcessors.get(inputProcessorName)).add(event);
    }

    /** Adds an event to run when touching the screen with a finger. For choosing a finger to interract with, use the
     * {@link InputAction} entries, from {@link InputAction#FINGER_1} to {@link InputAction#FINGER_10}.
     * <p>
     * The order in which fingers are being pressed is as follows: The first finger is pressed,
     * {@link InputAction#FINGER_1} is registered. The 2nd finger is pressed, {@link InputAction#FINGER_2} is
     * registered. When the 1st finger is released, the 2nd finger remains on {@link InputAction#FINGER_2}, and the next
     * finger that is going to be pressed will be registered at {@link InputAction#FINGER_1} again, with the 2nd next
     * finger registered at {@link InputAction#FINGER_3}.
     * 
     * 
     * @throws DolphinOESException
     *             if the <code>finger</code> integer variable is not within {@link InputAction#FINGER_1} and
     *             {@link InputAction#FINGER_10}.
     *             <p>
     * 
     * @throws NullPointerException
     *             if the <code>event</code> or <code>type</code> parameters are null, or there is no processor found by
     *             the requested name.
     * 
     * @see InputAction InputAction - for finger codes, all of them start with "Finger_".
     *      <p>
     * @see #addActionEvent(String, int, ActionEvent, InputEventType) AddActionEvent - for adding a general action,
     *      instead of a touch-only action
     * 
     * @param inputProcessorName
     *            the name of the input processor to assign the event to. The input processor can be taken by name using
     *            {@link #getInputProcessorNames()}. The available names are from the array set in
     *            {@link DolphinOESConfiguration#inputProcessorNames}, or if it has not been set, only "Default" can be
     *            found. If null value is provided, the first entry in the array is entered. The only moment in which an
     *            error is thrown regarding the input processor is if the name can't be found in the map of input
     *            processors.
     *            <p>
     * @param fingerCode
     *            the code assigned to a finger, to be taken from {@link InputAction}, 10 fingers being supported
     *            currently, between {@link InputAction#FINGER_1} and {@link InputAction#FINGER_10}.
     *            <p>
     * @param event
     *            the event to run when something happens to the finger.
     *            <p>
     * @param type
     *            The type of action to run the event on. The available options are {@link InputEventType#Hold} for
     *            holding the finger down, {@link InputEventType#Down} for the frame in which the finger was pressed,
     *            and {@link InputEventType#Up} for the frame in which the finger is released. */
    public void addTouchEvent(String inputProcessorName, int fingerCode, TouchEvent event, InputEventType type) {

        if (event == null)
            throw new NullPointerException("Input.addTouchEvent parameter event of type TouchEvent is null");
        if (type == null)
            throw new NullPointerException("Input.addTouchEvent parameter type of type InputEventType is null");
        if (inputProcessors.get(inputProcessorName) == null)
            throw new NullPointerException(
                    "Unable to find  input processor with the given name " + inputProcessorName);

        if (fingerCode > -11 || fingerCode < -20)
            throw new DolphinOESException(
                    "Input.addTouchEvent parameter finger of type int has to have values within the InputAction.Finger_1 and InputAction.Finger_10 ( -20 and -11");

        TouchEventWrapper wrapper = new TouchEventWrapper(fingerCode, event, type);
        touchEvents.get(inputProcessors.get(inputProcessorName)).add(wrapper);
    }

    /** Adds an event to run when a specific action is being accessed. For choosing an action to interract for, use any
     * of the values inside the {@link InputAction} class, or {@link InputAction#ANY_ACTION} for listening to any
     * particular action.
     * 
     * @throws DolphinOESException
     *             if the <code>code</code> integer variable is not a value found in {@link InputAction}.
     * 
     * @throws NullPointerException
     *             if the <code>event</code> or <code>type</code> parameters are null, or there is no processor found by
     *             the requested name.
     * 
     * 
     * @param inputProcessorName
     *            the name of the input processor to assign the event to. The input processor can be taken by name using
     *            {@link #getInputProcessorNames()}. The available names are from the array set in
     *            {@link DolphinOESConfiguration#inputProcessorNames}, or if it has not been set, only "Default" can be
     *            found. If null value is provided, the first entry in the array is entered. The only moment in which an
     *            error is thrown regarding the input processor is if the name can't be found in the map of input
     *            processors.
     * 
     * @param finger
     *            the code assigned to an action(key/button/finger), to be taken from {@link InputAction}. Use a value
     *            from {@link InputAction} for a specific value ( like {@link InputAction#MOUSE_LEFT} ) or
     *            {@link InputAction#ANY_ACTION} to listen to all of them.
     * 
     * @param event
     *            the event to run when something happens to the action(key,finger, mouse button).
     * 
     * @param type
     *            The type of action to run the event on. The available options are {@link InputEventType#Hold} for
     *            holding the finger down, {@link InputEventType#Down} for the frame in which the finger was pressed,
     *            and {@link InputEventType#Up} for the frame in which the finger is released. */
    public void addActionEvent(String inputProcessorName, int actionCode, ActionEvent event, InputEventType type) {

        if (event == null)
            throw new NullPointerException("Input.addActionEvent parameter event of type ActionEvent is null");
        if (type == null)
            throw new NullPointerException("Input.addActionEvent parameter type of type InputEventType is null");
        if (inputProcessors.get(inputProcessorName) == null)
            throw new NullPointerException(
                    "Unable to find  input processor with the given name " + inputProcessorName);

        if (InputAction.toString(actionCode).equals("Unknown") && actionCode < 0 || actionCode > 255)
            throw new DolphinOESException(
                    "Input.addActionEvent parameter code of type int can't be found in InputAction");

        ActionEventWrapper wrapper = new ActionEventWrapper(actionCode, event, type);
        actionEvents.get(inputProcessors.get(inputProcessorName)).add(wrapper);
    }

    /** Adds an event to run when the mouse is scrolled. The only data that the user can process is the integer value for
     * the wheel rotation passed in {@link ScrollEvent#run(int)}.
     * 
     * 
     * @throws NullPointerException
     *             if no processor is found by the requested name.
     * 
     * 
     * @param inputProcessorName
     *            the name of the input processor to assign the event to. The input processor can be taken by name using
     *            {@link #getInputProcessorNames()}. The available names are from the array set in
     *            {@link DolphinOESConfiguration#inputProcessorNames}, or if it has not been set, only "Default" can be
     *            found. If null value is provided, the first entry in the array is entered. The only moment in which an
     *            error is thrown regarding the input processor is if the name can't be found in the map of input
     *            processors.
     * 
     * 
     * @param event
     *            the event to run when something the mouse wheel is scrolled */
    public void addScrollEvent(String inputProcessorName, ScrollEvent event) {

        if (event == null)
            throw new NullPointerException("Input.addScrollEvent parameter event of type ScrollEvent is null");
        if (inputProcessors.get(inputProcessorName) == null)
            throw new NullPointerException(
                    "Unable to find  input processor with the given name " + inputProcessorName);

        scrollEvents.get(inputProcessors.get(inputProcessorName)).add(event);

    }

    /** Returns an array of input processor names. The keys are to be used in
     * {@link #addTouchEvent(String, int, TouchEvent, InputEventType)},
     * {@link #addActionEvent(String, int, ActionEvent, InputEventType)},
     * {@link #addKeyTypedEvent(String, KeyTypedEvent)} or {@link #addScrollEvent(String, ScrollEvent)}.
     * <p>
     * The array contains the data retrieved from the {@link DolphinOESConfiguration#inputProcessorNames}. If no
     * {@link DolphinOESConfiguration} instance is passed to the {@link DolphinOES} constructor, The configuration is
     * made automatically, {@link DolphinOESConfiguration#inputProcessorNames} containing only the "Default" value.
     * 
     * @return array of input processor names. */
    public String[] getInputProcessorNames() {

        return processorNames;
    }

    /** Attempts to remove an action event tied to a particular input processor, returning a boolean representing whether
     * or not the value was found and removed.
     * <p>
     * If there is no processor tied to the string parameter passed, false will be returned
     * <p>
     * 
     * @throws NullPointerException
     *             if the event passed is null
     * 
     * @param inputProcessorName
     *            name of the input processor to find the linked event to
     * @param event
     *            the event to remove
     * @return whether the event was removed */
    public boolean removeActionEvent(String inputProcessorName, ActionEvent event) {

        if (event == null)
            throw new NullPointerException("Input.removeActionEvent parameter event of type ActionEvent is null");

        InputProcessor targetprocessor = inputProcessors.get(inputProcessorName);

        if (targetprocessor == null)
            return false;

        // go over the events tied to the passed input processor, to find the
        // ActionEvent in one of those wrappers
        for (int i = 0; i < actionEvents.get(targetprocessor).size; i++) {
            if (actionEvents.get(targetprocessor).get(i).event.equals(event)) {
                actionEvents.get(targetprocessor).removeIndex(i);
                return true;
            }
        }

        return false;
    }

    /** Attempts to remove a touch event tied to a particular input processor, returning a boolean representing whether
     * or not the value was found and removed.
     * <p>
     * If there is no processor tied to the string parameter passed, false will be returned
     * <p>
     * 
     * @throws NullPointerException
     *             if the event passed is null
     * 
     * @param inputProcessorName
     *            name of the input processor to find the linked event to
     * @param event
     *            the event to remove
     * @return whether the event was removed */
    public boolean removeTouchEvent(String inputProcessorName, TouchEvent event) {

        if (event == null)
            throw new NullPointerException("Input.removeTouchEvent parameter event of type TouchEvent is null");

        InputProcessor targetprocessor = inputProcessors.get(inputProcessorName);

        if (targetprocessor == null)
            return false;

        // go over the events tied to the passed input processor, to find the
        // ActionEvent in one of those wrappers
        for (int i = 0; i < touchEvents.get(targetprocessor).size; i++) {
            if (touchEvents.get(targetprocessor).get(i).event.equals(event)) {
                touchEvents.get(targetprocessor).removeIndex(i);
                return true;
            }
        }

        return false;
    }

    /** Attempts to remove a scroll event tied to a particular input processor, returning a boolean representing whether
     * or not the value was found and removed.
     * <p>
     * If there is no processor tied to the string parameter passed, false will be returned
     * <p>
     * 
     * @throws NullPointerException
     *             if the event passed is null
     * 
     * @param inputProcessorName
     *            name of the input processor to find the linked event to
     * @param event
     *            the event to remove
     * @return whether the event was removed */
    public boolean removeScrollEvent(String inputProcessorName, ScrollEvent event) {

        if (event == null)
            throw new NullPointerException("Input.removeScrollEvent parameter event of type ScrollEvent is null");

        InputProcessor targetprocessor = inputProcessors.get(inputProcessorName);

        if (targetprocessor == null)
            return false;

        // go over the events tied to the passed input processor, to find the
        // ActionEvent in one of those wrappers
        for (int i = 0; i < scrollEvents.get(targetprocessor).size; i++) {
            if (scrollEvents.get(targetprocessor).get(i).equals(event)) {
                scrollEvents.get(targetprocessor).removeIndex(i);
                return true;
            }
        }

        return false;

    }

    /** Attempts to remove a key typed event tied to a particular input processor, returning a boolean representing
     * whether or not the value was found and removed.
     * <p>
     * If there is no processor tied to the string parameter passed, false will be returned
     * <p>
     * 
     * @throws NullPointerException
     *             if the event passed is null
     * 
     * @param inputProcessorName
     *            name of the input processor to find the linked event to
     * @param event
     *            the event to remove
     * @return whether the event was removed */
    public boolean removeKeyTypedEvent(String inputProcessorName, KeyTypedEvent event) {

        if (event == null)
            throw new NullPointerException(
                    "Input.removeKeyTypedEvent parameter event of type KeyTypedEvent is null");

        InputProcessor targetprocessor = inputProcessors.get(inputProcessorName);

        if (targetprocessor == null)
            return false;

        // go over the events tied to the passed input processor, to find the
        // ActionEvent in one of those wrappers
        for (int i = 0; i < keyTypedEvents.get(targetprocessor).size; i++) {
            if (keyTypedEvents.get(targetprocessor).get(i).equals(event)) {
                keyTypedEvents.get(targetprocessor).removeIndex(i);
                return true;
            }
        }

        return false;
    }

    /** Attempts to change the action code bound to the event passed. Just like the add and remove methods,
     * NullPointerExceptions and DolphinOESException will be thrown in multiple cases if the parameters passed are
     * either null, using them would harm the system or using them would give no result.
     * <p>
     * The method does not add the ActionEvent if it is not existent, as an {@link InputEventType} parameter would be
     * needed for the creation of the internal {@link ActionEventWrapper} used by the system.
     * <p>
     * If you want to be able to bind a specific action to a specific actionCode but you do not want to have a default
     * value for it (not bound ), you can add the event using
     * {@link #addActionEvent(String, int, ActionEvent, InputEventType)} and pass {@link InputAction#NO_ACTION} so it
     * will not be handled until the code that the event is bound to changes.
     * <p>
     * When successfully rebinding the event, the old action will not handle the event anymore.
     * 
     * @param inputProcessorName
     *            name of the input processor in which to find the event parameter
     * @param event
     *            the event that we want to switch the {@link InputAction} to.
     * @param newActionCode
     *             the new action code (from {@link InputAction}) that we want to bind the event to
     * @return */
    public boolean rebindActionEvent(String inputProcessorName, ActionEvent event, int newActionCode) {

        if (event == null)
            throw new NullPointerException(
                    "Input.changeActionEventCode parameter event of type ActionEvent is null");

        if (InputAction.toString(newActionCode).equals("Unknown") && newActionCode < 0 || newActionCode > 255)
            throw new DolphinOESException(
                    "Input.addActionEvent parameter code of type int can't be found in InputAction");

        InputProcessor targetprocessor = inputProcessors.get(inputProcessorName);

        if (targetprocessor == null)
            throw new NullPointerException(
                    "Unable to find  input processor with the given name " + inputProcessorName);

        // go over the events tied to the passed input processor, to find the
        // ActionEvent in one of those wrappers
        for (int i = 0; i < actionEvents.get(targetprocessor).size; i++) {
            if (actionEvents.get(targetprocessor).get(i).event.equals(event)) {
                actionEvents.get(targetprocessor).get(i).code = newActionCode;
                return true;
            }
        }

        return false;
    }

    // endregion methods

}