From d862f8cd53395eb020f8548dd6df094b4596e2cc Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Mon, 23 May 2022 23:47:23 -0500 Subject: [PATCH] Qt: Fix SDL initialization crash on macOS --- pcsx2-qt/EmuThread.cpp | 4 ++-- pcsx2/Frontend/InputManager.cpp | 12 ++++++------ pcsx2/Frontend/InputManager.h | 4 ++-- pcsx2/Frontend/InputSource.h | 4 ++-- pcsx2/Frontend/SDLInputSource.cpp | 32 +++++++++++++++++++++++++++---- pcsx2/Frontend/SDLInputSource.h | 4 ++-- pcsx2/Frontend/XInputSource.cpp | 4 ++-- pcsx2/Frontend/XInputSource.h | 4 ++-- pcsx2/VMManager.cpp | 4 ++-- 9 files changed, 48 insertions(+), 24 deletions(-) diff --git a/pcsx2-qt/EmuThread.cpp b/pcsx2-qt/EmuThread.cpp index 18c87d18ae..96de15b6c3 100644 --- a/pcsx2-qt/EmuThread.cpp +++ b/pcsx2-qt/EmuThread.cpp @@ -498,9 +498,9 @@ void EmuThread::reloadInputSources() return; } - auto lock = Host::GetSettingsLock(); + std::unique_lock lock = Host::GetSettingsLock(); SettingsInterface* si = Host::GetSettingsInterface(); - InputManager::ReloadSources(*si); + InputManager::ReloadSources(*si, lock); // skip loading bindings if we're not running, since it'll get done on startup anyway if (VMManager::HasValidVM()) diff --git a/pcsx2/Frontend/InputManager.cpp b/pcsx2/Frontend/InputManager.cpp index c7295191ef..5abd5f4004 100644 --- a/pcsx2/Frontend/InputManager.cpp +++ b/pcsx2/Frontend/InputManager.cpp @@ -955,19 +955,19 @@ GenericInputBindingMapping InputManager::GetGenericBindingMapping(const std::str } template -static void UpdateInputSourceState(SettingsInterface& si, InputSourceType type, bool default_state) +static void UpdateInputSourceState(SettingsInterface& si, std::unique_lock& settings_lock, InputSourceType type, bool default_state) { const bool enabled = si.GetBoolValue("InputSources", InputManager::InputSourceToString(type), default_state); if (enabled) { if (s_input_sources[static_cast(type)]) { - s_input_sources[static_cast(type)]->UpdateSettings(si); + s_input_sources[static_cast(type)]->UpdateSettings(si, settings_lock); } else { std::unique_ptr source = std::make_unique(); - if (!source->Initialize(si)) + if (!source->Initialize(si, settings_lock)) { Console.Error("(InputManager) Source '%s' failed to initialize.", InputManager::InputSourceToString(type)); return; @@ -994,12 +994,12 @@ static void UpdateInputSourceState(SettingsInterface& si, InputSourceType type, #include "Frontend/SDLInputSource.h" #endif -void InputManager::ReloadSources(SettingsInterface& si) +void InputManager::ReloadSources(SettingsInterface& si, std::unique_lock& settings_lock) { #ifdef _WIN32 - UpdateInputSourceState(si, InputSourceType::XInput, false); + UpdateInputSourceState(si, settings_lock, InputSourceType::XInput, false); #endif #ifdef SDL_BUILD - UpdateInputSourceState(si, InputSourceType::SDL, true); + UpdateInputSourceState(si, settings_lock, InputSourceType::SDL, true); #endif } diff --git a/pcsx2/Frontend/InputManager.h b/pcsx2/Frontend/InputManager.h index b01c1880a8..d178a3109f 100644 --- a/pcsx2/Frontend/InputManager.h +++ b/pcsx2/Frontend/InputManager.h @@ -229,7 +229,7 @@ namespace InputManager void ReloadBindings(SettingsInterface& si); /// Re-parses the sources part of the config and initializes any backends. - void ReloadSources(SettingsInterface& si); + void ReloadSources(SettingsInterface& si, std::unique_lock& settings_lock); /// Shuts down any enabled input sources. void CloseSources(); @@ -271,4 +271,4 @@ namespace Host /// Called when an input device is disconnected. void OnInputDeviceDisconnected(const std::string_view& identifier); -} \ No newline at end of file +} diff --git a/pcsx2/Frontend/InputSource.h b/pcsx2/Frontend/InputSource.h index caf98c58e0..fee3ebe46c 100644 --- a/pcsx2/Frontend/InputSource.h +++ b/pcsx2/Frontend/InputSource.h @@ -31,8 +31,8 @@ public: InputSource(); virtual ~InputSource(); - virtual bool Initialize(SettingsInterface& si) = 0; - virtual void UpdateSettings(SettingsInterface& si) = 0; + virtual bool Initialize(SettingsInterface& si, std::unique_lock& settings_lock) = 0; + virtual void UpdateSettings(SettingsInterface& si, std::unique_lock& settings_lock) = 0; virtual void Shutdown() = 0; virtual void PollEvents() = 0; diff --git a/pcsx2/Frontend/SDLInputSource.cpp b/pcsx2/Frontend/SDLInputSource.cpp index 360f89904b..27d61ebaa6 100644 --- a/pcsx2/Frontend/SDLInputSource.cpp +++ b/pcsx2/Frontend/SDLInputSource.cpp @@ -22,6 +22,9 @@ #include "common/StringUtil.h" #include "common/Console.h" #include +#ifdef __APPLE__ +#include +#endif static const char* s_sdl_axis_names[] = { "LeftX", // SDL_CONTROLLER_AXIS_LEFTX @@ -91,7 +94,7 @@ SDLInputSource::SDLInputSource() = default; SDLInputSource::~SDLInputSource() { pxAssert(m_controllers.empty()); } -bool SDLInputSource::Initialize(SettingsInterface& si) +bool SDLInputSource::Initialize(SettingsInterface& si, std::unique_lock& settings_lock) { std::optional> controller_db_data = Host::ReadResourceFile("game_controller_db.txt"); if (controller_db_data.has_value()) @@ -106,11 +109,14 @@ bool SDLInputSource::Initialize(SettingsInterface& si) } LoadSettings(si); + settings_lock.unlock(); SetHints(); - return InitializeSubsystem(); + bool result = InitializeSubsystem(); + settings_lock.lock(); + return result; } -void SDLInputSource::UpdateSettings(SettingsInterface& si) +void SDLInputSource::UpdateSettings(SettingsInterface& si, std::unique_lock& settings_lock) { const bool old_controller_enhanced_mode = m_controller_enhanced_mode; @@ -118,9 +124,11 @@ void SDLInputSource::UpdateSettings(SettingsInterface& si) if (m_controller_enhanced_mode != old_controller_enhanced_mode) { + settings_lock.unlock(); ShutdownSubsystem(); SetHints(); InitializeSubsystem(); + settings_lock.lock(); } } @@ -142,7 +150,17 @@ void SDLInputSource::SetHints() bool SDLInputSource::InitializeSubsystem() { - if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) < 0) + int result; +#ifdef __APPLE__ + // On macOS, SDL_InitSubSystem runs a main-thread-only call to some GameController framework method + // So send this to be run on the main thread + dispatch_sync_f(dispatch_get_main_queue(), &result, [](void* ctx){ + *static_cast(ctx) = SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC); + }); +#else + result = SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC); +#endif + if (result < 0) { Console.Error("SDL_InitSubSystem(SDL_INIT_JOYSTICK |SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) failed"); return false; @@ -160,7 +178,13 @@ void SDLInputSource::ShutdownSubsystem() if (m_sdl_subsystem_initialized) { +#ifdef __APPLE__ + dispatch_sync_f(dispatch_get_main_queue(), nullptr, [](void*){ + SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC); + }); +#else SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC); +#endif m_sdl_subsystem_initialized = false; } } diff --git a/pcsx2/Frontend/SDLInputSource.h b/pcsx2/Frontend/SDLInputSource.h index 42020130c5..582994f0cc 100644 --- a/pcsx2/Frontend/SDLInputSource.h +++ b/pcsx2/Frontend/SDLInputSource.h @@ -29,8 +29,8 @@ public: SDLInputSource(); ~SDLInputSource(); - bool Initialize(SettingsInterface& si) override; - void UpdateSettings(SettingsInterface& si) override; + bool Initialize(SettingsInterface& si, std::unique_lock& settings_lock) override; + void UpdateSettings(SettingsInterface& si, std::unique_lock& settings_lock) override; void Shutdown() override; void PollEvents() override; diff --git a/pcsx2/Frontend/XInputSource.cpp b/pcsx2/Frontend/XInputSource.cpp index 933dbce494..d60b465192 100644 --- a/pcsx2/Frontend/XInputSource.cpp +++ b/pcsx2/Frontend/XInputSource.cpp @@ -94,7 +94,7 @@ XInputSource::XInputSource() = default; XInputSource::~XInputSource() = default; -bool XInputSource::Initialize(SettingsInterface& si) +bool XInputSource::Initialize(SettingsInterface& si, std::unique_lock& settings_lock) { #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) // xinput1_3.dll is flawed and obsolete, but it's also commonly used by wrappers. @@ -142,7 +142,7 @@ bool XInputSource::Initialize(SettingsInterface& si) return true; } -void XInputSource::UpdateSettings(SettingsInterface& si) +void XInputSource::UpdateSettings(SettingsInterface& si, std::unique_lock& settings_lock) { } diff --git a/pcsx2/Frontend/XInputSource.h b/pcsx2/Frontend/XInputSource.h index c8bb4499d2..0c48dfda5c 100644 --- a/pcsx2/Frontend/XInputSource.h +++ b/pcsx2/Frontend/XInputSource.h @@ -63,8 +63,8 @@ public: XInputSource(); ~XInputSource(); - bool Initialize(SettingsInterface& si) override; - void UpdateSettings(SettingsInterface& si) override; + bool Initialize(SettingsInterface& si, std::unique_lock& settings_lock) override; + void UpdateSettings(SettingsInterface& si, std::unique_lock& settings_lock) override; void Shutdown() override; void PollEvents() override; diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index cf287c4b7a..32630115bc 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -292,12 +292,12 @@ SysCpuProviderPack& GetCpuProviders() void VMManager::LoadSettings() { - auto lock = Host::GetSettingsLock(); + std::unique_lock lock = Host::GetSettingsLock(); SettingsInterface* si = Host::GetSettingsInterface(); SettingsLoadWrapper slw(*si); EmuConfig.LoadSave(slw); PAD::LoadConfig(*si); - InputManager::ReloadSources(*si); + InputManager::ReloadSources(*si, lock); InputManager::ReloadBindings(*si); // Remove any user-specified hacks in the config (we don't want stale/conflicting values when it's globally disabled).