// Set up a config record for saving
// takes an input records, returns record user can save as they want 
// Note: the save rec must be pre-allocated by the calling app and will be filled out
void HIDSetElementConfig (pRecSaveHID pConfigRec, IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef, int actionCookie)
{
	// must save:
    // actionCookie
    // Device: serial,vendorID, productID, location, usagePage, usage
    // Element: cookie, usagePage, usage,
    pConfigRec->actionCookie = actionCookie;
    // device
    // need to add serial number when I have a test case
	if (inIOHIDDeviceRef && inIOHIDElementRef) {
		pConfigRec->device.vendorID = IOHIDDevice_GetVendorID( inIOHIDDeviceRef );
		pConfigRec->device.productID = IOHIDDevice_GetProductID( inIOHIDDeviceRef );
		pConfigRec->device.locID = IOHIDDevice_GetLocationID( inIOHIDDeviceRef );
		pConfigRec->device.usage = IOHIDDevice_GetUsage( inIOHIDDeviceRef );
		pConfigRec->device.usagePage = IOHIDDevice_GetUsagePage( inIOHIDDeviceRef );
		
		pConfigRec->element.usagePage = IOHIDElementGetUsagePage( inIOHIDElementRef );
		pConfigRec->element.usage = IOHIDElementGetUsage( inIOHIDElementRef );
		pConfigRec->element.minReport = IOHIDElement_GetCalibrationSaturationMin( inIOHIDElementRef );
		pConfigRec->element.maxReport = IOHIDElement_GetCalibrationSaturationMax( inIOHIDElementRef );
		pConfigRec->element.cookie = IOHIDElementGetCookie( inIOHIDElementRef );
	} else {
		pConfigRec->device.vendorID = 0;
		pConfigRec->device.productID = 0;
		pConfigRec->device.locID = 0;
		pConfigRec->device.usage = 0;
		pConfigRec->device.usagePage = 0;
		
		pConfigRec->element.usagePage = 0;
		pConfigRec->element.usage = 0;
		pConfigRec->element.minReport = 0;
		pConfigRec->element.maxReport = 0;
		pConfigRec->element.cookie = 0;
	}
}
Boolean HIDSaveElementPref( const CFStringRef inKeyCFStringRef,
						   CFStringRef inAppCFStringRef,
						   IOHIDDeviceRef inIOHIDDeviceRef,
						   IOHIDElementRef inIOHIDElementRef )
{
	Boolean success = FALSE;
	
	if ( inKeyCFStringRef && inAppCFStringRef && inIOHIDDeviceRef && inIOHIDElementRef ) {
		long vendorID = IOHIDDevice_GetVendorID( inIOHIDDeviceRef );
		require( vendorID, Oops );
		
		long productID = IOHIDDevice_GetProductID( inIOHIDDeviceRef );
		require( productID, Oops );
		
		long locID = IOHIDDevice_GetLocationID( inIOHIDDeviceRef );
		require( locID, Oops );
		
		uint32_t usagePage = IOHIDDevice_GetUsagePage( inIOHIDDeviceRef );
		uint32_t usage = IOHIDDevice_GetUsage( inIOHIDDeviceRef );
		
		if ( !usagePage || !usage ) {
			usagePage = IOHIDDevice_GetPrimaryUsagePage( inIOHIDDeviceRef );
			usage = IOHIDDevice_GetPrimaryUsage( inIOHIDDeviceRef );
		}
		require( usagePage && usage, Oops );
		
		uint32_t usagePageE = IOHIDElementGetUsagePage( inIOHIDElementRef );
		uint32_t usageE = IOHIDElementGetUsage( inIOHIDElementRef );
		IOHIDElementCookie eleCookie = IOHIDElementGetCookie( inIOHIDElementRef );
		
		CFStringRef prefCFStringRef = CFStringCreateWithFormat( kCFAllocatorDefault, NULL,
															   CFSTR( "d:{v:%ld, p:%ld, l:%ld, p:%ld, u:%ld}, e:{p:%ld, u:%ld, c:%ld}" ),
															   vendorID,
															   productID,
															   locID,
															   usagePage,
															   usage,
															   usagePageE,
															   usageE,
															   eleCookie );
		
		if ( prefCFStringRef ) {
			CFPreferencesSetAppValue( inKeyCFStringRef, prefCFStringRef, inAppCFStringRef );
			CFRelease( prefCFStringRef );
			success = TRUE;
		}
	}
Oops:   ;
	return success;
}   // HIDSaveElementPref
// Set up a config record for saving
// takes an input records, returns record user can save as they want
// Note: the save rec must be pre-allocated by the calling app and will be filled out
void HIDSetElementConfig(HID_info_ptr		inHIDInfoPtr,
                         IOHIDDeviceRef		inIOHIDDeviceRef,
                         IOHIDElementRef	inIOHIDElementRef,
                         IOHIDElementCookie actionCookie) {
	// must save:
	// actionCookie
	// Device: serial,vendorID, productID, location, usagePage, usage
	// Element: cookie, usagePage, usage,
	inHIDInfoPtr->actionCookie = actionCookie;
	// device
	// need to add serial number when I have a test case
	if (inIOHIDDeviceRef &&
	    inIOHIDElementRef)
	{
		inHIDInfoPtr->device.vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef);
		inHIDInfoPtr->device.productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef);
		inHIDInfoPtr->device.locID = IOHIDDevice_GetLocationID(inIOHIDDeviceRef);
		inHIDInfoPtr->device.usage = IOHIDDevice_GetUsage(inIOHIDDeviceRef);
		inHIDInfoPtr->device.usagePage = IOHIDDevice_GetUsagePage(inIOHIDDeviceRef);
		if (!inHIDInfoPtr->device.usagePage ||
		    !inHIDInfoPtr->device.usage)
		{
			inHIDInfoPtr->device.usagePage = IOHIDDevice_GetPrimaryUsagePage(inIOHIDDeviceRef);
			inHIDInfoPtr->device.usage = IOHIDDevice_GetPrimaryUsage(inIOHIDDeviceRef);
		}

		inHIDInfoPtr->element.usagePage = IOHIDElementGetUsagePage(inIOHIDElementRef);
		inHIDInfoPtr->element.usage = IOHIDElementGetUsage(inIOHIDElementRef);
		inHIDInfoPtr->element.minReport = IOHIDElement_GetCalibrationSaturationMin(inIOHIDElementRef);
		inHIDInfoPtr->element.maxReport = IOHIDElement_GetCalibrationSaturationMax(inIOHIDElementRef);
		inHIDInfoPtr->element.cookie = IOHIDElementGetCookie(inIOHIDElementRef);
	} else {
		inHIDInfoPtr->device.vendorID = 0;
		inHIDInfoPtr->device.productID = 0;
		inHIDInfoPtr->device.locID = 0;
		inHIDInfoPtr->device.usage = 0;
		inHIDInfoPtr->device.usagePage = 0;

		inHIDInfoPtr->element.usagePage = 0;
		inHIDInfoPtr->element.usage = 0;
		inHIDInfoPtr->element.minReport = 0;
		inHIDInfoPtr->element.maxReport = 0;
		inHIDInfoPtr->element.cookie = 0;
	}
}                                                                               // HIDSetElementConfig
// Get matching element from config record
// takes a pre-allocated and filled out config record
// search for matching device
// return IOHIDDeviceRef, IOHIDElementRef and cookie for action
IOHIDElementCookie HIDGetElementConfig(HID_info_ptr		inHIDInfoPtr,
                                       IOHIDDeviceRef * outIOHIDDeviceRef,
                                       IOHIDElementRef *outIOHIDElementRef) {
	if (!inHIDInfoPtr->device.locID &&
	    !inHIDInfoPtr->device.vendorID &&
	    !inHIDInfoPtr->device.productID &&
	    !inHIDInfoPtr->device.usage &&
	    !inHIDInfoPtr->device.usagePage)                                        //
	{                                                                           //
		                                                                        // early out
		*outIOHIDDeviceRef = NULL;
		*outIOHIDElementRef = NULL;

		return (inHIDInfoPtr->actionCookie);
	}

	IOHIDDeviceRef tIOHIDDeviceRef = NULL,
	               foundIOHIDDeviceRef = NULL;
	IOHIDElementRef tIOHIDElementRef = NULL,
	                foundIOHIDElementRef = NULL;

	CFIndex devIdx,
	        devCnt,
	        idx,
	        cnt;
	// compare to current device list for matches
	// look for device
	if (inHIDInfoPtr->device.locID &&
	    inHIDInfoPtr->device.vendorID &&
	    inHIDInfoPtr->device.productID)                                         // look for specific
	{                                                                           // device
		                                                                        // type plug in to same
		                                                                        // port
		devCnt = CFArrayGetCount(gDeviceCFArrayRef);
		for (devIdx = 0; devIdx < devCnt; devIdx++) {
			tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef,
			                                                          devIdx);
			if (!tIOHIDDeviceRef) {
				continue;                                                       // skip this device
			}

			uint32_t locID = IOHIDDevice_GetLocationID(tIOHIDDeviceRef);
			uint32_t vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef);
			uint32_t productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef);
			if ((inHIDInfoPtr->device.locID == locID) &&
			    (inHIDInfoPtr->device.vendorID == vendorID) &&
			    (inHIDInfoPtr->device.productID == productID))
			{
				foundIOHIDDeviceRef = tIOHIDDeviceRef;
			}
			if (foundIOHIDDeviceRef) {
				break;
			}
		}                                                                       // next devIdx
		if (foundIOHIDDeviceRef) {
			CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(foundIOHIDDeviceRef,
			                                                               NULL,
			                                                               kIOHIDOptionsTypeNone);
			if (elementCFArrayRef) {
				cnt = CFArrayGetCount(elementCFArrayRef);
				for (idx = 0; idx < cnt; idx++) {
					tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef,
					                                                            idx);
					if (!tIOHIDElementRef) {
						continue;                                               // skip this element
					}

					IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef);
					if (inHIDInfoPtr->element.cookie == cookie) {
						foundIOHIDElementRef = tIOHIDElementRef;
					}
					if (foundIOHIDElementRef) {
						break;
					}
				}
				if (!foundIOHIDElementRef) {
					cnt = CFArrayGetCount(elementCFArrayRef);
					for (idx = 0; idx < cnt; idx++) {
						tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef,
						                                                            idx);
						if (!tIOHIDElementRef) {
							continue;                                           // skip this element
						}

						uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef);
						uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef);
						if ((inHIDInfoPtr->element.usage == usage) &&
						    (inHIDInfoPtr->element.usagePage == usagePage))
						{
							foundIOHIDElementRef = tIOHIDElementRef;
						}
						if (foundIOHIDElementRef) {
							break;
						}
					}                                                           // next idx
				}                                                               // if (!foundIOHIDElementRef)
				if (foundIOHIDElementRef) {                                     // if same device
					                                                            // setup the calibration
					IOHIDElement_SetupCalibration(tIOHIDElementRef);

					IOHIDElement_SetCalibrationSaturationMin(tIOHIDElementRef,
					                                         inHIDInfoPtr->element.minReport);
					IOHIDElement_SetCalibrationSaturationMax(tIOHIDElementRef,
					                                         inHIDInfoPtr->element.maxReport);
				}

				CFRelease(elementCFArrayRef);
			}                                                                   // if (elementCFArrayRef)
		}                                                                       // if (foundIOHIDDeviceRef)
		                                                                        // if we have not found a match, look at just vendor
		                                                                        // and product
		if ((!foundIOHIDDeviceRef) &&
		    (inHIDInfoPtr->device.vendorID &&
		     inHIDInfoPtr->device.productID))
		{
			devCnt = CFArrayGetCount(gDeviceCFArrayRef);
			for (devIdx = 0; devIdx < devCnt; devIdx++) {
				tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef,
				                                                          devIdx);
				if (!tIOHIDDeviceRef) {
					continue;                                                   // skip this device
				}

				uint32_t vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef);
				uint32_t productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef);
				if ((inHIDInfoPtr->device.vendorID == vendorID) &&
				    (inHIDInfoPtr->device.productID == productID))
				{
					foundIOHIDDeviceRef = tIOHIDDeviceRef;
				}
				if (foundIOHIDDeviceRef) {
					break;
				}
			}
			// match elements by cookie since same device type
			if (foundIOHIDDeviceRef) {
				CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(foundIOHIDDeviceRef,
				                                                               NULL,
				                                                               kIOHIDOptionsTypeNone);
				if (elementCFArrayRef) {
					cnt = CFArrayGetCount(elementCFArrayRef);
					for (idx = 0; idx < cnt; idx++) {
						tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef,
						                                                            idx);
						if (!tIOHIDElementRef) {
							continue;                                           // skip this element
						}

						IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef);
						if (inHIDInfoPtr->element.cookie == cookie) {
							foundIOHIDElementRef = tIOHIDElementRef;
						}
						if (foundIOHIDElementRef) {
							break;
						}
					}
					// if no cookie match (should NOT occur) match on usage
					if (!foundIOHIDElementRef) {
						cnt = CFArrayGetCount(elementCFArrayRef);
						for (idx = 0; idx < cnt; idx++) {
							tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef,
							                                                            idx);
							if (!tIOHIDElementRef) {
								continue;                                       // skip this element
							}

							uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef);
							uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef);
							if ((inHIDInfoPtr->element.usage == usage) &&
							    (inHIDInfoPtr->element.usagePage == usagePage))
							{
								foundIOHIDElementRef = tIOHIDElementRef;
							}
							if (foundIOHIDElementRef) {
								break;
							}
						}                                                       // next idx
					}                                                           // if (!foundIOHIDElementRef)
					if (foundIOHIDElementRef) {                                 // if same device
						                                                        // setup the calibration
						IOHIDElement_SetupCalibration(tIOHIDElementRef);
						IOHIDElement_SetCalibrationSaturationMin(tIOHIDElementRef,
						                                         inHIDInfoPtr->element.minReport);
						IOHIDElement_SetCalibrationSaturationMax(tIOHIDElementRef,
						                                         inHIDInfoPtr->element.maxReport);
					}

					CFRelease(elementCFArrayRef);
				}                                                               // if (elementCFArrayRef)
			}                                                                   // if (foundIOHIDDeviceRef)
		}                                                                       // if (device not found & vendorID & productID)
	}                                                                           // if (inHIDInfoPtr->locID &&
	                                                                            // inHIDInfoPtr->device.vendorID &&
	                                                                            // inHIDInfoPtr->device.productID)
	                                                                            // can't find matching device return NULL, do not
	                                                                            // return first device
	if ((!foundIOHIDDeviceRef) ||
	    (!foundIOHIDElementRef))
	{
		// no HID device
		*outIOHIDDeviceRef = NULL;
		*outIOHIDElementRef = NULL;

		return (inHIDInfoPtr->actionCookie);
	} else {
		// HID device
		*outIOHIDDeviceRef = foundIOHIDDeviceRef;
		*outIOHIDElementRef = foundIOHIDElementRef;

		return (inHIDInfoPtr->actionCookie);
	}
}                                                                               // HIDGetElementConfig
Boolean HIDFindDeviceAndElement(const HID_info_rec *inSearchInfo,
                                IOHIDDeviceRef *	outFoundDevice,
                                IOHIDElementRef *	outFoundElement) {
	Boolean result = false;

	IOHIDDeviceRef bestIOHIDDeviceRef = NULL;
	IOHIDElementRef bestIOHIDElementRef = NULL;
	int bestScore = 0;

	CFIndex devIndex,
	        devCount = CFArrayGetCount(gDeviceCFArrayRef);

	for (devIndex = 0; devIndex < devCount; devIndex++) {
		int deviceScore = 1;

		IOHIDDeviceRef tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef,
		                                                                         devIndex);
		if (!tIOHIDDeviceRef) {
			continue;
		}
		// match vendorID, productID (+10, +8)
		if (inSearchInfo->device.vendorID) {
			uint32_t vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef);
			if (vendorID) {
				if (inSearchInfo->device.vendorID == vendorID) {
					deviceScore += 10;
					if (inSearchInfo->device.productID) {
						uint32_t productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef);
						if (productID) {
							if (inSearchInfo->device.productID == productID) {
								deviceScore += 8;
							}                                                   // if (inSearchInfo->device.productID == productID)
						}                                                       // if (productID)
					}                                                           // if (inSearchInfo->device.productID)
				}                                                               // if (inSearchInfo->device.vendorID == vendorID)
			}                                                                   // if vendorID
		}                                                                       // if search->device.vendorID
		                                                                        // match usagePage & usage (+9)
		if (inSearchInfo->device.usagePage &&
		    inSearchInfo->device.usage)
		{
			uint32_t usagePage = IOHIDDevice_GetUsagePage(tIOHIDDeviceRef);
			uint32_t usage = IOHIDDevice_GetUsage(tIOHIDDeviceRef);
			if (!usagePage ||
			    !usage)
			{
				usagePage = IOHIDDevice_GetPrimaryUsagePage(tIOHIDDeviceRef);
				usage = IOHIDDevice_GetPrimaryUsage(tIOHIDDeviceRef);
			}
			if (usagePage) {
				if (inSearchInfo->device.usagePage == usagePage) {
					if (usage) {
						if (inSearchInfo->device.usage == usage) {
							deviceScore += 9;
						}                                                       // if (inSearchInfo->usage == usage)
					}                                                           // if (usage)
				}                                                               // if (inSearchInfo->usagePage == usagePage)
			}                                                                   // if (usagePage)
		}                                                                       // if (inSearchInfo->usagePage &&
		                                                                        // inSearchInfo->usage)
		                                                                        // match location ID (+5)
		if (inSearchInfo->device.locID) {
			uint32_t locID = IOHIDDevice_GetLocationID(tIOHIDDeviceRef);
			if (locID) {
				if (inSearchInfo->device.locID == locID) {
					deviceScore += 5;
				}
			}
		}

		// iterate over all elements of this device
		gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef,
		                                                     NULL,
		                                                     0);
		if (gElementCFArrayRef) {
			CFIndex eleIndex,
			        eleCount = CFArrayGetCount(gElementCFArrayRef);
			for (eleIndex = 0; eleIndex < eleCount; eleIndex++) {
				IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef,
				                                                                            eleIndex);
				if (!tIOHIDElementRef) {
					continue;
				}

				int score = deviceScore;
				// match usage page, usage & cookie
				if (inSearchInfo->element.usagePage &&
				    inSearchInfo->element.usage)
				{
					uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef);
					if (inSearchInfo->element.usagePage == usagePage) {
						uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef);
						if (inSearchInfo->element.usage == usage) {
							score += 5;
							IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef);
							if (inSearchInfo->element.cookie == cookie) {
								score += 4;
							}                                                   // cookies match
						} else {
							score = 0;
						}                                                       // usages match
					} else {
						score = 0;
					}                                                           // usage pages match
				}                                                               // if (search usage page & usage)

#if LOG_SCORING
				if (kHIDPage_KeyboardOrKeypad != tElementRef->usagePage) {      // skip keyboards here
					printf("%s: (%ld:%ld)-I-Debug, score: %ld\t",
					       __PRETTY_FUNCTION__,
					       inSearchInfo->element.usagePage,
					       inSearchInfo->element.usage,
					       score);
					HIDPrintElement(tIOHIDElementRef);
				}

#endif                                                                          // LOG_SCORING
				if (score > bestScore) {
					bestIOHIDDeviceRef = tIOHIDDeviceRef;
					bestIOHIDElementRef = tIOHIDElementRef;
					bestScore = score;
#if LOG_SCORING
					printf("%s: (%ld:%ld)-I-Debug, better score: %ld\t",
					       __PRETTY_FUNCTION__,
					       inSearchInfo->element.usagePage,
					       inSearchInfo->element.usage,
					       score);
					HIDPrintElement(bestIOHIDElementRef);
#endif                                                                          // LOG_SCORING
				}
			}                                                                   // for elements...

			CFRelease(gElementCFArrayRef);
			gElementCFArrayRef = NULL;
		}                                                                       // if (gElementCFArrayRef)
	}                                                                           // for (devIndex = 0; devIndex < devCount;
	                                                                            // devIndex++)
	if (bestIOHIDDeviceRef ||
	    bestIOHIDElementRef)
	{
		*outFoundDevice = bestIOHIDDeviceRef;
		*outFoundElement = bestIOHIDElementRef;
#if LOG_SCORING
		printf("%s: (%ld:%ld)-I-Debug, best score: %ld\t",
		       __PRETTY_FUNCTION__,
		       inSearchInfo->element.usagePage,
		       inSearchInfo->element.usage,
		       bestScore);
		HIDPrintElement(bestIOHIDElementRef);
#endif                                                                          // LOG_SCORING
		result = true;
	}

	return (result);
}                                                                               // HIDFindDeviceAndElement
// *************************************************************************
//
// HIDConfigureAction(outIOHIDDeviceRef, outIOHIDElementRef, inTimeout)
//
// Purpose: polls all devices and elements for a change greater than kPercentMove.
// Times out after given time returns 1 and pointer to device and element
// if found; returns 0 and NULL for both parameters if not found
//
// Inputs:	outIOHIDDeviceRef	- address where to store the device
// outIOHIDElementRef	- address where to store the element
// inTimeout	- the timeout
// Returns: Boolean		- if successful
// outIOHIDDeviceRef	- the device
// outIOHIDElementRef	- the element
//
Boolean HIDConfigureActionOfType(actionTypeMask		inActionTypeMask,
                                 double				inTimeout,
                                 IOHIDDeviceRef *	outIOHIDDeviceRef,
                                 IOHIDElementRef *	outIOHIDElementRef) {
	// param error?
	if (!outIOHIDDeviceRef ||
	    !outIOHIDElementRef)
	{
		return (false);
	}
	if (!gDeviceCFArrayRef) {                                                   // if we do not have a device list
		                                                                        // and	we can't build another list
		if (!HIDBuildDeviceList(0,
		                        0) ||
		    !gDeviceCFArrayRef)
		{
			return (false);                                                     // bail
		}
	}

	IOHIDDeviceRef tIOHIDDeviceRef;
	IOHIDElementRef tIOHIDElementRef;

	IOHIDElementType elementType = 0;

	switch (inActionTypeMask) {
		case kActionTypeButton:
		{
			elementType = kIOHIDElementTypeInput_Button;
			break;
		}

		case kActionTypeAxis:
		{
			elementType = kIOHIDElementTypeInput_Misc;
			break;
		}

		case kActionTypeAll:
		default:
		{
			elementType = 0;
			break;
		}
	}                                                                           // switch

	// determine the maximum number of elements
	CFIndex maxElements = 0;
	CFIndex devIndex,
	        devCount = CFArrayGetCount(gDeviceCFArrayRef);
	for (devIndex = 0; devIndex < devCount; devIndex++) {
		tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef,
		                                                          devIndex);
		if (!tIOHIDDeviceRef) {
			continue;                                                           // skip this one
		}

		// HIDDumpDeviceInfo(tIOHIDDeviceRef);
		CFIndex count = HIDCountDeviceElementsOfType(tIOHIDDeviceRef,
		                                             elementType);
		if (count > maxElements) {
			maxElements = count;
		}
	}
	if (!(devCount *
	      maxElements))
	{
		return (false);
	}

#if true
//    NSDictionary * matchDictionary = @{@(kIOHIDElementTypeKey): @(elementType)};
	const void *keys[] = {CFSTR(kIOHIDElementTypeKey)};
	const void *vals[] = {CFNumberCreate(kCFAllocatorDefault,
		                                 kCFNumberIntType,
		                                 &elementType)};
	CFDictionaryRef matchingDict = CFDictionaryCreate(kCFAllocatorDefault,
	                                                  keys,
	                                                  vals,
	                                                  1,
	                                                  &kCFTypeDictionaryKeyCallBacks,
	                                                  &kCFTypeDictionaryValueCallBacks);
	CFRelease(vals[0]);
#endif                                                                          // if 1

	// allocate an array of int's in which to store devCount * maxElements values
	double *saveValueArray = (double *) calloc(devCount *
	                                           maxElements,
	                                           sizeof(double));                 // clear 2D array to save values

	// remember when we start; used to calculate timeout
	clock_t start = clock(),
	        end;

	// on first pass store initial values / compare current values to initial values on subsequent passes
	Boolean found = false,
	        first = true;

	while (!found) {
		double maxDeltaPercent = 0;                                             // we want to find the one that moves the most
		                                                                        // (percentage wise)
		for (devIndex = 0; devIndex < devCount; devIndex++) {
			tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef,
			                                                          devIndex);
			if (!tIOHIDDeviceRef) {
				continue;                                                       // skip this one
			}

			gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef,
			                                                     matchingDict,
			                                                     kIOHIDOptionsTypeNone);
			if (gElementCFArrayRef) {
				CFIndex eleIndex,
				        eleCount = CFArrayGetCount(gElementCFArrayRef);
				for (eleIndex = 0; eleIndex < eleCount; eleIndex++) {
					tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef,
					                                                            eleIndex);
					if (!tIOHIDElementRef) {
						continue;
					}

					IOHIDElementType tIOHIDElementType = IOHIDElementGetType(tIOHIDElementRef);
					// only care about inputs (no outputs or features)
					if (tIOHIDElementType <= kIOHIDElementTypeInput_ScanCodes) {
						if (IOHIDElementIsArray(tIOHIDElementRef)) {
							// printf("ARRAY!\n");
							continue;                                           // skip array elements
						}
						if (elementType &&
						    ((tIOHIDElementType != elementType)))
						{
							continue;
						}

						uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef);
						uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef);
						uint32_t reportCount = IOHIDElementGetReportCount(tIOHIDElementRef);
#ifdef DEBUG
						if (first) {
							HIDDumpElementInfo(tIOHIDElementRef);
							fflush(stdout);
							uint32_t vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef);
							uint32_t productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef);
							IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef);
							if ((0x054C == vendorID) &&
							    (0x0268 == productID) &&
							    (0x001E == (uint32_t) cookie))
							{
								// printf("DING!\n");
							}
						}

#endif                                                                          // ifdef DEBUG

#if true                                                                        // work-around for IOHIDValueGetScaledValue crash
						                                                        // (when element report count > 1)
						if (reportCount > 1) {
							// printf("REPORT!\n");
							continue;                                           // skip reports
						}

#endif                                                                          // if 1
						// ignore PID elements and arrays
						if ((kHIDPage_PID != usagePage) &&
						    ((-1) != usage))
						{
							// get this elements current value
							double value = 0.0;                                 // default value is zero
							IOHIDValueRef tIOHIDValueRef;
							IOReturn ioReturn = IOHIDDeviceGetValue(tIOHIDDeviceRef,
							                                        tIOHIDElementRef,
							                                        &tIOHIDValueRef);
							if (kIOReturnSuccess == ioReturn) {
								value = IOHIDValueGetScaledValue(tIOHIDValueRef,
								                                 kIOHIDValueScaleTypePhysical);
							}
							if (first) {
								saveValueArray[(devIndex *
								                maxElements) +
								               eleIndex] = value;
							} else {
								double initialValue = saveValueArray[(devIndex *
								                                      maxElements) +
								                                     eleIndex];

								CFIndex valueMin = IOHIDElementGetPhysicalMin(tIOHIDElementRef);
								CFIndex valueMax = IOHIDElementGetPhysicalMax(tIOHIDElementRef);

								double deltaPercent = fabs((initialValue -
								                            value) *
								                           100.0 /
								                           (valueMax -
								                            valueMin));
#if false                                                                       // debug code useful to dump out value info for
								                                                // specific (vendorID, productID, usagePage and
								                                                // usage) device
								if (!first) {
									// Device: 0x13b6a0 = { Logitech Inc. - WingMan Force 3D,	vendorID:	0x046D,		productID:	0xC283,
									// usage: 0x0001:0x0004, "Generic Desktop Joystick"
									if ((vendorID == 0x046D) &&
									    (productID == 0xC283))
									{
										if ((kHIDPage_GenericDesktop == usagePage) &&
										    (kHIDUsage_GD_Rz == usage))
										{
											printf("initial: %6.2f, value: %6.2f, diff: %6.2f, delta percent: %6.2f!\n",
											       initialValue,
											       value,
											       fabs(initialValue -
											            value),
											       deltaPercent);
										}
									}
								}

								deltaPercent = 0.0;
#endif // if false
								if (deltaPercent >= kPercentMove) {
									found = true;
									if (deltaPercent > maxDeltaPercent) {
										maxDeltaPercent = deltaPercent;

										*outIOHIDDeviceRef = tIOHIDDeviceRef;
										*outIOHIDElementRef = tIOHIDElementRef;
									}

									break;
								}
							}                                                   // if first
						}                                                       // if usage
					}                                                           // if type
				}                                                               // for elements...

				CFRelease(gElementCFArrayRef);
				gElementCFArrayRef = NULL;
			}                                                                   // if (gElementCFArrayRef)
			if (found) {
				// HIDDumpElementInfo(tIOHIDElementRef);
				break;                                                          // DONE!
			}
		}                                                                       // for devices

		first = false;                                                          // no longer the first pass

		// are we done?
		end = clock();
		double secs = (double) (end -
		                        start) /
		              CLOCKS_PER_SEC;
		if (secs > inTimeout) {
			break;                                                              // (yes) timeout
		}
	}                                                                           // while (!found)
	if (saveValueArray) {
		free(saveValueArray);
	}
	// return device and element moved
	if (!found) {
		*outIOHIDDeviceRef = NULL;
		*outIOHIDElementRef = NULL;
	}

	CFRelease(matchingDict);

	return (found);
}                                                                               // HIDConfigureAction
Boolean HIDConfigureAction( IOHIDDeviceRef* outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef, float inTimeout )
{
	// param error?
	if ( !outIOHIDDeviceRef || !outIOHIDElementRef ) {
		return 0;
	}
	
	if ( !gDeviceCFArrayRef ) { // if we do not have a device list
		// and  we can't build another list
		if ( !HIDBuildDeviceList( 0, 0 ) || !gDeviceCFArrayRef ) {
			return FALSE;	// bail
		}
	}
	
	IOHIDDeviceRef tIOHIDDeviceRef;	
	IOHIDElementRef tIOHIDElementRef;	
	
	// remember when we start; used to calculate timeout
	clock_t start = clock(), end;
	
	// determine the maximum number of elements
	CFIndex maxElements = 0;
	CFIndex devIndex, devCount = CFArrayGetCount( gDeviceCFArrayRef );
	for ( devIndex = 0; devIndex < devCount; devIndex++ ) {
		tIOHIDDeviceRef = ( IOHIDDeviceRef ) CFArrayGetValueAtIndex( gDeviceCFArrayRef, devIndex );
		
		if ( !tIOHIDDeviceRef ) {
			continue;               // skip this one
		}
		
		UInt32 count = HIDCountDeviceElements( tIOHIDDeviceRef, kHIDElementTypeInput );
		if ( count > maxElements ) {
			maxElements = count;
		}
	}
	
	// allocate an array of int's in which to store devCount * maxElements values
	double* saveValueArray = ( double * ) calloc( devCount * maxElements, sizeof( double ) ); // clear 2D array to save values
	
	// on first pass store initial values / compare current values to initial values on subsequent passes
	Boolean found = FALSE, first = TRUE;
	while ( !found ) {
		double maxDeltaPercent = 0;	// we want to find the one that moves the most ( percentage wise )
		for ( devIndex = 0; devIndex < devCount; devIndex++ ) {
			IOHIDDeviceRef tIOHIDDeviceRef = ( IOHIDDeviceRef ) CFArrayGetValueAtIndex( gDeviceCFArrayRef, devIndex );
			
			if ( !tIOHIDDeviceRef ) {
				continue;                       // skip this one
			}
#ifdef DEBUG
			long vendorID = IOHIDDevice_GetVendorID( tIOHIDDeviceRef );
			long productID = IOHIDDevice_GetProductID( tIOHIDDeviceRef );
#endif
			gElementCFArrayRef = IOHIDDeviceCopyMatchingElements( tIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone );
			
			if ( gElementCFArrayRef ) {
				CFIndex eleIndex, eleCount = CFArrayGetCount( gElementCFArrayRef );
				for ( eleIndex = 0; eleIndex < eleCount; eleIndex++ ) {
					tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, eleIndex );
					
					if ( !tIOHIDElementRef ) {
						continue;
					}
					
					IOHIDElementType tIOHIDElementType = IOHIDElementGetType( tIOHIDElementRef );
					
					// only care about inputs (no outputs or features)
					if ( tIOHIDElementType <= kIOHIDElementTypeInput_ScanCodes ) {

						if ( IOHIDElementIsArray( tIOHIDElementRef ) ) {
							//printf( "ARRAY!\n" );
							continue;	// skip array elements
						}
						uint32_t usagePage = IOHIDElementGetUsagePage( tIOHIDElementRef );
						uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef );
						uint32_t reportCount = IOHIDElementGetReportCount( tIOHIDElementRef );
#ifdef DEBUG
						if ( first ) {
							IOHIDElementCookie cookie = IOHIDElementGetCookie( tIOHIDElementRef );
							printf( "%s, dev: {ref:%p, ven: 0x%08lX, pro: 0x%08lX}, ele: {ref:%p, cookie: %p, usage:%04lX:%08lX}\n",
								   __PRETTY_FUNCTION__,
								   tIOHIDDeviceRef,
								   vendorID,
								   productID,
								   tIOHIDElementRef,
								   cookie,
								   (long unsigned int) usagePage,
								   (long unsigned int) usage ); fflush( stdout );
							
							if ( ( 0x054C == vendorID ) && ( 0x0268 == productID ) && ( 0x001E == (UInt32) cookie ) ) {
								//printf( "DING!\n" );
							}
						}
#endif
#if 1					// work-around for IOHIDValueGetScaledValue crash (when element report count > 1)
						if ( reportCount > 1 ) {
							//printf( "REPORT!\n" );
							continue; // skip reports
						}
#endif					
						// ignore PID elements and arrays
						if ( ( kHIDPage_PID != usagePage ) && ( -1 != usage ) ) {
							// get this elements current value
							double value = 0.0;	// default value is zero
							IOHIDValueRef tIOHIDValueRef;
							IOReturn ioReturn = IOHIDDeviceGetValue( tIOHIDDeviceRef, tIOHIDElementRef, &tIOHIDValueRef );
							if ( kIOReturnSuccess == ioReturn ) {
								value = IOHIDValueGetScaledValue( tIOHIDValueRef, kIOHIDValueScaleTypePhysical );
							}
							
							if ( first ) {
								saveValueArray[( devIndex * maxElements ) + eleIndex] = value;
							} else {
								double initialValue = saveValueArray[( devIndex * maxElements ) + eleIndex];
								
								CFIndex valueMin = IOHIDElementGetPhysicalMin( tIOHIDElementRef );
								CFIndex valueMax = IOHIDElementGetPhysicalMax( tIOHIDElementRef );
								
								double deltaPercent = fabs( ( initialValue - value ) * 100.0 / (valueMax - valueMin) );
#if 0
								if ( !first ) {
								// Device: 0x13b6a0 = { Logitech Inc. - WingMan Force 3D, 	vendorID:	0x046D, 	productID:	0xC283, usage: 0x0001:0x0004, "Generic Desktop Joystick"
									if ( ( vendorID == 0x046D ) && ( productID == 0xC283 ) ) {
										if ( ( kHIDPage_GenericDesktop == usagePage ) && ( kHIDUsage_GD_Rz == usage ) ) {
											printf( "initial: %6.2f, value: %6.2f, diff: %6.2f, delta percent: %6.2f!\n", initialValue, value, fabs( initialValue - value ), deltaPercent );
										}
									}
								}
								deltaPercent = 0.0;
#endif
								if ( deltaPercent >= kPercentMove ) {
									found = TRUE;
									if ( deltaPercent > maxDeltaPercent ) {
										maxDeltaPercent = deltaPercent;
										
										*outIOHIDDeviceRef = tIOHIDDeviceRef;
										*outIOHIDElementRef = tIOHIDElementRef;
									}
									break;
								}
							}   // if first
						}       // if usage
					}           // if type
				}               // for elements...
				CFRelease( gElementCFArrayRef );
				gElementCFArrayRef = NULL;
			}	// if ( gElementCFArrayRef )
			if ( found ) {
				// HIDDumpElementInfo( tIOHIDElementRef );
				break; // DONE!
			}
		}                   // for devices
		
		first = FALSE;          // no longer the first pass
		
		// are we done?
		end = clock();
		double secs = (double)( end - start ) / CLOCKS_PER_SEC;
		
		if ( secs > inTimeout ) {
			break;              // ( yes ) timeout
		}
	}	//	while ( !found )
	
	// return device and element moved
	if ( !found ) {
		*outIOHIDDeviceRef = NULL;
		*outIOHIDElementRef = NULL;
	}
	return found;
}   // HIDConfigureAction
Exemplo n.º 8
0
// utility routine to dump device info
void HIDDumpDeviceInfo(IOHIDDeviceRef inIOHIDDeviceRef) {
	char cstring[256];
	
	printf("Device: %p = { ", inIOHIDDeviceRef);
	
	char        manufacturer[256] = ""; // name of manufacturer
	CFStringRef tCFStringRef      = IOHIDDevice_GetManufacturer(inIOHIDDeviceRef);
	if (tCFStringRef) {
		(void) CFStringGetCString(tCFStringRef, manufacturer, sizeof(manufacturer), kCFStringEncodingUTF8);
	}
	
	char product[256] = "";      // name of product
	tCFStringRef = IOHIDDevice_GetProduct(inIOHIDDeviceRef);
	if (tCFStringRef) {
		(void) CFStringGetCString(tCFStringRef, product, sizeof(product), kCFStringEncodingUTF8);
	}
	
	printf("%s - %s, ", manufacturer, product);
	
	long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef);
	if (vendorID) {
		if ( HIDGetVendorNameFromVendorID(vendorID, cstring) ) {
			printf(" vendorID: 0x%04lX (\"%s\"), ", vendorID, cstring);
		} else {
			printf(" vendorID: 0x%04lX, ",          vendorID);
		}
	}
	
	long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef);
	if (productID) {
		if ( HIDGetProductNameFromVendorProductID(vendorID, productID, cstring) ) {
			printf(" productID: 0x%04lX (\"%s\"), ", productID, cstring);
		} else {
			printf(" productID: 0x%04lX, ",          productID);
		}
	}
	
	uint32_t usagePage = IOHIDDevice_GetUsagePage(inIOHIDDeviceRef);
	uint32_t usage     = IOHIDDevice_GetUsage(inIOHIDDeviceRef);
	if (!usagePage || !usage) {
		usagePage = IOHIDDevice_GetPrimaryUsagePage(inIOHIDDeviceRef);
		usage     = IOHIDDevice_GetPrimaryUsage(inIOHIDDeviceRef);
	}
	
	printf("usage: 0x%04lX:0x%04lX, ", (long unsigned int) usagePage, (long unsigned int) usage);
	
	tCFStringRef = HIDCopyUsageName(usagePage, usage);
	if (tCFStringRef) {
		(void) CFStringGetCString(tCFStringRef, cstring, sizeof(cstring), kCFStringEncodingUTF8);
		printf("\"%s\", ", cstring);
		CFRelease(tCFStringRef);
	}

	tCFStringRef = IOHIDDevice_GetTransport(inIOHIDDeviceRef);
	if (tCFStringRef) {
		(void) CFStringGetCString(tCFStringRef, cstring, sizeof(cstring), kCFStringEncodingUTF8);
		printf("Transport: \"%s\", ", cstring);
	}
	
	long vendorIDSource = IOHIDDevice_GetVendorIDSource(inIOHIDDeviceRef);
	if (vendorIDSource) {
		printf("VendorIDSource: %ld, ", vendorIDSource);
	}
	
	long version = IOHIDDevice_GetVersionNumber(inIOHIDDeviceRef);
	if (version) {
		printf("version: %ld, ", version);
	}
	
	tCFStringRef = IOHIDDevice_GetSerialNumber(inIOHIDDeviceRef);
	if (tCFStringRef) {
		(void) CFStringGetCString(tCFStringRef, cstring, sizeof(cstring), kCFStringEncodingUTF8);
		printf("SerialNumber: \"%s\", ", cstring);
	}
	
	long country = IOHIDDevice_GetCountryCode(inIOHIDDeviceRef);
	if (country) {
		printf("CountryCode: %ld, ", country);
	}
	
	long locationID = IOHIDDevice_GetLocationID(inIOHIDDeviceRef);
	if (locationID) {
		printf("locationID: 0x%08lX, ", locationID);
	}
	
#if false
	CFArrayRef pairs = IOHIDDevice_GetUsagePairs(inIOHIDDeviceRef);
	if (pairs) {
		CFIndex idx, cnt = CFArrayGetCount(pairs);
		for (idx = 0; idx < cnt; idx++) {
			const void * pair = CFArrayGetValueAtIndex(pairs, idx);
			CFShow(pair);
		}
	}
#endif // if false

	long maxInputReportSize = IOHIDDevice_GetMaxInputReportSize(inIOHIDDeviceRef);
	if (maxInputReportSize) {
		printf("MaxInputReportSize: %ld, ", maxInputReportSize);
	}
	
	long maxOutputReportSize = IOHIDDevice_GetMaxOutputReportSize(inIOHIDDeviceRef);
	if (maxOutputReportSize) {
		printf("MaxOutputReportSize: %ld, ", maxOutputReportSize);
	}
	
	long maxFeatureReportSize = IOHIDDevice_GetMaxFeatureReportSize(inIOHIDDeviceRef);
	if (maxFeatureReportSize) {
		printf("MaxFeatureReportSize: %ld, ", maxOutputReportSize);
	}
	
	long reportInterval = IOHIDDevice_GetReportInterval(inIOHIDDeviceRef);
	if (reportInterval) {
		printf("ReportInterval: %ld, ", reportInterval);
	}
	
	IOHIDQueueRef queueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef);
	if (queueRef) {
		printf("queue: %p, ", queueRef);
	}
	
	IOHIDTransactionRef transactionRef = IOHIDDevice_GetTransaction(inIOHIDDeviceRef);
	if (transactionRef) {
		printf("transaction: %p, ", transactionRef);
	}
	
	printf("}\n");
	fflush(stdout);
}   // HIDDumpDeviceInfo
Exemplo n.º 9
0
/*************************************************************************
 *
 * HIDAddDeviceToXML( inDevice )
 *
 * Purpose: Adds a devices info to the HID_device_usage_strings.plist( XML ) file
 *
 * Inputs: inDevice		- the device
 * Returns: Boolean		- if successful
 */
static Boolean HIDAddDeviceToXML(IOHIDDeviceRef inIOHIDDeviceRef) {
	Boolean result = FALSE;
	if ( HIDIsValidDevice(inIOHIDDeviceRef) ) {
		CFStringRef vendorCFStringRef = IOHIDDevice_GetManufacturer(inIOHIDDeviceRef);
		CFStringRef productCFStringRef = IOHIDDevice_GetProduct(inIOHIDDeviceRef);
		if ( vendorCFStringRef && productCFStringRef ) {
#if 0 // don't update the cookie xml file
			gCookieCFPropertyListRef =
			hu_XMLLoad(                           CFSTR(
														"HID_cookie_strings"), CFSTR("plist") );
			if ( gCookieCFPropertyListRef ) {
				CFMutableDictionaryRef tCFMutableDictionaryRef =
				CFDictionaryCreateMutableCopy(
											  kCFAllocatorDefault,
											  0,
											  gCookieCFPropertyListRef);
				if ( tCFMutableDictionaryRef ) {
					if ( hu_AddVendorProductToCFDict(tCFMutableDictionaryRef, vendorID, vendorCFStringRef, productID,
					                                 productCFStringRef) )
					{
						hu_XMLSave( tCFMutableDictionaryRef,
								   CFSTR(
										 "HID_cookie_strings"), CFSTR("plist") );
						result = TRUE;
					}
					
					CFRelease(tCFMutableDictionaryRef);
				}
			}
			
#endif
			if ( gUsageCFPropertyListRef ) {
				CFRelease(gUsageCFPropertyListRef);
			}
			
			gUsageCFPropertyListRef =
			hu_XMLLoad(                                 CFSTR(
															  "HID_device_usage_strings"), CFSTR("plist") );
			if ( gUsageCFPropertyListRef ) {
				CFMutableDictionaryRef tCFMutableDictionaryRef =
				CFDictionaryCreateMutableCopy(
											  kCFAllocatorDefault,
											  0,
											  gUsageCFPropertyListRef);
				if ( tCFMutableDictionaryRef ) {
					long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef);
					long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef);
					if ( hu_AddVendorProductToCFDict(tCFMutableDictionaryRef, vendorID, vendorCFStringRef, productID,
					                                 productCFStringRef) )
					{
						hu_XMLSave( tCFMutableDictionaryRef,
								   CFSTR(
										 "HID_device_usage_strings"), CFSTR("plist") );
						result = TRUE;
					}
					
					CFRelease(tCFMutableDictionaryRef);
				}
			}
		}
	}
	
	return (result);
}   // HIDAddDeviceToXML
Exemplo n.º 10
0
/*************************************************************************
 *
 * hu_AddDeviceElementToUsageXML( inDevice, inElement )
 *
 * Purpose: add a device and it's elements to our usage( XML ) file
 *
 * Inputs: inDevice		- the device
 *			inElement	- the element
 *
 * Returns: Boolean		- if successful
 */
static Boolean hu_AddDeviceElementToUsageXML(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef) {
	Boolean results = FALSE;
	if ( gUsageCFPropertyListRef ) {
		CFRelease(gUsageCFPropertyListRef);
	}
	
	gUsageCFPropertyListRef =
	hu_XMLLoad(                                 CFSTR(
													  "HID_device_usage_strings"), CFSTR("plist") );
	if ( gUsageCFPropertyListRef ) {
		CFMutableDictionaryRef tCFMutableDictionaryRef =
		CFDictionaryCreateMutableCopy(
									  kCFAllocatorDefault,
									  0,
									  gUsageCFPropertyListRef);
		if ( tCFMutableDictionaryRef ) {
			CFMutableDictionaryRef vendorCFMutableDictionaryRef;
			
			CFMutableDictionaryRef productCFMutableDictionaryRef;
			CFStringRef productKeyCFStringRef;
			
			CFStringRef usageKeyCFStringRef;
			
			// if the vendor dictionary exists...
			long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef);
			CFStringRef vendorKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), vendorID);
			if ( vendorKeyCFStringRef ) {
				if ( CFDictionaryGetValueIfPresent(tCFMutableDictionaryRef, vendorKeyCFStringRef,
				                                   (const void **) &vendorCFMutableDictionaryRef) )
				{
					// ...copy it...
					vendorCFMutableDictionaryRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault,
					                                                             0,
					                                                             vendorCFMutableDictionaryRef);
				} else {        // ...otherwise...
					// ...create it.
					vendorCFMutableDictionaryRef = CFDictionaryCreateMutable(kCFAllocatorDefault,
					                                                         0,
					                                                         &kCFTypeDictionaryKeyCallBacks,
					                                                         &kCFTypeDictionaryValueCallBacks);
					results = TRUE;
				}
				// if the vendor name key doesn't exist...
				if ( !CFDictionaryContainsKey(vendorCFMutableDictionaryRef, kNameKeyCFStringRef) ) {
					CFStringRef manCFStringRef = IOHIDDevice_GetManufacturer(inIOHIDDeviceRef);
					// ...create it.
					CFDictionaryAddValue(vendorCFMutableDictionaryRef, kNameKeyCFStringRef, manCFStringRef);
					results = TRUE;
				}
				
				// if the product key exists in the vendor dictionary...
				long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef);
				productKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), productID);
				if ( CFDictionaryGetValueIfPresent(vendorCFMutableDictionaryRef, productKeyCFStringRef,
				                                   (const void **) &productCFMutableDictionaryRef) )
				{
					// ...copy it...
					productCFMutableDictionaryRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault,
					                                                              0,
					                                                              productCFMutableDictionaryRef);
				} else {        // ...otherwise...
					// ...create it.
					productCFMutableDictionaryRef = CFDictionaryCreateMutable(kCFAllocatorDefault,
					                                                          0,
					                                                          &kCFTypeDictionaryKeyCallBacks,
					                                                          &kCFTypeDictionaryValueCallBacks);
					results = TRUE;
				}
				// if the product name key doesn't exist...
				if ( !CFDictionaryContainsKey(productCFMutableDictionaryRef, kNameKeyCFStringRef) ) {
					CFStringRef productCFStringRef = IOHIDDevice_GetProduct(inIOHIDDeviceRef);
					// ...create it.
					CFDictionaryAddValue(productCFMutableDictionaryRef, kNameKeyCFStringRef, productCFStringRef);
					results = TRUE;
				}
				
				// if the usage key doesn't exist in the product dictionary...
				uint32_t usagePage =  IOHIDElementGetUsagePage(inIOHIDElementRef);
				uint32_t usage =      IOHIDElementGetUsagePage(inIOHIDElementRef);
				usageKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld:%ld"), usagePage, usage);
				if ( usageKeyCFStringRef ) {
					if ( !CFDictionaryContainsKey(productCFMutableDictionaryRef, usageKeyCFStringRef) ) {
						// find it's generic name
						CFStringRef usageCFStringRef = HIDCopyUsageName(usagePage, usage);
						if ( usageCFStringRef ) {
							// and add that.
							CFDictionaryAddValue(productCFMutableDictionaryRef, usageKeyCFStringRef, usageCFStringRef);
							results = TRUE;
							CFRelease(usageCFStringRef);
						}
					}
					
					CFRelease(usageKeyCFStringRef);
				}
				if ( vendorCFMutableDictionaryRef ) {
					if ( productCFMutableDictionaryRef ) {
						if ( results ) {
							CFDictionarySetValue(vendorCFMutableDictionaryRef, productKeyCFStringRef, productCFMutableDictionaryRef);
						}
						
						CFRelease(productCFMutableDictionaryRef);
					}
					if ( results ) {
						CFDictionarySetValue(tCFMutableDictionaryRef, vendorKeyCFStringRef, vendorCFMutableDictionaryRef);
					}
					
					CFRelease(vendorCFMutableDictionaryRef);
				}
				
				CFRelease(vendorKeyCFStringRef);
			}
			if ( productKeyCFStringRef ) {
				CFRelease(productKeyCFStringRef);
			}
			if ( results ) {
				hu_XMLSave( tCFMutableDictionaryRef,
						   CFSTR(
								 "HID_device_usage_strings"), CFSTR("plist") );
			}
			
			CFRelease(
					  tCFMutableDictionaryRef);
		}
	}
	
	return (results);
}   // hu_AddDeviceElementToUsageXML