Centralize SDL input subsystem ownership for hotplug recovery
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 07:57:41 -07:00
commit d174341b6d
7 changed files with 315 additions and 117 deletions

View file

@ -4,10 +4,27 @@
#include <QGuiApplication>
#include <QWindow>
#include "settings/mappingmanager.h"
#include "streaming/input/sdlinputsubsystems.h"
#define AXIS_NAVIGATION_REPEAT_DELAY 150
namespace {
SdlInputSubsystems::LeaseOptions guiNavSubsystemLeaseOptions()
{
SdlInputSubsystems::LeaseOptions options = {};
options.joystick = true;
options.gameController = true;
#if !SDL_VERSION_ATLEAST(2, 0, 9)
options.haptic = false;
#endif
options.applyMappings = true;
options.flushControllerDeviceEvents = true;
return options;
}
}
SdlGamepadKeyNavigation::SdlGamepadKeyNavigation(StreamingPreferences* prefs)
: m_Prefs(prefs),
m_Enabled(false),
@ -31,32 +48,13 @@ void SdlGamepadKeyNavigation::enable()
return;
}
// We have to initialize and uninitialize this in enable()/disable()
// because we need to get out of the way of the Session class. If it
// doesn't get to reinitialize the GC subsystem, it won't get initial
// arrival events. Additionally, there's a race condition between
// our QML objects being destroyed and SDL being deinitialized that
// this solves too.
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0) {
if (!SdlInputSubsystems::acquire("GuiGamepadNavigation", guiNavSubsystemLeaseOptions())) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) failed: %s",
SDL_GetError());
"Failed to acquire SDL input subsystems for GUI gamepad navigation");
return;
}
MappingManager mappingManager;
mappingManager.applyMappings();
// Drop all pending gamepad add events. SDL will generate these for us
// on first init of the GC subsystem. We can't depend on them due to
// overlapping lifetimes of SdlGamepadKeyNavigation instances, so we
// will attach ourselves.
//
// NB: We use SDL_JoystickUpdate() instead of SDL_PumpEvents() because
// the latter can do a bit more work that we want (like handling video
// events that we intentionally do not want to process yet).
SDL_JoystickUpdate();
SDL_FlushEvent(SDL_CONTROLLERDEVICEADDED);
// Open all currently attached game controllers
int numJoysticks = SDL_NumJoysticks();
@ -90,7 +88,7 @@ void SdlGamepadKeyNavigation::disable()
m_Gamepads.removeAt(0);
}
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
SdlInputSubsystems::release("GuiGamepadNavigation", guiNavSubsystemLeaseOptions());
}
void SdlGamepadKeyNavigation::notifyWindowFocus(bool hasFocus)