Bläddra i källkod

Added SDL hints to filter the set of game controllers reported by SDL

Sam Lantinga 7 år sedan
förälder
incheckning
c49fa37c5b

+ 26 - 6
include/SDL_hints.h

@@ -359,7 +359,6 @@ extern "C" {
  */
 #define SDL_HINT_ACCELEROMETER_AS_JOYSTICK "SDL_ACCELEROMETER_AS_JOYSTICK"
 
-
 /**
  *  \brief  A variable that lets you disable the detection and use of Xinput gamepad devices
  *
@@ -369,7 +368,6 @@ extern "C" {
  */
 #define SDL_HINT_XINPUT_ENABLED "SDL_XINPUT_ENABLED"
 
-
 /**
  *  \brief  A variable that causes SDL to use the old axis and button mapping for XInput devices.
  *
@@ -379,9 +377,8 @@ extern "C" {
  */
 #define SDL_HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING "SDL_XINPUT_USE_OLD_JOYSTICK_MAPPING"
 
-
 /**
- *  \brief  A variable that lets you manually hint extra gamecontroller db entries
+ *  \brief  A variable that lets you manually hint extra gamecontroller db entries.
  *
  *  The variable should be newline delimited rows of gamecontroller config data, see SDL_gamecontroller.h
  *
@@ -390,6 +387,31 @@ extern "C" {
  */
 #define SDL_HINT_GAMECONTROLLERCONFIG "SDL_GAMECONTROLLERCONFIG"
 
+/**
+ *  \brief  A variable containing a list of devices to skip when scanning for game controllers.
+ *
+ *  The format of the string is a comma separated list of USB VID/PID pairs
+ *  in hexadecimal form, e.g.
+ *
+ *      0xAAAA/0xBBBB,0xCCCC/0xDDDD
+ *
+ *  The variable can also take the form of @file, in which case the named
+ *  file will be loaded and interpreted as the value of the variable.
+ */
+#define SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES "SDL_GAMECONTROLLER_IGNORE_DEVICES"
+
+/**
+ *  \brief  If set, all devices will be skipped when scanning for game controllers except for the ones listed in this variable.
+ *
+ *  The format of the string is a comma separated list of USB VID/PID pairs
+ *  in hexadecimal form, e.g.
+ *
+ *      0xAAAA/0xBBBB,0xCCCC/0xDDDD
+ *
+ *  The variable can also take the form of @file, in which case the named
+ *  file will be loaded and interpreted as the value of the variable.
+ */
+#define SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT "SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT"
 
 /**
  *  \brief  A variable that lets you enable joystick (and gamecontroller) events even when your app is in the background.
@@ -404,7 +426,6 @@ extern "C" {
  */
 #define SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS "SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS"
 
-
 /**
  *  \brief If set to "0" then never set the top most bit on a SDL Window, even if the video mode expects it.
  *      This is a debugging aid for developers and not expected to be used by end users. The default is "1"
@@ -415,7 +436,6 @@ extern "C" {
  */
 #define SDL_HINT_ALLOW_TOPMOST "SDL_ALLOW_TOPMOST"
 
-
 /**
  *  \brief A variable that controls the timer resolution, in milliseconds.
  *

+ 175 - 28
src/joystick/SDL_gamecontroller.c

@@ -112,6 +112,74 @@ struct _SDL_GameController
 };
 
 
+typedef struct
+{
+    int num_entries;
+    int max_entries;
+    Uint32 *entries;
+} SDL_vidpid_list;
+
+static SDL_vidpid_list SDL_allowed_controllers;
+static SDL_vidpid_list SDL_ignored_controllers;
+
+static void
+SDL_LoadVIDPIDListFromHint(const char *hint, SDL_vidpid_list *list)
+{
+    Uint32 entry;
+    char *spot;
+    char *file = NULL;
+
+    list->num_entries = 0;
+
+    if (hint && *hint == '@') {
+        spot = file = (char *)SDL_LoadFile(hint+1, NULL);
+    } else {
+        spot = (char *)hint;
+    }
+
+    if (!spot) {
+        return;
+    }
+
+    while ((spot = SDL_strstr(spot, "0x")) != NULL) {
+        entry = SDL_strtol(spot, &spot, 0);
+        entry <<= 16;
+        spot = SDL_strstr(spot, "0x");
+        if (!spot) {
+            break;
+        }
+        entry |= SDL_strtol(spot, &spot, 0);
+
+        if (list->num_entries == list->max_entries) {
+            int max_entries = list->max_entries + 16;
+            Uint32 *entries = (Uint32 *)SDL_realloc(list->entries, max_entries*sizeof(*list->entries));
+            if (entries == NULL) {
+                /* Out of memory, go with what we have already */
+                break;
+            }
+            list->entries = entries;
+            list->max_entries = max_entries;
+        }
+        list->entries[list->num_entries++] = entry;
+    }
+
+    if (file) {
+        SDL_free(file);
+    }
+}
+
+static void
+SDL_GameControllerIgnoreDevicesChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
+{
+    SDL_LoadVIDPIDListFromHint(hint, &SDL_ignored_controllers);
+}
+
+static void
+SDL_GameControllerIgnoreDevicesExceptChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
+{
+    SDL_LoadVIDPIDListFromHint(hint, &SDL_allowed_controllers);
+}
+
 static int SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value);
 static int SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state);
 
@@ -799,50 +867,51 @@ SDL_PrivateAddMappingForGUID(SDL_JoystickGUID jGUID, const char *mappingString,
 /*
  * Helper function to determine pre-calculated offset to certain joystick mappings
  */
-static ControllerMapping_t *SDL_PrivateGetControllerMapping(int device_index)
+static ControllerMapping_t *SDL_PrivateGetControllerMappingForNameAndGUID(const char *name, SDL_JoystickGUID guid)
 {
-    SDL_JoystickGUID jGUID = SDL_JoystickGetDeviceGUID(device_index);
     ControllerMapping_t *mapping;
 
-    (void) s_pEmscriptenMapping;  /* pacify ARMCC */
-
-    mapping = SDL_PrivateGetControllerMappingForGUID(&jGUID);
-#if SDL_JOYSTICK_XINPUT
-    if (!mapping && SDL_SYS_IsXInputGamepad_DeviceIndex(device_index)) {
-        mapping = s_pXInputMapping;
-    }
-#endif
+    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) {
-        const char *name = SDL_JoystickNameForIndex(device_index);
-        if (name) {
-            if (SDL_strstr(name, "Xbox 360 Wireless Receiver")) {
-                /* The Linux driver xpad.c maps the wireless dpad to buttons */
-                SDL_bool existing;
-                mapping = SDL_PrivateAddMappingForGUID(jGUID,
+    if (!mapping && name) {
+        if (SDL_strstr(name, "Xbox 360 Wireless Receiver")) {
+            /* The Linux driver xpad.c maps the wireless dpad to buttons */
+            SDL_bool existing;
+            mapping = SDL_PrivateAddMappingForGUID(jGUID,
 "none,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
-                              &existing, SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT);
-            }
+                          &existing, SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT);
         }
     }
 #endif /* __LINUX__ */
 
-    if (!mapping) {
-        const char *name = SDL_JoystickNameForIndex(device_index);
-        if (name) {
-            if (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box")) {
-                mapping = s_pXInputMapping;
-            }
+    if (!mapping && name) {
+        if (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box")) {
+            mapping = s_pXInputMapping;
         }
     }
     return mapping;
 }
 
+static ControllerMapping_t *SDL_PrivateGetControllerMapping(int device_index)
+{
+    const char *name = SDL_JoystickNameForIndex(device_index);
+    SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
+    ControllerMapping_t *mapping = SDL_PrivateGetControllerMappingForNameAndGUID(name, guid);
+#if SDL_JOYSTICK_XINPUT
+    if (!mapping && SDL_SYS_IsXInputGamepad_DeviceIndex(device_index)) {
+        mapping = s_pXInputMapping;
+    }
+#endif
+    return mapping;
+}
+
 /*
  * Add or update an entry into the Mappings Database
  */
@@ -1092,7 +1161,7 @@ SDL_GameControllerLoadHints()
  * Initialize the game controller system, mostly load our DB of controller config mappings
  */
 int
-SDL_GameControllerInit(void)
+SDL_GameControllerInitMappings(void)
 {
     int i = 0;
     const char *pMappingString = NULL;
@@ -1107,6 +1176,19 @@ SDL_GameControllerInit(void)
     /* load in any user supplied config */
     SDL_GameControllerLoadHints();
 
+    SDL_AddHintCallback(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES,
+                        SDL_GameControllerIgnoreDevicesChanged, NULL);
+    SDL_AddHintCallback(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT,
+                        SDL_GameControllerIgnoreDevicesExceptChanged, NULL);
+
+    return (0);
+}
+
+int
+SDL_GameControllerInit(void)
+{
+    int i;
+
     /* watch for joy events and fire controller ones if needed */
     SDL_AddEventWatch(SDL_GameControllerEventWatcher, NULL);
 
@@ -1138,6 +1220,19 @@ SDL_GameControllerNameForIndex(int device_index)
 }
 
 
+/*
+ * Return 1 if the joystick with this name and GUID is a supported controller
+ */
+SDL_bool
+SDL_IsGameControllerNameAndGUID(const char *name, SDL_JoystickGUID guid)
+{
+    ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMappingForNameAndGUID(name, guid);
+    if (pSupportedController) {
+        return SDL_TRUE;
+    }
+    return SDL_FALSE;
+}
+
 /*
  * Return 1 if the joystick at this device index is a supported controller
  */
@@ -1151,6 +1246,41 @@ SDL_IsGameController(int device_index)
     return SDL_FALSE;
 }
 
+/*
+ * Return 1 if the game controller should be ignored by SDL
+ */
+SDL_bool SDL_ShouldIgnoreGameController(const char *name, SDL_JoystickGUID guid)
+{
+    int i;
+    Uint16 vendor;
+    Uint16 product;
+    Uint32 vidpid;
+
+    if (SDL_allowed_controllers.num_entries == 0 &&
+        SDL_ignored_controllers.num_entries == 0) {
+        return SDL_FALSE;
+    }
+
+    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL);
+    vidpid = MAKE_VIDPID(vendor, product);
+
+    if (SDL_allowed_controllers.num_entries > 0) {
+        for (i = 0; i < SDL_allowed_controllers.num_entries; ++i) {
+            if (vidpid == SDL_allowed_controllers.entries[i]) {
+                return SDL_FALSE;
+            }
+        }
+        return SDL_TRUE;
+    } else {
+        for (i = 0; i < SDL_ignored_controllers.num_entries; ++i) {
+            if (vidpid == SDL_ignored_controllers.entries[i]) {
+                return SDL_TRUE;
+            }
+        }
+        return SDL_FALSE;
+    }
+}
+
 /*
  * Open a controller for use - the index passed as an argument refers to
  * the N'th controller on the system.  This index is the value which will
@@ -1536,14 +1666,18 @@ SDL_GameControllerClose(SDL_GameController * gamecontroller)
 void
 SDL_GameControllerQuit(void)
 {
-    ControllerMapping_t *pControllerMap;
-
     SDL_LockJoystickList();
     while (SDL_gamecontrollers) {
         SDL_gamecontrollers->ref_count = 1;
         SDL_GameControllerClose(SDL_gamecontrollers);
     }
     SDL_UnlockJoystickList();
+}
+
+void
+SDL_GameControllerQuitMappings(void)
+{
+    ControllerMapping_t *pControllerMap;
 
     while (s_pSupportedControllers) {
         pControllerMap = s_pSupportedControllers;
@@ -1555,6 +1689,19 @@ SDL_GameControllerQuit(void)
 
     SDL_DelEventWatch(SDL_GameControllerEventWatcher, NULL);
 
+    SDL_DelHintCallback(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES,
+                        SDL_GameControllerIgnoreDevicesChanged, NULL);
+    SDL_DelHintCallback(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT,
+                        SDL_GameControllerIgnoreDevicesExceptChanged, NULL);
+
+    if (SDL_allowed_controllers.entries) {
+        SDL_free(SDL_allowed_controllers.entries);
+        SDL_zero(SDL_allowed_controllers);
+    }
+    if (SDL_ignored_controllers.entries) {
+        SDL_free(SDL_ignored_controllers.entries);
+        SDL_zero(SDL_ignored_controllers);
+    }
 }
 
 /*

+ 8 - 2
src/joystick/SDL_joystick.c

@@ -70,6 +70,8 @@ SDL_JoystickInit(void)
 {
     int status;
 
+    SDL_GameControllerInitMappings();
+
     /* Create the joystick list lock */
     if (!SDL_joystick_lock) {
         SDL_joystick_lock = SDL_CreateMutex();
@@ -561,10 +563,15 @@ SDL_JoystickQuit(void)
     SDL_QuitSubSystem(SDL_INIT_EVENTS);
 #endif
 
+    SDL_DelHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
+                        SDL_JoystickAllowBackgroundEventsChanged, NULL);
+
     if (SDL_joystick_lock) {
         SDL_DestroyMutex(SDL_joystick_lock);
         SDL_joystick_lock = NULL;
     }
+
+    SDL_GameControllerQuitMappings();
 }
 
 
@@ -930,7 +937,7 @@ SDL_JoystickEventState(int state)
 #endif /* SDL_EVENTS_DISABLED */
 }
 
-static void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version)
+void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version)
 {
     Uint16 *guid16 = (Uint16 *)guid.data;
 
@@ -1273,5 +1280,4 @@ SDL_JoystickPowerLevel SDL_JoystickCurrentPowerLevel(SDL_Joystick * joystick)
     return joystick->epowerlevel;
 }
 
-
 /* vi: set ts=4 sw=4 expandtab: */

+ 11 - 0
src/joystick/SDL_joystick_c.h

@@ -28,6 +28,8 @@ extern int SDL_JoystickInit(void);
 extern void SDL_JoystickQuit(void);
 
 /* Initialization and shutdown functions */
+extern int SDL_GameControllerInitMappings(void);
+extern void SDL_GameControllerQuitMappings(void);
 extern int SDL_GameControllerInit(void);
 extern void SDL_GameControllerQuit(void);
 
@@ -35,6 +37,15 @@ extern void SDL_GameControllerQuit(void);
 extern void SDL_LockJoystickList(void);
 extern void SDL_UnlockJoystickList(void);
 
+/* Function to extract information from an SDL joystick GUID */
+extern void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version);
+
+/* Function to return whether a joystick name and GUID is a game controller  */
+extern SDL_bool SDL_IsGameControllerNameAndGUID(const char *name, SDL_JoystickGUID guid);
+
+/* Function to return whether a game controller should be ignored */
+extern SDL_bool SDL_ShouldIgnoreGameController(const char *name, SDL_JoystickGUID guid);
+
 /* Internal event queueing functions */
 extern void SDL_PrivateJoystickAdded(int device_index);
 extern void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance);

+ 6 - 0
src/joystick/darwin/SDL_sysjoystick.c

@@ -445,6 +445,12 @@ JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDevic
         return;   /* not a device we care about, probably. */
     }
 
+    if (SDL_IsGameControllerNameAndGUID(device->product, device->guid) &&
+        SDL_ShouldIgnoreGameController(device->product, device->guid)) {
+        SDL_free(device);
+        return;
+    }
+
     /* Get notified when this device is disconnected. */
     IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
     IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);

+ 4 - 0
src/joystick/linux/SDL_sysjoystick.c

@@ -233,6 +233,10 @@ IsJoystick(int fd, char *namebuf, const size_t namebuflen, SDL_JoystickGUID *gui
         SDL_strlcpy((char*)guid16, namebuf, sizeof(guid->data) - 4);
     }
 
+    if (SDL_IsGameControllerNameAndGUID(namebuf, *guid) &&
+        SDL_ShouldIgnoreGameController(namebuf, *guid)) {
+        return 0;
+    }
     return 1;
 }
 

+ 6 - 0
src/joystick/windows/SDL_dinputjoystick.c

@@ -428,6 +428,12 @@ EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
         SDL_strlcpy((char*)guid16, pNewJoystick->joystickname, sizeof(pNewJoystick->guid.data) - 4);
     }
 
+    if (SDL_IsGameControllerNameAndGUID(pNewJoystick->joystickname, pNewJoystick->guid) &&
+        SDL_ShouldIgnoreGameController(pNewJoystick->joystickname, pNewJoystick->guid)) {
+        SDL_free(pNewJoystick);
+        return DIENUM_CONTINUE;
+    }
+
     SDL_SYS_AddJoystickDevice(pNewJoystick);
 
     return DIENUM_CONTINUE; /* get next device, please */

+ 6 - 0
src/joystick/windows/SDL_xinputjoystick.c

@@ -251,6 +251,12 @@ AddXInputDevice(Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext)
     }
     pNewJoystick->SubType = SubType;
     pNewJoystick->XInputUserId = userid;
+
+    if (SDL_ShouldIgnoreGameController(pNewJoystick->joystickname, pNewJoystick->guid)) {
+        SDL_free(pNewJoystick);
+        return;
+    }
+
     SDL_SYS_AddJoystickDevice(pNewJoystick);
 }