diff --git a/Source/Core/Core/HW/GCKeyboard.cpp b/Source/Core/Core/HW/GCKeyboard.cpp index a306dc06a0..1b818bee84 100644 --- a/Source/Core/Core/HW/GCKeyboard.cpp +++ b/Source/Core/Core/HW/GCKeyboard.cpp @@ -36,6 +36,7 @@ void Initialize(void* const hwnd) } g_controller_interface.Initialize(hwnd); + g_controller_interface.RegisterHotplugCallback(LoadConfig); // Load the saved controller config s_config.LoadConfig(true); diff --git a/Source/Core/Core/HW/GCKeyboardEmu.cpp b/Source/Core/Core/HW/GCKeyboardEmu.cpp index 23e49898fc..b022527a0d 100644 --- a/Source/Core/Core/HW/GCKeyboardEmu.cpp +++ b/Source/Core/Core/HW/GCKeyboardEmu.cpp @@ -86,6 +86,7 @@ std::string GCKeyboard::GetName() const void GCKeyboard::GetInput(KeyboardStatus* const kb) { + auto lock = ControllerEmu::GetStateLock(); m_keys0x->GetState(&kb->key0x, keys0_bitmasks); m_keys1x->GetState(&kb->key1x, keys1_bitmasks); m_keys2x->GetState(&kb->key2x, keys2_bitmasks); diff --git a/Source/Core/Core/HW/GCPad.cpp b/Source/Core/Core/HW/GCPad.cpp index 70af354560..d88fd53be2 100644 --- a/Source/Core/Core/HW/GCPad.cpp +++ b/Source/Core/Core/HW/GCPad.cpp @@ -35,6 +35,7 @@ void Initialize(void* const hwnd) } g_controller_interface.Initialize(hwnd); + g_controller_interface.RegisterHotplugCallback(LoadConfig); // Load the saved controller config s_config.LoadConfig(true); diff --git a/Source/Core/Core/HW/GCPadEmu.cpp b/Source/Core/Core/HW/GCPadEmu.cpp index f985342b62..36ab385486 100644 --- a/Source/Core/Core/HW/GCPadEmu.cpp +++ b/Source/Core/Core/HW/GCPadEmu.cpp @@ -81,6 +81,8 @@ std::string GCPad::GetName() const void GCPad::GetInput(GCPadStatus* const pad) { + auto lock = ControllerEmu::GetStateLock(); + ControlState x, y, triggers[2]; // buttons @@ -116,6 +118,7 @@ void GCPad::GetInput(GCPadStatus* const pad) void GCPad::SetOutput(const ControlState strength) { + auto lock = ControllerEmu::GetStateLock(); m_rumble->controls[0]->control_ref->State(strength); } @@ -190,5 +193,6 @@ void GCPad::LoadDefaults(const ControllerInterface& ciface) bool GCPad::GetMicButton() const { + auto lock = ControllerEmu::GetStateLock(); return (0.0f != m_buttons->controls.back()->control_ref->State()); } diff --git a/Source/Core/Core/HW/Wiimote.cpp b/Source/Core/Core/HW/Wiimote.cpp index 69c78448d2..ea63468a31 100644 --- a/Source/Core/Core/HW/Wiimote.cpp +++ b/Source/Core/Core/HW/Wiimote.cpp @@ -37,6 +37,7 @@ void Initialize(void* const hwnd, InitializeMode init_mode) } g_controller_interface.Initialize(hwnd); + g_controller_interface.RegisterHotplugCallback(LoadConfig); s_config.LoadConfig(false); diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 276943b578..b9dec6f554 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -623,8 +623,11 @@ void Wiimote::Update() return; // returns true if a report was sent - if (Step()) - return; + { + auto lock = ControllerEmu::GetStateLock(); + if (Step()) + return; + } u8 data[MAX_PAYLOAD]; memset(data, 0, sizeof(data)); @@ -646,6 +649,8 @@ void Wiimote::Update() data[0] = 0xA1; data[1] = m_reporting_mode; + auto lock = ControllerEmu::GetStateLock(); + // core buttons if (rptf.core) GetButtonData(data + rptf.core); @@ -876,6 +881,7 @@ void Wiimote::ConnectOnInput() } u16 buttons = 0; + auto lock = ControllerEmu::GetStateLock(); m_buttons->GetState(&buttons, button_bitmasks); m_dpad->GetState(&buttons, dpad_bitmasks); diff --git a/Source/Core/Core/HotkeyManager.cpp b/Source/Core/Core/HotkeyManager.cpp index 707c4c05c4..b8fc387b3e 100644 --- a/Source/Core/Core/HotkeyManager.cpp +++ b/Source/Core/Core/HotkeyManager.cpp @@ -186,6 +186,7 @@ void Initialize(void* const hwnd) s_config.CreateController(); g_controller_interface.Initialize(hwnd); + g_controller_interface.RegisterHotplugCallback(LoadConfig); // load the saved controller config s_config.LoadConfig(true); @@ -239,6 +240,7 @@ std::string HotkeyManager::GetName() const void HotkeyManager::GetInput(HotkeyStatus* const kb) { + auto lock = ControllerEmu::GetStateLock(); for (int set = 0; set < (NUM_HOTKEYS + 31) / 32; set++) { std::vector bitmasks; diff --git a/Source/Core/DolphinWX/InputConfigDiag.cpp b/Source/Core/DolphinWX/InputConfigDiag.cpp index 2a5aa997e3..cad5d3496a 100644 --- a/Source/Core/DolphinWX/InputConfigDiag.cpp +++ b/Source/Core/DolphinWX/InputConfigDiag.cpp @@ -315,11 +315,12 @@ bool ControlDialog::Validate() { control_reference->expression = WxStrToStr(textctrl->GetValue()); + auto lock = ControllerEmu::GetStateLock(); g_controller_interface.UpdateReference(control_reference, m_parent->controller->default_device); UpdateGUI(); - return (control_reference->parse_error == EXPRESSION_PARSE_SUCCESS); + return control_reference->parse_error == EXPRESSION_PARSE_SUCCESS; } void GamepadPage::SetDevice(wxCommandEvent&) @@ -351,6 +352,7 @@ void ControlDialog::ClearControl(wxCommandEvent&) { control_reference->expression.clear(); + auto lock = ControllerEmu::GetStateLock(); g_controller_interface.UpdateReference(control_reference, m_parent->controller->default_device); UpdateGUI(); @@ -408,6 +410,7 @@ void ControlDialog::SetSelectedControl(wxCommandEvent&) textctrl->WriteText(expr); control_reference->expression = textctrl->GetValue(); + auto lock = ControllerEmu::GetStateLock(); g_controller_interface.UpdateReference(control_reference, m_parent->controller->default_device); UpdateGUI(); @@ -442,6 +445,7 @@ void ControlDialog::AppendControl(wxCommandEvent& event) textctrl->WriteText(expr); control_reference->expression = textctrl->GetValue(); + auto lock = ControllerEmu::GetStateLock(); g_controller_interface.UpdateReference(control_reference, m_parent->controller->default_device); UpdateGUI(); @@ -556,6 +560,7 @@ bool GamepadPage::DetectButton(ControlButton* button) wxString expr; GetExpressionForControl(expr, control_name); button->control_reference->expression = expr; + auto lock = ControllerEmu::GetStateLock(); g_controller_interface.UpdateReference(button->control_reference, controller->default_device); success = true; } diff --git a/Source/Core/DolphinWX/InputConfigDiagBitmaps.cpp b/Source/Core/DolphinWX/InputConfigDiagBitmaps.cpp index 13dcea4e2f..8f972c9091 100644 --- a/Source/Core/DolphinWX/InputConfigDiagBitmaps.cpp +++ b/Source/Core/DolphinWX/InputConfigDiagBitmaps.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -125,6 +124,7 @@ static void DrawButton(unsigned int* const bitmasks, unsigned int buttons, unsig } else { + auto lock = ControllerEmu::GetStateLock(); unsigned char amt = 255 - g->control_group->controls[(row * 8) + n]->control_ref->State() * 128; dc.SetBrush(wxBrush(wxColour(amt, amt, amt))); } @@ -232,17 +232,15 @@ static void DrawControlGroupBox(wxDC& dc, ControlGroupBox* g) } // raw dot - { - ControlState xx, yy; - xx = g->control_group->controls[3]->control_ref->State(); - xx -= g->control_group->controls[2]->control_ref->State(); - yy = g->control_group->controls[1]->control_ref->State(); - yy -= g->control_group->controls[0]->control_ref->State(); + ControlState xx, yy; + xx = g->control_group->controls[3]->control_ref->State(); + xx -= g->control_group->controls[2]->control_ref->State(); + yy = g->control_group->controls[1]->control_ref->State(); + yy -= g->control_group->controls[0]->control_ref->State(); - dc.SetPen(*wxGREY_PEN); - dc.SetBrush(*wxGREY_BRUSH); - DrawCoordinate(dc, xx, yy); - } + dc.SetPen(*wxGREY_PEN); + dc.SetBrush(*wxGREY_BRUSH); + DrawCoordinate(dc, xx, yy); // adjusted dot if (x != 0 || y != 0) @@ -403,6 +401,7 @@ static void DrawControlGroupBox(wxDC& dc, ControlGroupBox* g) for (unsigned int n = 0; n < trigger_count; ++n) { dc.SetBrush(*wxRED_BRUSH); + ControlState trig_d = g->control_group->controls[n]->control_ref->State(); ControlState trig_a = @@ -465,6 +464,7 @@ void InputConfigDialog::UpdateBitmaps(wxTimerEvent& WXUNUSED(event)) GamepadPage* const current_page = (GamepadPage*)m_pad_notebook->GetPage(m_pad_notebook->GetSelection()); + auto lock = ControllerEmu::GetStateLock(); for (ControlGroupBox* g : current_page->control_groups) { // if this control group has a bitmap diff --git a/Source/Core/InputCommon/ControllerEmu.cpp b/Source/Core/InputCommon/ControllerEmu.cpp index 117184c8db..b3b13b796a 100644 --- a/Source/Core/InputCommon/ControllerEmu.cpp +++ b/Source/Core/InputCommon/ControllerEmu.cpp @@ -6,8 +6,19 @@ #include #include "Common/Common.h" +// This should be called before calling GetState() or State() on a control reference +// to prevent a race condition. +// This is a recursive mutex because UpdateReferences is recursive. +static std::recursive_mutex s_get_state_mutex; +std::unique_lock ControllerEmu::GetStateLock() +{ + std::unique_lock lock(s_get_state_mutex); + return lock; +} + void ControllerEmu::UpdateReferences(ControllerInterface& devi) { + auto lock = ControllerEmu::GetStateLock(); for (auto& ctrlGroup : groups) { for (auto& control : ctrlGroup->controls) diff --git a/Source/Core/InputCommon/ControllerEmu.h b/Source/Core/InputCommon/ControllerEmu.h index d9470ca8a9..9416b5f0de 100644 --- a/Source/Core/InputCommon/ControllerEmu.h +++ b/Source/Core/InputCommon/ControllerEmu.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -444,6 +445,12 @@ public: void UpdateReferences(ControllerInterface& devi); + // This returns a lock that should be held before calling State() on any control + // references and GetState(), by extension. This prevents a race condition + // which happens while handling a hotplug event because a control reference's State() + // could be called before we have finished updating the reference. + static std::unique_lock GetStateLock(); + std::vector> groups; ciface::Core::DeviceQualifier default_device; diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index 700d390b92..f1e62e73cf 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -106,17 +106,6 @@ void ControllerInterface::Shutdown() if (!m_is_init) return; - std::lock_guard lk(m_devices_mutex); - - for (const auto& d : m_devices) - { - // Set outputs to ZERO before destroying device - for (ciface::Core::Device::Output* o : d->Outputs()) - o->SetState(0); - } - - m_devices.clear(); - #ifdef CIFACE_USE_XINPUT ciface::XInput::DeInit(); #endif @@ -136,6 +125,20 @@ void ControllerInterface::Shutdown() #ifdef CIFACE_USE_ANDROID // nothing needed #endif +#ifdef CIFACE_USE_EVDEV + ciface::evdev::Shutdown(); +#endif + + std::lock_guard lk(m_devices_mutex); + + for (const auto& d : m_devices) + { + // Set outputs to ZERO before destroying device + for (ciface::Core::Device::Output* o : d->Outputs()) + o->SetState(0); + } + + m_devices.clear(); m_is_init = false; } @@ -160,6 +163,14 @@ void ControllerInterface::AddDevice(std::shared_ptr device m_devices.emplace_back(std::move(device)); } +void ControllerInterface::RemoveDevice(std::function callback) +{ + std::lock_guard lk(m_devices_mutex); + m_devices.erase(std::remove_if(m_devices.begin(), m_devices.end(), + [&callback](const auto& dev) { return callback(dev.get()); }), + m_devices.end()); +} + // // UpdateInput // @@ -167,9 +178,35 @@ void ControllerInterface::AddDevice(std::shared_ptr device // void ControllerInterface::UpdateInput() { - std::lock_guard lk(m_devices_mutex); - for (const auto& d : m_devices) - d->UpdateInput(); + // Don't block the UI or CPU thread (to avoid a short but noticeable frame drop) + if (m_devices_mutex.try_lock()) + { + std::lock_guard lk(m_devices_mutex, std::adopt_lock); + for (const auto& d : m_devices) + d->UpdateInput(); + } +} + +// +// RegisterHotplugCallback +// +// Register a callback to be called from the input backends' hotplug thread +// when there is a new device +// +void ControllerInterface::RegisterHotplugCallback(std::function callback) +{ + m_hotplug_callbacks.emplace_back(std::move(callback)); +} + +// +// InvokeHotplugCallbacks +// +// Invoke all callbacks that were registered +// +void ControllerInterface::InvokeHotplugCallbacks() const +{ + for (const auto& callback : m_hotplug_callbacks) + callback(); } // diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h index 2f4212ba85..198a2c180b 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h @@ -122,12 +122,17 @@ public: void Reinitialize(); void Shutdown(); void AddDevice(std::shared_ptr device); + void RemoveDevice(std::function callback); bool IsInit() const { return m_is_init; } void UpdateReference(ControlReference* control, const ciface::Core::DeviceQualifier& default_device) const; void UpdateInput(); + void RegisterHotplugCallback(std::function callback); + void InvokeHotplugCallbacks() const; + private: + std::vector> m_hotplug_callbacks; bool m_is_init; void* m_hwnd; }; diff --git a/Source/Core/InputCommon/ControllerInterface/Device.h b/Source/Core/InputCommon/ControllerInterface/Device.h index e3fe4c11cd..657ece5016 100644 --- a/Source/Core/InputCommon/ControllerInterface/Device.h +++ b/Source/Core/InputCommon/ControllerInterface/Device.h @@ -98,6 +98,7 @@ public: virtual std::string GetName() const = 0; virtual std::string GetSource() const = 0; virtual void UpdateInput() {} + virtual bool IsValid() const { return true; } const std::vector& Inputs() const { return m_inputs; } const std::vector& Outputs() const { return m_outputs; } Input* FindInput(const std::string& name) const; diff --git a/Source/Core/InputCommon/ControllerInterface/ExpressionParser.cpp b/Source/Core/InputCommon/ControllerInterface/ExpressionParser.cpp index 6e9b8b77ab..2960888148 100644 --- a/Source/Core/InputCommon/ControllerInterface/ExpressionParser.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ExpressionParser.cpp @@ -235,8 +235,9 @@ public: ControlQualifier qualifier; Device::Control* control; - ControlExpression(ControlQualifier qualifier_, Device::Control* control_) - : qualifier(qualifier_), control(control_) + ControlExpression(ControlQualifier qualifier_, std::shared_ptr device, + Device::Control* control_) + : qualifier(qualifier_), control(control_), m_device(device) { } @@ -244,6 +245,8 @@ public: void SetValue(ControlState value) override { control->ToOutput()->SetGatedState(value); } int CountNumControls() override { return 1; } operator std::string() override { return "`" + (std::string)qualifier + "`"; } +private: + std::shared_ptr m_device; }; class BinaryExpression : public ExpressionNode @@ -393,6 +396,7 @@ private: { case TOK_CONTROL: { + std::shared_ptr device = finder.FindDevice(tok.qualifier); Device::Control* control = finder.FindControl(tok.qualifier); if (control == nullptr) { @@ -400,7 +404,7 @@ private: return EXPRESSION_PARSE_SUCCESS; } - *expr_out = new ControlExpression(tok.qualifier, control); + *expr_out = new ControlExpression(tok.qualifier, device, control); return EXPRESSION_PARSE_SUCCESS; } case TOK_LPAREN: @@ -550,10 +554,11 @@ ExpressionParseStatus ParseExpression(const std::string& str, ControlFinder& fin qualifier.control_name = str; qualifier.has_device = false; + std::shared_ptr device = finder.FindDevice(qualifier); Device::Control* control = finder.FindControl(qualifier); if (control) { - *expr_out = new Expression(new ControlExpression(qualifier, control)); + *expr_out = new Expression(new ControlExpression(qualifier, device, control)); return EXPRESSION_PARSE_SUCCESS; } diff --git a/Source/Core/InputCommon/ControllerInterface/ExpressionParser.h b/Source/Core/InputCommon/ControllerInterface/ExpressionParser.h index 992ca65f03..2c8f5a793f 100644 --- a/Source/Core/InputCommon/ControllerInterface/ExpressionParser.h +++ b/Source/Core/InputCommon/ControllerInterface/ExpressionParser.h @@ -37,10 +37,10 @@ public: : container(container_), default_device(default_), is_input(is_input_) { } + std::shared_ptr FindDevice(ControlQualifier qualifier); Core::Device::Control* FindControl(ControlQualifier qualifier); private: - std::shared_ptr FindDevice(ControlQualifier qualifier); const Core::DeviceContainer& container; const Core::DeviceQualifier& default_device; bool is_input; diff --git a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp index 16d0d108b2..08687332fb 100644 --- a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp +++ b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp @@ -5,12 +5,17 @@ #include #include #include +#include #include +#include + #include "Common/Assert.h" +#include "Common/Flag.h" #include "Common/Logging/Log.h" #include "Common/MathUtil.h" #include "Common/StringUtil.h" +#include "Common/Thread.h" #include "InputCommon/ControllerInterface/ControllerInterface.h" #include "InputCommon/ControllerInterface/evdev/evdev.h" @@ -18,6 +23,15 @@ namespace ciface { namespace evdev { +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. +static std::map s_devnode_name_map; + static std::string GetName(const std::string& devnode) { int fd = open(devnode.c_str(), O_RDWR | O_NONBLOCK); @@ -28,20 +42,110 @@ static std::string GetName(const std::string& devnode) close(fd); return std::string(); } - std::string res = libevdev_get_name(dev); + std::string res = StripSpaces(libevdev_get_name(dev)); libevdev_free(dev); close(fd); return res; } +static void HotplugThreadFunc() +{ + Common::SetCurrentThreadName("evdev Hotplug Thread"); + NOTICE_LOG(SERIALINTERFACE, "evdev hotplug thread started"); + + udev* udev = udev_new(); + _assert_msg_(PAD, udev != nullptr, "Couldn't initialize libudev."); + + // Set up monitoring + udev_monitor* monitor = udev_monitor_new_from_netlink(udev, "udev"); + udev_monitor_filter_add_match_subsystem_devtype(monitor, "input", nullptr); + udev_monitor_enable_receiving(monitor); + const int monitor_fd = udev_monitor_get_fd(monitor); + + while (s_hotplug_thread_running.IsSet()) + { + fd_set fds; + + FD_ZERO(&fds); + FD_SET(monitor_fd, &fds); + FD_SET(s_wakeup_eventfd, &fds); + + int ret = select(monitor_fd + 1, &fds, nullptr, nullptr, nullptr); + if (ret < 1 || !FD_ISSET(monitor_fd, &fds)) + continue; + + udev_device* dev = udev_monitor_receive_device(monitor); + + const char* action = udev_device_get_action(dev); + const char* devnode = udev_device_get_devnode(dev); + if (!devnode) + continue; + + if (strcmp(action, "remove") == 0) + { + const auto it = s_devnode_name_map.find(devnode); + if (it == s_devnode_name_map.end()) + continue; // we don't know the name for this device, so it is probably not an evdev device + const std::string& name = it->second; + g_controller_interface.RemoveDevice([&name](const auto& device) { + return device->GetSource() == "evdev" && device->GetName() == name && !device->IsValid(); + }); + NOTICE_LOG(SERIALINTERFACE, "Removed device: %s", name.c_str()); + s_devnode_name_map.erase(devnode); + g_controller_interface.InvokeHotplugCallbacks(); + } + // Only react to "device added" events for evdev devices that we can access. + else if (strcmp(action, "add") == 0 && access(devnode, W_OK) == 0) + { + const std::string name = GetName(devnode); + if (name.empty()) + continue; // probably not an evdev device + auto device = std::make_shared(devnode); + if (device->IsInteresting()) + { + g_controller_interface.AddDevice(std::move(device)); + s_devnode_name_map.insert(std::pair(devnode, name)); + NOTICE_LOG(SERIALINTERFACE, "Added new device: %s", name.c_str()); + g_controller_interface.InvokeHotplugCallbacks(); + } + } + udev_device_unref(dev); + } + NOTICE_LOG(SERIALINTERFACE, "evdev hotplug thread stopped"); +} + +static void StartHotplugThread() +{ + if (s_hotplug_thread_running.IsSet()) + return; + + s_wakeup_eventfd = eventfd(0, 0); + _assert_msg_(PAD, s_wakeup_eventfd != -1, "Couldn't create eventfd."); + s_hotplug_thread_running.Set(true); + s_hotplug_thread = std::thread(HotplugThreadFunc); +} + +static void StopHotplugThread() +{ + if (s_hotplug_thread_running.TestAndClear()) + { + // Write something to efd so that select() stops blocking. + uint64_t value = 1; + write(s_wakeup_eventfd, &value, sizeof(uint64_t)); + s_hotplug_thread.join(); + } +} + void Init() { - // We use Udev to find any devices. In the future this will allow for hotplugging. - // But for now it is essentially iterating over /dev/input/event0 to event31. However if the - // naming scheme is ever updated in the future, this *should* be forwards compatable. + s_devnode_name_map.clear(); - struct udev* udev = udev_new(); - _assert_msg_(PAD, udev != 0, "Couldn't initilize libudev."); + // During initialization we use udev to iterate over all /dev/input/event* devices. + // Note: the Linux kernel is currently limited to just 32 event devices. If this ever + // changes, hopefully udev will take care of this. + + udev* udev = udev_new(); + _assert_msg_(PAD, udev != nullptr, "Couldn't initialize libudev."); // List all input devices udev_enumerate* enumerate = udev_enumerate_new(udev); @@ -69,12 +173,20 @@ void Init() if (input->IsInteresting()) { g_controller_interface.AddDevice(std::move(input)); + s_devnode_name_map.insert(std::pair(devnode, name)); } } udev_device_unref(dev); } udev_enumerate_unref(enumerate); udev_unref(udev); + + StartHotplugThread(); +} + +void Shutdown() +{ + StopHotplugThread(); } evdevDevice::evdevDevice(const std::string& devnode) : m_devfile(devnode) @@ -152,6 +264,22 @@ void evdevDevice::UpdateInput() } while (rc >= 0); } +bool evdevDevice::IsValid() const +{ + int current_fd = libevdev_get_fd(m_dev); + if (current_fd == -1) + return false; + + libevdev* device; + if (libevdev_new_from_fd(current_fd, &device) != 0) + { + close(current_fd); + return false; + } + libevdev_free(device); + return true; +} + std::string evdevDevice::Button::GetName() const { // Buttons below 0x100 are mostly keyboard keys, and the names make sense diff --git a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h index 081913fa8e..dc8e917e23 100644 --- a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h +++ b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h @@ -8,11 +8,14 @@ #include #include +#include "InputCommon/ControllerInterface/ControllerInterface.h" + namespace ciface { namespace evdev { void Init(); +void Shutdown(); class evdevDevice : public Core::Device { @@ -62,6 +65,7 @@ private: public: void UpdateInput() override; + bool IsValid() const override; evdevDevice(const std::string& devnode); ~evdevDevice();