/* * The MIT License (MIT) * * Copyright (c) 2014-2017 Sri Harsha Chilakapati * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.shc.silenceengine.backend.gwt; import com.google.gwt.canvas.client.Canvas; import com.google.gwt.core.client.JsArray; import com.google.gwt.dom.client.CanvasElement; import com.google.gwt.dom.client.Touch; import com.google.gwt.event.dom.client.KeyCodes; import com.shc.silenceengine.core.SilenceEngine; import com.shc.silenceengine.events.ControllerConnectionEvent; import com.shc.silenceengine.input.Controller; import com.shc.silenceengine.input.InputDevice; import com.shc.silenceengine.input.Keyboard; import com.shc.silenceengine.input.Mouse; import java.util.HashMap; import java.util.Map; /** * @author Sri Harsha Chilakapati */ class GwtInputDevice extends InputDevice { private final Map<Integer, Integer> keysMap = new HashMap<>(); private final Map<Integer, Integer> mouseMap = new HashMap<>(); private final Map<Integer, Integer> keyShortCircuitMap = new HashMap<>(); private boolean gamepadsExist = false; GwtInputDevice() { createKeyMapping(); createMouseMapping(); Canvas canvas = ((GwtDisplayDevice) SilenceEngine.display).canvas; canvas.addKeyPressHandler(event -> { postTextEvent((char) event.getUnicodeCharCode()); event.preventDefault(); }); canvas.addKeyDownHandler(event -> { int keyCode = getKeyCode(event.getNativeKeyCode()); postKeyEvent(keyCode, true); switch (keyCode) { case Keyboard.KEY_UP: case Keyboard.KEY_DOWN: case Keyboard.KEY_RIGHT: case Keyboard.KEY_LEFT: event.preventDefault(); } }); canvas.addKeyUpHandler(event -> { int keyCode = getKeyCode(event.getNativeKeyCode()); postKeyEvent(keyCode, false); switch (keyCode) { case Keyboard.KEY_UP: case Keyboard.KEY_DOWN: case Keyboard.KEY_RIGHT: case Keyboard.KEY_LEFT: event.preventDefault(); } }); canvas.addMouseDownHandler(event -> { postMouseEvent(getMouseCode(event.getNativeButton()), true); canvas.setFocus(true); event.preventDefault(); }); canvas.addMouseUpHandler(event -> { postMouseEvent(getMouseCode(event.getNativeButton()), false); event.preventDefault(); }); canvas.addMouseMoveHandler(event -> { int x = event.getX(); int y = event.getY(); Mouse.dx = x - Mouse.x; Mouse.dy = y - Mouse.y; Mouse.x = x; Mouse.y = y; event.preventDefault(); }); canvas.addMouseWheelHandler(event -> { int dsy = event.getDeltaY(); // To normalize between 0 and 1 Mouse.deltaScrollY = dsy > 0 ? 1 : dsy == 0 ? 0 : -1; Mouse.deltaScrollX = 0; event.preventDefault(); }); canvas.addTouchStartHandler(event -> { postTouchEvents(event.getTargetTouches(), true); event.preventDefault(); }); canvas.addTouchMoveHandler(event -> { postTouchEvents(event.getTargetTouches(), true); event.preventDefault(); }); canvas.addTouchEndHandler(event -> { postTouchEvents(event.getTargetTouches(), false); event.preventDefault(); }); canvas.addTouchCancelHandler(event -> { postTouchEvents(event.getTargetTouches(), false); event.preventDefault(); }); preventContextMenu(canvas.getCanvasElement()); registerControllerConnectionEvents(this, Controller.MAX_CONTROLLERS); } static void pollControllers() { GwtInputDevice inputDevice = (GwtInputDevice) SilenceEngine.input; if (inputDevice.gamepadsExist) pollControllers(inputDevice); } private static native void pollControllers(GwtInputDevice gid) /*-{ var gamepads = navigator.getGamepads(); for (var i = 0; i < gamepads.length; i++) { var gamepad = gamepads[i]; if (gamepad && gamepad.connected) { var id = gamepad.index; var j, amount; for (j = 0; j < gamepad.buttons.length; j++) { var button = j; var state = gamepad.buttons[j].pressed; amount = gamepad.buttons[j].value; if (button != -1) gid.@com.shc.silenceengine.backend.gwt.GwtInputDevice::postControllerButtonEvent(*)(id, button, state, amount) } for (j = 0; j < gamepad.axes.length; j++) { var axe = j; amount = gamepad.axes[j]; if (axe != -1) gid.@com.shc.silenceengine.backend.gwt.GwtInputDevice::postControllerAxisEvent(*)(id, axe, amount); } } } }-*/; private void createControllerEvent(int id, boolean connected, boolean ideal, String name, int numButtons, int numAxes) { ControllerConnectionEvent event = new ControllerConnectionEvent(); event.buttonMapping = new Controller.Mapping(); event.axisMapping = new Controller.Mapping(); event.isControllerIdeal = ideal; event.controllerConnected = connected; event.controllerName = name; event.numButtons = numButtons; event.numAxes = numAxes; if (ideal) { // Controller mappings came from the HTML5 GamePad API // https://www.w3.org/TR/gamepad/#remapping event.buttonMapping.map(0, Controller.BUTTON_A); event.buttonMapping.map(1, Controller.BUTTON_B); event.buttonMapping.map(2, Controller.BUTTON_X); event.buttonMapping.map(3, Controller.BUTTON_Y); event.buttonMapping.map(4, Controller.BUTTON_L1); event.buttonMapping.map(5, Controller.BUTTON_R1); event.buttonMapping.map(6, Controller.BUTTON_L2); event.buttonMapping.map(7, Controller.BUTTON_R2); event.buttonMapping.map(8, Controller.BUTTON_SELECT); event.buttonMapping.map(9, Controller.BUTTON_START); event.buttonMapping.map(10, Controller.BUTTON_LEFT_STICK); event.buttonMapping.map(11, Controller.BUTTON_RIGHT_STICK); event.buttonMapping.map(12, Controller.BUTTON_DPAD_UP); event.buttonMapping.map(13, Controller.BUTTON_DPAD_DOWN); event.buttonMapping.map(14, Controller.BUTTON_DPAD_LEFT); event.buttonMapping.map(15, Controller.BUTTON_DPAD_RIGHT); event.axisMapping.map(0, Controller.AXIS_LEFT_X); event.axisMapping.map(1, Controller.AXIS_LEFT_Y); event.axisMapping.map(2, Controller.AXIS_RIGHT_X); event.axisMapping.map(3, Controller.AXIS_RIGHT_Y); } postControllerConnectionEvent(id, event); } private native void registerControllerConnectionEvents(GwtInputDevice gid, int maxGamePads) /*-{ $wnd.addEventListener('gamepadconnected', function gamePadConnectEventHandler(event) { gid.@com.shc.silenceengine.backend.gwt.GwtInputDevice::gamepadsExist = true; var gamepad = event.gamepad; var id = gamepad.index; var name = gamepad.id; if (id >= maxGamePads) return; var ideal = gamepad.mapping == 'standard'; gid.@com.shc.silenceengine.backend.gwt.GwtInputDevice::createControllerEvent(*)(id, true, ideal, name, gamepad.buttons.length, gamepad.axes.length); }); $wnd.addEventListener('gamepaddisconnected', function gamePadDisconnectEventHandler(event) { var gamepad = event.gamepad; var id = gamepad.index; var name = 'Null Gamepad'; if (id >= maxGamePads) return; gid.@com.shc.silenceengine.backend.gwt.GwtInputDevice::createControllerEvent(*)(id, false, false, name, 0, 0); }); }-*/; private native void preventContextMenu(CanvasElement canvas) /*-{ canvas.oncontextmenu = function (){ return false; }; }-*/; private void postTouchEvents(JsArray<Touch> touches, boolean isDown) { int i, j; // Browsers report only the available touches in a list. So update all the touches in the list accurately. // We ignore the touches past finger 9 (only ten fingers are read). for (i = 0, j = com.shc.silenceengine.input.Touch.FINGER_0; i < touches.length() && j <= com.shc.silenceengine.input.Touch.FINGER_9; i++, j++) { Touch touch = touches.get(i); postTouchEvent(j, isDown, touch.getClientX(), touch.getClientY()); } // For all the remain fingers, set the finger down state to false. Otherwise they are reported as down // continuously. This will fix that issue. while (j <= com.shc.silenceengine.input.Touch.FINGER_9) postTouchEvent(j++, false, 0, 0); } @Override public void postKeyEvent(int keyCode, boolean isDown) { super.postKeyEvent(keyCode, isDown); Integer shortCircuitCode = keyShortCircuitMap.get(keyCode); if (shortCircuitCode != null) super.postKeyEvent(shortCircuitCode, isDown); } private int getKeyCode(int nativeKeyCode) { Integer code = keysMap.get(nativeKeyCode); return code == null ? 0 : code; } private int getMouseCode(int nativeButtonCode) { Integer code = mouseMap.get(nativeButtonCode); return code == null ? 0 : code; } private void createKeyMapping() { // Escape key keysMap.put(KeyCodes.KEY_ESCAPE, Keyboard.KEY_ESCAPE); // Function keys keysMap.put(KeyCodes.KEY_F1, Keyboard.KEY_F1); keysMap.put(KeyCodes.KEY_F2, Keyboard.KEY_F2); keysMap.put(KeyCodes.KEY_F3, Keyboard.KEY_F3); keysMap.put(KeyCodes.KEY_F4, Keyboard.KEY_F4); keysMap.put(KeyCodes.KEY_F5, Keyboard.KEY_F5); keysMap.put(KeyCodes.KEY_F6, Keyboard.KEY_F6); keysMap.put(KeyCodes.KEY_F7, Keyboard.KEY_F7); keysMap.put(KeyCodes.KEY_F8, Keyboard.KEY_F8); keysMap.put(KeyCodes.KEY_F9, Keyboard.KEY_F9); keysMap.put(KeyCodes.KEY_F10, Keyboard.KEY_F10); keysMap.put(KeyCodes.KEY_F11, Keyboard.KEY_F11); keysMap.put(KeyCodes.KEY_F12, Keyboard.KEY_F12); // Number keys keysMap.put(KeyCodes.KEY_ONE, Keyboard.KEY_1); keysMap.put(KeyCodes.KEY_TWO, Keyboard.KEY_2); keysMap.put(KeyCodes.KEY_THREE, Keyboard.KEY_3); keysMap.put(KeyCodes.KEY_FOUR, Keyboard.KEY_4); keysMap.put(KeyCodes.KEY_FIVE, Keyboard.KEY_5); keysMap.put(KeyCodes.KEY_SIX, Keyboard.KEY_6); keysMap.put(KeyCodes.KEY_SEVEN, Keyboard.KEY_7); keysMap.put(KeyCodes.KEY_EIGHT, Keyboard.KEY_8); keysMap.put(KeyCodes.KEY_NINE, Keyboard.KEY_9); keysMap.put(KeyCodes.KEY_ZERO, Keyboard.KEY_0); // Symbol keys (codes missing from KeyCodes class are found out by checking output in keydown handler). keysMap.put(172, Keyboard.KEY_TILDE); keysMap.put(189, Keyboard.KEY_MINUS); keysMap.put(187, Keyboard.KEY_PLUS); keysMap.put(219, Keyboard.KEY_LEFT_BRACKET); keysMap.put(221, Keyboard.KEY_RIGHT_BRACKET); keysMap.put(220, Keyboard.KEY_PIPE); keysMap.put(186, Keyboard.KEY_COLON); keysMap.put(222, Keyboard.KEY_SINGLE_QUOTE); keysMap.put(188, Keyboard.KEY_LESS_THAN); keysMap.put(190, Keyboard.KEY_GREATER_THAN); keysMap.put(191, Keyboard.KEY_QUESTION); // Lock keys keysMap.put(KeyCodes.KEY_CAPS_LOCK, Keyboard.KEY_CAPS_LOCK); keysMap.put(KeyCodes.KEY_NUMLOCK, Keyboard.KEY_NUM_LOCK); keysMap.put(KeyCodes.KEY_SCROLL_LOCK, Keyboard.KEY_SCROLL_LOCK); // Special keys keysMap.put(KeyCodes.KEY_TAB, Keyboard.KEY_TAB); keysMap.put(KeyCodes.KEY_BACKSPACE, Keyboard.KEY_BACKSPACE); keysMap.put(KeyCodes.KEY_ENTER, Keyboard.KEY_ENTER); keysMap.put(KeyCodes.KEY_SHIFT, Keyboard.KEY_LEFT_SHIFT); keysMap.put(KeyCodes.KEY_CTRL, Keyboard.KEY_LEFT_CTRL); keysMap.put(KeyCodes.KEY_ALT, Keyboard.KEY_LEFT_ALT); keysMap.put(91, Keyboard.KEY_LEFT_SUPER); keysMap.put(92, Keyboard.KEY_RIGHT_SUPER); keysMap.put(KeyCodes.KEY_INSERT, Keyboard.KEY_INSERT); keysMap.put(KeyCodes.KEY_DELETE, Keyboard.KEY_DELETE); keysMap.put(KeyCodes.KEY_HOME, Keyboard.KEY_HOME); keysMap.put(KeyCodes.KEY_END, Keyboard.KEY_END); keysMap.put(KeyCodes.KEY_PAGEUP, Keyboard.KEY_PAGEUP); keysMap.put(KeyCodes.KEY_PAGEDOWN, Keyboard.KEY_PAGEDOWN); keysMap.put(KeyCodes.KEY_PAUSE, Keyboard.KEY_PAUSE_BREAK); keysMap.put(KeyCodes.KEY_PRINT_SCREEN, Keyboard.KEY_PRINT_SCREEN); // Keypad keys keysMap.put(KeyCodes.KEY_NUM_ZERO, Keyboard.KEY_KP_0); keysMap.put(KeyCodes.KEY_NUM_ONE, Keyboard.KEY_KP_1); keysMap.put(KeyCodes.KEY_NUM_TWO, Keyboard.KEY_KP_2); keysMap.put(KeyCodes.KEY_NUM_THREE, Keyboard.KEY_KP_3); keysMap.put(KeyCodes.KEY_NUM_FOUR, Keyboard.KEY_KP_4); keysMap.put(KeyCodes.KEY_NUM_FIVE, Keyboard.KEY_KP_5); keysMap.put(KeyCodes.KEY_NUM_SIX, Keyboard.KEY_KP_6); keysMap.put(KeyCodes.KEY_NUM_SEVEN, Keyboard.KEY_KP_7); keysMap.put(KeyCodes.KEY_NUM_EIGHT, Keyboard.KEY_KP_8); keysMap.put(KeyCodes.KEY_NUM_NINE, Keyboard.KEY_KP_9); keysMap.put(KeyCodes.KEY_NUM_DIVISION, Keyboard.KEY_KP_SLASH); keysMap.put(KeyCodes.KEY_NUM_MULTIPLY, Keyboard.KEY_KP_ASTERISK); keysMap.put(KeyCodes.KEY_NUM_MINUS, Keyboard.KEY_KP_MINUS); keysMap.put(KeyCodes.KEY_NUM_PLUS, Keyboard.KEY_KP_PLUS); keysMap.put(KeyCodes.KEY_NUM_PERIOD, Keyboard.KEY_KP_PERIOD); // Arrow keys keysMap.put(KeyCodes.KEY_UP, Keyboard.KEY_UP); keysMap.put(KeyCodes.KEY_DOWN, Keyboard.KEY_DOWN); keysMap.put(KeyCodes.KEY_LEFT, Keyboard.KEY_LEFT); keysMap.put(KeyCodes.KEY_RIGHT, Keyboard.KEY_RIGHT); // Alphabet keys keysMap.put(KeyCodes.KEY_A, Keyboard.KEY_A); keysMap.put(KeyCodes.KEY_B, Keyboard.KEY_B); keysMap.put(KeyCodes.KEY_C, Keyboard.KEY_C); keysMap.put(KeyCodes.KEY_D, Keyboard.KEY_D); keysMap.put(KeyCodes.KEY_E, Keyboard.KEY_E); keysMap.put(KeyCodes.KEY_F, Keyboard.KEY_F); keysMap.put(KeyCodes.KEY_G, Keyboard.KEY_G); keysMap.put(KeyCodes.KEY_H, Keyboard.KEY_H); keysMap.put(KeyCodes.KEY_I, Keyboard.KEY_I); keysMap.put(KeyCodes.KEY_J, Keyboard.KEY_J); keysMap.put(KeyCodes.KEY_K, Keyboard.KEY_K); keysMap.put(KeyCodes.KEY_L, Keyboard.KEY_L); keysMap.put(KeyCodes.KEY_M, Keyboard.KEY_M); keysMap.put(KeyCodes.KEY_N, Keyboard.KEY_N); keysMap.put(KeyCodes.KEY_O, Keyboard.KEY_O); keysMap.put(KeyCodes.KEY_P, Keyboard.KEY_P); keysMap.put(KeyCodes.KEY_Q, Keyboard.KEY_Q); keysMap.put(KeyCodes.KEY_R, Keyboard.KEY_R); keysMap.put(KeyCodes.KEY_S, Keyboard.KEY_S); keysMap.put(KeyCodes.KEY_T, Keyboard.KEY_T); keysMap.put(KeyCodes.KEY_U, Keyboard.KEY_U); keysMap.put(KeyCodes.KEY_V, Keyboard.KEY_V); keysMap.put(KeyCodes.KEY_W, Keyboard.KEY_W); keysMap.put(KeyCodes.KEY_X, Keyboard.KEY_X); keysMap.put(KeyCodes.KEY_Y, Keyboard.KEY_Y); keysMap.put(KeyCodes.KEY_Z, Keyboard.KEY_Z); keysMap.put(KeyCodes.KEY_SPACE, Keyboard.KEY_SPACE); // Short circuit some keys keyShortCircuitMap.put(Keyboard.KEY_ENTER, Keyboard.KEY_KP_ENTER); keyShortCircuitMap.put(Keyboard.KEY_LEFT_SHIFT, Keyboard.KEY_RIGHT_SHIFT); keyShortCircuitMap.put(Keyboard.KEY_LEFT_CTRL, Keyboard.KEY_RIGHT_CTRL); keyShortCircuitMap.put(Keyboard.KEY_LEFT_ALT, Keyboard.KEY_RIGHT_ALT); } private void createMouseMapping() { mouseMap.put(1, Mouse.BUTTON_LEFT); mouseMap.put(2, Mouse.BUTTON_RIGHT); mouseMap.put(4, Mouse.BUTTON_MIDDLE); } }