Home | History | Annotate | Download | only in mac
      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