Fixed memory leak in Unix JoystickImpl, refactored joystick code for all platforms, prevent accelerometers registering as joysticks on Unix.

This commit is contained in:
binary1248 2014-07-15 10:46:30 +02:00
parent e157e7a7a8
commit 86c81f7458
10 changed files with 416 additions and 507 deletions

View File

@ -28,16 +28,10 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#include <SFML/Window/JoystickImpl.hpp> #include <SFML/Window/JoystickImpl.hpp>
#include <SFML/System/Err.hpp> #include <SFML/System/Err.hpp>
#include <sys/stat.h> #include <sys/stat.h>
#include <dirent.h> #include <dirent.h>
#include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <cerrno>
#include <cstdio>
#include <cstring> #include <cstring>
#include <map> #include <map>
#include <string> #include <string>
@ -57,123 +51,107 @@
namespace namespace
{ {
std::map<unsigned int, std::string> plugged; std::map<unsigned int, std::string> plugged;
std::map<int, std::pair<int, int> > hatmap; std::map<int, std::pair<int, int> > hatValueMap;
bool isJoystick(const char *name) bool isJoystick(const char *name)
{ {
int fd, id; int file = ::open(name, O_RDONLY | O_NONBLOCK);
bool ret;
report_desc_t desc = NULL; if (file < 0)
hid_data_t data = NULL; return false;
report_desc_t desc = hid_get_report_desc(file);
if (!desc)
{
::close(file);
return false;
}
int id = hid_get_report_id(file);
hid_data_t data = hid_start_parse(desc, 1 << hid_input, id);
if (!data)
{
hid_dispose_report_desc(desc);
::close(file);
return false;
}
hid_item_t item; hid_item_t item;
// Assume it isn't // Assume it isn't
ret = false; bool result = false;
if ((fd = ::open(name, O_RDONLY | O_NONBLOCK)) < 0) while (hid_get_item(data, &item) > 0)
return false; {
if ((item.kind == hid_collection) && (HID_PAGE(item.usage) == HUP_GENERIC_DESKTOP))
if ((desc = hid_get_report_desc(fd)) == NULL) {
goto end; if ((HID_USAGE(item.usage) == HUG_JOYSTICK) || (HID_USAGE(item.usage) == HUG_GAME_PAD))
{
id = hid_get_report_id(fd); result = true;
if ((data = hid_start_parse(desc, 1 << hid_input, id)) == NULL)
goto end;
while (hid_get_item(data, &item) > 0) {
switch (item.kind) {
case hid_collection:
switch (HID_PAGE(item.usage)) {
case HUP_GENERIC_DESKTOP:
switch (HID_USAGE(item.usage)) {
case HUG_JOYSTICK:
case HUG_GAME_PAD:
ret = true;
break;
default:
break;
} }
default:
break;
}
default:
break;
} }
} }
end:
if (desc != NULL)
hid_dispose_report_desc(desc);
if (data != NULL)
hid_end_parse(data); hid_end_parse(data);
close(fd); hid_dispose_report_desc(desc);
::close(file);
return ret; return result;
} }
void updatePluggedList() void updatePluggedList()
{ {
DIR *dp;
struct dirent *entry;
/* /*
* Devices /dev/uhid<x> are shared between joystick and any other * Devices /dev/uhid<x> are shared between joystick and any other
* human interface device. We need to iterate over all found devices * human interface device. We need to iterate over all found devices
* and check if they are joysticks. The index of JoystickImpl::open * and check if they are joysticks. The index of JoystickImpl::open
* does not match the /dev/uhid<index> device! * does not match the /dev/uhid<index> device!
*/ */
if ((dp = opendir("/dev")) != NULL) { DIR* directory = opendir("/dev");
char name[FILENAME_MAX];
int jc = 0;
while ((entry = readdir(dp)) != NULL && jc < sf::Joystick::Count) { if (directory)
if (strncmp(entry->d_name, "uhid", 4) == 0) { {
std::sprintf(name, "/dev/%s", entry->d_name); int joystickCount = 0;
struct dirent* directoryEntry = readdir(directory);
if (isJoystick(name)) while (directoryEntry && joystickCount < sf::Joystick::Count)
plugged[jc++] = std::string(name); {
} if (!std::strncmp(directoryEntry->d_name, "uhid", 4))
{
std::string name("/dev/");
name += directoryEntry->d_name;
if (isJoystick(name.c_str()))
plugged[joystickCount++] = name;
} }
closedir(dp); directoryEntry = readdir(directory);
}
closedir(directory);
} }
} }
int usageToAxis(int usage) int usageToAxis(int usage)
{ {
int axis; switch (usage)
switch (usage) {
case HUG_X:
axis = sf::Joystick::X;
break;
case HUG_Y:
axis = sf::Joystick::Y;
break;
case HUG_Z:
axis = sf::Joystick::Z;
break;
case HUG_RZ:
axis = sf::Joystick::R;
break;
case HUG_RX:
axis = sf::Joystick::U;
break;
case HUG_RY:
axis = sf::Joystick::V;
break;
default:
axis = -1;
break;
}
return axis;
}
void hatvalToSFML(int value, sf::priv::JoystickState &state)
{ {
state.axes[sf::Joystick::PovX] = hatmap[value].first; case HUG_X: return sf::Joystick::X;
state.axes[sf::Joystick::PovY] = hatmap[value].second; case HUG_Y: return sf::Joystick::Y;
case HUG_Z: return sf::Joystick::Z;
case HUG_RZ: return sf::Joystick::R;
case HUG_RX: return sf::Joystick::U;
case HUG_RY: return sf::Joystick::V;
default: return -1;
}
}
void hatValueToSfml(int value, sf::priv::JoystickState& state)
{
state.axes[sf::Joystick::PovX] = hatValueMap[value].first;
state.axes[sf::Joystick::PovY] = hatValueMap[value].second;
} }
} }
@ -191,17 +169,17 @@ void JoystickImpl::initialize()
updatePluggedList(); updatePluggedList();
// Map of hat values // Map of hat values
hatmap[0] = std::make_pair<int, int>(0, 0); // center hatValueMap[0] = std::make_pair<int, int>(0, 0); // center
hatmap[1] = std::make_pair<int, int>(0, -100); // top hatValueMap[1] = std::make_pair<int, int>(0, -100); // top
hatmap[3] = std::make_pair<int, int>(100, 0); // right hatValueMap[3] = std::make_pair<int, int>(100, 0); // right
hatmap[5] = std::make_pair<int, int>(0, 100); // bottom hatValueMap[5] = std::make_pair<int, int>(0, 100); // bottom
hatmap[7] = std::make_pair<int, int>(-100, 0); // left hatValueMap[7] = std::make_pair<int, int>(-100, 0); // left
hatmap[2] = std::make_pair<int, int>(100, -100); // top-right hatValueMap[2] = std::make_pair<int, int>(100, -100); // top-right
hatmap[4] = std::make_pair<int, int>(100, 100); // bottom-right hatValueMap[4] = std::make_pair<int, int>(100, 100); // bottom-right
hatmap[6] = std::make_pair<int, int>(-100, 100); // bottom-left hatValueMap[6] = std::make_pair<int, int>(-100, 100); // bottom-left
hatmap[8] = std::make_pair<int, int>(-100, -100); // top-left hatValueMap[8] = std::make_pair<int, int>(-100, -100); // top-left
} }
@ -232,7 +210,8 @@ bool JoystickImpl::open(unsigned int index)
// Get the report descriptor // Get the report descriptor
m_desc = hid_get_report_desc(m_file); m_desc = hid_get_report_desc(m_file);
if (m_desc == NULL) { if (!m_desc)
{
::close(m_file); ::close(m_file);
return false; return false;
} }
@ -242,26 +221,15 @@ bool JoystickImpl::open(unsigned int index)
// Then allocate a buffer for data retrievement // Then allocate a buffer for data retrievement
m_length = hid_report_size(m_desc, hid_input, m_id); m_length = hid_report_size(m_desc, hid_input, m_id);
m_buffer = std::calloc(1, m_length); m_buffer.resize(m_length);
if (m_buffer == NULL) { m_state.connected = true;
::close(m_file);
::hid_dispose_report_desc(m_desc); return true;
}
}
return false; return false;
}
return m_state.connected = true;
}
else
{
return false;
}
}
else
{
return false;
}
} }
@ -269,8 +237,7 @@ bool JoystickImpl::open(unsigned int index)
void JoystickImpl::close() void JoystickImpl::close()
{ {
::close(m_file); ::close(m_file);
::hid_dispose_report_desc(m_desc); hid_dispose_report_desc(m_desc);
::free(m_buffer);
} }
@ -278,38 +245,35 @@ void JoystickImpl::close()
JoystickCaps JoystickImpl::getCapabilities() const JoystickCaps JoystickImpl::getCapabilities() const
{ {
JoystickCaps caps; JoystickCaps caps;
hid_data_t data;
hid_item_t item; hid_item_t item;
data = hid_start_parse(m_desc, 1 << hid_input, m_id); hid_data_t data = hid_start_parse(m_desc, 1 << hid_input, m_id);
while (hid_get_item(data, &item)) { while (hid_get_item(data, &item))
switch (item.kind) { {
case hid_input: if (item.kind == hid_input)
switch (HID_PAGE(item.usage)) {
case HUP_BUTTON:
caps.buttonCount ++;
break;
case HUP_GENERIC_DESKTOP:
{ {
int usage = HID_USAGE(item.usage); int usage = HID_USAGE(item.usage);
int axis;
if (usage == HUG_HAT_SWITCH) { if (usage == HUP_BUTTON)
{
caps.buttonCount++;
break;
}
else if (usage == HUP_GENERIC_DESKTOP)
{
int axis = usageToAxis(usage);
if (usage == HUG_HAT_SWITCH)
{
caps.axes[Joystick::PovX] = true; caps.axes[Joystick::PovX] = true;
caps.axes[Joystick::PovY] = true; caps.axes[Joystick::PovY] = true;
} }
else if ((axis = usageToAxis(usage)) != -1) else if (axis != -1)
{ {
caps.axes[axis] = true; caps.axes[axis] = true;
} }
break;
} }
default:
break;
}
default:
break;
} }
} }
@ -329,48 +293,45 @@ Joystick::Identification JoystickImpl::getIdentification() const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
JoystickState JoystickImpl::JoystickImpl::update() JoystickState JoystickImpl::JoystickImpl::update()
{ {
while (read(m_file, m_buffer, m_length) == m_length) { while (read(m_file, &m_buffer[0], m_length) == m_length)
hid_data_t data; {
hid_item_t item; hid_data_t data = hid_start_parse(m_desc, 1 << hid_input, m_id);
data = hid_start_parse(m_desc, 1 << hid_input, m_id);
// No memory? // No memory?
if (data == NULL) if (!data)
continue; continue;
int but = 0; int buttonIndex = 0;
while (hid_get_item(data, &item)) { hid_item_t item;
switch (item.kind) {
case hid_input: while (hid_get_item(data, &item))
switch (HID_PAGE(item.usage)) { {
case HUP_BUTTON: if (item.kind == hid_input)
m_state.buttons[but++] = hid_get_data(m_buffer, &item);
break;
case HUP_GENERIC_DESKTOP:
{ {
int usage = HID_USAGE(item.usage); int usage = HID_USAGE(item.usage);
int v = hid_get_data(m_buffer, &item);
int axis; if (usage == HUP_BUTTON)
{
m_state.buttons[buttonIndex++] = hid_get_data(&m_buffer[0], &item);
}
else if (usage == HUP_GENERIC_DESKTOP)
{
int value = hid_get_data(&m_buffer[0], &item);
int axis = usageToAxis(usage);
if (usage == HUG_HAT_SWITCH) if (usage == HUG_HAT_SWITCH)
hatvalToSFML(v, m_state);
else if ((axis = usageToAxis(usage)) != -1)
{ {
int &min = item.logical_minimum; hatValueToSfml(value, m_state);
int &max = item.logical_maximum; }
else if (axis != -1)
{
int minimum = item.logical_minimum;
int maximum = item.logical_maximum;
v = (v - min) * (200) / (max - min) -100; value = (value - minimum) * 200 / (maximum - minimum) - 100;
m_state.axes[axis] = v; m_state.axes[axis] = value;
} }
break;
} }
default:
break;
}
default:
break;
} }
} }
@ -383,5 +344,3 @@ JoystickState JoystickImpl::JoystickImpl::update()
} // namespace priv } // namespace priv
} // namespace sf } // namespace sf
// vim: set expandtab tabstop=4 softtabstop=4 shiftwidth=4:

View File

@ -28,9 +28,9 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Headers // Headers
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#include <dev/usb/usbhid.h> #include <dev/usb/usbhid.h>
#include <usbhid.h> #include <usbhid.h>
#include <vector>
namespace sf namespace sf
{ {
@ -113,14 +113,11 @@ private :
// Member data // Member data
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
int m_file; ///< File descriptor of the joystick int m_file; ///< File descriptor of the joystick
report_desc_t m_desc; ///< USB report descriptor report_desc_t m_desc; ///< USB report descriptor
int m_id; ///< USB id int m_id; ///< USB id
std::vector<char> m_buffer; ///< USB HID buffer
void *m_buffer; ///< USB HID buffer
int m_length; ///< Buffer length int m_length; ///< Buffer length
Joystick::Identification m_identification; ///< Joystick identification Joystick::Identification m_identification; ///< Joystick identification
JoystickState m_state; ///< Current state of the joystick JoystickState m_state; ///< Current state of the joystick
}; };

View File

@ -38,6 +38,46 @@ namespace
{ {
return IOHIDElementGetUsage(b1) < IOHIDElementGetUsage(b2); return IOHIDElementGetUsage(b1) < IOHIDElementGetUsage(b2);
} }
// Convert a CFStringRef to std::string
std::string stringFromCFString(CFStringRef cfString)
{
CFIndex length = CFStringGetLength(cfString);
std::vector<char> str(length);
CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
CFStringGetCString(cfString, &str[0], maxSize, kCFStringEncodingUTF8);
return &str[0];
}
// Get HID device property key as a string
std::string getDeviceString(IOHIDDeviceRef ref, CFStringRef prop, unsigned int index)
{
CFTypeRef typeRef = IOHIDDeviceGetProperty(ref, prop);
if (ref && (CFGetTypeID(typeRef) == CFStringGetTypeID()))
{
CFStringRef str = static_cast<CFStringRef>(typeRef);
return stringFromCFString(str);
}
sf::err() << "Unable to read string value for property '" << stringFromCFString(prop) << "' for joystick at index " << index << std::endl;
return "Unknown Joystick";
}
// Get HID device property key as an unsigned int
unsigned int getDeviceUint(IOHIDDeviceRef ref, CFStringRef prop, unsigned int index)
{
CFTypeRef typeRef = IOHIDDeviceGetProperty(ref, prop);
if (ref && (CFGetTypeID(typeRef) == CFNumberGetTypeID()))
{
SInt32 value;
CFNumberGetValue((CFNumberRef)typeRef, kCFNumberSInt32Type, &value);
return value;
}
sf::err() << "Unable to read uint value for property '" << stringFromCFString(prop) << "' for joystick at index " << index << std::endl;
return 0;
}
} }
@ -176,9 +216,9 @@ bool JoystickImpl::open(unsigned int index)
return false; return false;
} }
m_identification.name = getDeviceString(self, CFSTR(kIOHIDProductKey)); m_identification.name = getDeviceString(self, CFSTR(kIOHIDProductKey), m_index);
m_identification.vendorId = getDeviceUint(self, CFSTR(kIOHIDVendorIDKey)); m_identification.vendorId = getDeviceUint(self, CFSTR(kIOHIDVendorIDKey), m_index);
m_identification.productId = getDeviceUint(self, CFSTR(kIOHIDProductIDKey)); m_identification.productId = getDeviceUint(self, CFSTR(kIOHIDProductIDKey), m_index);
// Get a list of all elements attached to the device. // Get a list of all elements attached to the device.
CFArrayRef elements = IOHIDDeviceCopyMatchingElements(self, NULL, kIOHIDOptionsTypeNone); CFArrayRef elements = IOHIDDeviceCopyMatchingElements(self, NULL, kIOHIDOptionsTypeNone);
@ -389,48 +429,6 @@ JoystickState JoystickImpl::update()
return state; return state;
} }
////////////////////////////////////////////////////////////
std::string JoystickImpl::getDeviceString(IOHIDDeviceRef ref, CFStringRef prop)
{
CFTypeRef typeRef = IOHIDDeviceGetProperty(ref, prop);
if (ref && (CFGetTypeID(typeRef) == CFStringGetTypeID()))
{
CFStringRef str = static_cast<CFStringRef>(typeRef);
return stringFromCFString(str);
}
sf::err() << "Unable to read string value for property '" << stringFromCFString(prop) << "' for joystick at index " << m_index << std::endl;
return "Unknown Joystick";
}
////////////////////////////////////////////////////////////
unsigned int JoystickImpl::getDeviceUint(IOHIDDeviceRef ref, CFStringRef prop)
{
CFTypeRef typeRef = IOHIDDeviceGetProperty(ref, prop);
if (ref && (CFGetTypeID(typeRef) == CFNumberGetTypeID()))
{
SInt32 value;
CFNumberGetValue((CFNumberRef)typeRef, kCFNumberSInt32Type, &value);
return value;
}
sf::err() << "Unable to read uint value for property '" << stringFromCFString(prop) << "' for joystick at index " << m_index << std::endl;
return 0;
}
////////////////////////////////////////////////////////////
std::string JoystickImpl::stringFromCFString(CFStringRef cfString)
{
CFIndex length = CFStringGetLength(cfString);
std::vector<char> str(length);
CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
CFStringGetCString(cfString, &str[0], maxSize, kCFStringEncodingUTF8);
return &str[0];
}
} // namespace priv } // namespace priv
} // namespace sf } // namespace sf

View File

@ -113,38 +113,6 @@ public :
private : private :
////////////////////////////////////////////////////////////
/// Get HID device property key as a string
///
/// \param ref HID device
/// \param prop Property to retrieve from the device
///
/// \return Value of the property
///
////////////////////////////////////////////////////////////
std::string getDeviceString(IOHIDDeviceRef ref, CFStringRef prop);
////////////////////////////////////////////////////////////
/// Get HID device property key as an unsigned int
///
/// \param ref HID device
/// \param prop Property to retrieve from the device
///
/// \return Value of the property
///
////////////////////////////////////////////////////////////
unsigned int getDeviceUint(IOHIDDeviceRef ref, CFStringRef prop);
////////////////////////////////////////////////////////////
/// Convert a CFStringRef to std::string
///
/// \param cfString CFStringRef to convert
///
/// \return std::string
///
////////////////////////////////////////////////////////////
std::string stringFromCFString(CFStringRef cfString);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Member data // Member data
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -30,10 +30,11 @@
#include <sys/inotify.h> #include <sys/inotify.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h>
#include <errno.h> #include <errno.h>
#include <cstdio>
#include <libudev.h> #include <libudev.h>
#include <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <sstream> #include <sstream>
namespace namespace
@ -44,39 +45,131 @@ namespace
void updatePluggedList() void updatePluggedList()
{ {
udev* udevContext = udev_new();
for (unsigned int i = 0; i < sf::Joystick::Count; ++i) for (unsigned int i = 0; i < sf::Joystick::Count; ++i)
{ {
char name[32]; std::ostringstream name("js");
std::snprintf(name, sizeof(name), "/dev/input/js%u", i); name << i;
std::string nameString = name.str();
int file = ::open(name, O_RDONLY); int file = ::open(("/dev/input/" + nameString).c_str(), O_RDONLY);
if (file >= 0)
if (file < 0)
{ {
plugged[i] = true; plugged[i] = false;
continue;
}
::close(file); ::close(file);
// Check if the device is really a joystick or an
// accelerometer by inspecting whether
// ID_INPUT_ACCELEROMETER is present
if (!udevContext)
{
// Go safe and assume it is if udev isn't available
plugged[i] = true;
continue;
}
udev_device* udevDevice = udev_device_new_from_subsystem_sysname(udevContext, "input", nameString.c_str());
if (!udevDevice)
{
// Go safe and assume it is if we can't get the device
plugged[i] = true;
continue;
}
if (udev_device_get_property_value(udevDevice, "ID_INPUT_ACCELEROMETER"))
{
// This device is an accelerometer
plugged[i] = false;
} }
else else
{ {
plugged[i] = false; // This device is not an accelerometer
// Assume it's a joystick
plugged[i] = true;
} }
udev_device_unref(udevDevice);
} }
if (udevContext)
udev_unref(udevContext);
} }
bool canRead(int descriptor) bool canRead(int descriptor)
{ {
if (descriptor >= 0) if (descriptor >= 0)
{ {
fd_set set; fd_set descriptorSet;
FD_ZERO(&set); FD_ZERO(&descriptorSet);
FD_SET(descriptor, &set); FD_SET(descriptor, &descriptorSet);
timeval timeout = {0, 0}; timeval timeout = {0, 0};
return select(descriptor + 1, &set, NULL, NULL, &timeout) > 0 &&
FD_ISSET(notifyFd, &set); return (select(descriptor + 1, &descriptorSet, NULL, NULL, &timeout) > 0) &&
FD_ISSET(notifyFd, &descriptorSet);
}
return false;
}
// Get the joystick name
std::string getJoystickName(int file, unsigned int index)
{
// Get the name
char joyname[128];
if (ioctl(file, JSIOCGNAME(sizeof(joyname)), joyname) >= 0)
return std::string(joyname);
sf::err() << "Unable to get name for joystick at index " << index << std::endl;
return std::string("Unknown Joystick");
}
// Get a system attribute from udev as an unsigned int
unsigned int getUdevAttributeUint(unsigned int index, std::string attributeName)
{
unsigned int attribute = 0;
udev* udevContext = udev_new();
if (!udevContext)
{
sf::err() << "Unable to get attribute '" << attributeName << "'. Cannot create udev for reading info for joystick at index " << index << std::endl;
return attribute;
}
std::ostringstream sysname("js");
sysname << index;
udev_device* udevDevice = udev_device_new_from_subsystem_sysname(udevContext, "input", sysname.str().c_str());
if (!udevDevice)
{
sf::err() << "Unable to get attribute '" << attributeName << "'. Could not find USB device for joystick at index " << index << std::endl;
udev_unref(udevContext);
return attribute;
}
udev_device* udevDeviceParent = udev_device_get_parent_with_subsystem_devtype(udevDevice, "usb", "usb_device");
if (!udevDeviceParent)
{
sf::err() << "Unable to get attribute '" << attributeName << "'. Could not find parent USB device for joystick at index " << index << std::endl;
} }
else else
{ {
return false; attribute = static_cast<unsigned int>(strtoul(udev_device_get_sysattr_value(udevDeviceParent, attributeName.c_str()), NULL, 16));
} }
udev_device_unref(udevDevice);
udev_unref(udevContext);
return attribute;
} }
} }
@ -151,18 +244,18 @@ bool JoystickImpl::open(unsigned int index)
{ {
if (plugged[index]) if (plugged[index])
{ {
char name[32]; std::ostringstream name("/dev/input/js");
std::snprintf(name, sizeof(name), "/dev/input/js%u", index); name << 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, O_RDONLY | O_NONBLOCK); m_file = ::open(name.str().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(index); m_identification.name = getJoystickName(m_file, index);
m_identification.vendorId = getUdevAttributeUint(index, "idVendor"); m_identification.vendorId = getUdevAttributeUint(index, "idVendor");
m_identification.productId = getUdevAttributeUint(index, "idProduct"); m_identification.productId = getUdevAttributeUint(index, "idProduct");
@ -171,15 +264,9 @@ bool JoystickImpl::open(unsigned int index)
return true; return true;
} }
else }
{
return false; return false;
}
}
else
{
return false;
}
} }
@ -280,57 +367,6 @@ JoystickState JoystickImpl::JoystickImpl::update()
return m_state; return m_state;
} }
////////////////////////////////////////////////////////////
std::string JoystickImpl::getJoystickName(unsigned int index)
{
// Get the name
char joyname[128];
if (ioctl(m_file, JSIOCGNAME(sizeof(joyname)), joyname) >= 0)
{
return std::string(joyname);
}
err() << "Unable to get name for joystick at index " << index << std::endl;
return std::string("Unknown Joystick");
}
////////////////////////////////////////////////////////////
unsigned int JoystickImpl::getUdevAttributeUint(unsigned int index, std::string attributeName)
{
unsigned int attr = 0;
udev* udevContext = udev_new();
if (udevContext)
{
char sysname[32];
std::snprintf(sysname, sizeof(sysname), "js%u", index);
udev_device* dev = udev_device_new_from_subsystem_sysname(udevContext, "input", sysname);
dev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device");
if (dev)
{
std::stringstream ss;
ss << std::hex << udev_device_get_sysattr_value(dev, attributeName.c_str());
ss >> attr;
}
else
{
err() << "Unable to get attribute '" << attributeName << "'. Could not find parent USB device for joystick at index " << index << std::endl;
}
udev_device_unref(dev);
udev_unref(udevContext);
}
else
{
err() << "Unable to get attribute '" << attributeName << "'. Cannot create udev for reading info for joystick at index " << index << std::endl;
}
return attr;
}
} // namespace priv } // namespace priv
} // namespace sf } // namespace sf

View File

@ -109,27 +109,6 @@ public :
private : private :
////////////////////////////////////////////////////////////
/// Get the joystick name
///
/// \param index Index of the joystick
///
/// \return Joystick name
///
////////////////////////////////////////////////////////////
std::string getJoystickName(unsigned int index);
////////////////////////////////////////////////////////////
/// Get a system attribute from udev as an unsigned int
///
/// \param index Index of the joystick
/// \param attributeName Name of the attribute to retrieve
///
/// \return Attribute value as unsigned int
///
////////////////////////////////////////////////////////////
unsigned int getUdevAttributeUint(unsigned int index, std::string attributeName);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Member data // Member data
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -25,19 +25,16 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Headers // Headers
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#include <SFML/Window/JoystickImpl.hpp> #include <SFML/Window/JoystickImpl.hpp>
#include <SFML/System/Clock.hpp> #include <SFML/System/Clock.hpp>
#include <SFML/System/Err.hpp> #include <SFML/System/Err.hpp>
#include <windows.h> #include <windows.h>
#include <stdlib.h>
#include <cmath>
#include <stdio.h>
#include <regstr.h>
#include <tchar.h> #include <tchar.h>
#include <string> #include <regstr.h>
#include <algorithm>
#include <cmath>
#include <sstream> #include <sstream>
#include <vector> #include <string>
namespace namespace
{ {
@ -50,6 +47,98 @@ namespace
const sf::Time connectionRefreshDelay = sf::milliseconds(500); const sf::Time connectionRefreshDelay = sf::milliseconds(500);
ConnectionCache connectionCache[sf::Joystick::Count]; ConnectionCache connectionCache[sf::Joystick::Count];
// Get a system error string from an error code
std::string getErrorString(DWORD error)
{
TCHAR errorMessage[256];
DWORD messageLength = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, errorMessage, 256, NULL);
errorMessage[std::min<DWORD>(messageLength, 255)] = 0;
return sf::String(errorMessage).toAnsiString();
}
// Get the joystick's name
sf::String getDeviceName(unsigned int index, JOYCAPS caps)
{
// Give the joystick a default name
sf::String joystickDescription = "Unknown Joystick";
LONG result;
HKEY rootKey;
HKEY currentKey;
std::basic_string<TCHAR> subkey;
subkey = REGSTR_PATH_JOYCONFIG;
subkey += TEXT("\\");
subkey += caps.szRegKey;
subkey += TEXT("\\");
subkey += REGSTR_KEY_JOYCURR;
subkey.erase(256);
rootKey = HKEY_LOCAL_MACHINE;
result = RegOpenKeyEx(rootKey, subkey.c_str(), 0, KEY_READ, &currentKey);
if (result != ERROR_SUCCESS)
{
rootKey = HKEY_CURRENT_USER;
result = RegOpenKeyEx(rootKey, subkey.c_str(), 0, KEY_READ, &currentKey);
if (result != ERROR_SUCCESS)
{
sf::err() << "Unable to open registry for joystick at index " << index << ": " << getErrorString(result) << std::endl;
return joystickDescription;
}
}
std::basic_ostringstream<TCHAR> indexString;
indexString << index + 1;
subkey = TEXT("Joystick");
subkey += indexString.str();
subkey += REGSTR_VAL_JOYOEMNAME;
subkey.erase(256);
TCHAR keyData[256];
DWORD keyDataSize = sizeof(keyData);
result = RegQueryValueEx(currentKey, subkey.c_str(), NULL, NULL, reinterpret_cast<LPBYTE>(keyData), &keyDataSize);
RegCloseKey(currentKey);
if (result != ERROR_SUCCESS)
{
sf::err() << "Unable to query registry key for joystick at index " << index << ": " << getErrorString(result) << std::endl;
return joystickDescription;
}
subkey = REGSTR_PATH_JOYOEM;
subkey += TEXT("\\");
subkey.append(keyData, keyDataSize / sizeof(TCHAR));
subkey.erase(256);
result = RegOpenKeyEx(rootKey, subkey.c_str(), 0, KEY_READ, &currentKey);
if (result != ERROR_SUCCESS)
{
sf::err() << "Unable to open registry key for joystick at index " << index << ": " << getErrorString(result) << std::endl;
return joystickDescription;
}
keyDataSize = sizeof(keyData);
result = RegQueryValueEx(currentKey, REGSTR_VAL_JOYOEMNAME, NULL, NULL, reinterpret_cast<LPBYTE>(keyData), &keyDataSize);
RegCloseKey(currentKey);
if (result != ERROR_SUCCESS)
{
sf::err() << "Unable to query name for joystick at index " << index << ": " << getErrorString(result) << std::endl;
return joystickDescription;
}
keyData[_tcsnlen(keyData, 255)] = 0;
joystickDescription = keyData;
return joystickDescription;
}
} }
namespace sf namespace sf
@ -207,101 +296,6 @@ JoystickState JoystickImpl::update()
return state; return state;
} }
////////////////////////////////////////////////////////////
sf::String JoystickImpl::getDeviceName(unsigned int index, JOYCAPS caps)
{
// Give the joystick a default name
sf::String joystickDescription = "Unknown Joystick";
std::basic_ostringstream<TCHAR, std::char_traits<TCHAR> > ss;
ss << REGSTR_PATH_JOYCONFIG << "\\" << caps.szRegKey << "\\" << REGSTR_KEY_JOYCURR;
std::basic_string<TCHAR> subkey = ss.str().substr(0, 255);
HKEY currentKey;
LONG result;
HKEY rootKey = HKEY_LOCAL_MACHINE;
result = RegOpenKeyEx(rootKey, subkey.c_str(), 0, KEY_READ, &currentKey);
if (result != ERROR_SUCCESS)
{
rootKey = HKEY_CURRENT_USER;
result = RegOpenKeyEx(rootKey, subkey.c_str(), 0, KEY_READ, &currentKey);
}
if (result == ERROR_SUCCESS)
{
ss.clear();
ss.str(_T(""));
ss << "Joystick" << index + 1 << REGSTR_VAL_JOYOEMNAME;
std::basic_string<TCHAR> keyName = ss.str().substr(0, 255);
TCHAR keyData[256];
DWORD keyNameSize = sizeof(keyData);
result = RegQueryValueEx(currentKey, keyName.c_str(), NULL, NULL, (LPBYTE)keyData, &keyNameSize);
RegCloseKey(currentKey);
if (result == ERROR_SUCCESS)
{
ss.clear();
ss.str(_T(""));
ss << REGSTR_PATH_JOYOEM << "\\" << keyData;
subkey = ss.str().substr(0, 255);
result = RegOpenKeyEx(rootKey, subkey.c_str(), 0, KEY_READ, &currentKey);
if (result == ERROR_SUCCESS)
{
keyNameSize = sizeof(keyData);
unsigned int productKeyLength = keyNameSize / sizeof(TCHAR);
std::vector<TCHAR> productKey(productKeyLength);
result = RegQueryValueEx(currentKey, REGSTR_VAL_JOYOEMNAME, NULL, NULL, (LPBYTE) &productKey[0], &keyNameSize);
if (result == ERROR_SUCCESS)
{
while (productKeyLength > 0 && productKey[productKeyLength - 1] == 0)
{
--productKeyLength;
}
joystickDescription = std::basic_string<TCHAR>(&productKey[0], productKeyLength);
}
else
{
err() << "Unable to query name for joystick at index " << index << ": " << getErrorString(GetLastError()).toAnsiString() << std::endl;
}
RegCloseKey(currentKey);
}
else
{
err() << "Unable to open registry key for joystick at index " << index << ": " << getErrorString(GetLastError()).toAnsiString() << std::endl;
}
}
else
{
err() << "Unable to query registry key for joystick at index " << index << ": " << getErrorString(GetLastError()).toAnsiString() << std::endl;
}
}
else
{
err() << "Unable to open registry for joystick at index " << index << ": " << getErrorString(GetLastError()).toAnsiString() << std::endl;
}
return joystickDescription;
}
////////////////////////////////////////////////////////////
sf::String JoystickImpl::getErrorString(DWORD /*errorCode*/)
{
std::basic_ostringstream<TCHAR, std::char_traits<TCHAR> > ss;
TCHAR errBuff[256];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, errBuff, sizeof(errBuff), NULL);
ss << errBuff;
sf::String errMsg(ss.str());
return errMsg;
}
} // namespace priv } // namespace priv
} // namespace sf } // namespace sf

View File

@ -41,7 +41,6 @@
#include <SFML/System/String.hpp> #include <SFML/System/String.hpp>
#include <windows.h> #include <windows.h>
#include <mmsystem.h> #include <mmsystem.h>
#include <cmath>
namespace sf namespace sf
@ -120,27 +119,6 @@ public :
private : private :
////////////////////////////////////////////////////////////
/// Get the joystick's name
///
/// \param index Index of the joystick
/// \param caps JOYCAPS
///
/// \return Joystick name
///
////////////////////////////////////////////////////////////
sf::String getDeviceName(unsigned int index, JOYCAPS caps);
////////////////////////////////////////////////////////////
/// Get a system error string from an error code
///
/// \param errorCode Error code
///
/// \return Error message string
///
////////////////////////////////////////////////////////////
sf::String getErrorString(DWORD errorCode);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Member data // Member data
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////