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