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/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: hid_end_parse(data);
if (desc != NULL) hid_dispose_report_desc(desc);
hid_dispose_report_desc(desc); ::close(file);
if (data != NULL)
hid_end_parse(data);
close(fd);
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;
} }
directoryEntry = readdir(directory);
} }
closedir(dp); closedir(directory);
} }
} }
int usageToAxis(int usage) int usageToAxis(int usage)
{ {
int axis; switch (usage)
{
switch (usage) { case HUG_X: return sf::Joystick::X;
case HUG_X: case HUG_Y: return sf::Joystick::Y;
axis = sf::Joystick::X; case HUG_Z: return sf::Joystick::Z;
break; case HUG_RZ: return sf::Joystick::R;
case HUG_Y: case HUG_RX: return sf::Joystick::U;
axis = sf::Joystick::Y; case HUG_RY: return sf::Joystick::V;
break; default: return -1;
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) void hatValueToSfml(int value, sf::priv::JoystickState& state)
{ {
state.axes[sf::Joystick::PovX] = hatmap[value].first; state.axes[sf::Joystick::PovX] = hatValueMap[value].first;
state.axes[sf::Joystick::PovY] = hatmap[value].second; 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( 0, 0); // center
hatmap[1] = std::make_pair<int, int>(0, -100); // top hatValueMap[1] = std::make_pair( 0, -100); // top
hatmap[3] = std::make_pair<int, int>(100, 0); // right hatValueMap[3] = std::make_pair( 100, 0); // right
hatmap[5] = std::make_pair<int, int>(0, 100); // bottom hatValueMap[5] = std::make_pair( 0, 100); // bottom
hatmap[7] = std::make_pair<int, int>(-100, 0); // left hatValueMap[7] = std::make_pair(-100, 0); // left
hatmap[2] = std::make_pair<int, int>(100, -100); // top-right hatValueMap[2] = std::make_pair( 100, -100); // top-right
hatmap[4] = std::make_pair<int, int>(100, 100); // bottom-right hatValueMap[4] = std::make_pair( 100, 100); // bottom-right
hatmap[6] = std::make_pair<int, int>(-100, 100); // bottom-left hatValueMap[6] = std::make_pair(-100, 100); // bottom-left
hatmap[8] = std::make_pair<int, int>(-100, -100); // top-left hatValueMap[8] = std::make_pair(-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 false; return true;
}
return m_state.connected = true;
}
else
{
return false;
} }
} }
else
{ return false;
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: int usage = HID_USAGE(item.usage);
caps.buttonCount ++;
break;
case HUP_GENERIC_DESKTOP:
{
int usage = HID_USAGE(item.usage);
int axis;
if (usage == HUG_HAT_SWITCH) { if (usage == HUP_BUTTON)
caps.axes[Joystick::PovX] = true; {
caps.axes[Joystick::PovY] = true; caps.buttonCount++;
} break;
else if ((axis = usageToAxis(usage)) != -1) }
{ else if (usage == HUP_GENERIC_DESKTOP)
caps.axes[axis] = true; {
} int axis = usageToAxis(usage);
break;
} if (usage == HUG_HAT_SWITCH)
default: {
break; 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() 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:
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;
if (usage == HUG_HAT_SWITCH) while (hid_get_item(data, &item))
hatvalToSFML(v, m_state); {
else if ((axis = usageToAxis(usage)) != -1) if (item.kind == hid_input)
{ {
int &min = item.logical_minimum; int usage = HID_USAGE(item.usage);
int &max = item.logical_maximum;
v = (v - min) * (200) / (max - min) -100; if (usage == HUP_BUTTON)
m_state.axes[axis] = v; {
} m_state.buttons[buttonIndex++] = hid_get_data(&m_buffer[0], &item);
}
break; else if (usage == HUP_GENERIC_DESKTOP)
} {
default: int value = hid_get_data(&m_buffer[0], &item);
break; 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 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
{ {
@ -112,16 +112,13 @@ 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
int m_length; ///< Buffer length
void *m_buffer; ///< USB HID buffer
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
}; };
} // namespace priv } // namespace priv

View File

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

View File

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

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

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,141 @@ 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] = 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; 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 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; sf::err() << "Unable to get joystick attribute. "
FD_ZERO(&set); << "Could not find parent USB device for joystick at index " << index << "." << std::endl;
FD_SET(descriptor, &set); return 0;
timeval timeout = {0, 0};
return select(descriptor + 1, &set, NULL, NULL, &timeout) > 0 &&
FD_ISSET(notifyFd, &set);
} }
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 // Reset the array of plugged joysticks
std::fill(plugged, plugged + Joystick::Count, false); std::fill(plugged, plugged + Joystick::Count, false);
// Do an initial scan
updatePluggedList();
// Create the inotify instance // Create the inotify instance
notifyFd = inotify_init(); notifyFd = inotify_init();
if (notifyFd < 0) if (notifyFd < 0)
@ -104,11 +210,11 @@ void JoystickImpl::initialize()
if (inputFd < 0) if (inputFd < 0)
{ {
err() << "Failed to initialize inotify, joystick connections and disconnections won't be notified" << std::endl; err() << "Failed to initialize inotify, joystick connections and disconnections won't be notified" << std::endl;
return;
}
// Do an initial scan // No need to hang on to the inotify handle in this case
updatePluggedList(); ::close(notifyFd);
notifyFd = -1;
}
} }
@ -128,18 +234,21 @@ void JoystickImpl::cleanup()
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool JoystickImpl::isConnected(unsigned int index) bool JoystickImpl::isConnected(unsigned int index)
{ {
// First check if new joysticks were added/removed since last update // See if we can skip scanning if inotify is available
if (canRead(notifyFd)) 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 // Don't bother decomposing and interpreting the filename, just do a full scan
updatePluggedList(); updatePluggedList();
// Flush all the pending events // Flush all the pending events
while (canRead(notifyFd)) if (lseek(notifyFd, 0, SEEK_END) < 0)
{ err() << "Failed to flush inotify of all pending joystick events." << std::endl;
char buffer[128];
(void)read(notifyFd, buffer, sizeof(buffer));
}
} }
// Then check if the joystick is connected // Then check if the joystick is connected
@ -151,35 +260,29 @@ 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 = getAttributeUint(index, "idVendor");
m_identification.productId = getUdevAttributeUint(index, "idProduct"); m_identification.productId = getAttributeUint(index, "idProduct");
// Reset the joystick state // Reset the joystick state
m_state = JoystickState(); m_state = JoystickState();
return true; return true;
} }
else
{
return false;
}
}
else
{
return false;
} }
return false;
} }
@ -280,57 +383,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,34 +109,13 @@ 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
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
int m_file; ///< File descriptor of the joystick int m_file; ///< File descriptor of the joystick
char m_mapping[ABS_MAX + 1]; ///< Axes mapping (index to axis id) char m_mapping[ABS_MAX + 1]; ///< Axes mapping (index to axis id)
JoystickState m_state; ///< Current state of the joystick JoystickState m_state; ///< Current state of the joystick
sf::Joystick::Identification m_identification; ///< Identification of the joystick sf::Joystick::Identification m_identification; ///< Identification of the joystick
}; };
} // namespace priv } // namespace priv

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
@ -119,9 +208,9 @@ bool JoystickImpl::open(unsigned int index)
if (success) 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.productId = m_caps.wPid;
m_identification.vendorId = m_caps.wMid; m_identification.vendorId = m_caps.wMid;
} }
return success; return success;
@ -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,32 +119,11 @@ 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
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
unsigned int m_index; ///< Index of the joystick unsigned int m_index; ///< Index of the joystick
JOYCAPS m_caps; ///< Joystick capabilities JOYCAPS m_caps; ///< Joystick capabilities
Joystick::Identification m_identification; ///< Joystick identification Joystick::Identification m_identification; ///< Joystick identification
}; };