From 2e5cd5d51970fd90e87e54d53fe904cf9d777139 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 22 Oct 2022 17:10:38 -0500 Subject: [PATCH] ControllerInterface: evdev InputBackend implementation. --- .../ControllerInterface.cpp | 9 +- .../ControllerInterface/evdev/evdev.cpp | 133 +++++++++++------- .../ControllerInterface/evdev/evdev.h | 9 +- 3 files changed, 93 insertions(+), 58 deletions(-) diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index d1ee02370a..7f35c8ccfd 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -70,7 +70,7 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi) // 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 @@ -184,9 +184,6 @@ void ControllerInterface::RefreshDevices(RefreshReason reason) #ifdef CIFACE_USE_ANDROID ciface::Android::PopulateDevices(); #endif -#ifdef CIFACE_USE_EVDEV - ciface::evdev::PopulateDevices(); -#endif #ifdef CIFACE_USE_PIPES ciface::Pipes::PopulateDevices(); #endif @@ -245,13 +242,11 @@ void ControllerInterface::Shutdown() #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 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