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>
getDictionaryItemAsRef(CFDictionaryRef dict,const char * keyName)36 T getDictionaryItemAsRef(CFDictionaryRef dict, const char* keyName)
37 {
38 return CFDictionaryGetValue(dict, OIS_CFString(keyName));
39 }
40
41 template<>
getDictionaryItemAsRef(CFDictionaryRef dict,const char * keyName)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<>
getDictionaryItemAsRef(CFDictionaryRef dict,const char * keyName)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<>
getDictionaryItemAsRef(CFDictionaryRef dict,const char * keyName)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>
getArrayItemAsRef(CFArrayRef array,CFIndex idx)77 T getArrayItemAsRef(CFArrayRef array, CFIndex idx)
78 {
79 return CFArrayGetValueAtIndex(array, idx);
80 }
81
82 template<>
getArrayItemAsRef(CFArrayRef array,CFIndex idx)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 //------------------------------------------------------------------------------------------------------//
getInt32(CFNumberRef ref)94 int getInt32(CFNumberRef ref)
95 {
96 int r = 0;
97 if (r)
98 CFNumberGetValue(ref, kCFNumberIntType, &r);
99 return r;
100 }
101
102 //--------------------------------------------------------------------------------//
MacHIDManager()103 MacHIDManager::MacHIDManager()
104 {
105 }
106
107 //--------------------------------------------------------------------------------//
~MacHIDManager()108 MacHIDManager::~MacHIDManager()
109 {
110 }
111
112 //------------------------------------------------------------------------------------------------------//
initialize()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 //------------------------------------------------------------------------------------------------------//
lookUpDevices(int usage,int page)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 //------------------------------------------------------------------------------------------------------//
iterateAndOpenDevices(io_iterator_t iterator)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
getCString(CFStringRef cfString,bool * requireFree)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 //------------------------------------------------------------------------------------------------------//
enumerateDeviceProperties(CFMutableDictionaryRef propertyMap)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 //------------------------------------------------------------------------------------------------------//
parseDeviceProperties(CFDictionaryRef properties)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 //------------------------------------------------------------------------------------------------------//
parseDevicePropertiesGroup(CFDictionaryRef properties)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 //--------------------------------------------------------------------------------//
freeDeviceList()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 //--------------------------------------------------------------------------------//
totalDevices(Type iType)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 //--------------------------------------------------------------------------------//
freeDevices(Type iType)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 //--------------------------------------------------------------------------------//
vendorExist(Type iType,const std::string & vendor)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 //--------------------------------------------------------------------------------//
createObject(InputManager * creator,Type iType,bool bufferMode,const std::string & vendor)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 //--------------------------------------------------------------------------------//
destroyObject(Object * obj)469 void MacHIDManager::destroyObject(Object* obj)
470 {
471 delete obj;
472 }
473