diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c index d2e2ca3274..4f56cf752d 100644 --- a/src/joystick/SDL_gamepad.c +++ b/src/joystick/SDL_gamepad.c @@ -74,6 +74,7 @@ static bool SDL_gamepads_initialized; static SDL_Gamepad *SDL_gamepads SDL_GUARDED_BY(SDL_joystick_lock) = NULL; +static SDL_HashTable *SDL_gamepad_names SDL_GUARDED_BY(SDL_joystick_lock) = NULL; // The face button style of a gamepad typedef enum @@ -372,6 +373,45 @@ static void RecenterGamepad(SDL_Gamepad *gamepad) } } +static const char *SDL_UpdateGamepadNameForID(SDL_JoystickID instance_id) +{ + const char *current_name = NULL; + + GamepadMapping_t *mapping = SDL_PrivateGetGamepadMapping(instance_id, true); + if (mapping) { + if (SDL_strcmp(mapping->name, "*") == 0) { + current_name = SDL_GetJoystickNameForID(instance_id); + } else { + current_name = mapping->name; + } + } + + if (!SDL_gamepad_names) { + return SDL_GetPersistentString(current_name); + } + + char *name = NULL; + bool found = SDL_FindInHashTable(SDL_gamepad_names, (const void *)(uintptr_t)instance_id, (const void **)&name); + if (!current_name) { + if (!found) { + SDL_SetError("Gamepad %" SDL_PRIu32 " not found", instance_id); + return NULL; + } + if (!name) { + // SDL_strdup() failed during insert + SDL_OutOfMemory(); + return NULL; + } + return name; + } + + if (!name || SDL_strcmp(name, current_name) != 0) { + name = SDL_strdup(current_name); + SDL_InsertIntoHashTable(SDL_gamepad_names, (const void *)(uintptr_t)instance_id, name, true); + } + return name; +} + void SDL_PrivateGamepadAdded(SDL_JoystickID instance_id) { SDL_Event event; @@ -380,6 +420,8 @@ void SDL_PrivateGamepadAdded(SDL_JoystickID instance_id) return; } + SDL_UpdateGamepadNameForID(instance_id); + event.type = SDL_EVENT_GAMEPAD_ADDED; event.common.timestamp = 0; event.gdevice.which = instance_id; @@ -2969,6 +3011,10 @@ bool SDL_InitGamepads(void) SDL_gamepads_initialized = true; + SDL_LockJoysticks(); + + SDL_gamepad_names = SDL_CreateHashTable(0, false, SDL_HashID, SDL_KeyMatchID, SDL_DestroyHashValue, NULL); + // Watch for joystick events and fire gamepad ones if needed SDL_AddEventWatch(SDL_GamepadEventWatcher, NULL); @@ -2983,6 +3029,8 @@ bool SDL_InitGamepads(void) SDL_free(joysticks); } + SDL_UnlockJoysticks(); + return true; } @@ -3029,19 +3077,10 @@ SDL_JoystickID *SDL_GetGamepads(int *count) const char *SDL_GetGamepadNameForID(SDL_JoystickID instance_id) { - const char *result = NULL; + const char *result; SDL_LockJoysticks(); - { - GamepadMapping_t *mapping = SDL_PrivateGetGamepadMapping(instance_id, true); - if (mapping) { - if (SDL_strcmp(mapping->name, "*") == 0) { - result = SDL_GetJoystickNameForID(instance_id); - } else { - result = SDL_GetPersistentString(mapping->name); - } - } - } + result = SDL_UpdateGamepadNameForID(instance_id); SDL_UnlockJoysticks(); return result; @@ -3212,7 +3251,7 @@ bool SDL_ShouldIgnoreGamepad(Uint16 vendor_id, Uint16 product_id, Uint16 version return true; } break; - + case GAMEPAD_BLACKLIST_END: if (SDL_endswith(name, blacklist_word->str)) { return true; @@ -4298,6 +4337,11 @@ void SDL_QuitGamepads(void) SDL_CloseGamepad(SDL_gamepads); } + if (SDL_gamepad_names) { + SDL_DestroyHashTable(SDL_gamepad_names); + SDL_gamepad_names = NULL; + } + SDL_UnlockJoysticks(); } diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index 7e8c726dad..9bbbd848f9 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -123,6 +123,7 @@ static bool SDL_joystick_being_added; static SDL_Joystick *SDL_joysticks SDL_GUARDED_BY(SDL_joystick_lock) = NULL; static int SDL_joystick_player_count SDL_GUARDED_BY(SDL_joystick_lock) = 0; static SDL_JoystickID *SDL_joystick_players SDL_GUARDED_BY(SDL_joystick_lock) = NULL; +static SDL_HashTable *SDL_joystick_names SDL_GUARDED_BY(SDL_joystick_lock) = NULL; static bool SDL_joystick_allows_background_events = false; static Uint32 initial_old_xboxone_controllers[] = { @@ -833,6 +834,8 @@ bool SDL_InitJoysticks(void) SDL_joysticks_initialized = true; + SDL_joystick_names = SDL_CreateHashTable(0, false, SDL_HashID, SDL_KeyMatchID, SDL_DestroyHashValue, NULL); + SDL_LoadVIDPIDList(&old_xboxone_controllers); SDL_LoadVIDPIDList(&arcadestick_devices); SDL_LoadVIDPIDList(&blacklist_devices); @@ -980,20 +983,52 @@ const SDL_SteamVirtualGamepadInfo *SDL_GetJoystickVirtualGamepadInfoForID(SDL_Jo /* * Get the implementation dependent name of a joystick */ -const char *SDL_GetJoystickNameForID(SDL_JoystickID instance_id) +static const char *SDL_UpdateJoystickNameForID(SDL_JoystickID instance_id) { SDL_JoystickDriver *driver; int device_index; - const char *name = NULL; + const char *current_name = NULL; const SDL_SteamVirtualGamepadInfo *info; - SDL_LockJoysticks(); info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id); if (info) { - name = SDL_GetPersistentString(info->name); + current_name = info->name; } else if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) { - name = SDL_GetPersistentString(driver->GetDeviceName(device_index)); + current_name = driver->GetDeviceName(device_index); } + + if (!SDL_joystick_names) { + return SDL_GetPersistentString(current_name); + } + + char *name = NULL; + bool found = SDL_FindInHashTable(SDL_joystick_names, (const void *)(uintptr_t)instance_id, (const void **)&name); + if (!current_name) { + if (!found) { + SDL_SetError("Joystick %" SDL_PRIu32 " not found", instance_id); + return NULL; + } + if (!name) { + // SDL_strdup() failed during insert + SDL_OutOfMemory(); + return NULL; + } + return name; + } + + if (!name || SDL_strcmp(name, current_name) != 0) { + name = SDL_strdup(current_name); + SDL_InsertIntoHashTable(SDL_joystick_names, (const void *)(uintptr_t)instance_id, name, true); + } + return name; +} + +const char *SDL_GetJoystickNameForID(SDL_JoystickID instance_id) +{ + const char *name; + + SDL_LockJoysticks(); + name = SDL_UpdateJoystickNameForID(instance_id); SDL_UnlockJoysticks(); return name; @@ -2234,6 +2269,11 @@ void SDL_QuitJoysticks(void) SDL_QuitGamepadMappings(); + if (SDL_joystick_names) { + SDL_DestroyHashTable(SDL_joystick_names); + SDL_joystick_names = NULL; + } + SDL_joysticks_quitting = false; SDL_joysticks_initialized = false; @@ -2343,6 +2383,8 @@ void SDL_PrivateJoystickAdded(SDL_JoystickID instance_id) SDL_SetJoystickIDForPlayerIndex(player_index, instance_id); } + SDL_UpdateJoystickNameForID(instance_id); + { SDL_Event event; diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index c1158df700..d900f27a7f 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -1811,12 +1811,12 @@ void SDLTest_PrintEvent(const SDL_Event *event) event->wheel.x, event->wheel.y, event->wheel.direction, event->wheel.windowID); break; case SDL_EVENT_JOYSTICK_ADDED: - SDL_Log("SDL EVENT: Joystick %" SDL_PRIu32 " attached", - event->jdevice.which); + SDL_Log("SDL EVENT: Joystick %" SDL_PRIu32 " (%s) attached", + event->jdevice.which, SDL_GetJoystickNameForID(event->jdevice.which)); break; case SDL_EVENT_JOYSTICK_REMOVED: - SDL_Log("SDL EVENT: Joystick %" SDL_PRIu32 " removed", - event->jdevice.which); + SDL_Log("SDL EVENT: Joystick %" SDL_PRIu32 " (%s) removed", + event->jdevice.which, SDL_GetJoystickNameForID(event->jdevice.which)); break; case SDL_EVENT_JOYSTICK_AXIS_MOTION: SDL_Log("SDL EVENT: Joystick %" SDL_PRIu32 " axis %d value: %d", @@ -1877,12 +1877,12 @@ void SDLTest_PrintEvent(const SDL_Event *event) event->jbattery.which, event->jbattery.percent); break; case SDL_EVENT_GAMEPAD_ADDED: - SDL_Log("SDL EVENT: Gamepad %" SDL_PRIu32 " attached", - event->gdevice.which); + SDL_Log("SDL EVENT: Gamepad %" SDL_PRIu32 " (%s) attached", + event->gdevice.which, SDL_GetGamepadNameForID(event->gdevice.which)); break; case SDL_EVENT_GAMEPAD_REMOVED: - SDL_Log("SDL EVENT: Gamepad %" SDL_PRIu32 " removed", - event->gdevice.which); + SDL_Log("SDL EVENT: Gamepad %" SDL_PRIu32 " (%s) removed", + event->gdevice.which, SDL_GetGamepadNameForID(event->gdevice.which)); break; case SDL_EVENT_GAMEPAD_REMAPPED: SDL_Log("SDL EVENT: Gamepad %" SDL_PRIu32 " mapping changed",