From 5fbf147cc097697cec581615cdef1090949e9794 Mon Sep 17 00:00:00 2001 From: Marco Antognini Date: Sun, 10 Jul 2011 05:02:53 +0200 Subject: [PATCH] Added support for several connected keyboards in HIDInputManager --- src/SFML/Window/OSX/HIDInputManager.hpp | 55 +++- src/SFML/Window/OSX/HIDInputManager.mm | 354 ++++++++++++------------ 2 files changed, 224 insertions(+), 185 deletions(-) diff --git a/src/SFML/Window/OSX/HIDInputManager.hpp b/src/SFML/Window/OSX/HIDInputManager.hpp index e66612a5e..aee34dc53 100644 --- a/src/SFML/Window/OSX/HIDInputManager.hpp +++ b/src/SFML/Window/OSX/HIDInputManager.hpp @@ -36,6 +36,7 @@ #include #include #include +#include namespace sf { @@ -126,6 +127,27 @@ private : //////////////////////////////////////////////////////////// void InitializeKeyboard(); + //////////////////////////////////////////////////////////// + /// \brief Load the given keyboard into myKeys + /// + /// If the given keyboard has no key this function simply + /// returns. FreeUp is _not_ called because this is not fatal. + /// + /// \param keyboard Keyboard to load + /// + //////////////////////////////////////////////////////////// + void LoadKeyboard(IOHIDDeviceRef keyboard); + + //////////////////////////////////////////////////////////// + /// \brief Load the given key into myKeys + /// + /// FreeUp is _not_ called by this function. + /// + /// \param key Key to load + /// + //////////////////////////////////////////////////////////// + void LoadKey(IOHIDElementRef key); + //////////////////////////////////////////////////////////// /// \brief Release all resources /// @@ -146,13 +168,25 @@ private : static CFDictionaryRef CopyDevicesMaskForManager(UInt32 page, UInt32 usage); //////////////////////////////////////////////////////////// - /// \brief Converte a HID key usage to it's conrresponding virtual code + /// \brief Filter the devices and return them. + /// + /// If something went wrong FreeUp is called + /// + /// \param page HID page like kHIDPage_GenericDesktop + /// \param usage HID usage page like kHIDUsage_GD_Keyboard or kHIDUsage_GD_Mouse + /// \return a retained CFSetRef of IOHIDDeviceRef or NULL + /// + //////////////////////////////////////////////////////////// + CFSetRef CopyDevices(UInt32 page, UInt32 usage); + + //////////////////////////////////////////////////////////// + /// \brief Converte a HID key usage to its corresponding virtual code /// /// See IOHIDUsageTables.h /// /// \param usage Any kHIDUsage_Keyboard* usage - /// \return the virtual code associate to the given HID key usage - /// or 0xff if it is associate to no virtual code + /// \return the virtual code associate with the given HID key usage + /// or 0xff if it is associate with no virtual code /// //////////////////////////////////////////////////////////// static UInt8 UsageToVirtualCode(UInt32 usage); @@ -166,9 +200,18 @@ private : CFDataRef myLayoutData; ///< CFData containing the layout UCKeyboardLayout* myLayout; ///< Current Keyboard Layout IOHIDManagerRef myManager; ///< HID Manager - IOHIDElementRef myKeys[Keyboard::KeyCount]; ///< All the keys on the current keyboard - /* myKeys index correspond to sf::Keyboard::Key enum */ - /* if no key is assigned to a key XYZ then myKeys[XYZ] = 0 */ + + typedef std::vector IOHIDElements; + IOHIDElements myKeys[Keyboard::KeyCount]; ///< All the keys on the current keyboard + + //////////////////////////////////////////////////////////// + /// myKeys' index corresponds to sf::Keyboard::Key enum. + /// if no key is assigned with key XYZ then myKeys[XYZ].size() == 0. + /// if there are several keyboards connected and several HID keys associate + /// with the same sf::Keyboard::Key then myKeys[XYZ] contains all these + /// HID keys. + /// + //////////////////////////////////////////////////////////// }; } // namespace priv diff --git a/src/SFML/Window/OSX/HIDInputManager.mm b/src/SFML/Window/OSX/HIDInputManager.mm index e9165a9cb..d92057bc7 100644 --- a/src/SFML/Window/OSX/HIDInputManager.mm +++ b/src/SFML/Window/OSX/HIDInputManager.mm @@ -28,7 +28,6 @@ //////////////////////////////////////////////////////////// #include #include -#include #include namespace sf @@ -51,48 +50,42 @@ bool HIDInputManager::IsKeyPressed(Keyboard::Key key) return false; } - // Have we an associate IOHIDElementRef to that key ? - if (myKeys[key] == 0) { // No - - sf::Err() << key - << " is not associate to any IOHIDElementRef." - << std::endl; - - return false; - - } else { // Yes + // state = true if at least one corresponding HID key is pressed + bool state = false; + + for (IOHIDElements::iterator it = myKeys[key].begin(); it != myKeys[key].end(); ++it) { IOHIDValueRef value = 0; - IOHIDDeviceRef device = IOHIDElementGetDevice(myKeys[key]); - IOHIDDeviceGetValue(device, myKeys[key], &value); + IOHIDDeviceRef device = IOHIDElementGetDevice(*it); + IOHIDDeviceGetValue(device, *it, &value); if (!value) { // This means some kind of error / deconnection so we remove this // element from our keys - CFRelease(myKeys[key]); - myKeys[key] = 0; + CFRelease(*it); + it = myKeys[key].erase(it); sf::Err() << key << " is dead (cannot access its value)." << std::endl; - - return false; - + } else if (IOHIDValueGetIntegerValue(value) == 1) { // This means the key is pressed - return true; + state = true; + break; // Stop here. } else { // This means the key is released - return false; } } + + return state; } @@ -116,9 +109,6 @@ HIDInputManager::HIDInputManager() , myLayout(0) , myManager(0) { - // And initialize myKeys with 0s. - std::fill(myKeys, myKeys + Keyboard::KeyCount, (IOHIDElementRef)0); - // Get the current keyboard layout TISInputSourceRef tis = TISCopyCurrentKeyboardLayoutInputSource(); myLayoutData = (CFDataRef)TISGetInputSourceProperty(tis, @@ -172,65 +162,60 @@ void HIDInputManager::InitializeKeyboard() { //////////////////////////////////////////////////////////// // The purpose of this function is to initalize myKeys so we can get - // the associate IOHIDElementRef of a sf::Keyboard::Key in constant time. + // the associate IOHIDElementRef with a sf::Keyboard::Key in ~constant~ time. - - // Filter and keep only the keyboards - CFDictionaryRef mask = CopyDevicesMaskForManager(kHIDPage_GenericDesktop, - kHIDUsage_GD_Keyboard); - - IOHIDManagerSetDeviceMatching(myManager, mask); - - CFRelease(mask); - mask = 0; - - CFSetRef keyboards = IOHIDManagerCopyDevices(myManager); + // Get only keyboards + CFSetRef keyboards = CopyDevices(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard); if (keyboards == NULL) { - sf::Err() << "Cannot find any keyboard (1)" << std::endl; - FreeUp(); + + // FreeUp was already called return; } - // Is there at least one keyboard ? - CFIndex keyboardCount = CFSetGetCount(keyboards); - if (keyboardCount < 1) { - sf::Err() << "Cannot find any keyboard (2)" << std::endl; - CFRelease(keyboards); - FreeUp(); - return; - } + CFIndex keyboardCount = CFSetGetCount(keyboards); // >= 1 (asserted by CopyDevices) - // Get the first keyboard + // Get an iterable array CFTypeRef devicesArray[keyboardCount]; CFSetGetValues(keyboards, devicesArray); - IOHIDDeviceRef keyboard = (IOHIDDeviceRef)devicesArray[0]; + for (CFIndex i = 0; i < keyboardCount; ++i) { - CFArrayRef keys = IOHIDDeviceCopyMatchingElements(keyboard, - NULL, - kIOHIDOptionsTypeNone); + IOHIDDeviceRef keyboard = (IOHIDDeviceRef)devicesArray[i]; + + LoadKeyboard(keyboard); + } // Release unused stuff CFRelease(keyboards); + //////////////////////////////////////////////////////////// + // At this point myKeys is filled with as many IOHIDElementRef as possible +} + + +//////////////////////////////////////////////////////////// +void HIDInputManager::LoadKeyboard(IOHIDDeviceRef keyboard) +{ + CFArrayRef keys = IOHIDDeviceCopyMatchingElements(keyboard, + NULL, + kIOHIDOptionsTypeNone); if (keys == NULL) { sf::Err() << "We got a keyboard without any keys (1)" << std::endl; - FreeUp(); return; } // How many elements are there ? CFIndex keysCount = CFArrayGetCount(keys); - + if (keysCount == 0) { sf::Err() << "We got a keyboard without any keys (2)" << std::endl; CFRelease(keys); - FreeUp(); return; } // Go through all connected elements. - for (int i = 0; i < keysCount; ++i) { + for (CFIndex i = 0; i < keysCount; ++i) { + IOHIDElementRef aKey = (IOHIDElementRef) CFArrayGetValueAtIndex(keys, i); // Skip non-matching keys elements @@ -238,131 +223,110 @@ void HIDInputManager::InitializeKeyboard() continue; } - // Get its virtual code - UInt32 usageCode = IOHIDElementGetUsage(aKey); - UInt8 virtualCode = UsageToVirtualCode(usageCode); + LoadKey(aKey); - if (virtualCode == 0xff) { - continue; // no corresponding virtual code -> skip - } - - // Now translate the virtual code to unicode according to - // the current keyboard layout - - UInt32 deadKeyState = 0; - // unicode string length is usually less or equal to 4 - UniCharCount maxStringLength = 4; - UniCharCount actualStringLength = 0; - UniChar unicodeString[maxStringLength]; - - OSStatus error; - - error = UCKeyTranslate(myLayout, // current layout - virtualCode, // our key - kUCKeyActionDown, // or kUCKeyActionUp ? - 0x100, // no modifiers - LMGetKbdType(), // keyboard's type - kUCKeyTranslateNoDeadKeysBit,// some sort of option - &deadKeyState, // unused stuff - maxStringLength, // our memory limit - &actualStringLength, // length of what we get - unicodeString); // what we get - - if (error == noErr) { - // Translation went fine - - // The corresponding SFML key code - Keyboard::Key code = Keyboard::KeyCount; - - // First we look if the key down is from a list of caracter - // that depend on keyboard localization. - if (actualStringLength > 0) { - code = LocalizedKeys(unicodeString[0]); - } - - // The key is not a localized one, so we try to find a corresponding - // code through virtual key code. - if (code == Keyboard::KeyCount) { - code = NonLocalizedKeys(virtualCode); - } - - // A code was found, wonderful! - if (code != Keyboard::KeyCount) { - - // Last step : verify that the key was not found twice or more - - // Some keys (modifiers) appears to be twice on my keyboard - // I've no idea why -// if (myKeys[code] != 0) { -// UInt32 firstUsageCode = IOHIDElementGetUsage(myKeys[code]); -// sf::Err() << "The current keyboard has several times the " -// << "same keys. Only the first one is considered " -// << "as valid. sf::Keyboard::Key is " -// << code -// << ", virtual key code is 0x" -// << std::hex -// << (UInt32)virtualCode -// << " and HID usage code is 0x" -// << usageCode -// << ". First HID usage code is 0x" -// << firstUsageCode -// << std::dec -// << "." -// << std::endl; -// } - - if (myKeys[code] == 0) { - - // Ok, everything went fine. Now we have a unique - // corresponding sf::Keyboard::Key to one IOHIDElementRef. - - myKeys[code] = aKey; - - // And don't forget to keep the reference alive for our usage - CFRetain(myKeys[code]); - - } - - } - - //////////////////////////////////////////////////////////// - // These are known to be unbound : - // Supposed Virtual | HID | Supposed Key - // =============================================== - // 0x1b | 0x2d | Hyphen - // 0x39 | 0x39 | CapsLock - // 0x47 | 0x53 | NumLock - // 0x4c | 0x58 | Keypad Enter - // 0x41 | 0x63 | Keypad Period - // 0x6e | 0x65 | Application - // 0x51 | 0x67 | Keypad Equal - // 0x4c | 0x77 | Select - -// else { // The key is unknown. -// sf::Err() << "This is an unknow key. Virtual key code is 0x" -// << std::hex -// << (UInt32)virtualCode -// << " and HID usage code is 0x" -// << usageCode -// << std::dec -// << "." -// << std::endl; -// } - - } /* if (error == noErr) */ - - else { - sf::Err() << "Cannot translate the virtual key code, error : " - << error - << std::endl; - } - - } /* for (int i = 0; i < keysCount; ++i) */ + } + // Release unused stuff CFRelease(keys); +} + + +//////////////////////////////////////////////////////////// +void HIDInputManager::LoadKey(IOHIDElementRef key) +{ + // Get its virtual code + UInt32 usageCode = IOHIDElementGetUsage(key); + UInt8 virtualCode = UsageToVirtualCode(usageCode); - //////////////////////////////////////////////////////////// - // At this point myKeys is filled with as many IOHIDElementRef as possible + if (virtualCode == 0xff) { + return; // no corresponding virtual code -> skip + } + + // Now translate the virtual code to unicode according to + // the current keyboard layout + + UInt32 deadKeyState = 0; + // unicode string length is usually less or equal to 4 + UniCharCount maxStringLength = 4; + UniCharCount actualStringLength = 0; + UniChar unicodeString[maxStringLength]; + + OSStatus error; + + error = UCKeyTranslate(myLayout, // current layout + virtualCode, // our key + kUCKeyActionDown, // or kUCKeyActionUp ? + 0x100, // no modifiers + LMGetKbdType(), // keyboard's type + kUCKeyTranslateNoDeadKeysBit,// some sort of option + &deadKeyState, // unused stuff + maxStringLength, // our memory limit + &actualStringLength, // length of what we get + unicodeString); // what we get + + if (error == noErr) { + // Translation went fine + + // The corresponding SFML key code + Keyboard::Key code = Keyboard::KeyCount; // KeyCound means 'none' + + // First we look if the key down is from a list of characters + // that depend on keyboard localization + if (actualStringLength > 0) { + code = LocalizedKeys(unicodeString[0]); + } + + // The key is not a localized one so we try to find a + // corresponding code through virtual key code + if (code == Keyboard::KeyCount) { + code = NonLocalizedKeys(virtualCode); + } + + // A code was found, wonderful! + if (code != Keyboard::KeyCount) { + + // Ok, everything went fine. Now we have a unique + // corresponding sf::Keyboard::Key to one IOHIDElementRef + + myKeys[code].push_back(key); + + // And don't forget to keep the reference alive for our usage + CFRetain(myKeys[code].back()); + + } + + //////////////////////////////////////////////////////////// + // These are known to be unbound : + // Supposed Virtual | HID | Supposed Key + // =============================================== + // 0x1b | 0x2d | Hyphen + // 0x39 | 0x39 | CapsLock + // 0x47 | 0x53 | NumLock + // 0x4c | 0x58 | Keypad Enter + // 0x41 | 0x63 | Keypad Period + // 0x6e | 0x65 | Application + // 0x51 | 0x67 | Keypad Equal + // 0x4c | 0x77 | Select + + //if (code == Keyboard::KeyCount) { // The key is unknown. + // sf::Err() << "This is an unknow key. Virtual key code is 0x" + // << std::hex + // << (UInt32)virtualCode + // << " and HID usage code is 0x" + // << usageCode + // << std::dec + // << "." + // << std::endl; + //} + + } /* if (error == noErr) */ + else { + + sf::Err() << "Cannot translate the virtual key code, error : " + << error + << std::endl; + } } @@ -376,9 +340,10 @@ void HIDInputManager::FreeUp() if (myManager != 0) CFRelease(myManager); for (unsigned int i = 0; i < Keyboard::KeyCount; ++i) { - if (myKeys[i] != 0) { - CFRelease(myKeys[i]); + for (IOHIDElements::iterator it = myKeys[i].begin(); it != myKeys[i].end(); ++it) { + CFRelease(*it); } + myKeys[i].clear(); } } @@ -403,13 +368,44 @@ CFDictionaryRef HIDInputManager::CopyDevicesMaskForManager(UInt32 page, UInt32 u return dict; } + + +//////////////////////////////////////////////////////////// +CFSetRef HIDInputManager::CopyDevices(UInt32 page, UInt32 usage) +{ + // Filter and keep only the requested devices + CFDictionaryRef mask = CopyDevicesMaskForManager(page, usage); + + IOHIDManagerSetDeviceMatching(myManager, mask); + + CFRelease(mask); + mask = 0; + + CFSetRef devices = IOHIDManagerCopyDevices(myManager); + if (devices == NULL) { + sf::Err() << "Cannot find any devices." << std::endl; + FreeUp(); + return NULL; + } + + // Is there at least one keyboard ? + CFIndex deviceCount = CFSetGetCount(devices); + if (deviceCount < 1) { + sf::Err() << "Found no device." << std::endl; + CFRelease(devices); + FreeUp(); + return NULL; + } + + return devices; +} //////////////////////////////////////////////////////////// UInt8 HIDInputManager::UsageToVirtualCode(UInt32 usage) { - // Some usage key doesn't have any corresponding virtual code or it was not - // found. + // Some usage key doesn't have any corresponding virtual + // code or it was not found (return 0xff). switch (usage) { case kHIDUsage_KeyboardErrorRollOver: return 0xff; case kHIDUsage_KeyboardPOSTFail: return 0xff;