From 9db2cb3513195e913bb7e1a3171c14889b8dc424 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 18 Jul 2023 12:50:10 -0700
Subject: [PATCH] Added SDL_ReloadGamepadMappings() to reset the SDL gamepad
mappings
---
include/SDL3/SDL_gamepad.h | 12 ++++
src/dynapi/SDL_dynapi.sym | 1 +
src/dynapi/SDL_dynapi_overrides.h | 1 +
src/dynapi/SDL_dynapi_procs.h | 1 +
src/joystick/SDL_gamepad.c | 104 +++++++++++++++++++-----------
test/testcontroller.c | 2 +
6 files changed, 83 insertions(+), 38 deletions(-)
diff --git a/include/SDL3/SDL_gamepad.h b/include/SDL3/SDL_gamepad.h
index 1587702a3deb..93f7a7dac8a2 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 24f5cc80f3b7..751abf72b7c1 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 fb0e11285bd3..3fefcb19545f 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 4d32a52f59aa..7b93149fb749 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 6e86e989e813..25a850fef505 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_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
*/
diff --git a/test/testcontroller.c b/test/testcontroller.c
index ed76483b6440..a51445f07474 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;
}