#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; } }