1 /*************************************************************************/ 2 /* GodotInputHandler.java */ 3 /*************************************************************************/ 4 /* This file is part of: */ 5 /* GODOT ENGINE */ 6 /* https://godotengine.org */ 7 /*************************************************************************/ 8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ 9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ 10 /* */ 11 /* Permission is hereby granted, free of charge, to any person obtaining */ 12 /* a copy of this software and associated documentation files (the */ 13 /* "Software"), to deal in the Software without restriction, including */ 14 /* without limitation the rights to use, copy, modify, merge, publish, */ 15 /* distribute, sublicense, and/or sell copies of the Software, and to */ 16 /* permit persons to whom the Software is furnished to do so, subject to */ 17 /* the following conditions: */ 18 /* */ 19 /* The above copyright notice and this permission notice shall be */ 20 /* included in all copies or substantial portions of the Software. */ 21 /* */ 22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ 23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ 24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ 25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ 26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ 27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ 28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 29 /*************************************************************************/ 30 31 package org.godotengine.godot.input; 32 33 import static org.godotengine.godot.utils.GLUtils.DEBUG; 34 35 import org.godotengine.godot.GodotLib; 36 import org.godotengine.godot.GodotView; 37 import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener; 38 39 import android.util.Log; 40 import android.view.InputDevice; 41 import android.view.InputDevice.MotionRange; 42 import android.view.KeyEvent; 43 import android.view.MotionEvent; 44 45 import java.util.ArrayList; 46 import java.util.Collections; 47 import java.util.Comparator; 48 import java.util.List; 49 50 /** 51 * Handles input related events for the {@link GodotView} view. 52 */ 53 public class GodotInputHandler implements InputDeviceListener { 54 55 private final ArrayList<Joystick> joysticksDevices = new ArrayList<Joystick>(); 56 57 private final GodotView godotView; 58 private final InputManagerCompat inputManager; 59 GodotInputHandler(GodotView godotView)60 public GodotInputHandler(GodotView godotView) { 61 this.godotView = godotView; 62 this.inputManager = InputManagerCompat.Factory.getInputManager(godotView.getContext()); 63 this.inputManager.registerInputDeviceListener(this, null); 64 } 65 queueEvent(Runnable task)66 private void queueEvent(Runnable task) { 67 godotView.queueEvent(task); 68 } 69 isKeyEvent_GameDevice(int source)70 private boolean isKeyEvent_GameDevice(int source) { 71 // Note that keyboards are often (SOURCE_KEYBOARD | SOURCE_DPAD) 72 if (source == (InputDevice.SOURCE_KEYBOARD | InputDevice.SOURCE_DPAD)) 73 return false; 74 75 return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD; 76 } 77 onKeyUp(final int keyCode, KeyEvent event)78 public boolean onKeyUp(final int keyCode, KeyEvent event) { 79 if (keyCode == KeyEvent.KEYCODE_BACK) { 80 return true; 81 } 82 83 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { 84 return false; 85 }; 86 87 int source = event.getSource(); 88 if (isKeyEvent_GameDevice(source)) { 89 90 final int button = getGodotButton(keyCode); 91 final int device_id = findJoystickDevice(event.getDeviceId()); 92 93 // Check if the device exists 94 if (device_id > -1) { 95 queueEvent(new Runnable() { 96 @Override 97 public void run() { 98 GodotLib.joybutton(device_id, button, false); 99 } 100 }); 101 } 102 } else { 103 final int chr = event.getUnicodeChar(0); 104 queueEvent(new Runnable() { 105 @Override 106 public void run() { 107 GodotLib.key(keyCode, chr, false); 108 } 109 }); 110 }; 111 112 return true; 113 } 114 onKeyDown(final int keyCode, KeyEvent event)115 public boolean onKeyDown(final int keyCode, KeyEvent event) { 116 if (keyCode == KeyEvent.KEYCODE_BACK) { 117 godotView.onBackPressed(); 118 // press 'back' button should not terminate program 119 //normal handle 'back' event in game logic 120 return true; 121 } 122 123 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { 124 return false; 125 }; 126 127 int source = event.getSource(); 128 //Log.e(TAG, String.format("Key down! source %d, device %d, joystick %d, %d, %d", event.getDeviceId(), source, (source & InputDevice.SOURCE_JOYSTICK), (source & InputDevice.SOURCE_DPAD), (source & InputDevice.SOURCE_GAMEPAD))); 129 130 if (isKeyEvent_GameDevice(source)) { 131 132 if (event.getRepeatCount() > 0) // ignore key echo 133 return true; 134 135 final int button = getGodotButton(keyCode); 136 final int device_id = findJoystickDevice(event.getDeviceId()); 137 138 // Check if the device exists 139 if (device_id > -1) { 140 queueEvent(new Runnable() { 141 @Override 142 public void run() { 143 GodotLib.joybutton(device_id, button, true); 144 } 145 }); 146 } 147 } else { 148 final int chr = event.getUnicodeChar(0); 149 queueEvent(new Runnable() { 150 @Override 151 public void run() { 152 GodotLib.key(keyCode, chr, true); 153 } 154 }); 155 }; 156 157 return true; 158 } 159 onGenericMotionEvent(MotionEvent event)160 public boolean onGenericMotionEvent(MotionEvent event) { 161 if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) { 162 163 final int device_id = findJoystickDevice(event.getDeviceId()); 164 165 // Check if the device exists 166 if (device_id > -1) { 167 Joystick joy = joysticksDevices.get(device_id); 168 169 for (int i = 0; i < joy.axes.size(); i++) { 170 InputDevice.MotionRange range = joy.axes.get(i); 171 final float value = (event.getAxisValue(range.getAxis()) - range.getMin()) / range.getRange() * 2.0f - 1.0f; 172 final int idx = i; 173 queueEvent(new Runnable() { 174 @Override 175 public void run() { 176 GodotLib.joyaxis(device_id, idx, value); 177 } 178 }); 179 } 180 181 for (int i = 0; i < joy.hats.size(); i += 2) { 182 final int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis())); 183 final int hatY = Math.round(event.getAxisValue(joy.hats.get(i + 1).getAxis())); 184 queueEvent(new Runnable() { 185 @Override 186 public void run() { 187 GodotLib.joyhat(device_id, hatX, hatY); 188 } 189 }); 190 } 191 return true; 192 } 193 } else if ((event.getSource() & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS) { 194 final int x = Math.round(event.getX()); 195 final int y = Math.round(event.getY()); 196 final int type = event.getAction(); 197 queueEvent(new Runnable() { 198 @Override 199 public void run() { 200 GodotLib.hover(type, x, y); 201 } 202 }); 203 return true; 204 } 205 206 return false; 207 } 208 initInputDevices()209 public void initInputDevices() { 210 /* initially add input devices*/ 211 int[] deviceIds = inputManager.getInputDeviceIds(); 212 for (int deviceId : deviceIds) { 213 InputDevice device = inputManager.getInputDevice(deviceId); 214 if (DEBUG) { 215 Log.v("GodotView", String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName())); 216 } 217 onInputDeviceAdded(deviceId); 218 } 219 } 220 221 @Override onInputDeviceAdded(int deviceId)222 public void onInputDeviceAdded(int deviceId) { 223 int id = findJoystickDevice(deviceId); 224 225 // Check if the device has not been already added 226 if (id < 0) { 227 InputDevice device = inputManager.getInputDevice(deviceId); 228 //device can be null if deviceId is not found 229 if (device != null) { 230 int sources = device.getSources(); 231 if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) || 232 ((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK)) { 233 id = joysticksDevices.size(); 234 235 Joystick joy = new Joystick(); 236 joy.device_id = deviceId; 237 joy.name = device.getName(); 238 joy.axes = new ArrayList<InputDevice.MotionRange>(); 239 joy.hats = new ArrayList<InputDevice.MotionRange>(); 240 241 List<InputDevice.MotionRange> ranges = device.getMotionRanges(); 242 Collections.sort(ranges, new RangeComparator()); 243 244 for (InputDevice.MotionRange range : ranges) { 245 if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) { 246 joy.hats.add(range); 247 } else { 248 joy.axes.add(range); 249 } 250 } 251 252 joysticksDevices.add(joy); 253 254 final int device_id = id; 255 final String name = joy.name; 256 queueEvent(new Runnable() { 257 @Override 258 public void run() { 259 GodotLib.joyconnectionchanged(device_id, true, name); 260 } 261 }); 262 } 263 } 264 } 265 } 266 267 @Override onInputDeviceRemoved(int deviceId)268 public void onInputDeviceRemoved(int deviceId) { 269 final int device_id = findJoystickDevice(deviceId); 270 271 // Check if the evice has not been already removed 272 if (device_id > -1) { 273 joysticksDevices.remove(device_id); 274 275 queueEvent(new Runnable() { 276 @Override 277 public void run() { 278 GodotLib.joyconnectionchanged(device_id, false, ""); 279 } 280 }); 281 } 282 } 283 284 @Override onInputDeviceChanged(int deviceId)285 public void onInputDeviceChanged(int deviceId) { 286 onInputDeviceRemoved(deviceId); 287 onInputDeviceAdded(deviceId); 288 } 289 290 private static class RangeComparator implements Comparator<MotionRange> { 291 @Override compare(MotionRange arg0, MotionRange arg1)292 public int compare(MotionRange arg0, MotionRange arg1) { 293 return arg0.getAxis() - arg1.getAxis(); 294 } 295 } 296 getGodotButton(int keyCode)297 public static int getGodotButton(int keyCode) { 298 int button; 299 switch (keyCode) { 300 case KeyEvent.KEYCODE_BUTTON_A: // Android A is SNES B 301 button = 0; 302 break; 303 case KeyEvent.KEYCODE_BUTTON_B: 304 button = 1; 305 break; 306 case KeyEvent.KEYCODE_BUTTON_X: // Android X is SNES Y 307 button = 2; 308 break; 309 case KeyEvent.KEYCODE_BUTTON_Y: 310 button = 3; 311 break; 312 case KeyEvent.KEYCODE_BUTTON_L1: 313 button = 9; 314 break; 315 case KeyEvent.KEYCODE_BUTTON_L2: 316 button = 15; 317 break; 318 case KeyEvent.KEYCODE_BUTTON_R1: 319 button = 10; 320 break; 321 case KeyEvent.KEYCODE_BUTTON_R2: 322 button = 16; 323 break; 324 case KeyEvent.KEYCODE_BUTTON_SELECT: 325 button = 4; 326 break; 327 case KeyEvent.KEYCODE_BUTTON_START: 328 button = 6; 329 break; 330 case KeyEvent.KEYCODE_BUTTON_THUMBL: 331 button = 7; 332 break; 333 case KeyEvent.KEYCODE_BUTTON_THUMBR: 334 button = 8; 335 break; 336 case KeyEvent.KEYCODE_DPAD_UP: 337 button = 11; 338 break; 339 case KeyEvent.KEYCODE_DPAD_DOWN: 340 button = 12; 341 break; 342 case KeyEvent.KEYCODE_DPAD_LEFT: 343 button = 13; 344 break; 345 case KeyEvent.KEYCODE_DPAD_RIGHT: 346 button = 14; 347 break; 348 case KeyEvent.KEYCODE_BUTTON_C: 349 button = 17; 350 break; 351 case KeyEvent.KEYCODE_BUTTON_Z: 352 button = 18; 353 break; 354 355 default: 356 button = keyCode - KeyEvent.KEYCODE_BUTTON_1 + 20; 357 break; 358 } 359 return button; 360 } 361 findJoystickDevice(int device_id)362 private int findJoystickDevice(int device_id) { 363 for (int i = 0; i < joysticksDevices.size(); i++) { 364 if (joysticksDevices.get(i).device_id == device_id) { 365 return i; 366 } 367 } 368 369 return -1; 370 } 371 } 372