moonlight-qt/app/streaming/input/sdlinputsubsystems.cpp
Joey Yakimowich-Payne d174341b6d
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
Centralize SDL input subsystem ownership for hotplug recovery
2026-02-12 07:57:41 -07:00

191 lines
5.1 KiB
C++

#include "streaming/input/sdlinputsubsystems.h"
#include "settings/mappingmanager.h"
namespace {
int s_JoystickRefs = 0;
int s_GameControllerRefs = 0;
#if !SDL_VERSION_ATLEAST(2, 0, 9)
int s_HapticRefs = 0;
#endif
bool retainSubsystem(Uint32 subsystem, int& refs, const char* subsystemName, const char* ownerTag)
{
if (refs == 0 && SDL_InitSubSystem(subsystem) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to initialize %s subsystem for %s: %s",
subsystemName,
ownerTag,
SDL_GetError());
return false;
}
refs++;
return true;
}
void releaseSubsystem(Uint32 subsystem, int& refs, const char* subsystemName, const char* ownerTag)
{
if (refs <= 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Ignoring unbalanced release of %s subsystem for %s",
subsystemName,
ownerTag);
refs = 0;
return;
}
refs--;
if (refs == 0) {
SDL_QuitSubSystem(subsystem);
}
}
}
namespace SdlInputSubsystems {
bool acquire(const char* ownerTag, const LeaseOptions& options)
{
bool acquiredJoystick = false;
#if !SDL_VERSION_ATLEAST(2, 0, 9)
bool acquiredGameController = false;
bool acquiredHaptic = false;
#endif
if (options.joystick) {
if (!retainSubsystem(SDL_INIT_JOYSTICK, s_JoystickRefs, "joystick", ownerTag)) {
return false;
}
acquiredJoystick = true;
}
if (options.gameController) {
if (!retainSubsystem(SDL_INIT_GAMECONTROLLER, s_GameControllerRefs, "gamecontroller", ownerTag)) {
if (acquiredJoystick) {
releaseSubsystem(SDL_INIT_JOYSTICK, s_JoystickRefs, "joystick", ownerTag);
}
return false;
}
#if !SDL_VERSION_ATLEAST(2, 0, 9)
acquiredGameController = true;
#endif
}
#if !SDL_VERSION_ATLEAST(2, 0, 9)
if (options.haptic) {
if (!retainSubsystem(SDL_INIT_HAPTIC, s_HapticRefs, "haptic", ownerTag)) {
if (acquiredGameController) {
releaseSubsystem(SDL_INIT_GAMECONTROLLER, s_GameControllerRefs, "gamecontroller", ownerTag);
}
if (acquiredJoystick) {
releaseSubsystem(SDL_INIT_JOYSTICK, s_JoystickRefs, "joystick", ownerTag);
}
return false;
}
acquiredHaptic = true;
}
#endif
if (options.applyMappings) {
MappingManager mappingManager;
mappingManager.applyMappings();
}
if (options.flushControllerDeviceEvents) {
SDL_FlushEvent(SDL_CONTROLLERDEVICEADDED);
SDL_FlushEvent(SDL_CONTROLLERDEVICEREMOVED);
}
return true;
}
void release(const char* ownerTag, const LeaseOptions& options)
{
#if !SDL_VERSION_ATLEAST(2, 0, 9)
if (options.haptic) {
releaseSubsystem(SDL_INIT_HAPTIC, s_HapticRefs, "haptic", ownerTag);
}
#endif
if (options.gameController) {
releaseSubsystem(SDL_INIT_GAMECONTROLLER, s_GameControllerRefs, "gamecontroller", ownerTag);
}
if (options.joystick) {
releaseSubsystem(SDL_INIT_JOYSTICK, s_JoystickRefs, "joystick", ownerTag);
}
}
bool hasExclusiveGamepadOwnership()
{
if (s_JoystickRefs != 1 || s_GameControllerRefs != 1) {
return false;
}
#if !SDL_VERSION_ATLEAST(2, 0, 9)
if (s_HapticRefs != 1) {
return false;
}
#endif
return true;
}
bool reenumerateGamepadSubsystems(const char* ownerTag)
{
if (!hasExclusiveGamepadOwnership()) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Skipping subsystem re-enumeration for %s due to shared ownership (joystick=%d gamecontroller=%d)",
ownerTag,
s_JoystickRefs,
s_GameControllerRefs);
return false;
}
#if !SDL_VERSION_ATLEAST(2, 0, 9)
SDL_QuitSubSystem(SDL_INIT_HAPTIC);
#endif
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to reinitialize joystick subsystem for %s: %s",
ownerTag,
SDL_GetError());
return false;
}
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to reinitialize gamecontroller subsystem for %s: %s",
ownerTag,
SDL_GetError());
return false;
}
#if !SDL_VERSION_ATLEAST(2, 0, 9)
if (SDL_InitSubSystem(SDL_INIT_HAPTIC) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to reinitialize haptic subsystem for %s: %s",
ownerTag,
SDL_GetError());
return false;
}
#endif
MappingManager mappingManager;
mappingManager.applyMappings();
SDL_JoystickUpdate();
SDL_GameControllerUpdate();
SDL_FlushEvents(SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED);
SDL_FlushEvents(SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEREMAPPED);
return true;
}
}