Handle joystick hotplug fallback and avoid stale gamepad matches
Some checks are pending
Build / setup (push) Waiting to run
Build / build-appimage (push) Blocked by required conditions
Build / build-steamlink (push) Blocked by required conditions
Build / build-windows-macos (push) Blocked by required conditions

This commit is contained in:
Joey Yakimowich-Payne 2026-02-12 01:52:03 -07:00
commit cf652190a3

View file

@ -52,7 +52,7 @@ SdlInputHandler::findStateForGamepad(SDL_JoystickID id)
int i; int i;
for (i = 0; i < MAX_GAMEPADS; i++) { for (i = 0; i < MAX_GAMEPADS; i++) {
if (m_GamepadState[i].jsId == id) { if (m_GamepadState[i].controller != nullptr && m_GamepadState[i].jsId == id) {
SDL_assert(!m_MultiController || m_GamepadState[i].index == i); SDL_assert(!m_MultiController || m_GamepadState[i].index == i);
return &m_GamepadState[i]; return &m_GamepadState[i];
} }
@ -618,15 +618,17 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
return; return;
} }
SDL_JoystickID jsId = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller));
// SDL_CONTROLLERDEVICEADDED can be reported multiple times for the same // SDL_CONTROLLERDEVICEADDED can be reported multiple times for the same
// gamepad in rare cases, because SDL doesn't fixup the device index in // gamepad in rare cases, because SDL doesn't fixup the device index in
// the SDL_CONTROLLERDEVICEADDED event if an unopened gamepad disappears // the SDL_CONTROLLERDEVICEADDED event if an unopened gamepad disappears
// before we've processed the add event. // before we've processed the add event.
for (int i = 0; i < MAX_GAMEPADS; i++) { for (int i = 0; i < MAX_GAMEPADS; i++) {
if (m_GamepadState[i].controller == controller) { if (m_GamepadState[i].controller != nullptr && m_GamepadState[i].jsId == jsId) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Received duplicate add event for controller index: %d", "Received duplicate add event for joystick instance ID: %d",
event->which); jsId);
SDL_GameControllerClose(controller); SDL_GameControllerClose(controller);
return; return;
} }
@ -682,7 +684,7 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve
} }
state->controller = controller; state->controller = controller;
state->jsId = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(state->controller)); state->jsId = jsId;
hapticCaps = 0; hapticCaps = 0;
#if SDL_VERSION_ATLEAST(2, 0, 18) #if SDL_VERSION_ATLEAST(2, 0, 18)
@ -886,28 +888,34 @@ void SdlInputHandler::handleJoystickArrivalEvent(SDL_JoyDeviceEvent* event)
{ {
SDL_assert(event->type == SDL_JOYDEVICEADDED); SDL_assert(event->type == SDL_JOYDEVICEADDED);
if (!SDL_IsGameController(event->which)) { if (SDL_IsGameController(event->which)) {
char guidStr[33]; SDL_ControllerDeviceEvent controllerEvent = {};
SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(event->which), controllerEvent.type = SDL_CONTROLLERDEVICEADDED;
guidStr, sizeof(guidStr)); controllerEvent.which = event->which;
const char* name = SDL_JoystickNameForIndex(event->which); handleControllerDeviceEvent(&controllerEvent);
return;
}
char guidStr[33];
SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(event->which),
guidStr, sizeof(guidStr));
const char* name = SDL_JoystickNameForIndex(event->which);
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Joystick discovered with no mapping: %s %s",
name ? name : "<UNKNOWN>",
guidStr);
SDL_Joystick* joy = SDL_JoystickOpen(event->which);
if (joy != nullptr) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Joystick discovered with no mapping: %s %s", "Number of axes: %d | Number of buttons: %d | Number of hats: %d",
name ? name : "<UNKNOWN>", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy),
guidStr); SDL_JoystickNumHats(joy));
SDL_Joystick* joy = SDL_JoystickOpen(event->which); SDL_JoystickClose(joy);
if (joy != nullptr) { }
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, else {
"Number of axes: %d | Number of buttons: %d | Number of hats: %d", SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), "Unable to open joystick for query: %s",
SDL_JoystickNumHats(joy)); SDL_GetError());
SDL_JoystickClose(joy);
}
else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Unable to open joystick for query: %s",
SDL_GetError());
}
} }
} }