diff --git a/include/SFML/Window/WindowHandle.hpp b/include/SFML/Window/WindowHandle.hpp index 2947a7c5..81e44144 100644 --- a/include/SFML/Window/WindowHandle.hpp +++ b/include/SFML/Window/WindowHandle.hpp @@ -54,7 +54,7 @@ namespace sf #elif defined(SFML_SYSTEM_MACOS) // Window handle is NSWindow (void*) on Mac OS X - Cocoa - typedef void* WindowHandle; + typedef void* WindowHandle; #endif diff --git a/src/SFML/Window/GlContext.cpp b/src/SFML/Window/GlContext.cpp index fe5a59cf..196ed9c8 100644 --- a/src/SFML/Window/GlContext.cpp +++ b/src/SFML/Window/GlContext.cpp @@ -48,8 +48,8 @@ #elif defined(SFML_SYSTEM_MACOS) - #include - typedef sf::priv::SFContext ContextType; + #include + typedef sf::priv::SFContext ContextType; #endif diff --git a/src/SFML/Window/InputImpl.hpp b/src/SFML/Window/InputImpl.hpp index 7c501471..f4aa0247 100644 --- a/src/SFML/Window/InputImpl.hpp +++ b/src/SFML/Window/InputImpl.hpp @@ -40,7 +40,7 @@ #elif defined(SFML_SYSTEM_MACOS) - #include + #include #endif diff --git a/src/SFML/Window/JoystickImpl.hpp b/src/SFML/Window/JoystickImpl.hpp index 5ec45ecb..40fe6b32 100644 --- a/src/SFML/Window/JoystickImpl.hpp +++ b/src/SFML/Window/JoystickImpl.hpp @@ -87,7 +87,7 @@ struct JoystickState #elif defined(SFML_SYSTEM_MACOS) - #include + #include #endif diff --git a/src/SFML/Window/OSX/HIDInputManager.hpp b/src/SFML/Window/OSX/HIDInputManager.hpp index e6e13ad9..81befac8 100644 --- a/src/SFML/Window/OSX/HIDInputManager.hpp +++ b/src/SFML/Window/OSX/HIDInputManager.hpp @@ -47,7 +47,8 @@ namespace priv /// keyboard and mouse states. It's only purpose is /// 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 @@ -84,6 +85,27 @@ public : //////////////////////////////////////////////////////////// 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. /// @@ -199,7 +221,7 @@ private : //////////////////////////////////////////////////////////// /// \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 usage HID usage page like kHIDUsage_GD_Keyboard or kHIDUsage_GD_Mouse diff --git a/src/SFML/Window/OSX/HIDInputManager.mm b/src/SFML/Window/OSX/HIDInputManager.mm index a8118db8..84b51db9 100644 --- a/src/SFML/Window/OSX/HIDInputManager.mm +++ b/src/SFML/Window/OSX/HIDInputManager.mm @@ -84,7 +84,6 @@ bool HIDInputManager::IsKeyPressed(Keyboard::Key key) return state; } - //////////////////////////////////////////////////////////// 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() : amIValid(true) @@ -198,8 +226,7 @@ void HIDInputManager::InitializeKeyboard() // Get only keyboards CFSetRef keyboards = CopyDevices(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard); if (keyboards == NULL) { - - // FreeUp was already called + FreeUp(); return; } @@ -234,8 +261,7 @@ void HIDInputManager::InitializeMouse() // Get only mouses CFSetRef mouses = CopyDevices(kHIDPage_GenericDesktop, kHIDUsage_GD_Mouse); if (mouses == NULL) { - - // FreeUp was already called + FreeUp(); return; } @@ -525,17 +551,13 @@ CFSetRef HIDInputManager::CopyDevices(UInt32 page, UInt32 usage) CFSetRef devices = IOHIDManagerCopyDevices(myManager); if (devices == NULL) { - sf::Err() << "Cannot find any devices." << std::endl; - FreeUp(); return NULL; } - // Is there at least one keyboard ? + // Is there at least one device ? CFIndex deviceCount = CFSetGetCount(devices); if (deviceCount < 1) { - sf::Err() << "Found no device." << std::endl; CFRelease(devices); - FreeUp(); return NULL; } diff --git a/src/SFML/Window/OSX/InputImpl.mm b/src/SFML/Window/OSX/InputImpl.mm index 0690442f..62fe7ee3 100644 --- a/src/SFML/Window/OSX/InputImpl.mm +++ b/src/SFML/Window/OSX/InputImpl.mm @@ -102,14 +102,14 @@ SFOpenGLView* GetSFOpenGLViewFromSFMLWindow(const Window& window) //////////////////////////////////////////////////////////// bool InputImpl::IsKeyPressed(Keyboard::Key key) { - return HIDInputManager::GetInstance().IsKeyPressed(key); + return HIDInputManager::GetInstance().IsKeyPressed(key); } //////////////////////////////////////////////////////////// bool InputImpl::IsMouseButtonPressed(Mouse::Button button) { - return HIDInputManager::GetInstance().IsMouseButtonPressed(button); + return HIDInputManager::GetInstance().IsMouseButtonPressed(button); } @@ -117,7 +117,7 @@ bool InputImpl::IsMouseButtonPressed(Mouse::Button button) Vector2i InputImpl::GetMousePosition() { // Reverse Y axis to match SFML coord. - NSPoint pos = [NSEvent mouseLocation]; + NSPoint pos = [NSEvent mouseLocation]; pos.y = sf::VideoMode::GetDesktopMode().Height - pos.y; return Vector2i(pos.x, pos.y); diff --git a/src/SFML/Window/OSX/JoystickImpl.cpp b/src/SFML/Window/OSX/JoystickImpl.cpp index cd082fa3..becb3857 100644 --- a/src/SFML/Window/OSX/JoystickImpl.cpp +++ b/src/SFML/Window/OSX/JoystickImpl.cpp @@ -27,48 +27,332 @@ // Headers //////////////////////////////////////////////////////////// #include - +#include namespace sf { namespace priv { +//////////////////////////////////////////////////////////// +JoystickImpl::Location JoystickImpl::myLocationIDs[sf::Joystick::Count] = { 0 }; + + //////////////////////////////////////////////////////////// bool JoystickImpl::IsConnected(unsigned int index) { - // @to be implemented - return false; + bool state = false; // Is the index-th joystick connected ? + + // 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) { - // @to be implemented - return false; + 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; + } + + // 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() { - // @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 { - // @to be implemented - return JoystickCaps(); + JoystickCaps caps; + + // 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() { - // @to be implemented - return JoystickState(); + static const JoystickState disconnectedState; // return this if joystick was disconnected + 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 diff --git a/src/SFML/Window/OSX/JoystickImpl.hpp b/src/SFML/Window/OSX/JoystickImpl.hpp index 7dbef005..e0c0add8 100644 --- a/src/SFML/Window/OSX/JoystickImpl.hpp +++ b/src/SFML/Window/OSX/JoystickImpl.hpp @@ -30,7 +30,9 @@ // Headers //////////////////////////////////////////////////////////// #include - +#include +#include +#include namespace sf { @@ -91,6 +93,17 @@ private : //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// + typedef long Location; + typedef std::map AxisMap; + typedef std::vector 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