/* * This file is part of the Illarion project. * * Copyright © 2014 - Illarion e.V. * * Illarion is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Illarion is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ package org.illarion.engine.nifty; import de.lessvoid.nifty.NiftyInputConsumer; import de.lessvoid.nifty.input.keyboard.KeyboardInputEvent; import de.lessvoid.nifty.spi.input.InputSystem; import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader; import org.illarion.engine.input.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * This is the input system that is used to forward the input data provided by the game engine to the Nifty-GUI. * * @author Martin Karing <nitram@illarion.org> */ public class IgeInputSystem implements InputSystem, InputListener { private static final Logger log = LoggerFactory.getLogger(IgeInputSystem.class); /** * This value contains a key that was stalled as key down to properly detect typing events. */ @Nullable private Key stalledKeyDownKey; /** * The amount of mouse click events that should be consumed by the Nifty-GUI. */ private int consumeClicks; /** * The input implementation of the engine. */ @Nonnull private final Input input; /** * This is the consumer that receives the input data. This is {@code null} while Nifty is not expecting input data. */ @Nullable private NiftyInputConsumer currentConsumer; /** * This is the listener that is supposed to receive the input data that was not processed by the Nifty-GUI. */ @Nonnull private final InputListener listener; /** * Create a new input device and set the input implementation that provides the input data. * * @param input the input implementation * @param listener the listener that receives input data was was not processed by the Nifty-GUI */ public IgeInputSystem(@Nonnull Input input, @Nonnull InputListener listener) { input.setListener(this); this.input = input; this.listener = listener; } @Override public void keyDown(@Nonnull Key key) { if (currentConsumer == null) { throw new IllegalStateException("Receiving input data while none was requested"); } if (stalledKeyDownKey != null) { listener.keyDown(stalledKeyDownKey); stalledKeyDownKey = null; } if (input.isForwardingEnabled(ForwardingTarget.Keyboard) || input.isAnyKeyDown(Key.LeftAlt)) { listener.keyDown(key); } else { int keyCode = getNiftyKeyId(key); boolean shiftDown = input.isAnyKeyDown(Key.LeftShift, Key.RightShift); boolean controlDown = input.isAnyKeyDown(Key.LeftCtrl, Key.RightCtrl); KeyboardInputEvent event = new KeyboardInputEvent(keyCode, Character.MIN_VALUE, true, shiftDown, controlDown); if (!currentConsumer.processKeyboardEvent(event)) { stalledKeyDownKey = key; } else { stalledKeyDownKey = null; } } } @Override public void keyUp(@Nonnull Key key) { if (currentConsumer == null) { throw new IllegalStateException("Receiving input data while none was requested"); } if (input.isForwardingEnabled(ForwardingTarget.Keyboard)) { listener.keyUp(key); } else { if (stalledKeyDownKey != null) { listener.keyDown(stalledKeyDownKey); stalledKeyDownKey = null; } int keyCode = getNiftyKeyId(key); boolean shiftDown = input.isAnyKeyDown(Key.LeftShift, Key.RightShift); boolean controlDown = input.isAnyKeyDown(Key.LeftCtrl, Key.RightCtrl); KeyboardInputEvent event = new KeyboardInputEvent(keyCode, Character.MIN_VALUE, false, shiftDown, controlDown); if (!currentConsumer.processKeyboardEvent(event)) { listener.keyUp(key); } } } @Override public void keyTyped(char character) { if (currentConsumer == null) { throw new IllegalStateException("Receiving input data while none was requested"); } if (input.isForwardingEnabled(ForwardingTarget.Keyboard)) { listener.keyTyped(character); } else { if (Character.isDefined(character)) { boolean shiftDown = input.isAnyKeyDown(Key.LeftShift, Key.RightShift); boolean controlDown = input.isAnyKeyDown(Key.LeftCtrl, Key.RightCtrl); KeyboardInputEvent event = new KeyboardInputEvent(KeyboardInputEvent.KEY_NONE, character, true, shiftDown, controlDown); if (!currentConsumer.processKeyboardEvent(event)) { if (stalledKeyDownKey != null) { listener.keyDown(stalledKeyDownKey); } listener.keyTyped(character); } } else { if (stalledKeyDownKey != null) { listener.keyDown(stalledKeyDownKey); } } stalledKeyDownKey = null; } } @Override public void buttonDown(int mouseX, int mouseY, @Nonnull Button button) { if (currentConsumer == null) { throw new IllegalStateException("Receiving input data while none was requested"); } if (input.isForwardingEnabled(ForwardingTarget.Mouse)) { listener.buttonDown(mouseX, mouseY, button); } else { int buttonKey = getNiftyButtonKey(button); if (currentConsumer.processMouseEvent(mouseX, mouseY, 0, buttonKey, true)) { log.debug("Nifty processed event. Consuming next click."); consumeClicks++; } else { listener.buttonDown(mouseX, mouseY, button); } } } @Override public void buttonUp(int mouseX, int mouseY, @Nonnull Button button) { if (currentConsumer == null) { throw new IllegalStateException("Receiving input data while none was requested"); } if (input.isForwardingEnabled(ForwardingTarget.Mouse)) { listener.buttonUp(mouseX, mouseY, button); } else { int buttonKey = getNiftyButtonKey(button); if (consumeClicks > 0) { consumeClicks--; } if (!currentConsumer.processMouseEvent(mouseX, mouseY, 0, buttonKey, false)) { listener.buttonUp(mouseX, mouseY, button); } } } @Override public void buttonClicked(int mouseX, int mouseY, @Nonnull Button button, int count) { if (currentConsumer == null) { throw new IllegalStateException("Receiving input data while none was requested"); } if ((consumeClicks == 0) || input.isForwardingEnabled(ForwardingTarget.Mouse)) { listener.buttonClicked(mouseX, mouseY, button, count); } else { consumeClicks--; if (count > 1) { buttonClicked(mouseX, mouseY, button, count - 1); } else { log.debug("Consumed one click."); } } } @Override public void mouseMoved(int mouseX, int mouseY) { if (currentConsumer == null) { throw new IllegalStateException("Receiving input data while none was requested"); } if (input.isForwardingEnabled(ForwardingTarget.Mouse) || !currentConsumer.processMouseEvent(mouseX, mouseY, 0, -1, false)) { listener.mouseMoved(mouseX, mouseY); } consumeClicks = 0; } @Override public void mouseDragged( @Nonnull Button button, int fromX, int fromY, int toX, int toY) { if (currentConsumer == null) { throw new IllegalStateException("Receiving input data while none was requested"); } if (input.isForwardingEnabled(ForwardingTarget.Mouse)) { listener.mouseDragged(button, fromX, fromY, toX, toY); } else { int buttonKey = getNiftyButtonKey(button); boolean startUsed = currentConsumer.processMouseEvent(fromX, fromY, 0, buttonKey, true); boolean endUsed = currentConsumer.processMouseEvent(toX, toY, 0, buttonKey, true); if (!startUsed && !endUsed) { listener.mouseDragged(button, fromX, fromY, toX, toY); } consumeClicks = 0; } } @Override public void mouseWheelMoved(int mouseX, int mouseY, int delta) { if (currentConsumer == null) { throw new IllegalStateException("Receiving input data while none was requested"); } if (input.isForwardingEnabled(ForwardingTarget.Mouse) || !currentConsumer.processMouseEvent(mouseX, mouseY, delta, -1, false)) { listener.mouseWheelMoved(mouseX, mouseY, delta); } consumeClicks = 0; } /** * Get the Nifty-GUI button key for a mouse button. * * @param button the button * @return the button key for the Nifty-GUI */ private static int getNiftyButtonKey(@Nonnull Button button) { switch (button) { case Left: return 0; case Right: return 1; case Middle: return 2; } return -1; } /** * Get the Nifty-GUI key code for a keyboard key. * * @param key the key * @return the Nifty-GUI key code */ @SuppressWarnings("SwitchStatementWithTooManyBranches") private static int getNiftyKeyId(@Nonnull Key key) { switch (key) { case A: return KeyboardInputEvent.KEY_A; case B: return KeyboardInputEvent.KEY_B; case C: return KeyboardInputEvent.KEY_C; case D: return KeyboardInputEvent.KEY_D; case E: return KeyboardInputEvent.KEY_E; case F: return KeyboardInputEvent.KEY_F; case G: return KeyboardInputEvent.KEY_G; case H: return KeyboardInputEvent.KEY_H; case I: return KeyboardInputEvent.KEY_I; case J: return KeyboardInputEvent.KEY_J; case K: return KeyboardInputEvent.KEY_K; case L: return KeyboardInputEvent.KEY_L; case M: return KeyboardInputEvent.KEY_M; case N: return KeyboardInputEvent.KEY_N; case O: return KeyboardInputEvent.KEY_O; case P: return KeyboardInputEvent.KEY_P; case Q: return KeyboardInputEvent.KEY_Q; case R: return KeyboardInputEvent.KEY_R; case S: return KeyboardInputEvent.KEY_S; case T: return KeyboardInputEvent.KEY_T; case U: return KeyboardInputEvent.KEY_U; case V: return KeyboardInputEvent.KEY_V; case W: return KeyboardInputEvent.KEY_W; case X: return KeyboardInputEvent.KEY_X; case Y: return KeyboardInputEvent.KEY_Y; case Z: return KeyboardInputEvent.KEY_Z; case LeftShift: return KeyboardInputEvent.KEY_LSHIFT; case RightShift: return KeyboardInputEvent.KEY_RSHIFT; case LeftAlt: return KeyboardInputEvent.KEY_NONE; case RightAlt: return KeyboardInputEvent.KEY_NONE; case LeftCtrl: return KeyboardInputEvent.KEY_LCONTROL; case RightCtrl: return KeyboardInputEvent.KEY_RCONTROL; case CursorLeft: return KeyboardInputEvent.KEY_LEFT; case CursorRight: return KeyboardInputEvent.KEY_RIGHT; case CursorUp: return KeyboardInputEvent.KEY_UP; case CursorDown: return KeyboardInputEvent.KEY_DOWN; case Enter: return KeyboardInputEvent.KEY_RETURN; case Backspace: return KeyboardInputEvent.KEY_BACK; case NumPad0: return KeyboardInputEvent.KEY_NUMPAD0; case NumPad1: return KeyboardInputEvent.KEY_NUMPAD1; case NumPad2: return KeyboardInputEvent.KEY_NUMPAD2; case NumPad3: return KeyboardInputEvent.KEY_NUMPAD3; case NumPad4: return KeyboardInputEvent.KEY_NUMPAD4; case NumPad5: return KeyboardInputEvent.KEY_NUMPAD5; case NumPad6: return KeyboardInputEvent.KEY_NUMPAD6; case NumPad7: return KeyboardInputEvent.KEY_NUMPAD7; case NumPad8: return KeyboardInputEvent.KEY_NUMPAD8; case NumPad9: return KeyboardInputEvent.KEY_NUMPAD9; case NumLock: return KeyboardInputEvent.KEY_NUMLOCK; case Escape: return KeyboardInputEvent.KEY_ESCAPE; case F1: return KeyboardInputEvent.KEY_F1; case F2: return KeyboardInputEvent.KEY_F2; case F3: return KeyboardInputEvent.KEY_F3; case F4: return KeyboardInputEvent.KEY_F4; case F5: return KeyboardInputEvent.KEY_F5; case F6: return KeyboardInputEvent.KEY_F6; case F7: return KeyboardInputEvent.KEY_F7; case F8: return KeyboardInputEvent.KEY_F8; case F9: return KeyboardInputEvent.KEY_F9; case F10: return KeyboardInputEvent.KEY_F10; case F11: return KeyboardInputEvent.KEY_F11; case F12: return KeyboardInputEvent.KEY_F12; case Insert: return KeyboardInputEvent.KEY_INSERT; case Delete: return KeyboardInputEvent.KEY_DELETE; case Home: return KeyboardInputEvent.KEY_HOME; case End: return KeyboardInputEvent.KEY_END; case PageUp: return KeyboardInputEvent.KEY_PRIOR; case PageDown: return KeyboardInputEvent.KEY_NEXT; case Tab: return KeyboardInputEvent.KEY_TAB; } return KeyboardInputEvent.KEY_NONE; } @Override public void setResourceLoader(@Nonnull NiftyResourceLoader niftyResourceLoader) { // not needed } @Override public void forwardEvents(@Nonnull NiftyInputConsumer inputEventConsumer) { currentConsumer = inputEventConsumer; input.poll(); currentConsumer = null; } @Override public void setMousePosition(int x, int y) { input.setMouseLocation(x, y); } }