1 package org.libsdl.app; 2 3 import java.util.ArrayList; 4 import java.util.Collections; 5 import java.util.Comparator; 6 import java.util.List; 7 8 import android.content.Context; 9 import android.os.*; 10 import android.view.*; 11 import android.util.Log; 12 13 14 public class SDLControllerManager 15 { 16 nativeSetupJNI()17 public static native int nativeSetupJNI(); 18 nativeAddJoystick(int device_id, String name, String desc, int vendor_id, int product_id, boolean is_accelerometer, int button_mask, int naxes, int nhats, int nballs)19 public static native int nativeAddJoystick(int device_id, String name, String desc, 20 int vendor_id, int product_id, 21 boolean is_accelerometer, int button_mask, 22 int naxes, int nhats, int nballs); nativeRemoveJoystick(int device_id)23 public static native int nativeRemoveJoystick(int device_id); nativeAddHaptic(int device_id, String name)24 public static native int nativeAddHaptic(int device_id, String name); nativeRemoveHaptic(int device_id)25 public static native int nativeRemoveHaptic(int device_id); onNativePadDown(int device_id, int keycode)26 public static native int onNativePadDown(int device_id, int keycode); onNativePadUp(int device_id, int keycode)27 public static native int onNativePadUp(int device_id, int keycode); onNativeJoy(int device_id, int axis, float value)28 public static native void onNativeJoy(int device_id, int axis, 29 float value); onNativeHat(int device_id, int hat_id, int x, int y)30 public static native void onNativeHat(int device_id, int hat_id, 31 int x, int y); 32 33 protected static SDLJoystickHandler mJoystickHandler; 34 protected static SDLHapticHandler mHapticHandler; 35 36 private static final String TAG = "SDLControllerManager"; 37 initialize()38 public static void initialize() { 39 if (mJoystickHandler == null) { 40 if (Build.VERSION.SDK_INT >= 19) { 41 mJoystickHandler = new SDLJoystickHandler_API19(); 42 } else { 43 mJoystickHandler = new SDLJoystickHandler_API16(); 44 } 45 } 46 47 if (mHapticHandler == null) { 48 if (Build.VERSION.SDK_INT >= 26) { 49 mHapticHandler = new SDLHapticHandler_API26(); 50 } else { 51 mHapticHandler = new SDLHapticHandler(); 52 } 53 } 54 } 55 56 // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance handleJoystickMotionEvent(MotionEvent event)57 public static boolean handleJoystickMotionEvent(MotionEvent event) { 58 return mJoystickHandler.handleMotionEvent(event); 59 } 60 61 /** 62 * This method is called by SDL using JNI. 63 */ pollInputDevices()64 public static void pollInputDevices() { 65 mJoystickHandler.pollInputDevices(); 66 } 67 68 /** 69 * This method is called by SDL using JNI. 70 */ pollHapticDevices()71 public static void pollHapticDevices() { 72 mHapticHandler.pollHapticDevices(); 73 } 74 75 /** 76 * This method is called by SDL using JNI. 77 */ hapticRun(int device_id, float intensity, int length)78 public static void hapticRun(int device_id, float intensity, int length) { 79 mHapticHandler.run(device_id, intensity, length); 80 } 81 82 /** 83 * This method is called by SDL using JNI. 84 */ hapticStop(int device_id)85 public static void hapticStop(int device_id) 86 { 87 mHapticHandler.stop(device_id); 88 } 89 90 // Check if a given device is considered a possible SDL joystick isDeviceSDLJoystick(int deviceId)91 public static boolean isDeviceSDLJoystick(int deviceId) { 92 InputDevice device = InputDevice.getDevice(deviceId); 93 // We cannot use InputDevice.isVirtual before API 16, so let's accept 94 // only nonnegative device ids (VIRTUAL_KEYBOARD equals -1) 95 if ((device == null) || (deviceId < 0)) { 96 return false; 97 } 98 int sources = device.getSources(); 99 100 /* This is called for every button press, so let's not spam the logs */ 101 /** 102 if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { 103 Log.v(TAG, "Input device " + device.getName() + " has class joystick."); 104 } 105 if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) { 106 Log.v(TAG, "Input device " + device.getName() + " is a dpad."); 107 } 108 if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) { 109 Log.v(TAG, "Input device " + device.getName() + " is a gamepad."); 110 } 111 **/ 112 113 return ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 || 114 ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) || 115 ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) 116 ); 117 } 118 119 } 120 121 class SDLJoystickHandler { 122 123 /** 124 * Handles given MotionEvent. 125 * @param event the event to be handled. 126 * @return if given event was processed. 127 */ handleMotionEvent(MotionEvent event)128 public boolean handleMotionEvent(MotionEvent event) { 129 return false; 130 } 131 132 /** 133 * Handles adding and removing of input devices. 134 */ pollInputDevices()135 public void pollInputDevices() { 136 } 137 } 138 139 /* Actual joystick functionality available for API >= 12 devices */ 140 class SDLJoystickHandler_API16 extends SDLJoystickHandler { 141 142 static class SDLJoystick { 143 public int device_id; 144 public String name; 145 public String desc; 146 public ArrayList<InputDevice.MotionRange> axes; 147 public ArrayList<InputDevice.MotionRange> hats; 148 } 149 static class RangeComparator implements Comparator<InputDevice.MotionRange> { 150 @Override compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1)151 public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) { 152 // Some controllers, like the Moga Pro 2, return AXIS_GAS (22) for right trigger and AXIS_BRAKE (23) for left trigger - swap them so they're sorted in the right order for SDL 153 int arg0Axis = arg0.getAxis(); 154 int arg1Axis = arg1.getAxis(); 155 if (arg0Axis == MotionEvent.AXIS_GAS) { 156 arg0Axis = MotionEvent.AXIS_BRAKE; 157 } else if (arg0Axis == MotionEvent.AXIS_BRAKE) { 158 arg0Axis = MotionEvent.AXIS_GAS; 159 } 160 if (arg1Axis == MotionEvent.AXIS_GAS) { 161 arg1Axis = MotionEvent.AXIS_BRAKE; 162 } else if (arg1Axis == MotionEvent.AXIS_BRAKE) { 163 arg1Axis = MotionEvent.AXIS_GAS; 164 } 165 166 return arg0Axis - arg1Axis; 167 } 168 } 169 170 private ArrayList<SDLJoystick> mJoysticks; 171 SDLJoystickHandler_API16()172 public SDLJoystickHandler_API16() { 173 174 mJoysticks = new ArrayList<SDLJoystick>(); 175 } 176 177 @Override pollInputDevices()178 public void pollInputDevices() { 179 int[] deviceIds = InputDevice.getDeviceIds(); 180 for(int i=0; i < deviceIds.length; ++i) { 181 SDLJoystick joystick = getJoystick(deviceIds[i]); 182 if (joystick == null) { 183 joystick = new SDLJoystick(); 184 InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]); 185 if (SDLControllerManager.isDeviceSDLJoystick(deviceIds[i])) { 186 joystick.device_id = deviceIds[i]; 187 joystick.name = joystickDevice.getName(); 188 joystick.desc = getJoystickDescriptor(joystickDevice); 189 joystick.axes = new ArrayList<InputDevice.MotionRange>(); 190 joystick.hats = new ArrayList<InputDevice.MotionRange>(); 191 192 List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges(); 193 Collections.sort(ranges, new RangeComparator()); 194 for (InputDevice.MotionRange range : ranges ) { 195 if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { 196 if (range.getAxis() == MotionEvent.AXIS_HAT_X || 197 range.getAxis() == MotionEvent.AXIS_HAT_Y) { 198 joystick.hats.add(range); 199 } 200 else { 201 joystick.axes.add(range); 202 } 203 } 204 } 205 206 mJoysticks.add(joystick); 207 SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, getVendorId(joystickDevice), getProductId(joystickDevice), false, getButtonMask(joystickDevice), joystick.axes.size(), joystick.hats.size()/2, 0); 208 } 209 } 210 } 211 212 /* Check removed devices */ 213 ArrayList<Integer> removedDevices = new ArrayList<Integer>(); 214 for(int i=0; i < mJoysticks.size(); i++) { 215 int device_id = mJoysticks.get(i).device_id; 216 int j; 217 for (j=0; j < deviceIds.length; j++) { 218 if (device_id == deviceIds[j]) break; 219 } 220 if (j == deviceIds.length) { 221 removedDevices.add(Integer.valueOf(device_id)); 222 } 223 } 224 225 for(int i=0; i < removedDevices.size(); i++) { 226 int device_id = removedDevices.get(i).intValue(); 227 SDLControllerManager.nativeRemoveJoystick(device_id); 228 for (int j=0; j < mJoysticks.size(); j++) { 229 if (mJoysticks.get(j).device_id == device_id) { 230 mJoysticks.remove(j); 231 break; 232 } 233 } 234 } 235 } 236 getJoystick(int device_id)237 protected SDLJoystick getJoystick(int device_id) { 238 for(int i=0; i < mJoysticks.size(); i++) { 239 if (mJoysticks.get(i).device_id == device_id) { 240 return mJoysticks.get(i); 241 } 242 } 243 return null; 244 } 245 246 @Override handleMotionEvent(MotionEvent event)247 public boolean handleMotionEvent(MotionEvent event) { 248 if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) { 249 int actionPointerIndex = event.getActionIndex(); 250 int action = event.getActionMasked(); 251 switch(action) { 252 case MotionEvent.ACTION_MOVE: 253 SDLJoystick joystick = getJoystick(event.getDeviceId()); 254 if ( joystick != null ) { 255 for (int i = 0; i < joystick.axes.size(); i++) { 256 InputDevice.MotionRange range = joystick.axes.get(i); 257 /* Normalize the value to -1...1 */ 258 float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f; 259 SDLControllerManager.onNativeJoy(joystick.device_id, i, value ); 260 } 261 for (int i = 0; i < joystick.hats.size(); i+=2) { 262 int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) ); 263 int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) ); 264 SDLControllerManager.onNativeHat(joystick.device_id, i/2, hatX, hatY ); 265 } 266 } 267 break; 268 default: 269 break; 270 } 271 } 272 return true; 273 } 274 getJoystickDescriptor(InputDevice joystickDevice)275 public String getJoystickDescriptor(InputDevice joystickDevice) { 276 String desc = joystickDevice.getDescriptor(); 277 278 if (desc != null && !desc.isEmpty()) { 279 return desc; 280 } 281 282 return joystickDevice.getName(); 283 } getProductId(InputDevice joystickDevice)284 public int getProductId(InputDevice joystickDevice) { 285 return 0; 286 } getVendorId(InputDevice joystickDevice)287 public int getVendorId(InputDevice joystickDevice) { 288 return 0; 289 } getButtonMask(InputDevice joystickDevice)290 public int getButtonMask(InputDevice joystickDevice) { 291 return -1; 292 } 293 } 294 295 class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 { 296 297 @Override getProductId(InputDevice joystickDevice)298 public int getProductId(InputDevice joystickDevice) { 299 return joystickDevice.getProductId(); 300 } 301 302 @Override getVendorId(InputDevice joystickDevice)303 public int getVendorId(InputDevice joystickDevice) { 304 return joystickDevice.getVendorId(); 305 } 306 307 @Override getButtonMask(InputDevice joystickDevice)308 public int getButtonMask(InputDevice joystickDevice) { 309 int button_mask = 0; 310 int[] keys = new int[] { 311 KeyEvent.KEYCODE_BUTTON_A, 312 KeyEvent.KEYCODE_BUTTON_B, 313 KeyEvent.KEYCODE_BUTTON_X, 314 KeyEvent.KEYCODE_BUTTON_Y, 315 KeyEvent.KEYCODE_BACK, 316 KeyEvent.KEYCODE_BUTTON_MODE, 317 KeyEvent.KEYCODE_BUTTON_START, 318 KeyEvent.KEYCODE_BUTTON_THUMBL, 319 KeyEvent.KEYCODE_BUTTON_THUMBR, 320 KeyEvent.KEYCODE_BUTTON_L1, 321 KeyEvent.KEYCODE_BUTTON_R1, 322 KeyEvent.KEYCODE_DPAD_UP, 323 KeyEvent.KEYCODE_DPAD_DOWN, 324 KeyEvent.KEYCODE_DPAD_LEFT, 325 KeyEvent.KEYCODE_DPAD_RIGHT, 326 KeyEvent.KEYCODE_BUTTON_SELECT, 327 KeyEvent.KEYCODE_DPAD_CENTER, 328 329 // These don't map into any SDL controller buttons directly 330 KeyEvent.KEYCODE_BUTTON_L2, 331 KeyEvent.KEYCODE_BUTTON_R2, 332 KeyEvent.KEYCODE_BUTTON_C, 333 KeyEvent.KEYCODE_BUTTON_Z, 334 KeyEvent.KEYCODE_BUTTON_1, 335 KeyEvent.KEYCODE_BUTTON_2, 336 KeyEvent.KEYCODE_BUTTON_3, 337 KeyEvent.KEYCODE_BUTTON_4, 338 KeyEvent.KEYCODE_BUTTON_5, 339 KeyEvent.KEYCODE_BUTTON_6, 340 KeyEvent.KEYCODE_BUTTON_7, 341 KeyEvent.KEYCODE_BUTTON_8, 342 KeyEvent.KEYCODE_BUTTON_9, 343 KeyEvent.KEYCODE_BUTTON_10, 344 KeyEvent.KEYCODE_BUTTON_11, 345 KeyEvent.KEYCODE_BUTTON_12, 346 KeyEvent.KEYCODE_BUTTON_13, 347 KeyEvent.KEYCODE_BUTTON_14, 348 KeyEvent.KEYCODE_BUTTON_15, 349 KeyEvent.KEYCODE_BUTTON_16, 350 }; 351 int[] masks = new int[] { 352 (1 << 0), // A -> A 353 (1 << 1), // B -> B 354 (1 << 2), // X -> X 355 (1 << 3), // Y -> Y 356 (1 << 4), // BACK -> BACK 357 (1 << 5), // MODE -> GUIDE 358 (1 << 6), // START -> START 359 (1 << 7), // THUMBL -> LEFTSTICK 360 (1 << 8), // THUMBR -> RIGHTSTICK 361 (1 << 9), // L1 -> LEFTSHOULDER 362 (1 << 10), // R1 -> RIGHTSHOULDER 363 (1 << 11), // DPAD_UP -> DPAD_UP 364 (1 << 12), // DPAD_DOWN -> DPAD_DOWN 365 (1 << 13), // DPAD_LEFT -> DPAD_LEFT 366 (1 << 14), // DPAD_RIGHT -> DPAD_RIGHT 367 (1 << 4), // SELECT -> BACK 368 (1 << 0), // DPAD_CENTER -> A 369 (1 << 15), // L2 -> ?? 370 (1 << 16), // R2 -> ?? 371 (1 << 17), // C -> ?? 372 (1 << 18), // Z -> ?? 373 (1 << 20), // 1 -> ?? 374 (1 << 21), // 2 -> ?? 375 (1 << 22), // 3 -> ?? 376 (1 << 23), // 4 -> ?? 377 (1 << 24), // 5 -> ?? 378 (1 << 25), // 6 -> ?? 379 (1 << 26), // 7 -> ?? 380 (1 << 27), // 8 -> ?? 381 (1 << 28), // 9 -> ?? 382 (1 << 29), // 10 -> ?? 383 (1 << 30), // 11 -> ?? 384 (1 << 31), // 12 -> ?? 385 // We're out of room... 386 0xFFFFFFFF, // 13 -> ?? 387 0xFFFFFFFF, // 14 -> ?? 388 0xFFFFFFFF, // 15 -> ?? 389 0xFFFFFFFF, // 16 -> ?? 390 }; 391 boolean[] has_keys = joystickDevice.hasKeys(keys); 392 for (int i = 0; i < keys.length; ++i) { 393 if (has_keys[i]) { 394 button_mask |= masks[i]; 395 } 396 } 397 return button_mask; 398 } 399 } 400 401 class SDLHapticHandler_API26 extends SDLHapticHandler { 402 @Override run(int device_id, float intensity, int length)403 public void run(int device_id, float intensity, int length) { 404 SDLHaptic haptic = getHaptic(device_id); 405 if (haptic != null) { 406 Log.d("SDL", "Rtest: Vibe with intensity " + intensity + " for " + length); 407 if (intensity == 0.0f) { 408 stop(device_id); 409 return; 410 } 411 412 int vibeValue = Math.round(intensity * 255); 413 414 if (vibeValue > 255) { 415 vibeValue = 255; 416 } 417 if (vibeValue < 1) { 418 stop(device_id); 419 return; 420 } 421 try { 422 haptic.vib.vibrate(VibrationEffect.createOneShot(length, vibeValue)); 423 } 424 catch (Exception e) { 425 // Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if 426 // something went horribly wrong with the Android 8.0 APIs. 427 haptic.vib.vibrate(length); 428 } 429 } 430 } 431 } 432 433 class SDLHapticHandler { 434 435 class SDLHaptic { 436 public int device_id; 437 public String name; 438 public Vibrator vib; 439 } 440 441 private ArrayList<SDLHaptic> mHaptics; 442 SDLHapticHandler()443 public SDLHapticHandler() { 444 mHaptics = new ArrayList<SDLHaptic>(); 445 } 446 run(int device_id, float intensity, int length)447 public void run(int device_id, float intensity, int length) { 448 SDLHaptic haptic = getHaptic(device_id); 449 if (haptic != null) { 450 haptic.vib.vibrate(length); 451 } 452 } 453 stop(int device_id)454 public void stop(int device_id) { 455 SDLHaptic haptic = getHaptic(device_id); 456 if (haptic != null) { 457 haptic.vib.cancel(); 458 } 459 } 460 pollHapticDevices()461 public void pollHapticDevices() { 462 463 final int deviceId_VIBRATOR_SERVICE = 999999; 464 boolean hasVibratorService = false; 465 466 int[] deviceIds = InputDevice.getDeviceIds(); 467 // It helps processing the device ids in reverse order 468 // For example, in the case of the XBox 360 wireless dongle, 469 // so the first controller seen by SDL matches what the receiver 470 // considers to be the first controller 471 472 for (int i = deviceIds.length - 1; i > -1; i--) { 473 SDLHaptic haptic = getHaptic(deviceIds[i]); 474 if (haptic == null) { 475 InputDevice device = InputDevice.getDevice(deviceIds[i]); 476 Vibrator vib = device.getVibrator(); 477 if (vib.hasVibrator()) { 478 haptic = new SDLHaptic(); 479 haptic.device_id = deviceIds[i]; 480 haptic.name = device.getName(); 481 haptic.vib = vib; 482 mHaptics.add(haptic); 483 SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name); 484 } 485 } 486 } 487 488 /* Check VIBRATOR_SERVICE */ 489 Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE); 490 if (vib != null) { 491 hasVibratorService = vib.hasVibrator(); 492 493 if (hasVibratorService) { 494 SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE); 495 if (haptic == null) { 496 haptic = new SDLHaptic(); 497 haptic.device_id = deviceId_VIBRATOR_SERVICE; 498 haptic.name = "VIBRATOR_SERVICE"; 499 haptic.vib = vib; 500 mHaptics.add(haptic); 501 SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name); 502 } 503 } 504 } 505 506 /* Check removed devices */ 507 ArrayList<Integer> removedDevices = new ArrayList<Integer>(); 508 for(int i=0; i < mHaptics.size(); i++) { 509 int device_id = mHaptics.get(i).device_id; 510 int j; 511 for (j=0; j < deviceIds.length; j++) { 512 if (device_id == deviceIds[j]) break; 513 } 514 515 if (device_id == deviceId_VIBRATOR_SERVICE && hasVibratorService) { 516 // don't remove the vibrator if it is still present 517 } else if (j == deviceIds.length) { 518 removedDevices.add(device_id); 519 } 520 } 521 522 for(int i=0; i < removedDevices.size(); i++) { 523 int device_id = removedDevices.get(i); 524 SDLControllerManager.nativeRemoveHaptic(device_id); 525 for (int j=0; j < mHaptics.size(); j++) { 526 if (mHaptics.get(j).device_id == device_id) { 527 mHaptics.remove(j); 528 break; 529 } 530 } 531 } 532 } 533 getHaptic(int device_id)534 protected SDLHaptic getHaptic(int device_id) { 535 for(int i=0; i < mHaptics.size(); i++) { 536 if (mHaptics.get(i).device_id == device_id) { 537 return mHaptics.get(i); 538 } 539 } 540 return null; 541 } 542 } 543 544 class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener { 545 // Generic Motion (mouse hover, joystick...) events go here 546 @Override onGenericMotion(View v, MotionEvent event)547 public boolean onGenericMotion(View v, MotionEvent event) { 548 float x, y; 549 int action; 550 551 switch ( event.getSource() ) { 552 case InputDevice.SOURCE_JOYSTICK: 553 case InputDevice.SOURCE_GAMEPAD: 554 case InputDevice.SOURCE_DPAD: 555 return SDLControllerManager.handleJoystickMotionEvent(event); 556 557 case InputDevice.SOURCE_MOUSE: 558 action = event.getActionMasked(); 559 switch (action) { 560 case MotionEvent.ACTION_SCROLL: 561 x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0); 562 y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0); 563 SDLActivity.onNativeMouse(0, action, x, y, false); 564 return true; 565 566 case MotionEvent.ACTION_HOVER_MOVE: 567 x = event.getX(0); 568 y = event.getY(0); 569 570 SDLActivity.onNativeMouse(0, action, x, y, false); 571 return true; 572 573 default: 574 break; 575 } 576 break; 577 578 default: 579 break; 580 } 581 582 // Event was not managed 583 return false; 584 } 585 supportsRelativeMouse()586 public boolean supportsRelativeMouse() { 587 return false; 588 } 589 inRelativeMode()590 public boolean inRelativeMode() { 591 return false; 592 } 593 setRelativeMouseEnabled(boolean enabled)594 public boolean setRelativeMouseEnabled(boolean enabled) { 595 return false; 596 } 597 reclaimRelativeMouseModeIfNeeded()598 public void reclaimRelativeMouseModeIfNeeded() 599 { 600 601 } 602 getEventX(MotionEvent event)603 public float getEventX(MotionEvent event) { 604 return event.getX(0); 605 } 606 getEventY(MotionEvent event)607 public float getEventY(MotionEvent event) { 608 return event.getY(0); 609 } 610 611 } 612 613 class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API12 { 614 // Generic Motion (mouse hover, joystick...) events go here 615 616 private boolean mRelativeModeEnabled; 617 618 @Override onGenericMotion(View v, MotionEvent event)619 public boolean onGenericMotion(View v, MotionEvent event) { 620 621 // Handle relative mouse mode 622 if (mRelativeModeEnabled) { 623 if (event.getSource() == InputDevice.SOURCE_MOUSE) { 624 int action = event.getActionMasked(); 625 if (action == MotionEvent.ACTION_HOVER_MOVE) { 626 float x = event.getAxisValue(MotionEvent.AXIS_RELATIVE_X); 627 float y = event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y); 628 SDLActivity.onNativeMouse(0, action, x, y, true); 629 return true; 630 } 631 } 632 } 633 634 // Event was not managed, call SDLGenericMotionListener_API12 method 635 return super.onGenericMotion(v, event); 636 } 637 638 @Override supportsRelativeMouse()639 public boolean supportsRelativeMouse() { 640 return true; 641 } 642 643 @Override inRelativeMode()644 public boolean inRelativeMode() { 645 return mRelativeModeEnabled; 646 } 647 648 @Override setRelativeMouseEnabled(boolean enabled)649 public boolean setRelativeMouseEnabled(boolean enabled) { 650 mRelativeModeEnabled = enabled; 651 return true; 652 } 653 654 @Override getEventX(MotionEvent event)655 public float getEventX(MotionEvent event) { 656 if (mRelativeModeEnabled) { 657 return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X); 658 } 659 else { 660 return event.getX(0); 661 } 662 } 663 664 @Override getEventY(MotionEvent event)665 public float getEventY(MotionEvent event) { 666 if (mRelativeModeEnabled) { 667 return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y); 668 } 669 else { 670 return event.getY(0); 671 } 672 } 673 } 674 675 676 class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 { 677 // Generic Motion (mouse hover, joystick...) events go here 678 private boolean mRelativeModeEnabled; 679 680 @Override onGenericMotion(View v, MotionEvent event)681 public boolean onGenericMotion(View v, MotionEvent event) { 682 float x, y; 683 int action; 684 685 switch ( event.getSource() ) { 686 case InputDevice.SOURCE_JOYSTICK: 687 case InputDevice.SOURCE_GAMEPAD: 688 case InputDevice.SOURCE_DPAD: 689 return SDLControllerManager.handleJoystickMotionEvent(event); 690 691 case InputDevice.SOURCE_MOUSE: 692 // DeX desktop mouse cursor is a separate non-standard input type. 693 case InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN: 694 action = event.getActionMasked(); 695 switch (action) { 696 case MotionEvent.ACTION_SCROLL: 697 x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0); 698 y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0); 699 SDLActivity.onNativeMouse(0, action, x, y, false); 700 return true; 701 702 case MotionEvent.ACTION_HOVER_MOVE: 703 x = event.getX(0); 704 y = event.getY(0); 705 SDLActivity.onNativeMouse(0, action, x, y, false); 706 return true; 707 708 default: 709 break; 710 } 711 break; 712 713 case InputDevice.SOURCE_MOUSE_RELATIVE: 714 action = event.getActionMasked(); 715 switch (action) { 716 case MotionEvent.ACTION_SCROLL: 717 x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0); 718 y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0); 719 SDLActivity.onNativeMouse(0, action, x, y, false); 720 return true; 721 722 case MotionEvent.ACTION_HOVER_MOVE: 723 x = event.getX(0); 724 y = event.getY(0); 725 SDLActivity.onNativeMouse(0, action, x, y, true); 726 return true; 727 728 default: 729 break; 730 } 731 break; 732 733 default: 734 break; 735 } 736 737 // Event was not managed 738 return false; 739 } 740 741 @Override supportsRelativeMouse()742 public boolean supportsRelativeMouse() { 743 return (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27)); 744 } 745 746 @Override inRelativeMode()747 public boolean inRelativeMode() { 748 return mRelativeModeEnabled; 749 } 750 751 @Override setRelativeMouseEnabled(boolean enabled)752 public boolean setRelativeMouseEnabled(boolean enabled) { 753 if (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27)) { 754 if (enabled) { 755 SDLActivity.getContentView().requestPointerCapture(); 756 } 757 else { 758 SDLActivity.getContentView().releasePointerCapture(); 759 } 760 mRelativeModeEnabled = enabled; 761 return true; 762 } 763 else 764 { 765 return false; 766 } 767 } 768 769 @Override reclaimRelativeMouseModeIfNeeded()770 public void reclaimRelativeMouseModeIfNeeded() 771 { 772 if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) { 773 SDLActivity.getContentView().requestPointerCapture(); 774 } 775 } 776 777 @Override getEventX(MotionEvent event)778 public float getEventX(MotionEvent event) { 779 // Relative mouse in capture mode will only have relative for X/Y 780 return event.getX(0); 781 } 782 783 @Override getEventY(MotionEvent event)784 public float getEventY(MotionEvent event) { 785 // Relative mouse in capture mode will only have relative for X/Y 786 return event.getY(0); 787 } 788 } 789