diff --git a/include/SDL3/SDL_gamepad.h b/include/SDL3/SDL_gamepad.h index 1587702a3..93f7a7dac 100644 --- a/include/SDL3/SDL_gamepad.h +++ b/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. * diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 24f5cc80f..751abf72b 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/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: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index fb0e11285..3fefcb195 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/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 diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 4d32a52f5..7b93149fb 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/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) diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c index 6e86e989e..25a850fef 100644 --- a/src/joystick/SDL_gamepad.c +++ b/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_assert(s_mappingChangeTracker.refcount > 0); - --s_mappingChangeTracker.refcount; - if (s_mappingChangeTracker.refcount > 0) { + SDL_AssertJoysticksLocked(); + + 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 */ diff --git a/test/testcontroller.c b/test/testcontroller.c index ed76483b6..a51445f07 100644 --- a/test/testcontroller.c +++ b/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; }