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..6a6affd478 100644
--- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp
+++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp
@@ -64,19 +64,19 @@ 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
#endif
#ifdef CIFACE_USE_EVDEV
- ciface::evdev::Init();
+ m_input_backends.emplace_back(ciface::evdev::CreateInputBackend(this));
#endif
#ifdef CIFACE_USE_PIPES
// nothing needed
#endif
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
- ciface::DualShockUDPClient::Init();
+ m_input_backends.emplace_back(ciface::DualShockUDPClient::CreateInputBackend(this));
#endif
// Don't allow backends to add devices before the first RefreshDevices() as they will be cleaned
@@ -181,21 +181,15 @@ 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
-#ifdef CIFACE_USE_EVDEV
- ciface::evdev::PopulateDevices();
-#endif
#ifdef CIFACE_USE_PIPES
ciface::Pipes::PopulateDevices();
#endif
-#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
- ciface::DualShockUDPClient::PopulateDevices();
-#endif
+
+ for (auto& backend : m_input_backends)
+ backend->PopulateDevices();
WiimoteReal::PopulateDevices();
@@ -242,18 +236,12 @@ 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
-#ifdef CIFACE_USE_EVDEV
- ciface::evdev::Shutdown();
-#endif
-#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
- ciface::DualShockUDPClient::DeInit();
-#endif
+
+ // Empty the container of input backends to deconstruct and deinitialize them.
+ 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.
@@ -384,15 +372,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/DualShockUDPClient/DualShockUDPClient.cpp b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp
index 15b0915381..12279b504b 100644
--- a/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp
+++ b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp
@@ -130,7 +130,7 @@ private:
public:
void UpdateInput() override;
- Device(std::string name, int index, std::string server_address, u16 server_port);
+ Device(std::string name, int index, std::string server_address, u16 server_port, u32 client_uid);
std::string GetName() const final override;
std::string GetSource() const final override;
@@ -150,13 +150,15 @@ private:
bool m_prev_touch_valid = false;
int m_touch_x = 0;
int m_touch_y = 0;
- std::string m_server_address;
- u16 m_server_port;
+ const std::string m_server_address;
+ const u16 m_server_port;
s16 m_touch_x_min;
s16 m_touch_y_min;
s16 m_touch_x_max;
s16 m_touch_y_max;
+
+ const u32 m_client_uid;
};
using MathUtil::GRAVITY_ACCELERATION;
@@ -196,13 +198,34 @@ struct Server
SteadyClock::time_point m_disconnect_time = SteadyClock::now();
};
-static bool s_has_init;
-static bool s_servers_enabled;
-static std::vector s_servers;
-static u32 s_client_uid;
-static SteadyClock::time_point s_next_listports_time;
-static std::thread s_hotplug_thread;
-static Common::Flag s_hotplug_thread_running;
+class InputBackend final : public ciface::InputBackend
+{
+public:
+ InputBackend(ControllerInterface* controller_interface);
+ ~InputBackend();
+ void PopulateDevices() override;
+
+private:
+ void ConfigChanged();
+ void Restart();
+
+ void HotplugThreadFunc();
+ void StartHotplugThread();
+ void StopHotplugThread();
+
+ bool m_servers_enabled;
+ std::vector m_servers;
+ u32 m_client_uid;
+ SteadyClock::time_point m_next_listports_time;
+ std::thread m_hotplug_thread;
+ Common::Flag m_hotplug_thread_running;
+ std::size_t m_config_change_callback_id;
+};
+
+std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface)
+{
+ return std::make_unique(controller_interface);
+}
static bool IsSameController(const Proto::MessageType::PortInfo& a,
const Proto::MessageType::PortInfo& b)
@@ -212,29 +235,29 @@ static bool IsSameController(const Proto::MessageType::PortInfo& a,
std::tie(b.pad_id, b.pad_state, b.model, b.connection_type, b.pad_mac_address);
}
-static void HotplugThreadFunc()
+void InputBackend::HotplugThreadFunc()
{
Common::SetCurrentThreadName("DualShockUDPClient Hotplug Thread");
INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient hotplug thread started");
Common::ScopeGuard thread_stop_guard{
[] { INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient hotplug thread stopped"); }};
- std::vector timed_out_servers(s_servers.size(), false);
+ std::vector timed_out_servers(m_servers.size(), false);
- while (s_hotplug_thread_running.IsSet())
+ while (m_hotplug_thread_running.IsSet())
{
using namespace std::chrono;
using namespace std::chrono_literals;
const auto now = SteadyClock::now();
- if (now >= s_next_listports_time)
+ if (now >= m_next_listports_time)
{
- s_next_listports_time = now + SERVER_LISTPORTS_INTERVAL;
+ m_next_listports_time = now + SERVER_LISTPORTS_INTERVAL;
- for (size_t i = 0; i < s_servers.size(); ++i)
+ for (size_t i = 0; i < m_servers.size(); ++i)
{
- auto& server = s_servers[i];
- Proto::Message msg(s_client_uid);
+ auto& server = m_servers[i];
+ Proto::Message msg(m_client_uid);
auto& list_ports = msg.m_message;
// We ask for x possible devices. We will receive a message for every connected device.
list_ports.pad_request_count = SERVER_ASKED_PADS;
@@ -250,12 +273,12 @@ static void HotplugThreadFunc()
}
sf::SocketSelector selector;
- for (auto& server : s_servers)
+ for (auto& server : m_servers)
{
selector.add(server.m_socket);
}
- auto timeout = duration_cast(s_next_listports_time - SteadyClock::now());
+ auto timeout = duration_cast(m_next_listports_time - SteadyClock::now());
// Receive controller port info within a time from our request.
// Run this even if we sent no new requests, to disconnect devices,
@@ -271,9 +294,9 @@ static void HotplugThreadFunc()
if (selector.wait(sf::milliseconds(current_timeout.count())))
{
// Now check all the servers because we don't know which one(s) sent a reply
- for (size_t i = 0; i < s_servers.size(); ++i)
+ for (size_t i = 0; i < m_servers.size(); ++i)
{
- auto& server = s_servers[i];
+ auto& server = m_servers[i];
if (!selector.isReady(server.m_socket))
{
continue;
@@ -302,20 +325,20 @@ static void HotplugThreadFunc()
{
server.m_port_info[port_info->pad_id] = *port_info;
// Just remove and re-add all the devices for simplicity
- g_controller_interface.PlatformPopulateDevices([] { PopulateDevices(); });
+ GetControllerInterface().PlatformPopulateDevices([this] { PopulateDevices(); });
}
}
}
}
- if (!s_hotplug_thread_running.IsSet()) // Avoid hanging the thread for too long
+ if (!m_hotplug_thread_running.IsSet()) // Avoid hanging the thread for too long
return;
} while (timeout > 0ms);
// If we have failed to receive any information from the server (or even send it),
// disconnect all devices from it (after enough time has elapsed, to avoid false positives).
- for (size_t i = 0; i < s_servers.size(); ++i)
+ for (size_t i = 0; i < m_servers.size(); ++i)
{
- auto& server = s_servers[i];
+ auto& server = m_servers[i];
if (timed_out_servers[i] && SteadyClock::now() >= server.m_disconnect_time)
{
bool any_connected = false;
@@ -328,49 +351,49 @@ static void HotplugThreadFunc()
}
// We can't only remove devices added by this server as we wouldn't know which they are
if (any_connected)
- g_controller_interface.PlatformPopulateDevices([] { PopulateDevices(); });
+ GetControllerInterface().PlatformPopulateDevices([this] { PopulateDevices(); });
}
}
}
}
-static void StartHotplugThread()
+void InputBackend::StartHotplugThread()
{
// Mark the thread as running.
- if (!s_hotplug_thread_running.TestAndSet())
+ if (!m_hotplug_thread_running.TestAndSet())
{
// It was already running.
return;
}
- s_hotplug_thread = std::thread(HotplugThreadFunc);
+ m_hotplug_thread = std::thread(&InputBackend::HotplugThreadFunc, this);
}
-static void StopHotplugThread()
+void InputBackend::StopHotplugThread()
{
// Tell the hotplug thread to stop.
- if (!s_hotplug_thread_running.TestAndClear())
+ if (!m_hotplug_thread_running.TestAndClear())
{
// It wasn't running, we're done.
return;
}
- s_hotplug_thread.join();
+ m_hotplug_thread.join();
- for (auto& server : s_servers)
+ for (auto& server : m_servers)
{
server.m_socket.unbind(); // interrupt blocking socket
}
}
// Also just start
-static void Restart()
+void InputBackend::Restart()
{
INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient Restart");
StopHotplugThread();
- for (auto& server : s_servers)
+ for (auto& server : m_servers)
{
for (size_t port_index = 0; port_index < server.m_port_info.size(); port_index++)
{
@@ -380,37 +403,34 @@ static void Restart()
}
// Only removes devices as servers have been cleaned
- g_controller_interface.PlatformPopulateDevices([] { PopulateDevices(); });
+ GetControllerInterface().PlatformPopulateDevices([this] { PopulateDevices(); });
- s_client_uid = Common::Random::GenerateValue();
- s_next_listports_time = SteadyClock::now();
+ m_client_uid = Common::Random::GenerateValue();
+ m_next_listports_time = SteadyClock::now();
- if (s_servers_enabled && !s_servers.empty())
+ if (m_servers_enabled && !m_servers.empty())
StartHotplugThread();
}
-static void ConfigChanged()
+void InputBackend::ConfigChanged()
{
- if (!s_has_init)
- return;
-
const bool servers_enabled = Config::Get(Settings::SERVERS_ENABLED);
const std::string servers_setting = Config::Get(Settings::SERVERS);
std::string new_servers_setting;
- for (const auto& server : s_servers)
+ for (const auto& server : m_servers)
{
new_servers_setting +=
fmt::format("{}:{}:{};", server.m_description, server.m_address, server.m_port);
}
- if (servers_enabled != s_servers_enabled || servers_setting != new_servers_setting)
+ if (servers_enabled != m_servers_enabled || servers_setting != new_servers_setting)
{
- // Stop the thread before writing to s_servers
+ // Stop the thread before writing to m_servers
StopHotplugThread();
- s_servers_enabled = servers_enabled;
- s_servers.clear();
+ m_servers_enabled = servers_enabled;
+ m_servers.clear();
const auto server_details = SplitString(servers_setting, ';');
for (const auto& server_detail : server_details)
@@ -428,17 +448,15 @@ static void ConfigChanged()
}
u16 server_port = static_cast(port);
- s_servers.emplace_back(description, server_address, server_port);
+ m_servers.emplace_back(description, server_address, server_port);
}
Restart();
}
}
-void Init()
+InputBackend::InputBackend(ControllerInterface* controller_interface)
+ : ciface::InputBackend(controller_interface)
{
- // Does not support multiple init calls
- s_has_init = true;
-
// The following is added for backwards compatibility
const auto server_address_setting = Config::Get(Settings::SERVER_ADDRESS);
const auto server_port_setting = Config::Get(Settings::SERVER_PORT);
@@ -454,29 +472,30 @@ void Init()
Config::SetBase(Settings::SERVER_PORT, 0);
}
- // It would be much better to unbind from this callback on DeInit but it's not possible as of now
- Config::AddConfigChangedCallback(ConfigChanged);
- ConfigChanged(); // Call it immediately to load settings
+ m_config_change_callback_id =
+ Config::AddConfigChangedCallback(std::bind(&InputBackend::ConfigChanged, this));
+ // Call it immediately to load settings
+ ConfigChanged();
}
// This can be called by the host thread as well as the hotplug thread, concurrently.
// So use PlatformPopulateDevices().
-// s_servers is already safe because it can only be modified when the DSU thread is not running,
+// m_servers is already safe because it can only be modified when the DSU thread is not running,
// from the main thread
-void PopulateDevices()
+void InputBackend::PopulateDevices()
{
INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient PopulateDevices");
- // s_servers has already been updated so we can't use it to know which devices we removed,
+ // m_servers has already been updated so we can't use it to know which devices we removed,
// also it's good to remove all of them before adding new ones so that their id will be set
// correctly if they have the same name
- g_controller_interface.RemoveDevice(
+ GetControllerInterface().RemoveDevice(
[](const auto* dev) { return dev->GetSource() == DUALSHOCKUDP_SOURCE_NAME; });
// Users might have created more than one server on the same IP/Port.
// Devices might end up being duplicated (if the server responds two all requests)
// but they won't conflict.
- for (const auto& server : s_servers)
+ for (const auto& server : m_servers)
{
for (size_t port_index = 0; port_index < server.m_port_info.size(); port_index++)
{
@@ -484,24 +503,23 @@ void PopulateDevices()
if (port_info.pad_state != Proto::DsState::Connected)
continue;
- g_controller_interface.AddDevice(std::make_shared(
- server.m_description, static_cast(port_index), server.m_address, server.m_port));
+ GetControllerInterface().AddDevice(
+ std::make_shared(server.m_description, static_cast(port_index),
+ server.m_address, server.m_port, m_client_uid));
}
}
}
-void DeInit()
+InputBackend::~InputBackend()
{
+ Config::RemoveConfigChangedCallback(m_config_change_callback_id);
StopHotplugThread();
-
- s_has_init = false;
- s_servers_enabled = false;
- s_servers.clear();
}
-Device::Device(std::string name, int index, std::string server_address, u16 server_port)
+Device::Device(std::string name, int index, std::string server_address, u16 server_port,
+ u32 client_uid)
: m_name{std::move(name)}, m_index{index}, m_server_address{std::move(server_address)},
- m_server_port{server_port}
+ m_server_port{server_port}, m_client_uid(client_uid)
{
m_socket.setBlocking(false);
@@ -604,7 +622,7 @@ void Device::UpdateInput()
{
m_next_reregister = now + SERVER_REREGISTER_INTERVAL;
- Proto::Message msg(s_client_uid);
+ Proto::Message msg(m_client_uid);
auto& data_req = msg.m_message;
data_req.register_flags = Proto::RegisterFlags::PadID;
data_req.pad_id_to_register = m_index;
diff --git a/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h
index 1a2c2826ca..7f2d62fb58 100644
--- a/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h
+++ b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h
@@ -4,6 +4,7 @@
#pragma once
#include "Common/Config/Config.h"
+#include "InputCommon/ControllerInterface/InputBackend.h"
namespace ciface::DualShockUDPClient
{
@@ -20,7 +21,6 @@ extern const Config::Info SERVERS;
extern const Config::Info SERVERS_ENABLED;
} // namespace Settings
-void Init();
-void PopulateDevices();
-void DeInit();
+std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface);
+
} // namespace ciface::DualShockUDPClient
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();
diff --git a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp
index d3a222890b..cf811e5b41 100644
--- a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp
+++ b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp
@@ -25,6 +25,43 @@
namespace ciface::evdev
{
+class InputBackend final : public ciface::InputBackend
+{
+public:
+ InputBackend(ControllerInterface* controller_interface);
+ ~InputBackend();
+ void PopulateDevices() override;
+
+ void RemoveDevnodeObject(const std::string&);
+
+private:
+ std::shared_ptr
+ FindDeviceWithUniqueIDAndPhysicalLocation(const char* unique_id, const char* physical_location);
+
+ void AddDeviceNode(const char* devnode);
+
+ void StartHotplugThread();
+ void StopHotplugThread();
+ void HotplugThreadFunc();
+
+ std::thread m_hotplug_thread;
+ Common::Flag m_hotplug_thread_running;
+ int m_wakeup_eventfd;
+
+ // There is no easy way to get the device name from only a dev node
+ // during a device removed event, since libevdev can't work on removed devices;
+ // sysfs is not stable, so this is probably the easiest way to get a name for a node.
+ // This can, and will be modified by different thread, possibly concurrently,
+ // as devices can be destroyed by any thread at any time. As of now it's protected
+ // by ControllerInterface::m_devices_population_mutex.
+ std::map> m_devnode_objects;
+};
+
+std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface)
+{
+ return std::make_unique(controller_interface);
+}
+
class Input : public Core::Device::Input
{
public:
@@ -195,25 +232,14 @@ public:
bool IsDetectable() const override { return false; }
};
-static std::thread s_hotplug_thread;
-static Common::Flag s_hotplug_thread_running;
-static int s_wakeup_eventfd;
-
-// There is no easy way to get the device name from only a dev node
-// during a device removed event, since libevdev can't work on removed devices;
-// sysfs is not stable, so this is probably the easiest way to get a name for a node.
-// This can, and will be modified by different thread, possibly concurrently,
-// as devices can be destroyed by any thread at any time. As of now it's protected
-// by ControllerInterface::m_devices_population_mutex.
-static std::map> s_devnode_objects;
-
-static std::shared_ptr
-FindDeviceWithUniqueIDAndPhysicalLocation(const char* unique_id, const char* physical_location)
+std::shared_ptr
+InputBackend::FindDeviceWithUniqueIDAndPhysicalLocation(const char* unique_id,
+ const char* physical_location)
{
if (!unique_id || !physical_location)
return nullptr;
- for (auto& [node, dev] : s_devnode_objects)
+ for (auto& [node, dev] : m_devnode_objects)
{
if (const auto device = dev.lock())
{
@@ -231,7 +257,7 @@ FindDeviceWithUniqueIDAndPhysicalLocation(const char* unique_id, const char* phy
return nullptr;
}
-static void AddDeviceNode(const char* devnode)
+void InputBackend::AddDeviceNode(const char* devnode)
{
// Unfortunately udev gives us no way to filter out the non event device interfaces.
// So we open it and see if it works with evdev ioctls or not.
@@ -265,30 +291,30 @@ static void AddDeviceNode(const char* devnode)
// This will also give it the correct index and invoke device change callbacks.
// Make sure to force the device removal immediately (as they are shared ptrs and
// they could be kept alive, preventing us from re-creating the device)
- g_controller_interface.RemoveDevice(
+ GetControllerInterface().RemoveDevice(
[&evdev_device](const auto* device) {
return static_cast(device) == evdev_device.get();
},
true);
- g_controller_interface.AddDevice(evdev_device);
+ GetControllerInterface().AddDevice(evdev_device);
}
else
{
- evdev_device = std::make_shared();
+ evdev_device = std::make_shared(this);
const bool was_interesting = evdev_device->AddNode(devnode, fd, dev);
if (was_interesting)
- g_controller_interface.AddDevice(evdev_device);
+ GetControllerInterface().AddDevice(evdev_device);
}
- // If the devices failed to be added to g_controller_interface, it will be added here but then
+ // If the devices failed to be added to ControllerInterface, it will be added here but then
// immediately removed in its destructor due to the shared ptr not having any references left
- s_devnode_objects.emplace(devnode, std::move(evdev_device));
+ m_devnode_objects.emplace(devnode, std::move(evdev_device));
}
-static void HotplugThreadFunc()
+void InputBackend::HotplugThreadFunc()
{
Common::SetCurrentThreadName("evdev Hotplug Thread");
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "evdev hotplug thread started");
@@ -306,16 +332,16 @@ static void HotplugThreadFunc()
udev_monitor_enable_receiving(monitor);
const int monitor_fd = udev_monitor_get_fd(monitor);
- while (s_hotplug_thread_running.IsSet())
+ while (m_hotplug_thread_running.IsSet())
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(monitor_fd, &fds);
- FD_SET(s_wakeup_eventfd, &fds);
+ FD_SET(m_wakeup_eventfd, &fds);
const int ret =
- select(std::max(monitor_fd, s_wakeup_eventfd) + 1, &fds, nullptr, nullptr, nullptr);
+ select(std::max(monitor_fd, m_wakeup_eventfd) + 1, &fds, nullptr, nullptr, nullptr);
if (ret < 1 || !FD_ISSET(monitor_fd, &fds))
continue;
@@ -327,53 +353,54 @@ static void HotplugThreadFunc()
if (!devnode)
continue;
- // Use g_controller_interface.PlatformPopulateDevices() to protect access around
- // s_devnode_objects. Note that even if we get these events at the same time as a
+ // Use GetControllerInterface().PlatformPopulateDevices() to protect access around
+ // m_devnode_objects. Note that even if we get these events at the same time as a
// a PopulateDevices() request (e.g. on start up, we might get all the add events
// for connected devices), this won't ever cause duplicate devices as AddDeviceNode()
// automatically removes the old one if it already existed
if (strcmp(action, "remove") == 0)
{
- g_controller_interface.PlatformPopulateDevices([&devnode] {
+ GetControllerInterface().PlatformPopulateDevices([&devnode, this] {
std::shared_ptr ptr;
- const auto it = s_devnode_objects.find(devnode);
- if (it != s_devnode_objects.end())
+ const auto it = m_devnode_objects.find(devnode);
+ if (it != m_devnode_objects.end())
ptr = it->second.lock();
// If we don't recognize this device, ptr will be null and no device will be removed.
- g_controller_interface.RemoveDevice([&ptr](const auto* device) {
+ GetControllerInterface().RemoveDevice([&ptr](const auto* device) {
return static_cast(device) == ptr.get();
});
});
}
else if (strcmp(action, "add") == 0)
{
- g_controller_interface.PlatformPopulateDevices([&devnode] { AddDeviceNode(devnode); });
+ GetControllerInterface().PlatformPopulateDevices(
+ [&devnode, this] { AddDeviceNode(devnode); });
}
}
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "evdev hotplug thread stopped");
}
-static void StartHotplugThread()
+void InputBackend::StartHotplugThread()
{
// Mark the thread as running.
- if (!s_hotplug_thread_running.TestAndSet())
+ if (!m_hotplug_thread_running.TestAndSet())
{
// It was already running.
return;
}
- s_wakeup_eventfd = eventfd(0, 0);
- ASSERT_MSG(CONTROLLERINTERFACE, s_wakeup_eventfd != -1, "Couldn't create eventfd.");
- s_hotplug_thread = std::thread(HotplugThreadFunc);
+ m_wakeup_eventfd = eventfd(0, 0);
+ ASSERT_MSG(CONTROLLERINTERFACE, m_wakeup_eventfd != -1, "Couldn't create eventfd.");
+ m_hotplug_thread = std::thread(&InputBackend::HotplugThreadFunc, this);
}
-static void StopHotplugThread()
+void InputBackend::StopHotplugThread()
{
// Tell the hotplug thread to stop.
- if (!s_hotplug_thread_running.TestAndClear())
+ if (!m_hotplug_thread_running.TestAndClear())
{
// It wasn't running, we're done.
return;
@@ -381,22 +408,23 @@ static void StopHotplugThread()
// Write something to efd so that select() stops blocking.
const uint64_t value = 1;
- static_cast(!write(s_wakeup_eventfd, &value, sizeof(uint64_t)));
+ static_cast(!write(m_wakeup_eventfd, &value, sizeof(uint64_t)));
- s_hotplug_thread.join();
- close(s_wakeup_eventfd);
+ m_hotplug_thread.join();
+ close(m_wakeup_eventfd);
}
-void Init()
+InputBackend::InputBackend(ControllerInterface* controller_interface)
+ : ciface::InputBackend(controller_interface)
{
StartHotplugThread();
}
// Only call this when ControllerInterface::m_devices_population_mutex is locked
-void PopulateDevices()
+void InputBackend::PopulateDevices()
{
// Don't run if we are not initialized
- if (!s_hotplug_thread_running.IsSet())
+ if (!m_hotplug_thread_running.IsSet())
{
return;
}
@@ -431,7 +459,7 @@ void PopulateDevices()
udev_unref(udev);
}
-void Shutdown()
+InputBackend::~InputBackend()
{
StopHotplugThread();
}
@@ -627,16 +655,25 @@ const char* evdevDevice::GetPhysicalLocation() const
return libevdev_get_phys(m_nodes.front().device);
}
+evdevDevice::evdevDevice(InputBackend* input_backend) : m_input_backend(*input_backend)
+{
+}
+
evdevDevice::~evdevDevice()
{
for (auto& node : m_nodes)
{
- s_devnode_objects.erase(node.devnode);
+ m_input_backend.RemoveDevnodeObject(node.devnode);
libevdev_free(node.device);
close(node.fd);
}
}
+void InputBackend::RemoveDevnodeObject(const std::string& node)
+{
+ m_devnode_objects.erase(node);
+}
+
void evdevDevice::UpdateInput()
{
// Run through all evdev events
diff --git a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h
index fd497ad8b8..ff5ab8a72a 100644
--- a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h
+++ b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h
@@ -11,9 +11,9 @@
namespace ciface::evdev
{
-void Init();
-void PopulateDevices();
-void Shutdown();
+class InputBackend;
+
+std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface);
class evdevDevice : public Core::Device
{
@@ -75,6 +75,7 @@ public:
void UpdateInput() override;
bool IsValid() const override;
+ evdevDevice(InputBackend* input_backend);
~evdevDevice();
// Return true if node was "interesting".
@@ -97,5 +98,7 @@ private:
};
std::vector m_nodes;
+
+ InputBackend& m_input_backend;
};
} // namespace ciface::evdev