From 168a49c87ffbd05512c20db9735b3cb92afac93b Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 22 Oct 2022 17:33:21 -0500 Subject: [PATCH] ControllerInterface: DSU InputBackend implementation. --- .../ControllerInterface.cpp | 8 +- .../DualShockUDPClient/DualShockUDPClient.cpp | 162 ++++++++++-------- .../DualShockUDPClient/DualShockUDPClient.h | 6 +- 3 files changed, 94 insertions(+), 82 deletions(-) diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index 7f35c8ccfd..6a6affd478 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -76,7 +76,7 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi) // 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 @@ -187,9 +187,6 @@ void ControllerInterface::RefreshDevices(RefreshReason reason) #ifdef CIFACE_USE_PIPES ciface::Pipes::PopulateDevices(); #endif -#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT - ciface::DualShockUDPClient::PopulateDevices(); -#endif for (auto& backend : m_input_backends) backend->PopulateDevices(); @@ -241,9 +238,6 @@ void ControllerInterface::Shutdown() #endif #ifdef CIFACE_USE_ANDROID // nothing needed -#endif -#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT - ciface::DualShockUDPClient::DeInit(); #endif // Empty the container of input backends to deconstruct and deinitialize them. 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