Browse Source

Try to dynamically create a default Android game controller mapping based on the buttons and axes on the controller.
Include the controller USB VID/PID in the GUID where possible, as we do on other platforms.

Sam Lantinga 7 years ago
parent
commit
9e651b6915

+ 122 - 11
android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java

@@ -17,7 +17,8 @@ public class SDLControllerManager
     public static native int nativeSetupJNI();
 
     public static native int nativeAddJoystick(int device_id, String name, String desc,
-                                               int is_accelerometer, int nbuttons,
+                                               int vendor_id, int product_id,
+                                               boolean is_accelerometer, int button_mask,
                                                int naxes, int nhats, int nballs);
     public static native int nativeRemoveJoystick(int device_id);
     public static native int nativeAddHaptic(int device_id, String name);
@@ -42,7 +43,9 @@ public class SDLControllerManager
     }
 
     public static void setup() {
-        if (Build.VERSION.SDK_INT >= 16) {
+        if (Build.VERSION.SDK_INT >= 19) {
+            mJoystickHandler = new SDLJoystickHandler_API19();
+        } else if (Build.VERSION.SDK_INT >= 16) {
             mJoystickHandler = new SDLJoystickHandler_API16();
         } else if (Build.VERSION.SDK_INT >= 12) {
             mJoystickHandler = new SDLJoystickHandler_API12();
@@ -155,12 +158,7 @@ class SDLJoystickHandler_API12 extends SDLJoystickHandler {
     @Override
     public void pollInputDevices() {
         int[] deviceIds = InputDevice.getDeviceIds();
-        // It helps processing the device ids in reverse order
-        // For example, in the case of the XBox 360 wireless dongle,
-        // so the first controller seen by SDL matches what the receiver
-        // considers to be the first controller
-
-        for(int i=deviceIds.length-1; i>-1; i--) {
+        for(int i=0; i < deviceIds.length; ++i) {
             SDLJoystick joystick = getJoystick(deviceIds[i]);
             if (joystick == null) {
                 joystick = new SDLJoystick();
@@ -187,8 +185,7 @@ class SDLJoystickHandler_API12 extends SDLJoystickHandler {
                     }
 
                     mJoysticks.add(joystick);
-                    SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, 0, -1,
-                                                           joystick.axes.size(), joystick.hats.size()/2, 0);
+                    SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, getVendorId(joystickDevice), getProductId(joystickDevice), false, getButtonMask(joystickDevice), joystick.axes.size(), joystick.hats.size()/2, 0);
                 }
             }
         }
@@ -259,9 +256,17 @@ class SDLJoystickHandler_API12 extends SDLJoystickHandler {
     public String getJoystickDescriptor(InputDevice joystickDevice) {
         return joystickDevice.getName();
     }
+    public int getProductId(InputDevice joystickDevice) {
+        return 0;
+    }
+    public int getVendorId(InputDevice joystickDevice) {
+        return 0;
+    }
+    public int getButtonMask(InputDevice joystickDevice) {
+        return -1;
+    }
 }
 
-
 class SDLJoystickHandler_API16 extends SDLJoystickHandler_API12 {
 
     @Override
@@ -276,6 +281,112 @@ class SDLJoystickHandler_API16 extends SDLJoystickHandler_API12 {
     }
 }
 
+class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
+
+    @Override
+    public int getProductId(InputDevice joystickDevice) {
+        return joystickDevice.getProductId();
+    }
+
+    @Override
+    public int getVendorId(InputDevice joystickDevice) {
+        return joystickDevice.getVendorId();
+    }
+
+    @Override
+    public int getButtonMask(InputDevice joystickDevice) {
+        int button_mask = 0;
+        int[] keys = new int[] {
+            KeyEvent.KEYCODE_BUTTON_A,
+            KeyEvent.KEYCODE_BUTTON_B,
+            KeyEvent.KEYCODE_BUTTON_X,
+            KeyEvent.KEYCODE_BUTTON_Y,
+            KeyEvent.KEYCODE_BACK,
+            KeyEvent.KEYCODE_BUTTON_MODE,
+            KeyEvent.KEYCODE_BUTTON_START,
+            KeyEvent.KEYCODE_BUTTON_THUMBL,
+            KeyEvent.KEYCODE_BUTTON_THUMBR,
+            KeyEvent.KEYCODE_BUTTON_L1,
+            KeyEvent.KEYCODE_BUTTON_R1,
+            KeyEvent.KEYCODE_DPAD_UP,
+            KeyEvent.KEYCODE_DPAD_DOWN,
+            KeyEvent.KEYCODE_DPAD_LEFT,
+            KeyEvent.KEYCODE_DPAD_RIGHT,
+            KeyEvent.KEYCODE_BUTTON_SELECT,
+            KeyEvent.KEYCODE_DPAD_CENTER,
+
+            // These don't map into any SDL controller buttons directly
+            KeyEvent.KEYCODE_BUTTON_L2,
+            KeyEvent.KEYCODE_BUTTON_R2,
+            KeyEvent.KEYCODE_BUTTON_C,
+            KeyEvent.KEYCODE_BUTTON_Z,
+            KeyEvent.KEYCODE_BUTTON_1,
+            KeyEvent.KEYCODE_BUTTON_2,
+            KeyEvent.KEYCODE_BUTTON_3,
+            KeyEvent.KEYCODE_BUTTON_4,
+            KeyEvent.KEYCODE_BUTTON_5,
+            KeyEvent.KEYCODE_BUTTON_6,
+            KeyEvent.KEYCODE_BUTTON_7,
+            KeyEvent.KEYCODE_BUTTON_8,
+            KeyEvent.KEYCODE_BUTTON_9,
+            KeyEvent.KEYCODE_BUTTON_10,
+            KeyEvent.KEYCODE_BUTTON_11,
+            KeyEvent.KEYCODE_BUTTON_12,
+            KeyEvent.KEYCODE_BUTTON_13,
+            KeyEvent.KEYCODE_BUTTON_14,
+            KeyEvent.KEYCODE_BUTTON_15,
+            KeyEvent.KEYCODE_BUTTON_16,
+        };
+        int[] masks = new int[] {
+            (1 << 0),   // A -> A
+            (1 << 1),   // B -> B
+            (1 << 2),   // X -> X
+            (1 << 3),   // Y -> Y
+            (1 << 4),   // BACK -> BACK
+            (1 << 5),   // MODE -> GUIDE
+            (1 << 6),   // START -> START
+            (1 << 7),   // THUMBL -> LEFTSTICK
+            (1 << 8),   // THUMBR -> RIGHTSTICK
+            (1 << 9),   // L1 -> LEFTSHOULDER
+            (1 << 10),  // R1 -> RIGHTSHOULDER
+            (1 << 11),  // DPAD_UP -> DPAD_UP
+            (1 << 12),  // DPAD_DOWN -> DPAD_DOWN
+            (1 << 13),  // DPAD_LEFT -> DPAD_LEFT
+            (1 << 14),  // DPAD_RIGHT -> DPAD_RIGHT
+            (1 << 4),   // SELECT -> BACK
+            (1 << 0),   // DPAD_CENTER -> A
+            (1 << 15),  // L2 -> ??
+            (1 << 16),  // R2 -> ??
+            (1 << 17),  // C -> ??
+            (1 << 18),  // Z -> ??
+            (1 << 20),  // 1 -> ??
+            (1 << 21),  // 2 -> ??
+            (1 << 22),  // 3 -> ??
+            (1 << 23),  // 4 -> ??
+            (1 << 24),  // 5 -> ??
+            (1 << 25),  // 6 -> ??
+            (1 << 26),  // 7 -> ??
+            (1 << 27),  // 8 -> ??
+            (1 << 28),  // 9 -> ??
+            (1 << 29),  // 10 -> ??
+            (1 << 30),  // 11 -> ??
+            (1 << 31),  // 12 -> ??
+            // We're out of room...
+            0xFFFFFFFF,  // 13 -> ??
+            0xFFFFFFFF,  // 14 -> ??
+            0xFFFFFFFF,  // 15 -> ??
+            0xFFFFFFFF,  // 16 -> ??
+        };
+        boolean[] has_keys = joystickDevice.hasKeys(keys);
+        for (int i = 0; i < keys.length; ++i) {
+            if (has_keys[i]) {
+                button_mask |= masks[i];
+            }
+        }
+        return button_mask;
+    }
+}
+
 class SDLHapticHandler {
 
     class SDLHaptic {

+ 6 - 5
src/core/android/SDL_android.c

@@ -169,8 +169,8 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
 
 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
         JNIEnv* env, jclass jcls,
-        jint device_id, jstring device_name, jstring device_desc, jint is_accelerometer,
-        jint nbuttons, jint naxes, jint nhats, jint nballs);
+        jint device_id, jstring device_name, jstring device_desc, jint vendor_id, jint product_id,
+        jboolean is_accelerometer, jint button_mask, jint naxes, jint nhats, jint nballs);
 
 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
         JNIEnv* env, jclass jcls,
@@ -543,14 +543,15 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
 
 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
                                     JNIEnv* env, jclass jcls,
-                                    jint device_id, jstring device_name, jstring device_desc, jint is_accelerometer,
-                                    jint nbuttons, jint naxes, jint nhats, jint nballs)
+                                    jint device_id, jstring device_name, jstring device_desc,
+                                    jint vendor_id, jint product_id, jboolean is_accelerometer,
+                                    jint button_mask, jint naxes, jint nhats, jint nballs)
 {
     int retval;
     const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
     const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL);
 
-    retval = Android_AddJoystick(device_id, name, desc, (SDL_bool) is_accelerometer, nbuttons, naxes, nhats, nballs);
+    retval = Android_AddJoystick(device_id, name, desc, vendor_id, product_id, is_accelerometer ? SDL_TRUE : SDL_FALSE, button_mask, naxes, nhats, nballs);
 
     (*env)->ReleaseStringUTFChars(env, device_name, name);
     (*env)->ReleaseStringUTFChars(env, device_desc, desc);

+ 127 - 26
src/joystick/SDL_gamecontroller.c

@@ -97,8 +97,8 @@ typedef struct _ControllerMapping_t
 
 static SDL_JoystickGUID s_zeroGUID;
 static ControllerMapping_t *s_pSupportedControllers = NULL;
+static ControllerMapping_t *s_pDefaultMapping = NULL;
 static ControllerMapping_t *s_pXInputMapping = NULL;
-static ControllerMapping_t *s_pEmscriptenMapping = NULL;
 
 /* The SDL game controller structure */
 struct _SDL_GameController
@@ -869,6 +869,117 @@ SDL_PrivateAddMappingForGUID(SDL_JoystickGUID jGUID, const char *mappingString,
     return pControllerMapping;
 }
 
+#ifdef __ANDROID__
+/*
+ * Helper function to guess at a mapping based on the elements reported for this controller
+ */
+static ControllerMapping_t *SDL_CreateMappingForAndroidController(const char *name, SDL_JoystickGUID guid)
+{
+    SDL_bool existing;
+    char name_string[128];
+    char mapping_string[1024];
+    int button_mask;
+    int axis_mask;
+
+    button_mask = SDL_SwapLE16(*(Uint16*)(&guid.data[sizeof(guid.data)-4]));
+    axis_mask = SDL_SwapLE16(*(Uint16*)(&guid.data[sizeof(guid.data)-2]));
+    if (!button_mask && !axis_mask) {
+        /* Accelerometer, shouldn't have a game controller mapping */
+        return NULL;
+    }
+
+    /* Remove any commas in the name */
+    SDL_strlcpy(name_string, name, sizeof(name_string));
+    {
+        char *spot;
+        for (spot = name_string; *spot; ++spot) {
+            if (*spot == ',') {
+                *spot = ' ';
+            }
+        }
+    }
+    SDL_snprintf(mapping_string, sizeof(mapping_string), "none,%s,", name_string);
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_A)) {
+        SDL_strlcat(mapping_string, "a:b0,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_B)) {
+        SDL_strlcat(mapping_string, "b:b1,", sizeof(mapping_string));
+    } else if (button_mask & (1 << SDL_CONTROLLER_BUTTON_BACK)) {
+        /* Use the back button as "B" for easy UI navigation with TV remotes */
+        SDL_strlcat(mapping_string, "b:b4,", sizeof(mapping_string));
+        button_mask &= ~(1 << SDL_CONTROLLER_BUTTON_BACK);
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_X)) {
+        SDL_strlcat(mapping_string, "x:b2,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_Y)) {
+        SDL_strlcat(mapping_string, "y:b3,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_BACK)) {
+        SDL_strlcat(mapping_string, "back:b4,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_GUIDE)) {
+        SDL_strlcat(mapping_string, "guide:b5,", sizeof(mapping_string));
+#if 0 /* Actually this will be done in Steam */
+    } else if (button_mask & (1 << SDL_CONTROLLER_BUTTON_START)) {
+        /* The guide button doesn't exist, use the start button instead,
+           so you can do Steam guide button chords and open the Steam overlay.
+         */
+        SDL_strlcat(mapping_string, "guide:b6,", sizeof(mapping_string));
+        button_mask &= ~(1 << SDL_CONTROLLER_BUTTON_START);
+#endif
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_START)) {
+        SDL_strlcat(mapping_string, "start:b6,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_LEFTSTICK)) {
+        SDL_strlcat(mapping_string, "leftstick:b7,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_RIGHTSTICK)) {
+        SDL_strlcat(mapping_string, "rightstick:b8,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) {
+        SDL_strlcat(mapping_string, "leftshoulder:b9,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) {
+        SDL_strlcat(mapping_string, "rightshoulder:b10,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_UP)) {
+        SDL_strlcat(mapping_string, "dpup:b11,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN)) {
+        SDL_strlcat(mapping_string, "dpdown:b12,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT)) {
+        SDL_strlcat(mapping_string, "dpleft:b13,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) {
+        SDL_strlcat(mapping_string, "dpright:b14,", sizeof(mapping_string));
+    }
+    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_LEFTX)) {
+        SDL_strlcat(mapping_string, "leftx:a0,", sizeof(mapping_string));
+    }
+    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_LEFTY)) {
+        SDL_strlcat(mapping_string, "lefty:a1,", sizeof(mapping_string));
+    }
+    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_RIGHTX)) {
+        SDL_strlcat(mapping_string, "rightx:a2,", sizeof(mapping_string));
+    }
+    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_RIGHTY)) {
+        SDL_strlcat(mapping_string, "righty:a3,", sizeof(mapping_string));
+    }
+    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_TRIGGERLEFT)) {
+        SDL_strlcat(mapping_string, "lefttrigger:a4,", sizeof(mapping_string));
+    }
+    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) {
+        SDL_strlcat(mapping_string, "righttrigger:a5,", sizeof(mapping_string));
+    }
+    return SDL_PrivateAddMappingForGUID(guid, mapping_string,
+                      &existing, SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT);
+}
+#endif /* __ANDROID__ */
+
+
 /*
  * Helper function to determine pre-calculated offset to certain joystick mappings
  */
@@ -877,13 +988,6 @@ static ControllerMapping_t *SDL_PrivateGetControllerMappingForNameAndGUID(const
     ControllerMapping_t *mapping;
 
     mapping = SDL_PrivateGetControllerMappingForGUID(&guid);
-#if defined(SDL_JOYSTICK_EMSCRIPTEN)
-    if (!mapping && s_pEmscriptenMapping) {
-        mapping = s_pEmscriptenMapping;
-    }
-#else
-    (void) s_pEmscriptenMapping;  /* pacify ARMCC */
-#endif
 #ifdef __LINUX__
     if (!mapping && name) {
         if (SDL_strstr(name, "Xbox 360 Wireless Receiver")) {
@@ -901,6 +1005,14 @@ static ControllerMapping_t *SDL_PrivateGetControllerMappingForNameAndGUID(const
             mapping = s_pXInputMapping;
         }
     }
+#ifdef __ANDROID__
+    if (!mapping) {
+        mapping = SDL_CreateMappingForAndroidController(name, guid);
+    }
+#endif
+    if (!mapping) {
+        mapping = s_pDefaultMapping;
+    }
     return mapping;
 }
 
@@ -926,15 +1038,6 @@ static ControllerMapping_t *SDL_PrivateGetControllerMapping(int device_index)
         mapping = s_pXInputMapping;
     }
 #endif
-#if defined(__ANDROID__)
-    if (!mapping && SDL_SYS_IsDPAD_DeviceIndex(device_index)) {
-        SDL_bool existing;
-        char mapping_string[1024];
-        SDL_snprintf(mapping_string, sizeof(mapping_string), "none,%s,a:b0,b:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,", name);
-        mapping = SDL_PrivateAddMappingForGUID(guid, mapping_string,
-                          &existing, SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT);
-    }
-#endif /* __ANDROID__ */
     SDL_UnlockJoysticks();
     return mapping;
 }
@@ -1018,8 +1121,8 @@ SDL_PrivateGameControllerAddMapping(const char *mappingString, SDL_ControllerMap
 {
     char *pchGUID;
     SDL_JoystickGUID jGUID;
+    SDL_bool is_default_mapping = SDL_FALSE;
     SDL_bool is_xinput_mapping = SDL_FALSE;
-    SDL_bool is_emscripten_mapping = SDL_FALSE;
     SDL_bool existing = SDL_FALSE;
     ControllerMapping_t *pControllerMapping;
 
@@ -1031,12 +1134,11 @@ SDL_PrivateGameControllerAddMapping(const char *mappingString, SDL_ControllerMap
     if (!pchGUID) {
         return SDL_SetError("Couldn't parse GUID from %s", mappingString);
     }
-    if (!SDL_strcasecmp(pchGUID, "xinput")) {
+    if (!SDL_strcasecmp(pchGUID, "default")) {
+        is_default_mapping = SDL_TRUE;
+    } else if (!SDL_strcasecmp(pchGUID, "xinput")) {
         is_xinput_mapping = SDL_TRUE;
     }
-    if (!SDL_strcasecmp(pchGUID, "emscripten")) {
-        is_emscripten_mapping = SDL_TRUE;
-    }
     jGUID = SDL_JoystickGetGUIDFromString(pchGUID);
     SDL_free(pchGUID);
 
@@ -1048,12 +1150,11 @@ SDL_PrivateGameControllerAddMapping(const char *mappingString, SDL_ControllerMap
     if (existing) {
         return 0;
     } else {
-        if (is_xinput_mapping) {
+        if (is_default_mapping) {
+            s_pDefaultMapping = pControllerMapping;
+        } else if (is_xinput_mapping) {
             s_pXInputMapping = pControllerMapping;
         }
-        if (is_emscripten_mapping) {
-            s_pEmscriptenMapping = pControllerMapping;
-        }
         return 1;
     }
 }

+ 5 - 8
src/joystick/SDL_gamecontrollerdb.h

@@ -208,14 +208,11 @@ static const char *s_ControllerMappings [] =
     "03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,",
 #endif
 #if defined(__ANDROID__)
-    "34323662653333636330306631326233,ASUS Gamepad,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,",
-    "64633436313965656664373634323364,Microsoft X-Box 360 pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,",
-    "4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
-    "61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
-    "37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
-    "35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,",
+    "050000004c05000068020000dfff3f00,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+    "050000004c050000cc090000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,",
     "05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
-    "34356136633366613530316338376136,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,",
+    "050000005e040000e00200000ffe3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,",
+    "050000005e04000091020000ff073f00,Xbox Wireless Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,", /* The DPAD doesn't seem to work on this controller on Android TV? */
 #endif
 #if defined(SDL_JOYSTICK_MFI)
     "4d466947616d65706164010000000000,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
@@ -224,7 +221,7 @@ static const char *s_ControllerMappings [] =
     "05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
 #endif
 #if defined(SDL_JOYSTICK_EMSCRIPTEN)
-    "emscripten,Standard Gamepad,a:b0,b:b1,back:b8,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b16,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "default,Standard Gamepad,a:b0,b:b1,back:b8,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b16,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
 #endif
     NULL
 };

+ 19 - 2
src/joystick/SDL_joystick.c

@@ -104,6 +104,23 @@ SDL_NumJoysticks(void)
     return SDL_SYS_NumJoysticks();
 }
 
+/*
+ * Perform any needed fixups for joystick names
+ */
+static const char *
+SDL_FixupJoystickName(const char *name)
+{
+    if (name) {
+        const char *skip_prefix = "NVIDIA Corporation ";
+
+        if (SDL_strncmp(name, skip_prefix, SDL_strlen(skip_prefix)) == 0) {
+            name += SDL_strlen(skip_prefix);
+        }
+    }
+    return name;
+}
+
+
 /*
  * Get the implementation dependent name of a joystick
  */
@@ -114,7 +131,7 @@ SDL_JoystickNameForIndex(int device_index)
         SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
         return (NULL);
     }
-    return (SDL_SYS_JoystickNameForDeviceIndex(device_index));
+    return SDL_FixupJoystickName(SDL_SYS_JoystickNameForDeviceIndex(device_index));
 }
 
 /*
@@ -481,7 +498,7 @@ SDL_JoystickName(SDL_Joystick * joystick)
         return (NULL);
     }
 
-    return (joystick->name);
+    return SDL_FixupJoystickName(joystick->name);
 }
 
 /*

+ 0 - 5
src/joystick/SDL_sysjoystick.h

@@ -126,11 +126,6 @@ extern SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick);
 extern SDL_bool SDL_SYS_IsXInputGamepad_DeviceIndex(int device_index);
 #endif
 
-#if defined(__ANDROID__)
-/* Function returns SDL_TRUE if this device is a DPAD (maybe a TV remote) */
-extern SDL_bool SDL_SYS_IsDPAD_DeviceIndex(int device_index);
-#endif
-
 #endif /* SDL_sysjoystick_h_ */
 
 /* vi: set ts=4 sw=4 expandtab: */

+ 123 - 23
src/joystick/android/SDL_sysjoystick.c

@@ -72,6 +72,28 @@ static int numjoysticks = 0;
 static int instance_counter = 0;
 
 
+/* Public domain CRC implementation adapted from:
+   http://home.thep.lu.se/~bjorn/crc/crc32_simple.c
+*/
+static Uint32 crc32_for_byte(Uint32 r)
+{
+    int i;
+    for(i = 0; i < 8; ++i) {
+        r = (r & 1? 0: (Uint32)0xEDB88320L) ^ r >> 1;
+    }
+    return r ^ (Uint32)0xFF000000L;
+}
+
+static Uint32 crc32(const void *data, int count)
+{
+    Uint32 crc = 0;
+    int i;
+    for(i = 0; i < count; ++i) {
+        crc = crc32_for_byte((Uint8)crc ^ ((const Uint8*)data)[i]) ^ crc >> 8;
+    }
+    return crc;
+}
+
 /* Function to convert Android keyCodes into SDL ones.
  * This code manipulation is done to get a sequential list of codes.
  * FIXME: This is only suited for the case where we use a fixed number of buttons determined by ANDROID_MAX_NBUTTONS
@@ -213,7 +235,7 @@ Android_OnPadDown(int device_id, int keycode)
     if (button >= 0) {
         item = JoystickByDeviceId(device_id);
         if (item && item->joystick) {
-            SDL_PrivateJoystickButton(item->joystick, button , SDL_PRESSED);
+            SDL_PrivateJoystickButton(item->joystick, button, SDL_PRESSED);
         } else {
             SDL_SendKeyboardKey(SDL_PRESSED, button_to_scancode(button));
         }
@@ -256,16 +278,43 @@ Android_OnJoy(int device_id, int axis, float value)
 int
 Android_OnHat(int device_id, int hat_id, int x, int y)
 {
-    const Uint8 position_map[3][3] = {
-        {SDL_HAT_LEFTUP, SDL_HAT_UP, SDL_HAT_RIGHTUP},
-        {SDL_HAT_LEFT, SDL_HAT_CENTERED, SDL_HAT_RIGHT},
-        {SDL_HAT_LEFTDOWN, SDL_HAT_DOWN, SDL_HAT_RIGHTDOWN}
-    };
+    const int DPAD_UP_MASK = (1 << SDL_CONTROLLER_BUTTON_DPAD_UP);
+    const int DPAD_DOWN_MASK = (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN);
+    const int DPAD_LEFT_MASK = (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT);
+    const int DPAD_RIGHT_MASK = (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
 
-    if (x >= -1 && x <=1 && y >= -1 && y <= 1) {
+    if (x >= -1 && x <= 1 && y >= -1 && y <= 1) {
         SDL_joylist_item *item = JoystickByDeviceId(device_id);
         if (item && item->joystick) {
-            SDL_PrivateJoystickHat(item->joystick, hat_id, position_map[y+1][x+1]);
+            int dpad_state = 0;
+            int dpad_delta;
+            if (x < 0) {
+                dpad_state |= DPAD_LEFT_MASK;
+            } else if (x > 0) {
+                dpad_state |= DPAD_RIGHT_MASK;
+            }
+            if (y < 0) {
+                dpad_state |= DPAD_UP_MASK;
+            } else if (y > 0) {
+                dpad_state |= DPAD_DOWN_MASK;
+            }
+
+            dpad_delta = (dpad_state ^ item->dpad_state);
+            if (dpad_delta) {
+                if (dpad_delta & DPAD_UP_MASK) {
+                    SDL_PrivateJoystickButton(item->joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (dpad_state & DPAD_UP_MASK) ? SDL_PRESSED : SDL_RELEASED);
+                }
+                if (dpad_delta & DPAD_DOWN_MASK) {
+                    SDL_PrivateJoystickButton(item->joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (dpad_state & DPAD_DOWN_MASK) ? SDL_PRESSED : SDL_RELEASED);
+                }
+                if (dpad_delta & DPAD_LEFT_MASK) {
+                    SDL_PrivateJoystickButton(item->joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (dpad_state & DPAD_LEFT_MASK) ? SDL_PRESSED : SDL_RELEASED);
+                }
+                if (dpad_delta & DPAD_RIGHT_MASK) {
+                    SDL_PrivateJoystickButton(item->joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (dpad_state & DPAD_RIGHT_MASK) ? SDL_PRESSED : SDL_RELEASED);
+                }
+                item->dpad_state = dpad_state;
+            }
         }
         return 0;
     }
@@ -275,10 +324,15 @@ Android_OnHat(int device_id, int hat_id, int x, int y)
 
 
 int
-Android_AddJoystick(int device_id, const char *name, const char *desc, SDL_bool is_accelerometer, int nbuttons, int naxes, int nhats, int nballs)
+Android_AddJoystick(int device_id, const char *name, const char *desc, int vendor_id, int product_id, SDL_bool is_accelerometer, int button_mask, int naxes, int nhats, int nballs)
 {
-    SDL_JoystickGUID guid;
+    const Uint16 BUS_BLUETOOTH = 0x05;
     SDL_joylist_item *item;
+    SDL_JoystickGUID guid;
+    Uint16 *guid16 = (Uint16 *)guid.data;
+    int i;
+    int axis_mask;
+
 
     if (!SDL_GetHintBoolean(SDL_HINT_TV_REMOTE_AS_JOYSTICK, SDL_TRUE)) {
         /* Ignore devices that aren't actually controllers (e.g. remotes), they'll be handled as keyboard input */
@@ -291,9 +345,57 @@ Android_AddJoystick(int device_id, const char *name, const char *desc, SDL_bool
         return -1;
     }
     
-    /* the GUID is just the first 16 chars of the name for now */
-    SDL_zero(guid);
-    SDL_memcpy(&guid, desc, SDL_min(sizeof(guid), SDL_strlen(desc)));
+#ifdef DEBUG_JOYSTICK
+    SDL_Log("Joystick: %s, descriptor %s, vendor = 0x%.4x, product = 0x%.4x, %d axes, %d hats\n", name, desc, vendor_id, product_id, naxes, nhats);
+#endif
+
+    /* Add the available buttons and axes
+       The axis mask should probably come from Java where there is more information about the axes...
+     */
+    axis_mask = 0;
+    if (!is_accelerometer) {
+        if (naxes >= 2) {
+            axis_mask |= ((1 << SDL_CONTROLLER_AXIS_LEFTX) | (1 << SDL_CONTROLLER_AXIS_LEFTY));
+        }
+        if (naxes >= 4) {
+            axis_mask |= ((1 << SDL_CONTROLLER_AXIS_RIGHTX) | (1 << SDL_CONTROLLER_AXIS_RIGHTY));
+        }
+        if (naxes >= 6) {
+            axis_mask |= ((1 << SDL_CONTROLLER_AXIS_TRIGGERLEFT) | (1 << SDL_CONTROLLER_AXIS_TRIGGERRIGHT));
+        }
+    }
+
+    if (nhats > 0) {
+        /* Hat is translated into DPAD buttons */
+        button_mask |= ((1 << SDL_CONTROLLER_BUTTON_DPAD_UP) |
+                        (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN) |
+                        (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT) |
+                        (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT));
+        nhats = 0;
+    }
+
+    SDL_memset(guid.data, 0, sizeof(guid.data));
+
+    /* We only need 16 bits for each of these; space them out to fill 128. */
+    /* Byteswap so devices get same GUID on little/big endian platforms. */
+    *guid16++ = SDL_SwapLE16(BUS_BLUETOOTH);
+    *guid16++ = 0;
+
+    if (vendor_id && product_id) {
+        *guid16++ = SDL_SwapLE16(vendor_id);
+        *guid16++ = 0;
+        *guid16++ = SDL_SwapLE16(product_id);
+        *guid16++ = 0;
+    } else {
+        Uint32 crc = crc32(desc, SDL_strlen(desc));
+        SDL_memcpy(guid16, desc, SDL_min(2*sizeof(*guid16), SDL_strlen(desc)));
+        guid16 += 2;
+        *(Uint32 *)guid16 = SDL_SwapLE32(crc);
+        guid16 += 2;
+    }
+
+    *guid16++ = SDL_SwapLE16(button_mask);
+    *guid16++ = SDL_SwapLE16(axis_mask);
 
     item = (SDL_joylist_item *) SDL_malloc(sizeof (SDL_joylist_item));
     if (item == NULL) {
@@ -310,11 +412,14 @@ Android_AddJoystick(int device_id, const char *name, const char *desc, SDL_bool
     }
     
     item->is_accelerometer = is_accelerometer;
-    if (nbuttons > -1) {
-        item->nbuttons = nbuttons;
-    }
-    else {
+    if (button_mask == 0xFFFFFFFF) {
         item->nbuttons = ANDROID_MAX_NBUTTONS;
+    } else {
+        for (i = 0; i < sizeof(button_mask)*8; ++i) {
+            if (button_mask & (1 << i)) {
+                item->nbuttons = i+1;
+            }
+        }
     }
     item->naxes = naxes;
     item->nhats = nhats;
@@ -467,7 +572,7 @@ SDL_SYS_JoystickInit(void)
     
     if (SDL_GetHintBoolean(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, SDL_TRUE)) {
         /* Default behavior, accelerometer as joystick */
-        Android_AddJoystick(ANDROID_ACCELEROMETER_DEVICE_ID, ANDROID_ACCELEROMETER_NAME, ANDROID_ACCELEROMETER_NAME, SDL_TRUE, 0, 3, 0, 0);
+        Android_AddJoystick(ANDROID_ACCELEROMETER_DEVICE_ID, ANDROID_ACCELEROMETER_NAME, ANDROID_ACCELEROMETER_NAME, 0, 0, SDL_TRUE, 0, 3, 0, 0);
     }
    
     SDL_InitSteamControllers(SteamControllerConnectedCallback,
@@ -677,11 +782,6 @@ SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
     return guid;
 }
 
-SDL_bool SDL_SYS_IsDPAD_DeviceIndex(int device_index)
-{
-    return JoystickByDevIndex(device_index)->naxes == 0;
-}
-
 #endif /* SDL_JOYSTICK_ANDROID */
 
 /* vi: set ts=4 sw=4 expandtab: */

+ 2 - 1
src/joystick/android/SDL_sysjoystick_c.h

@@ -32,7 +32,7 @@ extern int Android_OnPadDown(int device_id, int keycode);
 extern int Android_OnPadUp(int device_id, int keycode);
 extern int Android_OnJoy(int device_id, int axisnum, float value);
 extern int Android_OnHat(int device_id, int hat_id, int x, int y);
-extern int Android_AddJoystick(int device_id, const char *name, const char *desc, SDL_bool is_accelerometer, int nbuttons, int naxes, int nhats, int nballs);
+extern int Android_AddJoystick(int device_id, const char *name, const char *desc, int vendor_id, int product_id, SDL_bool is_accelerometer, int button_mask, int naxes, int nhats, int nballs);
 extern int Android_RemoveJoystick(int device_id);
 
 /* A linked list of available joysticks */
@@ -45,6 +45,7 @@ typedef struct SDL_joylist_item
     SDL_bool is_accelerometer;
     SDL_Joystick *joystick;
     int nbuttons, naxes, nhats, nballs;
+    int dpad_state;
     
     /* Steam Controller support */
     SDL_bool m_bSteamController;