From cf652190a347c049984c33c0a8bbcfef26ea9490 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 12 Feb 2026 01:52:03 -0700 Subject: [PATCH] Handle joystick hotplug fallback and avoid stale gamepad matches --- app/streaming/input/gamepad.cpp | 60 +++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/app/streaming/input/gamepad.cpp b/app/streaming/input/gamepad.cpp index 70854dd1..d6a05902 100644 --- a/app/streaming/input/gamepad.cpp +++ b/app/streaming/input/gamepad.cpp @@ -52,7 +52,7 @@ SdlInputHandler::findStateForGamepad(SDL_JoystickID id) int 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); return &m_GamepadState[i]; } @@ -618,15 +618,17 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve return; } + SDL_JoystickID jsId = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller)); + // SDL_CONTROLLERDEVICEADDED can be reported multiple times for the same // gamepad in rare cases, because SDL doesn't fixup the device index in // the SDL_CONTROLLERDEVICEADDED event if an unopened gamepad disappears // before we've processed the add event. 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, - "Received duplicate add event for controller index: %d", - event->which); + "Received duplicate add event for joystick instance ID: %d", + jsId); SDL_GameControllerClose(controller); return; } @@ -682,7 +684,7 @@ void SdlInputHandler::handleControllerDeviceEvent(SDL_ControllerDeviceEvent* eve } state->controller = controller; - state->jsId = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(state->controller)); + state->jsId = jsId; hapticCaps = 0; #if SDL_VERSION_ATLEAST(2, 0, 18) @@ -886,28 +888,34 @@ void SdlInputHandler::handleJoystickArrivalEvent(SDL_JoyDeviceEvent* event) { SDL_assert(event->type == SDL_JOYDEVICEADDED); - if (!SDL_IsGameController(event->which)) { - char guidStr[33]; - SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(event->which), - guidStr, sizeof(guidStr)); - const char* name = SDL_JoystickNameForIndex(event->which); + if (SDL_IsGameController(event->which)) { + SDL_ControllerDeviceEvent controllerEvent = {}; + controllerEvent.type = SDL_CONTROLLERDEVICEADDED; + controllerEvent.which = 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 : "", + guidStr); + SDL_Joystick* joy = SDL_JoystickOpen(event->which); + if (joy != nullptr) { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Joystick discovered with no mapping: %s %s", - name ? name : "", - guidStr); - SDL_Joystick* joy = SDL_JoystickOpen(event->which); - if (joy != nullptr) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Number of axes: %d | Number of buttons: %d | Number of hats: %d", - SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), - SDL_JoystickNumHats(joy)); - SDL_JoystickClose(joy); - } - else { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Unable to open joystick for query: %s", - SDL_GetError()); - } + "Number of axes: %d | Number of buttons: %d | Number of hats: %d", + SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), + SDL_JoystickNumHats(joy)); + SDL_JoystickClose(joy); + } + else { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "Unable to open joystick for query: %s", + SDL_GetError()); } }