Merge branch 'bugfix/joystick'

This commit is contained in:
Lukas Dürrenberger 2014-09-23 10:46:13 +02:00
commit dcba593e8f
10 changed files with 448 additions and 523 deletions

View File

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

View File

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

View File

@ -77,8 +77,8 @@ void JoystickManager::update()
if (!item.state.connected)
{
item.joystick.close();
item.capabilities = JoystickCaps();
item.state = JoystickState();
item.capabilities = JoystickCaps();
item.state = JoystickState();
item.identification = Joystick::Identification();
}
}
@ -89,8 +89,8 @@ void JoystickManager::update()
{
if (item.joystick.open(i))
{
item.capabilities = item.joystick.getCapabilities();
item.state = item.joystick.update();
item.capabilities = item.joystick.getCapabilities();
item.state = item.joystick.update();
item.identification = item.joystick.getIdentification();
}
}

View File

@ -109,9 +109,9 @@ private:
////////////////////////////////////////////////////////////
struct Item
{
JoystickImpl joystick; ///< Joystick implementation
JoystickState state; ///< The current joystick state
JoystickCaps capabilities; ///< The joystick capabilities
JoystickImpl joystick; ///< Joystick implementation
JoystickState state; ///< The current joystick state
JoystickCaps capabilities; ///< The joystick capabilities
Joystick::Identification identification; ///< The joystick identification
};

View File

@ -38,6 +38,46 @@ namespace
{
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;
}
m_identification.name = getDeviceString(self, CFSTR(kIOHIDProductKey));
m_identification.vendorId = getDeviceUint(self, CFSTR(kIOHIDVendorIDKey));
m_identification.productId = getDeviceUint(self, CFSTR(kIOHIDProductIDKey));
m_identification.name = getDeviceString(self, CFSTR(kIOHIDProductKey), m_index);
m_identification.vendorId = getDeviceUint(self, CFSTR(kIOHIDVendorIDKey), m_index);
m_identification.productId = getDeviceUint(self, CFSTR(kIOHIDProductIDKey), m_index);
// Get a list of all elements attached to the device.
CFArrayRef elements = IOHIDDeviceCopyMatchingElements(self, NULL, kIOHIDOptionsTypeNone);
@ -389,48 +429,6 @@ JoystickState JoystickImpl::update()
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 sf

View File

@ -113,38 +113,6 @@ public :
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
////////////////////////////////////////////////////////////
@ -152,9 +120,9 @@ private :
typedef std::map<sf::Joystick::Axis, IOHIDElementRef> AxisMap;
typedef std::vector<IOHIDElementRef> ButtonsVector;
AxisMap m_axis; ///< Axis (IOHIDElementRef) connected to the joystick
ButtonsVector m_buttons; ///< Buttons (IOHIDElementRef) connected to the joystick
unsigned int m_index; ///< SFML index
AxisMap m_axis; ///< Axis (IOHIDElementRef) connected to the joystick
ButtonsVector m_buttons; ///< Buttons (IOHIDElementRef) connected to the joystick
unsigned int m_index; ///< SFML index
Joystick::Identification m_identification; ///< Joystick identification
static Location m_locationIDs[sf::Joystick::Count]; ///< Global Joystick register

View File

@ -30,10 +30,11 @@
#include <sys/inotify.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <cstdio>
#include <libudev.h>
#include <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <sstream>
namespace
@ -44,39 +45,141 @@ namespace
void updatePluggedList()
{
udev* udevContext = udev_new();
for (unsigned int i = 0; i < sf::Joystick::Count; ++i)
{
char name[32];
std::snprintf(name, sizeof(name), "/dev/input/js%u", i);
std::ostringstream name("js");
name << i;
std::string nameString = name.str();
int file = ::open(name, O_RDONLY);
if (file >= 0)
int file = ::open(("/dev/input/" + nameString).c_str(), O_RDONLY);
if (file < 0)
{
plugged[i] = false;
continue;
}
::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;
::close(file);
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
{
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 hasInotifyEvent()
{
if (descriptor >= 0)
fd_set descriptorSet;
FD_ZERO(&descriptorSet);
FD_SET(notifyFd, &descriptorSet);
timeval timeout = {0, 0};
return (select(notifyFd + 1, &descriptorSet, NULL, NULL, &timeout) > 0) &&
FD_ISSET(notifyFd, &descriptorSet);
}
// Get the joystick name
std::string getJoystickName(int file, unsigned int index)
{
// Get the name
char name[128];
if (ioctl(file, JSIOCGNAME(sizeof(name)), name) >= 0)
return std::string(name);
sf::err() << "Unable to get name for joystick at index " << index << std::endl;
return std::string("Unknown Joystick");
}
// Get a system attribute from a udev device as an unsigned int
unsigned int getUdevAttributeUint(udev_device* device, unsigned int index, const std::string& attributeName)
{
udev_device* udevDeviceParent = udev_device_get_parent_with_subsystem_devtype(device, "usb", "usb_device");
if (!udevDeviceParent)
{
fd_set set;
FD_ZERO(&set);
FD_SET(descriptor, &set);
timeval timeout = {0, 0};
return select(descriptor + 1, &set, NULL, NULL, &timeout) > 0 &&
FD_ISSET(notifyFd, &set);
sf::err() << "Unable to get joystick attribute. "
<< "Could not find parent USB device for joystick at index " << index << "." << std::endl;
return 0;
}
else
const char* attributeString = udev_device_get_sysattr_value(udevDeviceParent, attributeName.c_str());
if (!attributeString)
{
return false;
sf::err() << "Unable to get joystick attribute '" << attributeName << "'. "
<< "Attribute does not exist for joystick at index " << index << "." << std::endl;
return 0;
}
return static_cast<unsigned int>(std::strtoul(attributeString, NULL, 16));
}
// Get a system attribute for a joystick at index as an unsigned int
unsigned int getAttributeUint(unsigned int index, const std::string& attributeName)
{
udev* udevContext = udev_new();
if (!udevContext)
{
sf::err() << "Unable to get joystick attribute. "
<< "Could not create udev context." << std::endl;
return 0;
}
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 joystick attribute. "
<< "Could not find USB device for joystick at index " << index << "." << std::endl;
udev_unref(udevContext);
return 0;
}
unsigned int attribute = getUdevAttributeUint(udevDevice, index, attributeName);
udev_device_unref(udevDevice);
udev_unref(udevContext);
return attribute;
}
}
@ -91,6 +194,9 @@ void JoystickImpl::initialize()
// Reset the array of plugged joysticks
std::fill(plugged, plugged + Joystick::Count, false);
// Do an initial scan
updatePluggedList();
// Create the inotify instance
notifyFd = inotify_init();
if (notifyFd < 0)
@ -104,11 +210,11 @@ void JoystickImpl::initialize()
if (inputFd < 0)
{
err() << "Failed to initialize inotify, joystick connections and disconnections won't be notified" << std::endl;
return;
}
// Do an initial scan
updatePluggedList();
// No need to hang on to the inotify handle in this case
::close(notifyFd);
notifyFd = -1;
}
}
@ -128,18 +234,21 @@ void JoystickImpl::cleanup()
////////////////////////////////////////////////////////////
bool JoystickImpl::isConnected(unsigned int index)
{
// First check if new joysticks were added/removed since last update
if (canRead(notifyFd))
// See if we can skip scanning if inotify is available
if (notifyFd < 0)
{
// inotify is not available, perform a scan every query
updatePluggedList();
}
else if (hasInotifyEvent())
{
// Check if new joysticks were added/removed since last update
// Don't bother decomposing and interpreting the filename, just do a full scan
updatePluggedList();
// Flush all the pending events
while (canRead(notifyFd))
{
char buffer[128];
(void)read(notifyFd, buffer, sizeof(buffer));
}
if (lseek(notifyFd, 0, SEEK_END) < 0)
err() << "Failed to flush inotify of all pending joystick events." << std::endl;
}
// Then check if the joystick is connected
@ -151,35 +260,29 @@ bool JoystickImpl::open(unsigned int index)
{
if (plugged[index])
{
char name[32];
std::snprintf(name, sizeof(name), "/dev/input/js%u", index);
std::ostringstream name("/dev/input/js");
name << index;
// 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)
{
// Retrieve the axes mapping
ioctl(m_file, JSIOCGAXMAP, m_mapping);
// Get info
m_identification.name = getJoystickName(index);
m_identification.vendorId = getUdevAttributeUint(index, "idVendor");
m_identification.productId = getUdevAttributeUint(index, "idProduct");
m_identification.name = getJoystickName(m_file, index);
m_identification.vendorId = getAttributeUint(index, "idVendor");
m_identification.productId = getAttributeUint(index, "idProduct");
// Reset the joystick state
m_state = JoystickState();
return true;
}
else
{
return false;
}
}
else
{
return false;
}
return false;
}
@ -280,57 +383,6 @@ JoystickState JoystickImpl::JoystickImpl::update()
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 sf

View File

@ -109,34 +109,13 @@ public :
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
////////////////////////////////////////////////////////////
int m_file; ///< File descriptor of the joystick
char m_mapping[ABS_MAX + 1]; ///< Axes mapping (index to axis id)
JoystickState m_state; ///< Current state of the joystick
sf::Joystick::Identification m_identification; ///< Identification of the joystick
int m_file; ///< File descriptor of the joystick
char m_mapping[ABS_MAX + 1]; ///< Axes mapping (index to axis id)
JoystickState m_state; ///< Current state of the joystick
sf::Joystick::Identification m_identification; ///< Identification of the joystick
};
} // namespace priv

View File

@ -25,19 +25,16 @@
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/JoystickImpl.hpp>
#include <SFML/System/Clock.hpp>
#include <SFML/System/Err.hpp>
#include <windows.h>
#include <stdlib.h>
#include <cmath>
#include <stdio.h>
#include <regstr.h>
#include <tchar.h>
#include <string>
#include <regstr.h>
#include <algorithm>
#include <cmath>
#include <sstream>
#include <vector>
#include <string>
namespace
{
@ -50,6 +47,98 @@ namespace
const sf::Time connectionRefreshDelay = sf::milliseconds(500);
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
@ -119,9 +208,9 @@ bool JoystickImpl::open(unsigned int index)
if (success)
{
m_identification.name = getDeviceName(m_index, m_caps);
m_identification.name = getDeviceName(m_index, m_caps);
m_identification.productId = m_caps.wPid;
m_identification.vendorId = m_caps.wMid;
m_identification.vendorId = m_caps.wMid;
}
return success;
@ -207,101 +296,6 @@ JoystickState JoystickImpl::update()
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 sf

View File

@ -41,7 +41,6 @@
#include <SFML/System/String.hpp>
#include <windows.h>
#include <mmsystem.h>
#include <cmath>
namespace sf
@ -120,32 +119,11 @@ public :
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
////////////////////////////////////////////////////////////
unsigned int m_index; ///< Index of the joystick
JOYCAPS m_caps; ///< Joystick capabilities
unsigned int m_index; ///< Index of the joystick
JOYCAPS m_caps; ///< Joystick capabilities
Joystick::Identification m_identification; ///< Joystick identification
};