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

@ -1,7 +1,7 @@
#include <Limelight.h>
#include "SDL_compat.h"
#include "streaming/session.h"
#include "settings/mappingmanager.h"
#include "streaming/input/sdlinputsubsystems.h"
#include "path.h"
#include "utils.h"
@ -9,6 +9,23 @@
#include <QDir>
#include <QGuiApplication>
namespace {
SdlInputSubsystems::LeaseOptions streamInputSubsystemLeaseOptions()
{
SdlInputSubsystems::LeaseOptions options = {};
options.joystick = true;
options.gameController = true;
#if !SDL_VERSION_ATLEAST(2, 0, 9)
options.haptic = true;
#endif
options.applyMappings = true;
options.flushControllerDeviceEvents = true;
return options;
}
}
SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, int streamWidth, int streamHeight)
: m_MultiController(prefs.multiController),
m_GamepadMouse(prefs.gamepadMouse),
@ -20,6 +37,7 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, int streamWidth, i
m_PointerRegionLockActive(false),
m_PointerRegionLockToggledByUser(false),
m_LastJoystickCount(-1),
m_LastNonZeroJoystickTick(0),
m_FakeCaptureActive(false),
m_CaptureSystemKeysMode(prefs.captureSysKeysMode),
m_MouseCursorCapturedVisibilityState(SDL_DISABLE),
@ -178,45 +196,11 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, int streamWidth, i
SDL_SetHint(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES, streamIgnoreDevices.toUtf8());
SDL_SetHint(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT, streamIgnoreDevicesExcept.toUtf8());
// We must initialize joystick explicitly before gamecontroller in order
// to ensure we receive gamecontroller attach events for gamepads where
// SDL doesn't have a built-in mapping. By starting joystick first, we
// can allow mapping manager to update the mappings before GC attach
// events are generated.
SDL_assert(!SDL_WasInit(SDL_INIT_JOYSTICK));
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) != 0) {
if (!SdlInputSubsystems::acquire("StreamInput", streamInputSubsystemLeaseOptions())) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_JOYSTICK) failed: %s",
SDL_GetError());
"Failed to acquire SDL input subsystems for streaming input");
}
MappingManager mappingManager;
mappingManager.applyMappings();
// Flush gamepad arrival and departure events which may be queued before
// starting the gamecontroller subsystem again. This prevents us from
// receiving duplicate arrival and departure events for the same gamepad.
SDL_FlushEvent(SDL_CONTROLLERDEVICEADDED);
SDL_FlushEvent(SDL_CONTROLLERDEVICEREMOVED);
// We need to reinit this each time, since you only get
// an initial set of gamepad arrival events once per init.
SDL_assert(!SDL_WasInit(SDL_INIT_GAMECONTROLLER));
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) failed: %s",
SDL_GetError());
}
#if !SDL_VERSION_ATLEAST(2, 0, 9)
SDL_assert(!SDL_WasInit(SDL_INIT_HAPTIC));
if (SDL_InitSubSystem(SDL_INIT_HAPTIC) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_HAPTIC) failed: %s",
SDL_GetError());
}
#endif
// Initialize the gamepad mask with currently attached gamepads to avoid
// causing gamepads to unexpectedly disappear and reappear on the host
// during stream startup as we detect currently attached gamepads one at a time.
@ -250,16 +234,7 @@ SdlInputHandler::~SdlInputHandler()
SDL_RemoveTimer(m_RightButtonReleaseTimer);
SDL_RemoveTimer(m_DragTimer);
#if !SDL_VERSION_ATLEAST(2, 0, 9)
SDL_QuitSubSystem(SDL_INIT_HAPTIC);
SDL_assert(!SDL_WasInit(SDL_INIT_HAPTIC));
#endif
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
SDL_assert(!SDL_WasInit(SDL_INIT_GAMECONTROLLER));
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
SDL_assert(!SDL_WasInit(SDL_INIT_JOYSTICK));
SdlInputSubsystems::release("StreamInput", streamInputSubsystemLeaseOptions());
// Return background event handling to off
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "0");