• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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