1 /* 2 The zlib/libpng License 3 4 Copyright (c) 2006 Phillip Castaneda 5 6 This software is provided 'as-is', without any express or implied warranty. In no event will 7 the authors be held liable for any damages arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, including commercial 10 applications, and to alter it and redistribute it freely, subject to the following 11 restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not claim that 14 you wrote the original software. If you use this software in a product, 15 an acknowledgment in the product documentation would be appreciated but is 16 not required. 17 18 2. Altered source versions must be plainly marked as such, and must not be 19 misrepresented as being the original software. 20 21 3. This notice may not be removed or altered from any source distribution. 22 */ 23 #include "mac/MacHIDManager.h" 24 #include "mac/MacJoyStick.h" 25 #include "OISException.h" 26 #include "OISObject.h" 27 28 #include <iostream> 29 using namespace std; 30 31 using namespace OIS; 32 33 //------------------------------------------------------------------------------------------------------// 34 //------------------------------------------------------------------------------------------------------// 35 template<typename T> 36 T getDictionaryItemAsRef(CFDictionaryRef dict, const char* keyName) 37 { 38 return CFDictionaryGetValue(dict, OIS_CFString(keyName)); 39 } 40 41 template<> 42 CFArrayRef getDictionaryItemAsRef(CFDictionaryRef dict, const char* keyName) 43 { 44 CFTypeRef temp = CFDictionaryGetValue(dict, OIS_CFString(keyName)); 45 46 if(temp && CFGetTypeID(temp) == CFArrayGetTypeID()) 47 return (CFArrayRef)temp; 48 else 49 return 0; 50 } 51 52 template<> 53 CFStringRef getDictionaryItemAsRef(CFDictionaryRef dict, const char* keyName) 54 { 55 CFTypeRef temp = CFDictionaryGetValue(dict, OIS_CFString(keyName)); 56 57 if(temp && CFGetTypeID(temp) == CFStringGetTypeID()) 58 return (CFStringRef)temp; 59 else 60 return 0; 61 } 62 63 template<> 64 CFNumberRef getDictionaryItemAsRef(CFDictionaryRef dict, const char* keyName) 65 { 66 CFTypeRef temp = CFDictionaryGetValue(dict, OIS_CFString(keyName)); 67 68 if(temp && CFGetTypeID(temp) == CFNumberGetTypeID()) 69 return (CFNumberRef)temp; 70 else 71 return 0; 72 } 73 74 //------------------------------------------------------------------------------------------------------// 75 //------------------------------------------------------------------------------------------------------// 76 template<typename T> 77 T getArrayItemAsRef(CFArrayRef array, CFIndex idx) 78 { 79 return CFArrayGetValueAtIndex(array, idx); 80 } 81 82 template<> 83 CFDictionaryRef getArrayItemAsRef(CFArrayRef array, CFIndex idx) 84 { 85 CFTypeRef temp = CFArrayGetValueAtIndex(array, idx); 86 87 if(temp && CFGetTypeID(temp) == CFDictionaryGetTypeID()) 88 return (CFDictionaryRef)temp; 89 else 90 return 0; 91 } 92 93 //------------------------------------------------------------------------------------------------------// 94 int getInt32(CFNumberRef ref) 95 { 96 int r = 0; 97 if (r) 98 CFNumberGetValue(ref, kCFNumberIntType, &r); 99 return r; 100 } 101 102 //--------------------------------------------------------------------------------// 103 MacHIDManager::MacHIDManager() 104 { 105 } 106 107 //--------------------------------------------------------------------------------// 108 MacHIDManager::~MacHIDManager() 109 { 110 } 111 112 //------------------------------------------------------------------------------------------------------// 113 void MacHIDManager::initialize() 114 { 115 //Make the search more specific by adding usage flags 116 int usage = kHIDUsage_GD_Joystick; 117 int page = kHIDPage_GenericDesktop; 118 119 io_iterator_t iterator = lookUpDevices(usage, page); 120 121 if(iterator) 122 iterateAndOpenDevices(iterator); 123 124 //Doesn't support multiple usage flags, iterate twice 125 usage = kHIDUsage_GD_GamePad; 126 iterator = lookUpDevices(usage, page); 127 128 if(iterator) 129 iterateAndOpenDevices(iterator); 130 } 131 132 //------------------------------------------------------------------------------------------------------// 133 io_iterator_t MacHIDManager::lookUpDevices(int usage, int page) 134 { 135 CFMutableDictionaryRef deviceLookupMap = IOServiceMatching(kIOHIDDeviceKey); 136 if(!deviceLookupMap) 137 OIS_EXCEPT(E_General, "Could not setup HID device search parameters"); 138 139 CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage); 140 CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page); 141 142 CFDictionarySetValue(deviceLookupMap, CFSTR(kIOHIDPrimaryUsageKey), usageRef); 143 CFDictionarySetValue(deviceLookupMap, CFSTR(kIOHIDPrimaryUsagePageKey), pageRef); 144 145 //IOServiceGetMatchingServices consumes the map so we do not have to release it ourself 146 io_iterator_t iterator = 0; 147 IOReturn result = IOServiceGetMatchingServices(kIOMasterPortDefault, deviceLookupMap, &iterator); 148 149 CFRelease(usageRef); 150 CFRelease(pageRef); 151 152 if(result == kIOReturnSuccess) 153 { 154 return iterator; 155 } 156 //TODO: Throw exception instead? 157 else 158 { 159 return 0; 160 } 161 } 162 163 //------------------------------------------------------------------------------------------------------// 164 void MacHIDManager::iterateAndOpenDevices(io_iterator_t iterator) 165 { 166 io_object_t hidDevice = 0; 167 while ((hidDevice = IOIteratorNext(iterator)) !=0) 168 { 169 //Get the current registry items property map 170 CFMutableDictionaryRef propertyMap = 0; 171 if (IORegistryEntryCreateCFProperties(hidDevice, &propertyMap, kCFAllocatorDefault, kNilOptions) == KERN_SUCCESS && propertyMap) 172 { 173 //Go through device to find all needed info 174 HidInfo* hid = enumerateDeviceProperties(propertyMap); 175 176 if(hid) 177 { 178 //todo - we need to hold an open interface so we do not have to enumerate again later 179 //should be able to watch for device removals also 180 181 // Testing opening / closing interface 182 IOCFPlugInInterface **pluginInterface = NULL; 183 SInt32 score = 0; 184 if (IOCreatePlugInInterfaceForService(hidDevice, kIOHIDDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &pluginInterface, &score) == kIOReturnSuccess) 185 { 186 IOHIDDeviceInterface **interface; 187 188 HRESULT pluginResult = (*pluginInterface)->QueryInterface(pluginInterface, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), (void **)&(interface)); 189 190 if(pluginResult != S_OK) 191 OIS_EXCEPT(E_General, "Not able to create plugin interface"); 192 193 IODestroyPlugInInterface(pluginInterface); 194 195 hid->interface = interface; 196 197 //Check for duplicates - some devices have multiple usage 198 if(std::find(mDeviceList.begin(), mDeviceList.end(), hid) == mDeviceList.end()) 199 mDeviceList.push_back(hid); 200 } 201 } 202 } 203 } 204 205 IOObjectRelease(iterator); 206 } 207 208 const char* getCString(CFStringRef cfString, bool* requireFree) { 209 const char *useUTF8StringPtr = NULL; 210 UInt8 *freeUTF8StringPtr = NULL; 211 *requireFree = false; 212 213 CFIndex stringLength = CFStringGetLength(cfString), usedBytes = 0L; 214 215 if ((useUTF8StringPtr = CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8)) == NULL) { 216 if ((freeUTF8StringPtr = (UInt8*)malloc(stringLength + 1L)) != NULL) { 217 CFStringGetBytes(cfString, CFRangeMake(0L, stringLength), 218 kCFStringEncodingUTF8, '?', false, freeUTF8StringPtr, 219 stringLength, &usedBytes); 220 freeUTF8StringPtr[usedBytes] = 0; 221 useUTF8StringPtr = (const char *) freeUTF8StringPtr; 222 *requireFree = true; 223 } 224 } 225 226 return useUTF8StringPtr; 227 } 228 229 //------------------------------------------------------------------------------------------------------// 230 HidInfo* MacHIDManager::enumerateDeviceProperties(CFMutableDictionaryRef propertyMap) 231 { 232 HidInfo* info = new HidInfo(); 233 bool requireFree; 234 235 info->type = OISJoyStick; 236 237 CFStringRef str = getDictionaryItemAsRef<CFStringRef>(propertyMap, kIOHIDManufacturerKey); 238 if (str) { 239 const char* str_c = getCString(str, &requireFree); 240 if(str_c) { 241 info->vendor = str_c; 242 if (requireFree) 243 free((char*)str_c); 244 } else { 245 info->vendor = "Unknown Vendor"; 246 } 247 } 248 249 str = getDictionaryItemAsRef<CFStringRef>(propertyMap, kIOHIDProductKey); 250 if (str) { 251 const char* str_c = getCString(str, &requireFree); 252 if(str_c) { 253 info->productKey = str_c; 254 if (requireFree) 255 free((char*)str_c); 256 } else { 257 info->productKey = "Unknown Product"; 258 } 259 } 260 261 info->combinedKey = info->vendor + " " + info->productKey; 262 263 //Go through all items in this device (i.e. buttons, hats, sticks, axes, etc) 264 CFArrayRef array = getDictionaryItemAsRef<CFArrayRef>(propertyMap, kIOHIDElementKey); 265 if (array) 266 for (int i = 0; i < CFArrayGetCount(array); i++) 267 parseDeviceProperties(getArrayItemAsRef<CFDictionaryRef>(array, i)); 268 269 return info; 270 } 271 272 //------------------------------------------------------------------------------------------------------// 273 void MacHIDManager::parseDeviceProperties(CFDictionaryRef properties) 274 { 275 if(!properties) 276 return; 277 278 CFArrayRef array = getDictionaryItemAsRef<CFArrayRef>(properties, kIOHIDElementKey); 279 if (array) 280 { 281 for (int i = 0; i < CFArrayGetCount(array); i++) 282 { 283 CFDictionaryRef element = getArrayItemAsRef<CFDictionaryRef>(array, i); 284 if (element) 285 { 286 if(getInt32(getDictionaryItemAsRef<CFNumberRef>(element, kIOHIDElementTypeKey)) == kIOHIDElementTypeCollection) 287 { //Check if we need to recurse further intoi another collection 288 if(getInt32(getDictionaryItemAsRef<CFNumberRef>(element, kIOHIDElementUsagePageKey)) == kHIDPage_GenericDesktop) 289 parseDeviceProperties(element); 290 } 291 else 292 { 293 switch(getInt32(getDictionaryItemAsRef<CFNumberRef>(element, kIOHIDElementUsagePageKey))) 294 { 295 case kHIDPage_GenericDesktop: 296 switch(getInt32(getDictionaryItemAsRef<CFNumberRef>(element, kIOHIDElementUsageKey))) 297 { 298 case kHIDUsage_GD_Pointer: 299 cout << "\tkHIDUsage_GD_Pointer\n"; 300 parseDevicePropertiesGroup(element); 301 break; 302 case kHIDUsage_GD_X: 303 case kHIDUsage_GD_Y: 304 case kHIDUsage_GD_Z: 305 case kHIDUsage_GD_Rx: 306 case kHIDUsage_GD_Ry: 307 case kHIDUsage_GD_Rz: 308 cout << "\tAxis\n"; 309 break; 310 case kHIDUsage_GD_Slider: 311 case kHIDUsage_GD_Dial: 312 case kHIDUsage_GD_Wheel: 313 cout << "\tUnsupported kHIDUsage_GD_Wheel\n"; 314 break; 315 case kHIDUsage_GD_Hatswitch: 316 cout << "\tUnsupported - kHIDUsage_GD_Hatswitch\n"; 317 break; 318 } 319 break; 320 case kHIDPage_Button: 321 cout << "\tkHIDPage_Button\n"; 322 break; 323 } 324 } 325 } 326 } 327 } 328 } 329 330 //------------------------------------------------------------------------------------------------------// 331 void MacHIDManager::parseDevicePropertiesGroup(CFDictionaryRef properties) 332 { 333 if(!properties) 334 return; 335 336 CFArrayRef array = getDictionaryItemAsRef<CFArrayRef>(properties, kIOHIDElementKey); 337 if(array) 338 { 339 for (int i = 0; i < CFArrayGetCount(array); i++) 340 { 341 CFDictionaryRef element = getArrayItemAsRef<CFDictionaryRef>(array, i); 342 if (element) 343 { 344 switch(getInt32(getDictionaryItemAsRef<CFNumberRef>(element, kIOHIDElementUsagePageKey))) 345 { 346 case kHIDPage_GenericDesktop: 347 switch(getInt32(getDictionaryItemAsRef<CFNumberRef>(element, kIOHIDElementUsageKey))) 348 { 349 case kHIDUsage_GD_X: 350 case kHIDUsage_GD_Y: 351 case kHIDUsage_GD_Z: 352 case kHIDUsage_GD_Rx: 353 case kHIDUsage_GD_Ry: 354 case kHIDUsage_GD_Rz: 355 cout << "\t\tAxis\n"; 356 break; 357 case kHIDUsage_GD_Slider: 358 case kHIDUsage_GD_Dial: 359 case kHIDUsage_GD_Wheel: 360 cout << "\tUnsupported - kHIDUsage_GD_Wheel\n"; 361 break; 362 case kHIDUsage_GD_Hatswitch: 363 cout << "\tUnsupported - kHIDUsage_GD_Hatswitch\n"; 364 break; 365 } 366 break; 367 case kHIDPage_Button: 368 break; 369 } 370 } 371 } 372 } 373 } 374 375 //--------------------------------------------------------------------------------// 376 DeviceList MacHIDManager::freeDeviceList() 377 { 378 DeviceList ret; 379 HidInfoList::iterator it = mDeviceList.begin(), end = mDeviceList.end(); 380 for(; it != end; ++it) 381 { 382 if((*it)->inUse == false) 383 ret.insert(std::make_pair((*it)->type, (*it)->combinedKey)); 384 } 385 386 return ret; 387 } 388 389 //--------------------------------------------------------------------------------// 390 int MacHIDManager::totalDevices(Type iType) 391 { 392 int ret = 0; 393 HidInfoList::iterator it = mDeviceList.begin(), end = mDeviceList.end(); 394 395 for(; it != end; ++it) 396 { 397 if((*it)->type == iType) 398 ret++; 399 } 400 401 return ret; 402 } 403 404 //--------------------------------------------------------------------------------// 405 int MacHIDManager::freeDevices(Type iType) 406 { 407 int ret = 0; 408 HidInfoList::iterator it = mDeviceList.begin(), end = mDeviceList.end(); 409 410 for(; it != end; ++it) 411 { 412 if((*it)->inUse == false && (*it)->type == iType) 413 ret++; 414 } 415 416 return ret; 417 } 418 419 //--------------------------------------------------------------------------------// 420 bool MacHIDManager::vendorExist(Type iType, const std::string & vendor) 421 { 422 HidInfoList::iterator it = mDeviceList.begin(), end = mDeviceList.end(); 423 424 for(; it != end; ++it) 425 { 426 if((*it)->type == iType && (*it)->combinedKey == vendor) 427 return true; 428 } 429 430 return false; 431 } 432 433 //--------------------------------------------------------------------------------// 434 Object* MacHIDManager::createObject(InputManager* creator, Type iType, bool bufferMode, 435 const std::string & vendor) 436 { 437 Object *obj = 0; 438 439 HidInfoList::iterator it = mDeviceList.begin(), end = mDeviceList.end(); 440 for(; it != end; ++it) 441 { 442 if((*it)->inUse == false && (*it)->type == iType && (vendor == "" || (*it)->combinedKey == vendor)) 443 { 444 int totalDevs = totalDevices(iType); 445 int freeDevs = freeDevices(iType); 446 int devID = totalDevs - freeDevs; 447 448 switch(iType) 449 { 450 case OISJoyStick: 451 { 452 obj = new MacJoyStick((*it)->combinedKey, bufferMode, *it, creator, devID); 453 (*it)->inUse = true; 454 return obj; 455 } 456 case OISTablet: 457 //Create MacTablet 458 break; 459 default: 460 break; 461 } 462 } 463 } 464 465 return obj; 466 } 467 468 //--------------------------------------------------------------------------------// 469 void MacHIDManager::destroyObject(Object* obj) 470 { 471 delete obj; 472 } 473