Modernize management of Udev pointers

This commit is contained in:
Chris Thrasher 2024-07-04 15:19:36 -06:00
parent f911d01e01
commit ee036b064c

View File

@ -32,6 +32,7 @@
#include <fcntl.h>
#include <libudev.h>
#include <linux/joystick.h>
#include <memory>
#include <ostream>
#include <poll.h>
#include <string>
@ -43,8 +44,34 @@
namespace
{
udev* udevContext = nullptr;
udev_monitor* udevMonitor = nullptr;
struct UdevDeleter
{
void operator()(udev_device* device) const
{
udev_device_unref(device);
}
void operator()(udev_monitor* monitor) const
{
udev_monitor_unref(monitor);
}
void operator()(udev_enumerate* enumerate) const
{
udev_enumerate_unref(enumerate);
}
void operator()(udev* context) const
{
udev_unref(context);
}
};
template <typename T>
using UdevPtr = std::unique_ptr<T, UdevDeleter>;
UdevPtr<udev> udevContext;
UdevPtr<udev_monitor> udevMonitor;
struct JoystickRecord
{
@ -181,7 +208,7 @@ void updatePluggedList(udev_device* udevDevice = nullptr)
for (JoystickRecord& record : joystickList)
record.plugged = false;
udev_enumerate* udevEnumerator = udev_enumerate_new(udevContext);
const auto udevEnumerator = UdevPtr<udev_enumerate>(udev_enumerate_new(udevContext.get()));
if (!udevEnumerator)
{
@ -189,36 +216,30 @@ void updatePluggedList(udev_device* udevDevice = nullptr)
return;
}
int result = 0;
result = udev_enumerate_add_match_subsystem(udevEnumerator, "input");
if (result < 0)
if (udev_enumerate_add_match_subsystem(udevEnumerator.get(), "input") < 0)
{
sf::err() << "Error while adding udev enumerator match" << std::endl;
return;
}
result = udev_enumerate_scan_devices(udevEnumerator);
if (result < 0)
if (udev_enumerate_scan_devices(udevEnumerator.get()) < 0)
{
sf::err() << "Error while enumerating udev devices" << std::endl;
return;
}
udev_list_entry* devices = udev_enumerate_get_list_entry(udevEnumerator);
udev_list_entry* devices = udev_enumerate_get_list_entry(udevEnumerator.get());
udev_list_entry* device = nullptr;
udev_list_entry_foreach(device, devices)
{
const char* syspath = udev_list_entry_get_name(device);
udev_device* newUdevDevice = udev_device_new_from_syspath(udevContext, syspath);
const auto newUdevDevice = UdevPtr<udev_device>(udev_device_new_from_syspath(udevContext.get(), syspath));
if (newUdevDevice && isJoystick(newUdevDevice))
if (newUdevDevice && isJoystick(newUdevDevice.get()))
{
// Since isJoystick returned true, this has to succeed
const char* devnode = udev_device_get_devnode(newUdevDevice);
const char* devnode = udev_device_get_devnode(newUdevDevice.get());
JoystickList::iterator recordIt;
@ -243,17 +264,13 @@ void updatePluggedList(udev_device* udevDevice = nullptr)
joystickList.push_back(newRecord);
}
}
udev_device_unref(newUdevDevice);
}
udev_enumerate_unref(udevEnumerator);
}
bool hasMonitorEvent()
{
// This will not fail since we make sure udevMonitor is valid
const int monitorFd = udev_monitor_get_fd(udevMonitor);
const int monitorFd = udev_monitor_get_fd(udevMonitor.get());
pollfd fds{monitorFd, POLLIN, 0};
@ -316,7 +333,8 @@ unsigned int getJoystickVendorId(unsigned int index)
return 0;
}
udev_device* udevDevice = udev_device_new_from_syspath(udevContext, joystickList[index].systemPath.c_str());
const auto udevDevice = UdevPtr<udev_device>(
udev_device_new_from_syspath(udevContext.get(), joystickList[index].systemPath.c_str()));
if (!udevDevice)
{
@ -324,23 +342,12 @@ unsigned int getJoystickVendorId(unsigned int index)
return 0;
}
unsigned int id = 0;
// First try using udev
id = getUdevAttributeUint(udevDevice, "ID_VENDOR_ID");
if (id)
{
udev_device_unref(udevDevice);
if (const unsigned int id = getUdevAttributeUint(udevDevice.get(), "ID_VENDOR_ID"))
return id;
}
// Fall back to using USB attribute
id = getUsbAttributeUint(udevDevice, "idVendor");
udev_device_unref(udevDevice);
if (id)
if (const unsigned int id = getUsbAttributeUint(udevDevice.get(), "idVendor"))
return id;
sf::err() << "Failed to get vendor ID of joystick " << joystickList[index].deviceNode << std::endl;
@ -357,7 +364,8 @@ unsigned int getJoystickProductId(unsigned int index)
return 0;
}
udev_device* udevDevice = udev_device_new_from_syspath(udevContext, joystickList[index].systemPath.c_str());
const auto udevDevice = UdevPtr<udev_device>(
udev_device_new_from_syspath(udevContext.get(), joystickList[index].systemPath.c_str()));
if (!udevDevice)
{
@ -365,23 +373,12 @@ unsigned int getJoystickProductId(unsigned int index)
return 0;
}
unsigned int id = 0;
// First try using udev
id = getUdevAttributeUint(udevDevice, "ID_MODEL_ID");
if (id)
{
udev_device_unref(udevDevice);
if (const unsigned int id = getUdevAttributeUint(udevDevice.get(), "ID_MODEL_ID"))
return id;
}
// Fall back to using USB attribute
id = getUsbAttributeUint(udevDevice, "idProduct");
udev_device_unref(udevDevice);
if (id)
if (const unsigned int id = getUsbAttributeUint(udevDevice.get(), "idProduct"))
return id;
sf::err() << "Failed to get product ID of joystick " << joystickList[index].deviceNode << std::endl;
@ -412,17 +409,13 @@ std::string getJoystickName(unsigned int index)
// Fall back to manual USB chain walk via udev
if (udevContext)
{
udev_device* udevDevice = udev_device_new_from_syspath(udevContext, joystickList[index].systemPath.c_str());
const auto udevDevice = UdevPtr<udev_device>(
udev_device_new_from_syspath(udevContext.get(), joystickList[index].systemPath.c_str()));
if (udevDevice)
{
const char* product = getUsbAttribute(udevDevice, "product");
udev_device_unref(udevDevice);
if (product)
if (const char* product = getUsbAttribute(udevDevice.get(), "product"))
return {product};
}
}
sf::err() << "Unable to get name for joystick " << devnode << std::endl;
@ -436,7 +429,7 @@ namespace sf::priv
////////////////////////////////////////////////////////////
void JoystickImpl::initialize()
{
udevContext = udev_new();
udevContext = UdevPtr<udev>(udev_new());
if (!udevContext)
{
@ -444,7 +437,7 @@ void JoystickImpl::initialize()
return;
}
udevMonitor = udev_monitor_new_from_netlink(udevContext, "udev");
udevMonitor = UdevPtr<udev_monitor>(udev_monitor_new_from_netlink(udevContext.get(), "udev"));
if (!udevMonitor)
{
@ -452,27 +445,25 @@ void JoystickImpl::initialize()
}
else
{
int error = udev_monitor_filter_add_match_subsystem_devtype(udevMonitor, "input", nullptr);
int error = udev_monitor_filter_add_match_subsystem_devtype(udevMonitor.get(), "input", nullptr);
if (error < 0)
{
err() << "Failed to add udev monitor filter, joystick connections and disconnections won't be notified: "
<< error << std::endl;
udev_monitor_unref(udevMonitor);
udevMonitor = nullptr;
udevMonitor.reset();
}
else
{
error = udev_monitor_enable_receiving(udevMonitor);
error = udev_monitor_enable_receiving(udevMonitor.get());
if (error < 0)
{
err() << "Failed to enable udev monitor, joystick connections and disconnections won't be notified: "
<< error << std::endl;
udev_monitor_unref(udevMonitor);
udevMonitor = nullptr;
udevMonitor.reset();
}
}
}
@ -486,18 +477,10 @@ void JoystickImpl::initialize()
void JoystickImpl::cleanup()
{
// Unreference the udev monitor to destroy it
if (udevMonitor)
{
udev_monitor_unref(udevMonitor);
udevMonitor = nullptr;
}
udevMonitor.reset();
// Unreference the udev context to destroy it
if (udevContext)
{
udev_unref(udevContext);
udevContext = nullptr;
}
udevContext.reset();
}
@ -513,14 +496,11 @@ bool JoystickImpl::isConnected(unsigned int index)
else if (hasMonitorEvent())
{
// Check if new joysticks were added/removed since last update
udev_device* udevDevice = udev_monitor_receive_device(udevMonitor);
const auto udevDevice = UdevPtr<udev_device>(udev_monitor_receive_device(udevMonitor.get()));
// If we can get the specific device, we check that,
// otherwise just do a full scan if udevDevice == nullptr
updatePluggedList(udevDevice);
if (udevDevice)
udev_device_unref(udevDevice);
updatePluggedList(udevDevice.get());
}
if (index >= joystickList.size())