android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java
author Sam Lantinga <slouken@libsdl.org>
Tue, 16 Oct 2018 15:00:43 -0700
changeset 12334 0789a425e8d7
parent 12332 c80f702e4b8b
child 12354 7846ac86d46e
permissions -rw-r--r--
Fixed bug 4318 - Android move Haptic code to API26 class

Sylvain

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