Browse Source

Added SDL_ReloadGamepadMappings() to reset the SDL gamepad mappings

Sam Lantinga 1 year ago
parent
commit
9db2cb3513

+ 12 - 0
include/SDL3/SDL_gamepad.h

@@ -213,6 +213,18 @@ extern DECLSPEC int SDLCALL SDL_AddGamepadMappingsFromRW(SDL_RWops *src, int fre
  */
 extern DECLSPEC int SDLCALL SDL_AddGamepadMappingsFromFile(const char *file);
 
+/**
+ * Reinitialize the SDL mapping database to its initial state.
+ *
+ * This will generate gamepad events as needed if device mappings change.
+ *
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern DECLSPEC int SDLCALL SDL_ReloadGamepadMappings(void);
+
 /**
  * Get the number of mappings installed.
  *

+ 1 - 0
src/dynapi/SDL_dynapi.sym

@@ -878,6 +878,7 @@ SDL3_0.0.0 {
     SDL_wcsnlen;
     SDL_strnlen;
     SDL_AddGamepadMappingsFromFile;
+    SDL_ReloadGamepadMappings;
     # extra symbols go here (don't modify this line)
   local: *;
 };

+ 1 - 0
src/dynapi/SDL_dynapi_overrides.h

@@ -904,3 +904,4 @@
 #define SDL_wcsnlen SDL_wcsnlen_REAL
 #define SDL_strnlen SDL_strnlen_REAL
 #define SDL_AddGamepadMappingsFromFile SDL_AddGamepadMappingsFromFile_REAL
+#define SDL_ReloadGamepadMappings SDL_ReloadGamepadMappings_REAL

+ 1 - 0
src/dynapi/SDL_dynapi_procs.h

@@ -949,3 +949,4 @@ SDL_DYNAPI_PROC(SDL_GamepadType,SDL_GetRealGamepadType,(SDL_Gamepad *a),(a),retu
 SDL_DYNAPI_PROC(size_t,SDL_wcsnlen,(const wchar_t *a, size_t b),(a,b),return)
 SDL_DYNAPI_PROC(size_t,SDL_strnlen,(const char *a, size_t b),(a,b),return)
 SDL_DYNAPI_PROC(int,SDL_AddGamepadMappingsFromFile,(const char *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_ReloadGamepadMappings,(void),(),return)

+ 66 - 38
src/joystick/SDL_gamepad.c

@@ -139,7 +139,7 @@ static SDL_JoystickGUID s_zeroGUID;
 static GamepadMapping_t *s_pSupportedGamepads SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
 static GamepadMapping_t *s_pDefaultMapping SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
 static GamepadMapping_t *s_pXInputMapping SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
-static MappingChangeTracker s_mappingChangeTracker;
+static MappingChangeTracker *s_mappingChangeTracker SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
 static char gamepad_magic;
 
 #define _guarded SDL_GUARDED_BY(SDL_joystick_lock)
@@ -506,52 +506,61 @@ void SDL_GamepadSensorWatcher(Uint64 timestamp, SDL_SensorID sensor, Uint64 sens
 
 static void PushMappingChangeTracking(void)
 {
+    MappingChangeTracker *tracker;
     int i, num_joysticks;
 
     SDL_AssertJoysticksLocked();
 
-    ++s_mappingChangeTracker.refcount;
-    if (s_mappingChangeTracker.refcount > 1) {
+    if (s_mappingChangeTracker) {
+        ++s_mappingChangeTracker->refcount;
         return;
     }
+    s_mappingChangeTracker = (MappingChangeTracker *)SDL_calloc(1, sizeof(*tracker));
+    s_mappingChangeTracker->refcount = 1;
 
     /* Save the list of joysticks and associated mappings */
-    s_mappingChangeTracker.joysticks = SDL_GetJoysticks(&num_joysticks);
-    if (!s_mappingChangeTracker.joysticks) {
+    tracker = s_mappingChangeTracker;
+    tracker->joysticks = SDL_GetJoysticks(&num_joysticks);
+    if (!tracker->joysticks) {
         return;
     }
     if (num_joysticks == 0) {
         return;
     }
-    s_mappingChangeTracker.joystick_mappings = (GamepadMapping_t **)SDL_malloc(num_joysticks * sizeof(*s_mappingChangeTracker.joystick_mappings));
-    if (!s_mappingChangeTracker.joystick_mappings) {
+    tracker->joystick_mappings = (GamepadMapping_t **)SDL_malloc(num_joysticks * sizeof(*tracker->joystick_mappings));
+    if (!tracker->joystick_mappings) {
         return;
     }
     for (i = 0; i < num_joysticks; ++i) {
-        s_mappingChangeTracker.joystick_mappings[i] = SDL_PrivateGetGamepadMapping(s_mappingChangeTracker.joysticks[i]);
+        tracker->joystick_mappings[i] = SDL_PrivateGetGamepadMapping(tracker->joysticks[i]);
     }
 }
 
 static void AddMappingChangeTracking(GamepadMapping_t *mapping)
 {
+    MappingChangeTracker *tracker;
     int num_mappings;
     GamepadMapping_t **new_mappings;
 
-    num_mappings = s_mappingChangeTracker.num_changed_mappings;
-    new_mappings = (GamepadMapping_t **)SDL_realloc(s_mappingChangeTracker.changed_mappings, (num_mappings + 1) * sizeof(*new_mappings));
+    SDL_AssertJoysticksLocked();
+
+    SDL_assert(s_mappingChangeTracker != NULL);
+    tracker = s_mappingChangeTracker;
+    num_mappings = tracker->num_changed_mappings;
+    new_mappings = (GamepadMapping_t **)SDL_realloc(tracker->changed_mappings, (num_mappings + 1) * sizeof(*new_mappings));
     if (new_mappings) {
-        s_mappingChangeTracker.changed_mappings = new_mappings;
-        s_mappingChangeTracker.changed_mappings[num_mappings] = mapping;
-        s_mappingChangeTracker.num_changed_mappings = (num_mappings + 1);
+        tracker->changed_mappings = new_mappings;
+        tracker->changed_mappings[num_mappings] = mapping;
+        tracker->num_changed_mappings = (num_mappings + 1);
     }
 }
 
-static SDL_bool HasMappingChangeTracking(GamepadMapping_t *mapping)
+static SDL_bool HasMappingChangeTracking(MappingChangeTracker *tracker, GamepadMapping_t *mapping)
 {
     int i;
 
-    for (i = 0; i < s_mappingChangeTracker.num_changed_mappings; ++i) {
-        if (s_mappingChangeTracker.changed_mappings[i] == mapping) {
+    for (i = 0; i < tracker->num_changed_mappings; ++i) {
+        if (tracker->changed_mappings[i] == mapping) {
             return SDL_TRUE;
         }
     }
@@ -561,26 +570,32 @@ static SDL_bool HasMappingChangeTracking(GamepadMapping_t *mapping)
 static void PopMappingChangeTracking(void)
 {
     int i;
+    MappingChangeTracker *tracker;
+
+    SDL_AssertJoysticksLocked();
 
-    SDL_assert(s_mappingChangeTracker.refcount > 0);
-    --s_mappingChangeTracker.refcount;
-    if (s_mappingChangeTracker.refcount > 0) {
+    SDL_assert(s_mappingChangeTracker != NULL);
+    tracker = s_mappingChangeTracker;
+    --tracker->refcount;
+    if (tracker->refcount > 0) {
         return;
     }
+    s_mappingChangeTracker = NULL;
 
     /* Now check to see what gamepads changed because of the mapping changes */
-    if (s_mappingChangeTracker.joysticks && s_mappingChangeTracker.joystick_mappings) {
-        for (i = 0; s_mappingChangeTracker.joysticks[i]; ++i) {
-            SDL_JoystickID joystick = s_mappingChangeTracker.joysticks[i];
-            GamepadMapping_t *old_mapping = s_mappingChangeTracker.joystick_mappings[i];
-            GamepadMapping_t *new_mapping = SDL_PrivateGetGamepadMapping(joystick);
+    if (tracker->joysticks && tracker->joystick_mappings) {
+        for (i = 0; tracker->joysticks[i]; ++i) {
+            /* Looking up the new mapping might create one and associate it with the gamepad (and generate events) */
+            SDL_JoystickID joystick = tracker->joysticks[i];
             SDL_Gamepad *gamepad = SDL_GetGamepadFromInstanceID(joystick);
+            GamepadMapping_t *new_mapping = SDL_PrivateGetGamepadMapping(joystick);
+            GamepadMapping_t *old_mapping = gamepad ? gamepad->mapping : tracker->joystick_mappings[i];
 
             if (new_mapping && !old_mapping) {
                 SDL_PrivateGamepadAdded(joystick);
             } else if (old_mapping && !new_mapping) {
                 SDL_PrivateGamepadRemoved(joystick);
-            } else if (old_mapping != new_mapping || HasMappingChangeTracking(new_mapping)) {
+            } else if (old_mapping != new_mapping || HasMappingChangeTracking(tracker, new_mapping)) {
                 if (gamepad) {
                     SDL_PrivateLoadButtonMapping(gamepad, new_mapping);
                 }
@@ -589,19 +604,10 @@ static void PopMappingChangeTracking(void)
         }
     }
 
-    if (s_mappingChangeTracker.joysticks) {
-        SDL_free(s_mappingChangeTracker.joysticks);
-        s_mappingChangeTracker.joysticks = NULL;
-    }
-    if (s_mappingChangeTracker.joystick_mappings) {
-        SDL_free(s_mappingChangeTracker.joystick_mappings);
-        s_mappingChangeTracker.joystick_mappings = NULL;
-    }
-    if (s_mappingChangeTracker.changed_mappings) {
-        SDL_free(s_mappingChangeTracker.changed_mappings);
-        s_mappingChangeTracker.changed_mappings = NULL;
-    }
-    s_mappingChangeTracker.num_changed_mappings = 0;
+    SDL_free(tracker->joysticks);
+    SDL_free(tracker->joystick_mappings);
+    SDL_free(tracker->changed_mappings);
+    SDL_free(tracker);
 }
 
 #ifdef __ANDROID__
@@ -1738,6 +1744,28 @@ int SDL_AddGamepadMappingsFromFile(const char *file)
     return SDL_AddGamepadMappingsFromRW(SDL_RWFromFile(file, "rb"), 1);
 }
 
+int SDL_ReloadGamepadMappings(void)
+{
+    SDL_Gamepad *gamepad;
+
+    SDL_LockJoysticks();
+
+    PushMappingChangeTracking();
+
+    for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {
+        AddMappingChangeTracking(gamepad->mapping);
+    }
+
+    SDL_QuitGamepadMappings();
+    SDL_InitGamepadMappings();
+
+    PopMappingChangeTracking();
+
+    SDL_UnlockJoysticks();
+
+    return 0;
+}
+
 /*
  * Add or update an entry into the Mappings Database with a priority
  */

+ 2 - 0
test/testcontroller.c

@@ -1627,6 +1627,8 @@ static void loop(void *arg)
                     OpenVirtualGamepad();
                 } else if (event.key.keysym.sym == SDLK_d) {
                     CloseVirtualGamepad();
+                } else if (event.key.keysym.sym == SDLK_r && (event.key.keysym.mod & SDL_KMOD_CTRL)) {
+                    SDL_ReloadGamepadMappings();
                 } else if (event.key.keysym.sym == SDLK_ESCAPE) {
                     done = SDL_TRUE;
                 }