Replaced Unix joystick enumeration with a fully native udev implementation which supports an unlimited number of devices (still limited higher up by sf::Joystick::Count though).
This commit is contained in:
parent
bbfef72fc6
commit
0076ea50db
@ -32,60 +32,42 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sstream>
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
struct udev* udevContext = 0;
|
udev* udevContext = 0;
|
||||||
struct udev_monitor* udevMonitor = 0;
|
udev_monitor* udevMonitor = 0;
|
||||||
bool plugged[sf::Joystick::Count];
|
|
||||||
|
|
||||||
struct udev_device* getUdevDevice(const std::string& node)
|
struct JoystickRecord
|
||||||
{
|
{
|
||||||
struct stat statBuffer;
|
std::string deviceNode;
|
||||||
|
std::string systemPath;
|
||||||
|
bool plugged;
|
||||||
|
};
|
||||||
|
|
||||||
if (stat(node.c_str(), &statBuffer) < 0)
|
typedef std::vector<JoystickRecord> JoystickList;
|
||||||
return NULL;
|
JoystickList joystickList;
|
||||||
|
|
||||||
char deviceType;
|
|
||||||
|
|
||||||
if (S_ISBLK(statBuffer.st_mode))
|
|
||||||
deviceType = 'b';
|
|
||||||
else if (S_ISCHR(statBuffer.st_mode))
|
|
||||||
deviceType = 'c';
|
|
||||||
else
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return udev_device_new_from_devnum(udevContext, deviceType, statBuffer.st_rdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
int getJoystickNumberFromNode(std::string node)
|
|
||||||
{
|
|
||||||
std::string::size_type n = node.find("/js");
|
|
||||||
|
|
||||||
if (n != std::string::npos)
|
|
||||||
node = node.substr(n + 3);
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
std::stringstream sstr(node);
|
|
||||||
|
|
||||||
int i = -1;
|
|
||||||
|
|
||||||
if (!(sstr >> i))
|
|
||||||
i = -1;
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isJoystick(udev_device* udevDevice)
|
bool isJoystick(udev_device* udevDevice)
|
||||||
{
|
{
|
||||||
// If anything goes wrong, we go safe and return true
|
// If anything goes wrong, we go safe and return true
|
||||||
|
|
||||||
// No device to check, assume joystick
|
// No device to check, assume not a joystick
|
||||||
if (!udevDevice)
|
if (!udevDevice)
|
||||||
return true;
|
return false;
|
||||||
|
|
||||||
|
const char* devnode = udev_device_get_devnode(udevDevice);
|
||||||
|
|
||||||
|
// We only consider devices with a device node
|
||||||
|
if (!devnode)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// SFML doesn't support evdev yet, so make sure we only handle /js nodes
|
||||||
|
if (!std::strstr(devnode, "/js"))
|
||||||
|
return false;
|
||||||
|
|
||||||
// Check if this device is a joystick
|
// Check if this device is a joystick
|
||||||
if (udev_device_get_property_value(udevDevice, "ID_INPUT_JOYSTICK"))
|
if (udev_device_get_property_value(udevDevice, "ID_INPUT_JOYSTICK"))
|
||||||
@ -126,7 +108,7 @@ namespace
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, assume it's a joystick
|
// At this point, assume it is a joystick
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,68 +116,136 @@ namespace
|
|||||||
{
|
{
|
||||||
if (udevDevice)
|
if (udevDevice)
|
||||||
{
|
{
|
||||||
const char* node = udev_device_get_devnode(udevDevice);
|
const char* action = udev_device_get_action(udevDevice);
|
||||||
|
|
||||||
if (node)
|
if (action)
|
||||||
{
|
{
|
||||||
int i = getJoystickNumberFromNode(node);
|
if (isJoystick(udevDevice))
|
||||||
|
{
|
||||||
|
// Since isJoystick returned true, this has to succeed
|
||||||
|
const char* devnode = udev_device_get_devnode(udevDevice);
|
||||||
|
|
||||||
if ((i >= 0) && (i < sf::Joystick::Count))
|
JoystickList::iterator record;
|
||||||
{
|
|
||||||
int file = ::open(node, O_RDONLY);
|
|
||||||
|
|
||||||
if (file < 0)
|
for (record = joystickList.begin(); record != joystickList.end(); ++record)
|
||||||
{
|
{
|
||||||
plugged[i] = false;
|
if (record->deviceNode == devnode)
|
||||||
|
{
|
||||||
|
if (std::strstr(action, "add"))
|
||||||
|
{
|
||||||
|
// The system path might have changed so update it
|
||||||
|
const char* syspath = udev_device_get_syspath(udevDevice);
|
||||||
|
|
||||||
|
record->plugged = true;
|
||||||
|
record->systemPath = syspath ? syspath : "";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
else if (std::strstr(action, "remove"))
|
||||||
{
|
{
|
||||||
::close(file);
|
record->plugged = false;
|
||||||
plugged[i] = isJoystick(udevDevice);
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record == joystickList.end())
|
||||||
|
{
|
||||||
|
if (std::strstr(action, "add"))
|
||||||
|
{
|
||||||
|
// If not mapped before and it got added, map it now
|
||||||
|
const char* syspath = udev_device_get_syspath(udevDevice);
|
||||||
|
|
||||||
|
JoystickRecord record;
|
||||||
|
record.deviceNode = devnode;
|
||||||
|
record.systemPath = syspath ? syspath : "";
|
||||||
|
record.plugged = true;
|
||||||
|
|
||||||
|
joystickList.push_back(record);
|
||||||
|
}
|
||||||
|
else if (std::strstr(action, "remove"))
|
||||||
|
{
|
||||||
|
// Not mapped during the initial scan, and removed (shouldn't happen)
|
||||||
|
sf::err() << "Trying to disconnect joystick that wasn't connected" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
// Do a full rescan if there was no action just to be sure
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i < sf::Joystick::Count; ++i)
|
// Reset the plugged status of each mapping since we are doing a full rescan
|
||||||
|
for (JoystickList::iterator record = joystickList.begin(); record != joystickList.end(); ++record)
|
||||||
|
record->plugged = false;
|
||||||
|
|
||||||
|
udev_enumerate* udevEnumerator = udev_enumerate_new(udevContext);
|
||||||
|
|
||||||
|
if (!udevEnumerator)
|
||||||
{
|
{
|
||||||
std::ostringstream name;
|
sf::err() << "Error while creating udev enumerator" << std::endl;
|
||||||
name << "js" << i;
|
return;
|
||||||
std::string nameString = name.str();
|
|
||||||
|
|
||||||
int file = ::open(("/dev/input/" + nameString).c_str(), O_RDONLY);
|
|
||||||
|
|
||||||
if (file < 0)
|
|
||||||
{
|
|
||||||
plugged[i] = false;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::close(file);
|
int result = 0;
|
||||||
|
|
||||||
// Now we check if the device is really a joystick
|
result = udev_enumerate_add_match_subsystem(udevEnumerator, "input");
|
||||||
|
|
||||||
if (!udevContext)
|
if (result < 0)
|
||||||
{
|
{
|
||||||
// Go safe and assume it is if udev isn't available
|
sf::err() << "Error while adding udev enumerator match" << std::endl;
|
||||||
plugged[i] = true;
|
return;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
udev_device* udevDevice = udev_device_new_from_subsystem_sysname(udevContext, "input", nameString.c_str());
|
result = udev_enumerate_scan_devices(udevEnumerator);
|
||||||
|
|
||||||
if (!udevDevice)
|
if (result < 0)
|
||||||
{
|
{
|
||||||
// Go safe and assume it is if we can't get the device
|
sf::err() << "Error while enumerating udev devices" << std::endl;
|
||||||
plugged[i] = true;
|
return;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
plugged[i] = isJoystick(udevDevice);
|
udev_list_entry* devices = udev_enumerate_get_list_entry(udevEnumerator);
|
||||||
|
udev_list_entry* device;
|
||||||
|
|
||||||
|
udev_list_entry_foreach(device, devices) {
|
||||||
|
const char* syspath = udev_list_entry_get_name(device);
|
||||||
|
udev_device* udevDevice = udev_device_new_from_syspath(udevContext, syspath);
|
||||||
|
|
||||||
|
if (udevDevice && isJoystick(udevDevice))
|
||||||
|
{
|
||||||
|
// Since isJoystick returned true, this has to succeed
|
||||||
|
const char* devnode = udev_device_get_devnode(udevDevice);
|
||||||
|
|
||||||
|
JoystickList::iterator record;
|
||||||
|
|
||||||
|
// Check if the device node has been mapped before
|
||||||
|
for (record = joystickList.begin(); record != joystickList.end(); ++record)
|
||||||
|
{
|
||||||
|
if (record->deviceNode == devnode)
|
||||||
|
{
|
||||||
|
record->plugged = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not mapped before, map it now
|
||||||
|
if (record == joystickList.end())
|
||||||
|
{
|
||||||
|
JoystickRecord record;
|
||||||
|
record.deviceNode = devnode;
|
||||||
|
record.systemPath = syspath;
|
||||||
|
record.plugged = true;
|
||||||
|
|
||||||
|
joystickList.push_back(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
udev_device_unref(udevDevice);
|
udev_device_unref(udevDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
udev_enumerate_unref(udevEnumerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasMonitorEvent()
|
bool hasMonitorEvent()
|
||||||
@ -229,17 +279,11 @@ namespace
|
|||||||
return udev_device_get_sysattr_value(udevDeviceParent, attributeName.c_str());
|
return udev_device_get_sysattr_value(udevDeviceParent, attributeName.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a system attribute for a joystick devnode as an unsigned int
|
// Get a USB attribute for a joystick as an unsigned int
|
||||||
unsigned int getUsbAttributeUint(const std::string& node, const std::string& attributeName)
|
unsigned int getUsbAttributeUint(udev_device* udevDevice, const std::string& attributeName)
|
||||||
{
|
{
|
||||||
udev_device* udevDevice = getUdevDevice(node);
|
|
||||||
|
|
||||||
if (!udevDevice)
|
if (!udevDevice)
|
||||||
{
|
|
||||||
sf::err() << "Unable to get joystick attribute. "
|
|
||||||
<< "Could not get udev device for joystick " << node << std::endl;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
const char* attribute = getUsbAttribute(udevDevice, attributeName);
|
const char* attribute = getUsbAttribute(udevDevice, attributeName);
|
||||||
unsigned int value = 0;
|
unsigned int value = 0;
|
||||||
@ -247,21 +291,14 @@ namespace
|
|||||||
if (attribute)
|
if (attribute)
|
||||||
value = static_cast<unsigned int>(std::strtoul(attribute, NULL, 16));
|
value = static_cast<unsigned int>(std::strtoul(attribute, NULL, 16));
|
||||||
|
|
||||||
udev_device_unref(udevDevice);
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a property value for a joystick devnode as an unsigned int
|
// Get a udev property value for a joystick as an unsigned int
|
||||||
unsigned int getUdevAttributeUint(const std::string& node, const std::string& attributeName)
|
unsigned int getUdevAttributeUint(udev_device* udevDevice, const std::string& attributeName)
|
||||||
{
|
{
|
||||||
udev_device* udevDevice = getUdevDevice(node);
|
|
||||||
|
|
||||||
if (!udevDevice)
|
if (!udevDevice)
|
||||||
{
|
|
||||||
sf::err() << "Unable to get joystick attribute. "
|
|
||||||
<< "Could not get udev device for joystick " << node << std::endl;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
const char* attribute = getUdevAttribute(udevDevice, attributeName);
|
const char* attribute = getUdevAttribute(udevDevice, attributeName);
|
||||||
unsigned int value = 0;
|
unsigned int value = 0;
|
||||||
@ -269,59 +306,98 @@ namespace
|
|||||||
if (attribute)
|
if (attribute)
|
||||||
value = static_cast<unsigned int>(std::strtoul(attribute, NULL, 16));
|
value = static_cast<unsigned int>(std::strtoul(attribute, NULL, 16));
|
||||||
|
|
||||||
udev_device_unref(udevDevice);
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get vendor id for a joystick devnode
|
// Get the joystick vendor id
|
||||||
unsigned int getJoystickVendorId(const std::string& node)
|
unsigned int getJoystickVendorId(unsigned int index)
|
||||||
{
|
{
|
||||||
|
if (!udevContext)
|
||||||
|
{
|
||||||
|
sf::err() << "Failed to get vendor ID of joystick " << joystickList[index].deviceNode << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
udev_device* udevDevice = udev_device_new_from_syspath(udevContext, joystickList[index].systemPath.c_str());
|
||||||
|
|
||||||
|
if (!udevDevice)
|
||||||
|
{
|
||||||
|
sf::err() << "Failed to get vendor ID of joystick " << joystickList[index].deviceNode << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int id = 0;
|
unsigned int id = 0;
|
||||||
|
|
||||||
// First try using udev
|
// First try using udev
|
||||||
id = getUdevAttributeUint(node, "ID_VENDOR_ID");
|
id = getUdevAttributeUint(udevDevice, "ID_VENDOR_ID");
|
||||||
|
|
||||||
if (id)
|
if (id)
|
||||||
|
{
|
||||||
|
udev_device_unref(udevDevice);
|
||||||
return id;
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
// Fall back to using USB attribute
|
// Fall back to using USB attribute
|
||||||
id = getUsbAttributeUint(node, "idVendor");
|
id = getUsbAttributeUint(udevDevice, "idVendor");
|
||||||
|
|
||||||
|
udev_device_unref(udevDevice);
|
||||||
|
|
||||||
if (id)
|
if (id)
|
||||||
return id;
|
return id;
|
||||||
|
|
||||||
sf::err() << "Failed to get vendor ID of joystick " << node << std::endl;
|
sf::err() << "Failed to get vendor ID of joystick " << joystickList[index].deviceNode << std::endl;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get product id for a joystick devnode
|
// Get the joystick product id
|
||||||
unsigned int getJoystickProductId(const std::string& node)
|
unsigned int getJoystickProductId(unsigned int index)
|
||||||
{
|
{
|
||||||
|
if (!udevContext)
|
||||||
|
{
|
||||||
|
sf::err() << "Failed to get product ID of joystick " << joystickList[index].deviceNode << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
udev_device* udevDevice = udev_device_new_from_syspath(udevContext, joystickList[index].systemPath.c_str());
|
||||||
|
|
||||||
|
if (!udevDevice)
|
||||||
|
{
|
||||||
|
sf::err() << "Failed to get product ID of joystick " << joystickList[index].deviceNode << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int id = 0;
|
unsigned int id = 0;
|
||||||
|
|
||||||
// First try using udev
|
// First try using udev
|
||||||
id = getUdevAttributeUint(node, "ID_MODEL_ID");
|
id = getUdevAttributeUint(udevDevice, "ID_MODEL_ID");
|
||||||
|
|
||||||
if (id)
|
if (id)
|
||||||
|
{
|
||||||
|
udev_device_unref(udevDevice);
|
||||||
return id;
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
// Fall back to using USB attribute
|
// Fall back to using USB attribute
|
||||||
id = getUsbAttributeUint(node, "idProduct");
|
id = getUsbAttributeUint(udevDevice, "idProduct");
|
||||||
|
|
||||||
|
udev_device_unref(udevDevice);
|
||||||
|
|
||||||
if (id)
|
if (id)
|
||||||
return id;
|
return id;
|
||||||
|
|
||||||
sf::err() << "Failed to get product ID of joystick " << node << std::endl;
|
sf::err() << "Failed to get product ID of joystick " << joystickList[index].deviceNode << std::endl;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the joystick name
|
// Get the joystick name
|
||||||
std::string getJoystickName(const std::string& node)
|
std::string getJoystickName(unsigned int index)
|
||||||
{
|
{
|
||||||
|
std::string devnode = joystickList[index].deviceNode;
|
||||||
|
|
||||||
// First try using ioctl with JSIOCGNAME
|
// First try using ioctl with JSIOCGNAME
|
||||||
int fd = ::open(node.c_str(), O_RDONLY | O_NONBLOCK);
|
int fd = ::open(devnode.c_str(), O_RDONLY | O_NONBLOCK);
|
||||||
|
|
||||||
if (fd >= 0)
|
if (fd >= 0)
|
||||||
{
|
{
|
||||||
@ -340,7 +416,7 @@ namespace
|
|||||||
// Fall back to manual USB chain walk via udev
|
// Fall back to manual USB chain walk via udev
|
||||||
if (udevContext)
|
if (udevContext)
|
||||||
{
|
{
|
||||||
udev_device* udevDevice = getUdevDevice(node);
|
udev_device* udevDevice = udev_device_new_from_syspath(udevContext, joystickList[index].systemPath.c_str());
|
||||||
|
|
||||||
if (udevDevice)
|
if (udevDevice)
|
||||||
{
|
{
|
||||||
@ -352,7 +428,7 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sf::err() << "Unable to get name for joystick " << node << std::endl;
|
sf::err() << "Unable to get name for joystick " << devnode << std::endl;
|
||||||
|
|
||||||
return std::string("Unknown Joystick");
|
return std::string("Unknown Joystick");
|
||||||
}
|
}
|
||||||
@ -366,14 +442,14 @@ namespace priv
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void JoystickImpl::initialize()
|
void JoystickImpl::initialize()
|
||||||
{
|
{
|
||||||
// Reset the array of plugged joysticks
|
udevContext = udev_new();
|
||||||
std::fill(plugged, plugged + Joystick::Count, false);
|
|
||||||
|
|
||||||
udev* udevContext = udev_new();
|
|
||||||
|
|
||||||
if (!udevContext)
|
if (!udevContext)
|
||||||
sf::err() << "Failed to create udev context, getting joystick attributes not available" << std::endl;
|
{
|
||||||
else
|
sf::err() << "Failed to create udev context, joystick support not available" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
udevMonitor = udev_monitor_new_from_netlink(udevContext, "udev");
|
udevMonitor = udev_monitor_new_from_netlink(udevContext, "udev");
|
||||||
|
|
||||||
if (!udevMonitor)
|
if (!udevMonitor)
|
||||||
@ -441,7 +517,7 @@ bool JoystickImpl::isConnected(unsigned int index)
|
|||||||
else if (hasMonitorEvent())
|
else if (hasMonitorEvent())
|
||||||
{
|
{
|
||||||
// Check if new joysticks were added/removed since last update
|
// Check if new joysticks were added/removed since last update
|
||||||
struct udev_device* udevDevice = udev_monitor_receive_device(udevMonitor);
|
udev_device* udevDevice = udev_monitor_receive_device(udevMonitor);
|
||||||
|
|
||||||
// If we can get the specific device, we check that,
|
// If we can get the specific device, we check that,
|
||||||
// otherwise just do a full scan if udevDevice == NULL
|
// otherwise just do a full scan if udevDevice == NULL
|
||||||
@ -451,32 +527,37 @@ bool JoystickImpl::isConnected(unsigned int index)
|
|||||||
udev_device_unref(udevDevice);
|
udev_device_unref(udevDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (index >= joystickList.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
// Then check if the joystick is connected
|
// Then check if the joystick is connected
|
||||||
return plugged[index];
|
return joystickList[index].plugged;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
bool JoystickImpl::open(unsigned int index)
|
bool JoystickImpl::open(unsigned int index)
|
||||||
{
|
{
|
||||||
if (plugged[index])
|
if (index >= joystickList.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (joystickList[index].plugged)
|
||||||
{
|
{
|
||||||
std::ostringstream name;
|
std::string devnode = joystickList[index].deviceNode;
|
||||||
name << "/dev/input/js" << index;
|
|
||||||
|
|
||||||
// Open the joystick's file descriptor (read-only and non-blocking)
|
// Open the joystick's file descriptor (read-only and non-blocking)
|
||||||
m_file = ::open(name.str().c_str(), O_RDONLY | O_NONBLOCK);
|
m_file = ::open(devnode.c_str(), O_RDONLY | O_NONBLOCK);
|
||||||
if (m_file >= 0)
|
if (m_file >= 0)
|
||||||
{
|
{
|
||||||
// Retrieve the axes mapping
|
// Retrieve the axes mapping
|
||||||
ioctl(m_file, JSIOCGAXMAP, m_mapping);
|
ioctl(m_file, JSIOCGAXMAP, m_mapping);
|
||||||
|
|
||||||
// Get info
|
// Get info
|
||||||
m_identification.name = getJoystickName(name.str());
|
m_identification.name = getJoystickName(index);
|
||||||
|
|
||||||
if (udevContext)
|
if (udevContext)
|
||||||
{
|
{
|
||||||
m_identification.vendorId = getJoystickVendorId(name.str());
|
m_identification.vendorId = getJoystickVendorId(index);
|
||||||
m_identification.productId = getJoystickProductId(name.str());
|
m_identification.productId = getJoystickProductId(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the joystick state
|
// Reset the joystick state
|
||||||
@ -486,7 +567,7 @@ bool JoystickImpl::open(unsigned int index)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
err() << "Failed to open joystick " << name.str() << ": " << errno << std::endl;
|
err() << "Failed to open joystick " << devnode << ": " << errno << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user