diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index ec5342b71f..d79af3b78c 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -487,6 +487,7 @@ + @@ -1111,6 +1112,7 @@ + diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index 2caa334cca..20ae0f3f17 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -56,6 +56,8 @@ add_library(inputcommon ControllerInterface/ControllerInterface.h ControllerInterface/CoreDevice.cpp ControllerInterface/CoreDevice.h + ControllerInterface/InputBackend.cpp + ControllerInterface/InputBackend.h ControllerInterface/MappingCommon.cpp ControllerInterface/MappingCommon.h ControllerInterface/Wiimote/WiimoteController.cpp diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index 442e5502fa..d1ee02370a 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -64,7 +64,7 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi) // nothing needed for OSX and Quartz #endif #ifdef CIFACE_USE_SDL - ciface::SDL::Init(); + m_input_backends.emplace_back(ciface::SDL::CreateInputBackend(this)); #endif #ifdef CIFACE_USE_ANDROID // nothing needed @@ -181,9 +181,6 @@ void ControllerInterface::RefreshDevices(RefreshReason reason) ciface::Quartz::PopulateDevices(m_wsi.render_window); } #endif -#ifdef CIFACE_USE_SDL - ciface::SDL::PopulateDevices(); -#endif #ifdef CIFACE_USE_ANDROID ciface::Android::PopulateDevices(); #endif @@ -197,6 +194,9 @@ void ControllerInterface::RefreshDevices(RefreshReason reason) ciface::DualShockUDPClient::PopulateDevices(); #endif + for (auto& backend : m_input_backends) + backend->PopulateDevices(); + WiimoteReal::PopulateDevices(); if (m_populating_devices_counter.fetch_sub(1) == 1) @@ -242,9 +242,6 @@ void ControllerInterface::Shutdown() ciface::OSX::DeInit(); ciface::Quartz::DeInit(); #endif -#ifdef CIFACE_USE_SDL - ciface::SDL::DeInit(); -#endif #ifdef CIFACE_USE_ANDROID // nothing needed #endif @@ -255,6 +252,8 @@ void ControllerInterface::Shutdown() ciface::DualShockUDPClient::DeInit(); #endif + m_input_backends.clear(); + // Make sure no devices had been added within Shutdown() in the time // between checking they checked atomic m_is_init bool and we changed it. // We couldn't have locked m_devices_population_mutex for the whole Shutdown() @@ -384,15 +383,19 @@ void ControllerInterface::UpdateInput() // TODO: if we are an emulation input channel, we should probably always lock // Prefer outdated values over blocking UI or CPU thread (avoids short but noticeable frame drop) - if (m_devices_mutex.try_lock()) + if (!m_devices_mutex.try_lock()) + return; + + std::lock_guard lk(m_devices_mutex, std::adopt_lock); + + for (auto& backend : m_input_backends) + backend->UpdateInput(); + + for (const auto& d : m_devices) { - std::lock_guard lk(m_devices_mutex, std::adopt_lock); - for (const auto& d : m_devices) - { - // Theoretically we could avoid updating input on devices that don't have any references to - // them, but in practice a few devices types could break in different ways, so we don't - d->UpdateInput(); - } + // Theoretically we could avoid updating input on devices that don't have any references to + // them, but in practice a few devices types could break in different ways, so we don't + d->UpdateInput(); } } diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h index 3a0276de41..59909aeec8 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h @@ -12,6 +12,7 @@ #include "Common/Matrix.h" #include "Common/WindowSystemInfo.h" #include "InputCommon/ControllerInterface/CoreDevice.h" +#include "InputCommon/ControllerInterface/InputBackend.h" // enable disable sources #ifdef _WIN32 @@ -133,6 +134,8 @@ private: WindowSystemInfo m_wsi; std::atomic m_aspect_ratio_adjustment = 1; std::atomic m_requested_mouse_centering = false; + + std::vector> m_input_backends; }; namespace ciface diff --git a/Source/Core/InputCommon/ControllerInterface/InputBackend.cpp b/Source/Core/InputCommon/ControllerInterface/InputBackend.cpp new file mode 100644 index 0000000000..91685eea00 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/InputBackend.cpp @@ -0,0 +1,24 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "InputCommon/ControllerInterface/InputBackend.h" + +namespace ciface +{ +InputBackend::InputBackend(ControllerInterface* controller_interface) + : m_controller_interface(*controller_interface) +{ +} + +InputBackend::~InputBackend() = default; + +void InputBackend::UpdateInput() +{ +} + +ControllerInterface& InputBackend::GetControllerInterface() +{ + return m_controller_interface; +} + +} // namespace ciface diff --git a/Source/Core/InputCommon/ControllerInterface/InputBackend.h b/Source/Core/InputCommon/ControllerInterface/InputBackend.h new file mode 100644 index 0000000000..653bc16df1 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/InputBackend.h @@ -0,0 +1,26 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +class ControllerInterface; + +namespace ciface +{ +class InputBackend +{ +public: + InputBackend(ControllerInterface* controller_interface); + + virtual ~InputBackend(); + + virtual void PopulateDevices() = 0; + virtual void UpdateInput(); + + ControllerInterface& GetControllerInterface(); + +private: + ControllerInterface& m_controller_interface; +}; + +} // namespace ciface diff --git a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp index 0ea15251ad..327002c3a3 100644 --- a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp +++ b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp @@ -29,7 +29,33 @@ static std::string GetJoystickName(int index) #endif } -static void OpenAndAddDevice(int index) +class InputBackend final : public ciface::InputBackend +{ +public: + InputBackend(ControllerInterface* controller_interface); + ~InputBackend(); + void PopulateDevices() override; + void UpdateInput() override; + +private: + void OpenAndAddDevice(int index); + +#if SDL_VERSION_ATLEAST(2, 0, 0) + bool HandleEventAndContinue(const SDL_Event& e); + + Common::Event m_init_event; + Uint32 m_stop_event_type; + Uint32 m_populate_event_type; + std::thread m_hotplug_thread; +#endif +}; + +std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface) +{ + return std::make_unique(controller_interface); +} + +void InputBackend::OpenAndAddDevice(int index) { SDL_Joystick* const dev = SDL_JoystickOpen(index); if (dev) @@ -37,17 +63,13 @@ static void OpenAndAddDevice(int index) auto js = std::make_shared(dev, index); // only add if it has some inputs/outputs if (!js->Inputs().empty() || !js->Outputs().empty()) - g_controller_interface.AddDevice(std::move(js)); + GetControllerInterface().AddDevice(std::move(js)); } } #if SDL_VERSION_ATLEAST(2, 0, 0) -static Common::Event s_init_event; -static Uint32 s_stop_event_type; -static Uint32 s_populate_event_type; -static std::thread s_hotplug_thread; -static bool HandleEventAndContinue(const SDL_Event& e) +bool InputBackend::HandleEventAndContinue(const SDL_Event& e) { if (e.type == SDL_JOYDEVICEADDED) { @@ -55,20 +77,20 @@ static bool HandleEventAndContinue(const SDL_Event& e) } else if (e.type == SDL_JOYDEVICEREMOVED) { - g_controller_interface.RemoveDevice([&e](const auto* device) { + GetControllerInterface().RemoveDevice([&e](const auto* device) { return device->GetSource() == "SDL" && SDL_JoystickInstanceID(static_cast(device)->GetSDLJoystick()) == e.jdevice.which; }); } - else if (e.type == s_populate_event_type) + else if (e.type == m_populate_event_type) { - g_controller_interface.PlatformPopulateDevices([] { + GetControllerInterface().PlatformPopulateDevices([this] { for (int i = 0; i < SDL_NumJoysticks(); ++i) OpenAndAddDevice(i); }); } - else if (e.type == s_stop_event_type) + else if (e.type == m_stop_event_type) { return false; } @@ -144,7 +166,8 @@ static void EnableSDLLogging() nullptr); } -void Init() +InputBackend::InputBackend(ControllerInterface* controller_interface) + : ciface::InputBackend(controller_interface) { #if !SDL_VERSION_ATLEAST(2, 0, 0) if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC) != 0) @@ -168,13 +191,13 @@ void Init() SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); #endif - s_hotplug_thread = std::thread([] { + m_hotplug_thread = std::thread([this] { Common::ScopeGuard quit_guard([] { // TODO: there seems to be some sort of memory leak with SDL, quit isn't freeing everything up SDL_Quit(); }); { - Common::ScopeGuard init_guard([] { s_init_event.Set(); }); + Common::ScopeGuard init_guard([this] { m_init_event.Set(); }); if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER) != 0) { @@ -188,8 +211,8 @@ void Init() ERROR_LOG_FMT(CONTROLLERINTERFACE, "SDL failed to register custom events"); return; } - s_stop_event_type = custom_events_start; - s_populate_event_type = custom_events_start + 1; + m_stop_event_type = custom_events_start; + m_populate_event_type = custom_events_start + 1; // Drain all of the events and add the initial joysticks before returning. Otherwise, the // individual joystick events as well as the custom populate event will be handled _after_ @@ -235,26 +258,26 @@ void Init() } }); - s_init_event.Wait(); + m_init_event.Wait(); #endif } -void DeInit() +InputBackend::~InputBackend() { #if !SDL_VERSION_ATLEAST(2, 0, 0) SDL_Quit(); #else - if (!s_hotplug_thread.joinable()) + if (!m_hotplug_thread.joinable()) return; - SDL_Event stop_event{s_stop_event_type}; + SDL_Event stop_event{m_stop_event_type}; SDL_PushEvent(&stop_event); - s_hotplug_thread.join(); + m_hotplug_thread.join(); #endif } -void PopulateDevices() +void InputBackend::PopulateDevices() { #if !SDL_VERSION_ATLEAST(2, 0, 0) if (!SDL_WasInit(SDL_INIT_JOYSTICK)) @@ -263,10 +286,10 @@ void PopulateDevices() for (int i = 0; i < SDL_NumJoysticks(); ++i) OpenAndAddDevice(i); #else - if (!s_hotplug_thread.joinable()) + if (!m_hotplug_thread.joinable()) return; - SDL_Event populate_event{s_populate_event_type}; + SDL_Event populate_event{m_populate_event_type}; SDL_PushEvent(&populate_event); #endif } @@ -617,9 +640,8 @@ void Joystick::Motor::SetState(ControlState state) } #endif -void Joystick::UpdateInput() +void InputBackend::UpdateInput() { - // TODO: Don't call this for every Joystick, only once per ControllerInterface::UpdateInput() SDL_JoystickUpdate(); } diff --git a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.h b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.h index f789c8846a..1b37a22b9e 100644 --- a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.h +++ b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.h @@ -22,12 +22,11 @@ #endif #include "InputCommon/ControllerInterface/CoreDevice.h" +#include "InputCommon/ControllerInterface/InputBackend.h" namespace ciface::SDL { -void Init(); -void DeInit(); -void PopulateDevices(); +std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface); class Joystick : public Core::Device { @@ -182,8 +181,6 @@ private: #endif public: - void UpdateInput() override; - Joystick(SDL_Joystick* const joystick, const int sdl_index); ~Joystick();