Merge pull request #3890 from leoetlino/evdev-hotplug
evdev: Add hotplug support
This commit is contained in:
commit
e0cbb9d1ae
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -623,8 +623,11 @@ void Wiimote::Update()
|
|||
return;
|
||||
|
||||
// returns true if a report was sent
|
||||
{
|
||||
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);
|
||||
|
||||
|
|
|
@ -186,6 +186,7 @@ void Initialize(void* const hwnd)
|
|||
s_config.CreateController<HotkeyManager>();
|
||||
|
||||
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<u32> bitmasks;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <wx/bitmap.h>
|
||||
|
@ -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,7 +232,6 @@ 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();
|
||||
|
@ -242,7 +241,6 @@ static void DrawControlGroupBox(wxDC& dc, ControlGroupBox* g)
|
|||
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
|
||||
|
|
|
@ -6,8 +6,19 @@
|
|||
#include <memory>
|
||||
#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<std::recursive_mutex> ControllerEmu::GetStateLock()
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> 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)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -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<std::recursive_mutex> GetStateLock();
|
||||
|
||||
std::vector<std::unique_ptr<ControlGroup>> groups;
|
||||
|
||||
ciface::Core::DeviceQualifier default_device;
|
||||
|
|
|
@ -106,17 +106,6 @@ void ControllerInterface::Shutdown()
|
|||
if (!m_is_init)
|
||||
return;
|
||||
|
||||
std::lock_guard<std::mutex> 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<std::mutex> 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<ciface::Core::Device> device
|
|||
m_devices.emplace_back(std::move(device));
|
||||
}
|
||||
|
||||
void ControllerInterface::RemoveDevice(std::function<bool(const ciface::Core::Device*)> callback)
|
||||
{
|
||||
std::lock_guard<std::mutex> 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,10 +178,36 @@ void ControllerInterface::AddDevice(std::shared_ptr<ciface::Core::Device> device
|
|||
//
|
||||
void ControllerInterface::UpdateInput()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_devices_mutex);
|
||||
// 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<std::mutex> 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<void()> 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();
|
||||
}
|
||||
|
||||
//
|
||||
// InputReference :: State
|
||||
|
|
|
@ -122,12 +122,17 @@ public:
|
|||
void Reinitialize();
|
||||
void Shutdown();
|
||||
void AddDevice(std::shared_ptr<ciface::Core::Device> device);
|
||||
void RemoveDevice(std::function<bool(const ciface::Core::Device*)> 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<void(void)> callback);
|
||||
void InvokeHotplugCallbacks() const;
|
||||
|
||||
private:
|
||||
std::vector<std::function<void()>> m_hotplug_callbacks;
|
||||
bool m_is_init;
|
||||
void* m_hwnd;
|
||||
};
|
||||
|
|
|
@ -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<Input*>& Inputs() const { return m_inputs; }
|
||||
const std::vector<Output*>& Outputs() const { return m_outputs; }
|
||||
Input* FindInput(const std::string& name) const;
|
||||
|
|
|
@ -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,
|
||||
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<Device> m_device;
|
||||
};
|
||||
|
||||
class BinaryExpression : public ExpressionNode
|
||||
|
@ -393,6 +396,7 @@ private:
|
|||
{
|
||||
case TOK_CONTROL:
|
||||
{
|
||||
std::shared_ptr<Device> 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> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,10 +37,10 @@ public:
|
|||
: container(container_), default_device(default_), is_input(is_input_)
|
||||
{
|
||||
}
|
||||
std::shared_ptr<Core::Device> FindDevice(ControlQualifier qualifier);
|
||||
Core::Device::Control* FindControl(ControlQualifier qualifier);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Core::Device> FindDevice(ControlQualifier qualifier);
|
||||
const Core::DeviceContainer& container;
|
||||
const Core::DeviceQualifier& default_device;
|
||||
bool is_input;
|
||||
|
|
|
@ -5,12 +5,17 @@
|
|||
#include <fcntl.h>
|
||||
#include <libudev.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
#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<std::string, std::string> 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<evdevDevice>(devnode);
|
||||
if (device->IsInteresting())
|
||||
{
|
||||
g_controller_interface.AddDevice(std::move(device));
|
||||
s_devnode_name_map.insert(std::pair<std::string, std::string>(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<std::string, std::string>(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
|
||||
|
|
|
@ -8,11 +8,14 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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();
|
||||
|
|
Loading…
Reference in New Issue