Implemented OSX/Joysticks and fixed some tabulations

This commit is contained in:
Marco Antognini 2011-07-13 22:14:49 +02:00
parent 61adc51d09
commit 535733f5c7
9 changed files with 372 additions and 31 deletions

View File

@ -47,7 +47,8 @@ namespace priv
/// keyboard and mouse states. It's only purpose is /// keyboard and mouse states. It's only purpose is
/// to help sf::priv::InputImpl class. /// to help sf::priv::InputImpl class.
/// ///
/// sf::priv::JoystickImpl is not concerned by this class. /// HIDInputManager provides a function to get all connected joysticks
/// to help sf::priv::JoystickImpl.
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
class HIDInputManager : NonCopyable class HIDInputManager : NonCopyable
@ -84,6 +85,27 @@ public :
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool IsMouseButtonPressed(Mouse::Button button); bool IsMouseButtonPressed(Mouse::Button button);
////////////////////////////////////////////////////////////
/// \brief List all connected joysticks
///
/// \return a retained CFSetRef of IOHIDDeviceRef or NULL
///
////////////////////////////////////////////////////////////
CFSetRef CopyJoystickDevices();
public :
////////////////////////////////////////////////////////////
/// \brief Get the usb location ID of a given device
///
/// This location ID is unique and can be used a usb port identifier
///
/// \param device HID device to query
/// \return the device's location ID or 0 if something went wrong
///
////////////////////////////////////////////////////////////
static long GetLocationID(IOHIDDeviceRef device);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// Try to convert a character into a SFML key code. /// Try to convert a character into a SFML key code.
/// ///
@ -199,7 +221,7 @@ private :
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Filter the devices and return them. /// \brief Filter the devices and return them.
/// ///
/// If something went wrong FreeUp is called /// FreeUp is _not_ called by this function.
/// ///
/// \param page HID page like kHIDPage_GenericDesktop /// \param page HID page like kHIDPage_GenericDesktop
/// \param usage HID usage page like kHIDUsage_GD_Keyboard or kHIDUsage_GD_Mouse /// \param usage HID usage page like kHIDUsage_GD_Keyboard or kHIDUsage_GD_Mouse

View File

@ -84,7 +84,6 @@ bool HIDInputManager::IsKeyPressed(Keyboard::Key key)
return state; return state;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool HIDInputManager::IsMouseButtonPressed(Mouse::Button button) bool HIDInputManager::IsMouseButtonPressed(Mouse::Button button)
{ {
@ -128,6 +127,35 @@ bool HIDInputManager::IsMouseButtonPressed(Mouse::Button button)
} }
////////////////////////////////////////////////////////////
CFSetRef HIDInputManager::CopyJoystickDevices()
{
return CopyDevices(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
}
////////////////////////////////////////////////////////////
long HIDInputManager::GetLocationID(IOHIDDeviceRef device)
{
long loc = 0;
// Get a unique ID : its usb location ID
CFTypeRef typeRef = IOHIDDeviceGetProperty(device,
CFSTR(kIOHIDLocationIDKey));
if (!typeRef || CFGetTypeID(typeRef) != CFNumberGetTypeID()) {
return 0;
}
CFNumberRef locRef = (CFNumberRef)typeRef;
if (!CFNumberGetValue(locRef, kCFNumberLongType, &loc)) {
return 0;
}
return loc;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
HIDInputManager::HIDInputManager() HIDInputManager::HIDInputManager()
: amIValid(true) : amIValid(true)
@ -198,8 +226,7 @@ void HIDInputManager::InitializeKeyboard()
// Get only keyboards // Get only keyboards
CFSetRef keyboards = CopyDevices(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard); CFSetRef keyboards = CopyDevices(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard);
if (keyboards == NULL) { if (keyboards == NULL) {
FreeUp();
// FreeUp was already called
return; return;
} }
@ -234,8 +261,7 @@ void HIDInputManager::InitializeMouse()
// Get only mouses // Get only mouses
CFSetRef mouses = CopyDevices(kHIDPage_GenericDesktop, kHIDUsage_GD_Mouse); CFSetRef mouses = CopyDevices(kHIDPage_GenericDesktop, kHIDUsage_GD_Mouse);
if (mouses == NULL) { if (mouses == NULL) {
FreeUp();
// FreeUp was already called
return; return;
} }
@ -525,17 +551,13 @@ CFSetRef HIDInputManager::CopyDevices(UInt32 page, UInt32 usage)
CFSetRef devices = IOHIDManagerCopyDevices(myManager); CFSetRef devices = IOHIDManagerCopyDevices(myManager);
if (devices == NULL) { if (devices == NULL) {
sf::Err() << "Cannot find any devices." << std::endl;
FreeUp();
return NULL; return NULL;
} }
// Is there at least one keyboard ? // Is there at least one device ?
CFIndex deviceCount = CFSetGetCount(devices); CFIndex deviceCount = CFSetGetCount(devices);
if (deviceCount < 1) { if (deviceCount < 1) {
sf::Err() << "Found no device." << std::endl;
CFRelease(devices); CFRelease(devices);
FreeUp();
return NULL; return NULL;
} }

View File

@ -27,48 +27,332 @@
// Headers // Headers
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#include <SFML/Window/JoystickImpl.hpp> #include <SFML/Window/JoystickImpl.hpp>
#include <SFML/Window/OSX/HIDInputManager.hpp>
namespace sf namespace sf
{ {
namespace priv namespace priv
{ {
////////////////////////////////////////////////////////////
JoystickImpl::Location JoystickImpl::myLocationIDs[sf::Joystick::Count] = { 0 };
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool JoystickImpl::IsConnected(unsigned int index) bool JoystickImpl::IsConnected(unsigned int index)
{ {
// @to be implemented bool state = false; // Is the index-th joystick connected ?
return false;
// First, let's check if the device was previously detected :
if (myLocationIDs[index] != 0) {
state = true;
}
// Otherwise, let's check if it is now connected :
else { // i.e., myLocationIDs[index] == 0
// Get all devices
CFSetRef devices = HIDInputManager::GetInstance().CopyJoystickDevices();
if (devices != NULL) {
CFIndex size = CFSetGetCount(devices);
if (size > 0) {
CFTypeRef array[size]; // array of IOHIDDeviceRef
CFSetGetValues(devices, array);
// If there exists a device d s.t. there is no j s.t.
// myLocationIDs[j] == d's location then we have a new device.
for (CFIndex didx(0); didx < size; ++didx) {
IOHIDDeviceRef d = (IOHIDDeviceRef)array[didx];
Location dloc = HIDInputManager::GetLocationID(d);
bool foundJ = false;
for (unsigned int j(0); j < Joystick::Count; ++j) {
if (myLocationIDs[j] == dloc) {
foundJ = true;
break; // no need to loop again
}
}
if (foundJ) {
// This is a known device
// Nothing else to do
} else {
// This is a new device
// We set it up for Open(..)
myLocationIDs[index] = dloc;
state = true;
break; // We stop looking for a new device
}
}
}
CFRelease(devices);
}
}
return state;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool JoystickImpl::Open(unsigned int index) bool JoystickImpl::Open(unsigned int index)
{ {
// @to be implemented myIndex = index;
Location deviceLoc = myLocationIDs[index]; // The device we need to load
// Get all devices
CFSetRef devices = HIDInputManager::GetInstance().CopyJoystickDevices();
if (devices == NULL) {
return false; return false;
} }
// Get a usable copy of the joysticks devices.
CFIndex joysticksCount = CFSetGetCount(devices);
CFTypeRef devicesArray[joysticksCount];
CFSetGetValues(devices, devicesArray);
// Get the desired joystick.
IOHIDDeviceRef self = 0;
for (CFIndex i(0); i < joysticksCount; ++i) {
IOHIDDeviceRef d = (IOHIDDeviceRef)devicesArray[i];
if (deviceLoc == HIDInputManager::GetLocationID(d)) {
self = d;
break; // We found it so we stop looping.
}
}
if (self == 0) {
// This shouldn't happen!
CFRelease(devices);
return false;
}
// Get a list of all elements attached to the device.
CFArrayRef elements = IOHIDDeviceCopyMatchingElements(self,
NULL,
kIOHIDOptionsTypeNone);
if (elements == NULL) {
CFRelease(devices);
return false;
}
// How many elements are there ?
CFIndex elementsCount = CFArrayGetCount(elements);
if (elementsCount == 0) {
// What is a joystick with no element ?
CFRelease(elements);
CFRelease(devices);
return false;
}
// Go through all connected elements.
for (int i = 0; i < elementsCount; ++i) {
IOHIDElementRef element = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, i);
switch (IOHIDElementGetType(element)) {
case kIOHIDElementTypeInput_Misc:
switch (IOHIDElementGetUsage(element)) {
case kHIDUsage_GD_X:
myAxis[Joystick::X] = element;
break;
case kHIDUsage_GD_Y:
myAxis[Joystick::Y] = element;
break;
case kHIDUsage_GD_Z:
myAxis[Joystick::Z] = element;
break;
case kHIDUsage_GD_Rx:
myAxis[Joystick::U] = element;
break;
case kHIDUsage_GD_Ry:
myAxis[Joystick::V] = element;
break;
case kHIDUsage_GD_Rz:
myAxis[Joystick::R] = element;
break;
// kHIDUsage_GD_Vx, kHIDUsage_GD_Vy, kHIDUsage_GD_Vz are ignored.
}
break;
case kIOHIDElementTypeInput_Button:
if (myButtons.size() < Joystick::ButtonCount) { // If we have free slot...
myButtons.push_back(element); // ...we add this element to the list
} else {
// Too many buttons. We ignore this one.
}
break;
default: // Make compiler happy
break;
}
}
// Note : Joy::AxisPovX/Y are not supported (yet).
// Maybe kIOHIDElementTypeInput_Axis is the corresponding type but I can't test.
// Retain all these objets for personal use
for (ButtonsVector::iterator it(myButtons.begin()); it != myButtons.end(); ++it) {
CFRetain(*it);
}
for (AxisMap::iterator it(myAxis.begin()); it != myAxis.end(); ++it) {
CFRetain(it->second);
}
// Note : we didn't retain element in the switch because we might have multiple
// Axis X (for example) and we want to keep only the last one. So to prevent
// leaking we retain objects 'only' now.
CFRelease(devices);
CFRelease(elements);
return true;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void JoystickImpl::Close() void JoystickImpl::Close()
{ {
// @to be implemented for (ButtonsVector::iterator it(myButtons.begin()); it != myButtons.end(); ++it) {
CFRelease(*it);
}
myButtons.clear();
for (AxisMap::iterator it(myAxis.begin()); it != myAxis.end(); ++it) {
CFRelease(it->second);
}
myAxis.clear();
// And we unregister this joystick
myLocationIDs[myIndex] = 0;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
JoystickCaps JoystickImpl::GetCapabilities() const JoystickCaps JoystickImpl::GetCapabilities() const
{ {
// @to be implemented JoystickCaps caps;
return JoystickCaps();
// Buttons :
caps.ButtonCount = myButtons.size();
// Axis :
for (AxisMap::const_iterator it(myAxis.begin()); it != myAxis.end(); ++it) {
caps.Axes[it->first] = true;
}
return caps;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
JoystickState JoystickImpl::Update() JoystickState JoystickImpl::Update()
{ {
// @to be implemented static const JoystickState disconnectedState; // return this if joystick was disconnected
return JoystickState(); JoystickState state; // otherwise return that
state.Connected = true;
// Note : free up is done in Close() which is called, if required,
// by the joystick manager. So we don't release buttons nor axes here.
// First, let's determine if the joystick is still connected
Location selfLoc = myLocationIDs[myIndex];
// Get all devices
CFSetRef devices = HIDInputManager::GetInstance().CopyJoystickDevices();
if (devices == NULL) {
return disconnectedState;
}
// Get a usable copy of the joysticks devices.
CFIndex joysticksCount = CFSetGetCount(devices);
CFTypeRef devicesArray[joysticksCount];
CFSetGetValues(devices, devicesArray);
// Search for it
bool found = false;
for (CFIndex i(0); i < joysticksCount; ++i) {
IOHIDDeviceRef d = (IOHIDDeviceRef)devicesArray[i];
if (selfLoc == HIDInputManager::GetLocationID(d)) {
found = true;
break; // Stop looping
}
}
// Release unused stuff
CFRelease(devices);
// Was it found ?
if (found) {
// Yes, so we can continue.
} else {
// No, so we stop here
return disconnectedState;
}
// Update buttons' state
unsigned int i = 0;
for (ButtonsVector::iterator it(myButtons.begin()); it != myButtons.end(); ++it, ++i) {
IOHIDValueRef value = 0;
IOHIDDeviceGetValue(IOHIDElementGetDevice(*it), *it, &value);
// Check for plug out.
if (!value) {
// No value ? Hum... Seems like the joystick is gone
return disconnectedState;
}
// 1 means pressed, others mean released
state.Buttons[i] = IOHIDValueGetIntegerValue(value) == 1;
}
// Update axes' state
for (AxisMap::iterator it = myAxis.begin(); it != myAxis.end(); ++it) {
IOHIDValueRef value = 0;
IOHIDDeviceGetValue(IOHIDElementGetDevice(it->second), it->second, &value);
// Check for plug out.
if (!value) {
// No value ? Hum... Seems like the joystick is gone
return disconnectedState;
}
// We want to bind [physicalMin,physicalMax] to [-100=min,100=max].
//
// General formula to bind [a,b] to [c,d] with a linear progression :
//
// f : [a, b] -> [c, d]
// x |-> (x-a)(d-c)/(b-a)+c
//
// This method might not be very accurate (the "0 position" can be
// slightly shift with some device) but we don't care because most
// of devices are so sensitive that this is not relevant.
double physicalMax = IOHIDElementGetPhysicalMax(it->second);
double physicalMin = IOHIDElementGetPhysicalMin(it->second);
double scaledMin = -100;
double scaledMax = 100;
double physicalValue = IOHIDValueGetScaledValue(value, kIOHIDValueScaleTypePhysical);
float scaledValue = ((physicalValue - physicalMin) * (scaledMax - scaledMin) / (physicalMax - physicalMin)) + scaledMin;
state.Axes[it->first] = scaledValue;
}
return state;
} }
} // namespace priv } // namespace priv

View File

@ -30,7 +30,9 @@
// Headers // Headers
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#include <SFML/Window/JoystickImpl.hpp> #include <SFML/Window/JoystickImpl.hpp>
#include <IOKit/hid/IOHIDDevice.h>
#include <map>
#include <vector>
namespace sf namespace sf
{ {
@ -91,6 +93,17 @@ private :
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Member data // Member data
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
typedef long Location;
typedef std::map<sf::Joystick::Axis, IOHIDElementRef> AxisMap;
typedef std::vector<IOHIDElementRef> ButtonsVector;
AxisMap myAxis; ///< Axis (IOHIDElementRef) connected to the joystick
ButtonsVector myButtons; ///< Buttons (IOHIDElementRef) connected to the joystick
unsigned int myIndex; ///< SFML index
static Location myLocationIDs[sf::Joystick::Count]; ///< Global Joystick register
/// For a corresponding SFML index, myLocationIDs is either some usb
/// location or 0 if there isn't currently a connected joystick device
}; };
} // namespace priv } // namespace priv