# SDL: Use SDL\_HINT\_GAMECONTROLLER\_SENSOR\_FUSION as a list of controllers to enable sensor fusion

**URL:** <https://discourse.libsdl.org/t/sdl-use-sdl-hint-gamecontroller-sensor-fusion-as-a-list-of-controllers-to-enable-sensor-fusion/44297>\
**Category:** SDL Commits\
**Created:** [June 17, 2023, 4:02pm UTC](https://discourse.libsdl.org/t/sdl-use-sdl-hint-gamecontroller-sensor-fusion-as-a-list-of-controllers-to-enable-sensor-fusion/44297 "2023-06-17T16:02:02Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![SDLPushPostBot](https://discourse.libsdl.org/user_avatar/discourse.libsdl.org/sdlpushpostbot/32/4370_2.png) [@SDLPushPostBot](https://discourse.libsdl.org/u/SDLPushPostBot)\
**Post date:** [June 17, 2023, 4:02pm UTC](https://discourse.libsdl.org/t/sdl-use-sdl-hint-gamecontroller-sensor-fusion-as-a-list-of-controllers-to-enable-sensor-fusion/44297/1 "2023-06-17T16:02:02Z")

</div>

> <https://github.com/libsdl-org/SDL/commit/d91e96e7f50b9c3cb0e1dacda39274b4af037a96>
>
> There are too many wraparound style controllers out there to enumerate them all,… so instead make this a user option in applications that support it.

```diff
From d91e96e7f50b9c3cb0e1dacda39274b4af037a96 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 17 Jun 2023 08:59:52 -0700
Subject: [PATCH] Use SDL_HINT_GAMECONTROLLER_SENSOR_FUSION as a list of
 controllers to enable sensor fusion

There are too many wraparound style controllers out there to enumerate them all, so instead make this a user option in applications that support it.
---
 include/SDL3/SDL_hints.h | 11 +++-
 src/joystick/SDL_gamepad.c | 78 ++-----------------------
 src/joystick/SDL_joystick.c | 107 +++++++++++++++++++++++++++-------
 src/joystick/SDL_joystick_c.h | 12 ++++
 4 files changed, 112 insertions(+), 96 deletions(-)

diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h
index 9114c2f2cb54..e475619769b6 100644
--- a/include/SDL3/SDL_hints.h
+++ b/include/SDL3/SDL_hints.h
@@ -620,11 +620,18 @@ extern "C" {
  * \brief Controls whether the device's built-in accelerometer and gyro should be used as sensors for gamepads.
  *
  * The variable can be set to the following values:
- * "auto" - Sensor fusion is enabled for known wraparound controllers like the Razer Kishi and Backbone One
  * "0" - Sensor fusion is disabled
  * "1" - Sensor fusion is enabled for all controllers that lack sensors
  *
- * The default value is "auto". This hint is checked when a gamepad is opened.
+ * Or the variable can be 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.
+ *
+ * This hint is checked when a gamepad is opened.
  */
 #define SDL_HINT_GAMECONTROLLER_SENSOR_FUSION "SDL_GAMECONTROLLER_SENSOR_FUSION"
 
diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c
index 3282c7284efb..8443ba03abcf 100644
--- a/src/joystick/SDL_gamepad.c
+++ b/src/joystick/SDL_gamepad.c
@@ -150,61 +150,9 @@ struct SDL_Gamepad
         return retval; \
     }
 
-typedef struct
-{
- int num_entries;
- int max_entries;
- Uint32 *entries;
-} SDL_vidpid_list;
-
 static SDL_vidpid_list SDL_allowed_gamepads;
 static SDL_vidpid_list SDL_ignored_gamepads;
 
-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 == NULL) {
- return;
- }
-
- while ((spot = SDL_strstr(spot, "0x")) != NULL) {
- entry = (Uint16)SDL_strtol(spot, &spot, 0);
- entry <<= 16;
- spot = SDL_strstr(spot, "0x");
- if (spot == NULL) {
- break;
- }
- entry |= (Uint16)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 SDLCALL SDL_GamepadIgnoreDevicesChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
 {
     SDL_LoadVIDPIDListFromHint(hint, &SDL_ignored_gamepads);
@@ -2112,11 +2060,9 @@ static SDL_bool SDL_endswith(const char *string, const char *suffix)
  */
 SDL_bool SDL_ShouldIgnoreGamepad(const char *name, SDL_JoystickGUID guid)
 {
- int i;
     Uint16 vendor;
     Uint16 product;
     Uint16 version;
- Uint32 vidpid;
 
 #ifdef __LINUX__
     if (SDL_endswith(name, " Motion Sensors")) {
@@ -2165,20 +2111,14 @@ SDL_bool SDL_ShouldIgnoreGamepad(const char *name, SDL_JoystickGUID guid)
         }
     }
 
- vidpid = MAKE_VIDPID(vendor, product);
-
     if (SDL_allowed_gamepads.num_entries > 0) {
- for (i = 0; i < SDL_allowed_gamepads.num_entries; ++i) {
- if (vidpid == SDL_allowed_gamepads.entries[i]) {
- return SDL_FALSE;
- }
+ if (SDL_VIDPIDInList(vendor, product, &SDL_allowed_gamepads)) {
+ return SDL_FALSE;
         }
         return SDL_TRUE;
     } else {
- for (i = 0; i < SDL_ignored_gamepads.num_entries; ++i) {
- if (vidpid == SDL_ignored_gamepads.entries[i]) {
- return SDL_TRUE;
- }
+ if (SDL_VIDPIDInList(vendor, product, &SDL_ignored_gamepads)) {
+ return SDL_TRUE;
         }
         return SDL_FALSE;
     }
@@ -3100,14 +3040,8 @@ void SDL_QuitGamepadMappings(void)
     SDL_DelHintCallback(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT,
                         SDL_GamepadIgnoreDevicesExceptChanged, NULL);
 
- if (SDL_allowed_gamepads.entries) {
- SDL_free(SDL_allowed_gamepads.entries);
- SDL_zero(SDL_allowed_gamepads);
- }
- if (SDL_ignored_gamepads.entries) {
- SDL_free(SDL_ignored_gamepads.entries);
- SDL_zero(SDL_ignored_gamepads);
- }
+ SDL_FreeVIDPIDList(&SDL_allowed_gamepads);
+ SDL_FreeVIDPIDList(&SDL_ignored_gamepads);
 }
 
 /*
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 993871b11a4c..20f35b224cf4 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -564,19 +564,8 @@ static SDL_bool IsROGAlly(SDL_Joystick *joystick)
 
 static SDL_bool ShouldAttemptSensorFusion(SDL_Joystick *joystick, SDL_bool *invert_sensors)
 {
- static Uint32 wraparound_gamepads[] = {
- MAKE_VIDPID(0x1532, 0x0709), /* Razer Junglecat (L) */
- MAKE_VIDPID(0x1532, 0x070a), /* Razer Junglecat (R) */
- MAKE_VIDPID(0x1532, 0x0717), /* Razer Edge controller */
- MAKE_VIDPID(0x1949, 0x0402), /* Ipega PG-9083S */
- MAKE_VIDPID(0x27f8, 0x0bbc), /* Gamevice */
- MAKE_VIDPID(0x27f8, 0x0bbf), /* Razer Kishi */
- };
- SDL_JoystickGUID guid;
- Uint16 vendor, product;
- Uint32 vidpid;
- int i;
- int hint;
+ const char *hint;
+ int hint_value;
 
     *invert_sensors = SDL_FALSE;
 
@@ -590,20 +579,28 @@ static SDL_bool ShouldAttemptSensorFusion(SDL_Joystick *joystick, SDL_bool *inve
         return SDL_FALSE;
     }
 
- hint = SDL_GetStringInteger(SDL_GetHint(SDL_HINT_GAMECONTROLLER_SENSOR_FUSION), -1);
- if (hint > 0) {
+ hint = SDL_GetHint(SDL_HINT_GAMECONTROLLER_SENSOR_FUSION);
+ hint_value = SDL_GetStringInteger(hint, -1);
+ if (hint_value > 0) {
         return SDL_TRUE;
     }
- if (hint == 0) {
+ if (hint_value == 0) {
         return SDL_FALSE;
     }
 
- /* See if the controller is in our list of wraparound gamepads */
- guid = SDL_GetJoystickGUID(joystick);
- SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
- vidpid = MAKE_VIDPID(vendor, product);
- for (i = 0; i < SDL_arraysize(wraparound_gamepads); ++i) {
- if (vidpid == wraparound_gamepads[i]) {
+ if (hint) {
+ SDL_vidpid_list gamepads;
+ SDL_JoystickGUID guid;
+ Uint16 vendor, product;
+ SDL_bool enabled;
+
+ /* See if the gamepad is in our list of devices to enable */
+ guid = SDL_GetJoystickGUID(joystick);
+ SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
+ SDL_LoadVIDPIDListFromHint(hint, &gamepads);
+ enabled = SDL_VIDPIDInList(vendor, product, &gamepads);
+ SDL_FreeVIDPIDList(&gamepads);
+ if (enabled) {
             return SDL_TRUE;
         }
     }
@@ -3263,3 +3260,69 @@ int SDL_SendJoystickSensor(Uint64 timestamp, SDL_Joystick *joystick, SDL_SensorT
     }
     return posted;
 }
+
+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 == NULL) {
+ return;
+ }
+
+ while ((spot = SDL_strstr(spot, "0x")) != NULL) {
+ entry = (Uint16)SDL_strtol(spot, &spot, 0);
+ entry <<= 16;
+ spot = SDL_strstr(spot, "0x");
+ if (spot == NULL) {
+ break;
+ }
+ entry |= (Uint16)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);
+ }
+}
+
+SDL_bool SDL_VIDPIDInList(Uint16 vendor_id, Uint16 product_id, const SDL_vidpid_list *list)
+{
+ int i;
+ Uint32 vidpid = MAKE_VIDPID(vendor_id, product_id);
+
+ for (i = 0; i < list->num_entries; ++i) {
+ if (vidpid == list->entries[i]) {
+ return SDL_TRUE;
+ }
+ }
+ return SDL_FALSE;
+}
+
+void SDL_FreeVIDPIDList(SDL_vidpid_list *list)
+{
+ if (list->entries) {
+ SDL_free(list->entries);
+ SDL_zerop(list);
+ }
+}
diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h
index 46de50d0aa65..0af5aafb9405 100644
--- a/src/joystick/SDL_joystick_c.h
+++ b/src/joystick/SDL_joystick_c.h
@@ -202,6 +202,18 @@ typedef struct SDL_GamepadMapping
 extern SDL_bool SDL_PrivateJoystickGetAutoGamepadMapping(SDL_JoystickID instance_id,
                                                          SDL_GamepadMapping *out);
 
+
+typedef struct
+{
+ int num_entries;
+ int max_entries;
+ Uint32 *entries;
+} SDL_vidpid_list;
+
+extern void SDL_LoadVIDPIDListFromHint(const char *hint, SDL_vidpid_list *list);
+extern SDL_bool SDL_VIDPIDInList(Uint16 vendor_id, Uint16 product_id, const SDL_vidpid_list *list);
+extern void SDL_FreeVIDPIDList(SDL_vidpid_list *list);
+
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
 }

```
