Commit 3c90a52aa7c04aa746315b3ba94ffb5b83b64561

Sam Lantinga 2017-01-27T05:59:58

Added an API to get the type of a connected joystick

diff --git a/include/SDL_joystick.h b/include/SDL_joystick.h
index a15fbb5..d40432e 100644
--- a/include/SDL_joystick.h
+++ b/include/SDL_joystick.h
@@ -75,6 +75,19 @@ typedef Sint32 SDL_JoystickID;
 
 typedef enum
 {
+    SDL_JOYSTICK_TYPE_UNKNOWN,
+    SDL_JOYSTICK_TYPE_GAMECONTROLLER,
+    SDL_JOYSTICK_TYPE_WHEEL,
+    SDL_JOYSTICK_TYPE_ARCADE_STICK,
+    SDL_JOYSTICK_TYPE_FLIGHT_STICK,
+    SDL_JOYSTICK_TYPE_DANCE_PAD,
+    SDL_JOYSTICK_TYPE_GUITAR,
+    SDL_JOYSTICK_TYPE_DRUM_KIT,
+    SDL_JOYSTICK_TYPE_ARCADE_PAD,
+} SDL_JoystickType;
+
+typedef enum
+{
     SDL_JOYSTICK_POWER_UNKNOWN = -1,
     SDL_JOYSTICK_POWER_EMPTY,
     SDL_JOYSTICK_POWER_LOW,
@@ -125,6 +138,12 @@ extern DECLSPEC Uint16 SDLCALL SDL_JoystickGetDeviceProduct(int device_index);
 extern DECLSPEC Uint16 SDLCALL SDL_JoystickGetDeviceProductVersion(int device_index);
 
 /**
+ *  Get the type of a joystick, if available.
+ *  This can be called before any joysticks are opened.
+ */
+extern DECLSPEC SDL_JoystickType SDLCALL SDL_JoystickGetDeviceType(int device_index);
+
+/**
  *  Open a joystick for use.
  *  The index passed as an argument refers to the N'th joystick on the system.
  *  This index is not the value which will identify this joystick in future
@@ -170,6 +189,11 @@ extern DECLSPEC Uint16 SDLCALL SDL_JoystickGetProduct(SDL_Joystick * joystick);
 extern DECLSPEC Uint16 SDLCALL SDL_JoystickGetProductVersion(SDL_Joystick * joystick);
 
 /**
+ *  Get the type of an opened joystick.
+ */
+extern DECLSPEC SDL_JoystickType SDLCALL SDL_JoystickGetType(SDL_Joystick * joystick);
+
+/**
  *  Return a string representation for this guid. pszGUID must point to at least 33 bytes
  *  (32 for the string plus a NULL terminator).
  */
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 3fe4b86..4e4acec 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -625,3 +625,5 @@
 #define SDL_GameControllerNumMappings SDL_GameControllerNumMappings_REAL
 #define SDL_GameControllerMappingForIndex SDL_GameControllerMappingForIndex_REAL
 #define SDL_JoystickGetAxisInitialState SDL_JoystickGetAxisInitialState_REAL
+#define SDL_JoystickGetDeviceType SDL_JoystickGetDeviceType_REAL
+#define SDL_JoystickGetType SDL_JoystickGetType_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index e416b4a..bec6267 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -657,3 +657,5 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_HasNEON,(void),(),return)
 SDL_DYNAPI_PROC(int,SDL_GameControllerNumMappings,(void),(),return)
 SDL_DYNAPI_PROC(char*,SDL_GameControllerMappingForIndex,(int a),(a),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_JoystickGetAxisInitialState,(SDL_Joystick *a, int b, Sint16 *c),(a,b,c),return)
+SDL_DYNAPI_PROC(SDL_JoystickType,SDL_JoystickGetDeviceType,(int a),(a),return)
+SDL_DYNAPI_PROC(SDL_JoystickType,SDL_JoystickGetType,(SDL_Joystick *a),(a),return)
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index c2f107a..7a6819c 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -32,6 +32,8 @@
 #include "../events/SDL_events_c.h"
 #endif
 
+#define MAKE_VIDPID(VID, PID)   (((Uint32)(VID))<<16|(PID))
+
 static SDL_bool SDL_joystick_allows_background_events = SDL_FALSE;
 static SDL_Joystick *SDL_joysticks = NULL;
 static SDL_bool SDL_updating_joystick = SDL_FALSE;
@@ -121,17 +123,14 @@ SDL_JoystickNameForIndex(int device_index)
 static SDL_bool
 SDL_JoystickAxesCenteredAtZero(SDL_Joystick *joystick)
 {
-    struct {
-        Uint16 vendor;
-        Uint16 product;
-    } zero_centered_joysticks[] = {
-        { 0x0e8f, 0x3013 }, /* HuiJia SNES USB adapter */
-        { 0x05a0, 0x3232 }, /* 8Bitdo Zero Gamepad */
+    static Uint32 zero_centered_joysticks[] = {
+        MAKE_VIDPID(0x0e8f, 0x3013),    /* HuiJia SNES USB adapter */
+        MAKE_VIDPID(0x05a0, 0x3232),    /* 8Bitdo Zero Gamepad */
     };
 
     int i;
-    Uint16 vendor = SDL_JoystickGetVendor(joystick);
-    Uint16 product = SDL_JoystickGetProduct(joystick);
+    Uint32 id = MAKE_VIDPID(SDL_JoystickGetVendor(joystick),
+                            SDL_JoystickGetProduct(joystick));
 
 /*printf("JOYSTICK '%s' VID/PID 0x%.4x/0x%.4x AXES: %d\n", joystick->name, vendor, product, joystick->naxes);*/
 
@@ -141,8 +140,7 @@ SDL_JoystickAxesCenteredAtZero(SDL_Joystick *joystick)
     }
 
     for (i = 0; i < SDL_arraysize(zero_centered_joysticks); ++i) {
-        if (vendor == zero_centered_joysticks[i].vendor &&
-            product == zero_centered_joysticks[i].product) {
+        if (id == zero_centered_joysticks[i]) {
             return SDL_TRUE;
         }
     }
@@ -185,14 +183,13 @@ SDL_JoystickOpen(int device_index)
     }
 
     /* Create and initialize the joystick */
-    joystick = (SDL_Joystick *) SDL_malloc((sizeof *joystick));
+    joystick = (SDL_Joystick *) SDL_calloc(sizeof(*joystick), 1);
     if (joystick == NULL) {
         SDL_OutOfMemory();
         SDL_UnlockJoystickList();
         return NULL;
     }
 
-    SDL_memset(joystick, 0, (sizeof *joystick));
     if (SDL_SYS_JoystickOpen(joystick, device_index) < 0) {
         SDL_free(joystick);
         SDL_UnlockJoystickList();
@@ -237,6 +234,8 @@ SDL_JoystickOpen(int device_index)
         }
     }
 
+    joystick->is_game_controller = SDL_IsGameController(device_index);
+
     /* Add joystick to list */
     ++joystick->ref_count;
     /* Link the joystick in the list */
@@ -932,7 +931,7 @@ SDL_JoystickEventState(int state)
 #endif /* SDL_EVENTS_DISABLED */
 }
 
-static void GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version)
+static void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version)
 {
     Uint16 *guid16 = (Uint16 *)guid.data;
 
@@ -967,6 +966,73 @@ static void GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *p
     }
 }
 
+static SDL_bool SDL_IsJoystickGUIDWheel(SDL_JoystickGUID guid)
+{
+    static Uint32 wheel_joysticks[] = {
+        MAKE_VIDPID(0x046d, 0xc294),    /* Logitech generic wheel */
+        MAKE_VIDPID(0x046d, 0xc295),    /* Logitech Momo Force */
+        MAKE_VIDPID(0x046d, 0xc298),    /* Logitech Driving Force Pro */
+        MAKE_VIDPID(0x046d, 0xc299),    /* Logitech G25 */
+        MAKE_VIDPID(0x046d, 0xc29a),    /* Logitech Driving Force GT */
+        MAKE_VIDPID(0x046d, 0xc29b),    /* Logitech G27 */
+        MAKE_VIDPID(0x046d, 0xc261),    /* Logitech G920 (initial mode) */
+        MAKE_VIDPID(0x046d, 0xc262),    /* Logitech G920 (active mode) */
+        MAKE_VIDPID(0x044f, 0xb65d),    /* Thrustmaster Wheel FFB */
+        MAKE_VIDPID(0x044f, 0xb677),    /* Thrustmaster T150 */
+        MAKE_VIDPID(0x044f, 0xb664),    /* Thrustmaster TX (initial mode) */
+        MAKE_VIDPID(0x044f, 0xb669),    /* Thrustmaster TX (active mode) */
+    };
+    Uint16 vendor;
+    Uint16 product;
+    Uint32 id;
+    int i;
+
+    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL);
+    id = MAKE_VIDPID(vendor, product);
+
+    for (i = 0; i < SDL_arraysize(wheel_joysticks); ++i) {
+        if (id == wheel_joysticks[i]) {
+            return SDL_TRUE;
+        }
+    }
+    return SDL_FALSE;
+}
+
+static SDL_JoystickType SDL_GetJoystickGUIDType(SDL_JoystickGUID guid)
+{
+    if (guid.data[14] == 'x') {
+        /* XInput GUID, get the type based on the XInput device subtype */
+        switch (guid.data[15]) {
+        case 0x01:  /* XINPUT_DEVSUBTYPE_GAMEPAD */
+            return SDL_JOYSTICK_TYPE_GAMECONTROLLER;
+        case 0x02:  /* XINPUT_DEVSUBTYPE_WHEEL */
+            return SDL_JOYSTICK_TYPE_WHEEL;
+        case 0x03:  /* XINPUT_DEVSUBTYPE_ARCADE_STICK */
+            return SDL_JOYSTICK_TYPE_ARCADE_STICK;
+        case 0x04:  /* XINPUT_DEVSUBTYPE_FLIGHT_STICK */
+            return SDL_JOYSTICK_TYPE_FLIGHT_STICK;
+        case 0x05:  /* XINPUT_DEVSUBTYPE_DANCE_PAD */
+            return SDL_JOYSTICK_TYPE_DANCE_PAD;
+        case 0x06:  /* XINPUT_DEVSUBTYPE_GUITAR */
+        case 0x07:  /* XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE */
+        case 0x0B:  /* XINPUT_DEVSUBTYPE_GUITAR_BASS */
+            return SDL_JOYSTICK_TYPE_GUITAR;
+        case 0x08:  /* XINPUT_DEVSUBTYPE_DRUM_KIT */
+            return SDL_JOYSTICK_TYPE_DRUM_KIT;
+        case 0x13:  /* XINPUT_DEVSUBTYPE_ARCADE_PAD */
+            return SDL_JOYSTICK_TYPE_ARCADE_PAD;
+        default:
+            return SDL_JOYSTICK_TYPE_UNKNOWN;
+        }
+    }
+
+    if (SDL_IsJoystickGUIDWheel(guid)) {
+        return SDL_JOYSTICK_TYPE_WHEEL;
+    }
+
+    return SDL_JOYSTICK_TYPE_UNKNOWN;
+}
+
 /* return the guid for this index */
 SDL_JoystickGUID SDL_JoystickGetDeviceGUID(int device_index)
 {
@@ -984,7 +1050,7 @@ Uint16 SDL_JoystickGetDeviceVendor(int device_index)
     Uint16 vendor;
     SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
 
-    GetJoystickGUIDInfo(guid, &vendor, NULL, NULL);
+    SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL);
     return vendor;
 }
 
@@ -993,7 +1059,7 @@ Uint16 SDL_JoystickGetDeviceProduct(int device_index)
     Uint16 product;
     SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
 
-    GetJoystickGUIDInfo(guid, NULL, &product, NULL);
+    SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL);
     return product;
 }
 
@@ -1002,10 +1068,24 @@ Uint16 SDL_JoystickGetDeviceProductVersion(int device_index)
     Uint16 version;
     SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
 
-    GetJoystickGUIDInfo(guid, NULL, NULL, &version);
+    SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version);
     return version;
 }
 
+SDL_JoystickType SDL_JoystickGetDeviceType(int device_index)
+{
+    SDL_JoystickType type;
+    SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
+
+    type = SDL_GetJoystickGUIDType(guid);
+    if (type == SDL_JOYSTICK_TYPE_UNKNOWN) {
+        if (SDL_IsGameController(device_index)) {
+            type = SDL_JOYSTICK_TYPE_GAMECONTROLLER;
+        }
+    }
+    return type;
+}
+
 /* return the guid for this opened device */
 SDL_JoystickGUID SDL_JoystickGetGUID(SDL_Joystick * joystick)
 {
@@ -1022,7 +1102,7 @@ Uint16 SDL_JoystickGetVendor(SDL_Joystick * joystick)
     Uint16 vendor;
     SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
 
-    GetJoystickGUIDInfo(guid, &vendor, NULL, NULL);
+    SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL);
     return vendor;
 }
 
@@ -1031,7 +1111,7 @@ Uint16 SDL_JoystickGetProduct(SDL_Joystick * joystick)
     Uint16 product;
     SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
 
-    GetJoystickGUIDInfo(guid, NULL, &product, NULL);
+    SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL);
     return product;
 }
 
@@ -1040,10 +1120,24 @@ Uint16 SDL_JoystickGetProductVersion(SDL_Joystick * joystick)
     Uint16 version;
     SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
 
-    GetJoystickGUIDInfo(guid, NULL, NULL, &version);
+    SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version);
     return version;
 }
 
+SDL_JoystickType SDL_JoystickGetType(SDL_Joystick * joystick)
+{
+    SDL_JoystickType type;
+    SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
+
+    type = SDL_GetJoystickGUIDType(guid);
+    if (type == SDL_JOYSTICK_TYPE_UNKNOWN) {
+        if (joystick && joystick->is_game_controller) {
+            type = SDL_JOYSTICK_TYPE_GAMECONTROLLER;
+        }
+    }
+    return type;
+}
+
 /* convert the guid to a printable string */
 void SDL_JoystickGetGUIDString(SDL_JoystickGUID guid, char *pszGUID, int cbGUID)
 {
diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
index 53a7ff7..c3db03b 100644
--- a/src/joystick/SDL_sysjoystick.h
+++ b/src/joystick/SDL_sysjoystick.h
@@ -62,6 +62,7 @@ struct _SDL_Joystick
 
     int ref_count;              /* Reference count for multiple opens */
 
+    SDL_bool is_game_controller;
     SDL_bool force_recentering; /* SDL_TRUE if this device needs to have its state reset to 0 */
     SDL_JoystickPowerLevel epowerlevel; /* power level of this joystick, SDL_JOYSTICK_POWER_UNKNOWN if not supported */
     struct _SDL_Joystick *next; /* pointer to next joystick we have allocated */