Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
HID Utilities/HID_Utilities.c
// |
// File: HID_Utilities.c |
// |
// Contains: Implementation of the HID configuration utilities |
// |
// Copyright: Copyright (c) 2007 Apple Inc., All Rights Reserved |
// |
// Disclaimer: IMPORTANT: This Apple software is supplied to you by |
// Apple Inc. ("Apple") in consideration of your agreement to the |
// following terms, and your use, installation, modification or |
// redistribution of this Apple software constitutes acceptance of these |
// terms. If you do not agree with these terms, please do not use, |
// install, modify or redistribute this Apple software. |
// |
// In consideration of your agreement to abide by the following terms, and |
// subject to these terms, Apple grants you a personal, non-exclusive |
// license, under Apple's copyrights in this original Apple software (the |
// "Apple Software"), to use, reproduce, modify and redistribute the Apple |
// Software, with or without modifications, in source and/or binary forms; |
// provided that if you redistribute the Apple Software in its entirety and |
// without modifications, you must retain this notice and the following |
// text and disclaimers in all such redistributions of the Apple Software. |
// Neither the name, trademarks, service marks or logos of Apple Inc. |
// may be used to endorse or promote products derived from the Apple |
// Software without specific prior written permission from Apple. Except |
// as expressly stated in this notice, no other rights or licenses, express |
// or implied, are granted by Apple herein, including but not limited to |
// any patent rights that may be infringed by your derivative works or by |
// other works in which the Apple Software may be incorporated. |
// |
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
// |
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
// POSSIBILITY OF SUCH DAMAGE. |
// |
//*************************************************** |
#pragma mark - includes & imports |
//----------------------------------------------------- |
//#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacTypes.h> |
#include <stdlib.h> // malloc |
#include <time.h> // clock |
#include <AssertMacros.h> |
#include "HID_Utilities.h" |
//*************************************************** |
#pragma mark - typedefs, enums, defines, etc. |
//----------------------------------------------------- |
#define FAKE_MISSING_NAMES 1 // set this to true while debuging to get more explicit element names; false for the generic ones |
#define kPercentMove 10 // precent of overall range a element must move to register |
#define kNameKeyCFStringRef CFSTR( "Name" ) // dictionary key |
//*************************************************** |
#pragma mark - local ( static ) function prototypes |
//----------------------------------------------------- |
static void CFSetApplierFunctionCopyToCFArray(const void *value, void *context); |
static CFComparisonResult CFDeviceArrayComparatorFunction(const void *val1, const void *val2, void *context); |
static CFMutableDictionaryRef hu_SetUpMatchingDictionary( UInt32 inUsagePage, UInt32 inUsage ); |
static CFPropertyListRef hu_XMLLoad( CFStringRef inResourceName, CFStringRef inResourceExtension ); |
static CFPropertyListRef hu_LoadFromXMLFile( CFURLRef inCFURLRef ); |
//*************************************************** |
#pragma mark - exported globals |
//----------------------------------------------------- |
IOHIDManagerRef gIOHIDManagerRef = NULL; |
CFMutableArrayRef gDeviceCFArrayRef = NULL; |
CFArrayRef gElementCFArrayRef = NULL; |
//*************************************************** |
#pragma mark - local ( static ) globals |
//----------------------------------------------------- |
//*************************************************** |
#pragma mark - exported function implementations |
//----------------------------------------------------- |
//************************************************************************* |
// |
// HIDBuildMultiDeviceList( inUsagePages, inUsages, inNumDeviceTypes ) |
// |
// Purpose: builds list of devices with elements |
// |
// Inputs: inUsagePages - inNumDeviceTypes sized array of matching usage pages |
// inUsages - inNumDeviceTypes sized array of matching usages |
// inNumDeviceTypes - number of usage pages & usages |
// |
// Returns: Boolean - if successful |
// |
Boolean HIDBuildMultiDeviceList( UInt32 inUsagePages[], UInt32 inUsages[], UInt32 inNumDeviceTypes ) |
{ |
Boolean result = FALSE; // assume failure ( pessimist! ) |
Boolean first = (!gIOHIDManagerRef); // not yet created? |
if ( first ) { |
// create the manager |
gIOHIDManagerRef = IOHIDManagerCreate( kCFAllocatorDefault, kIOHIDOptionsTypeNone ); |
} |
if ( gIOHIDManagerRef ) { |
CFMutableArrayRef hidMatchingCFMutableArrayRef = NULL; |
if ( inUsages && inUsagePages && inNumDeviceTypes ) { |
hidMatchingCFMutableArrayRef = CFArrayCreateMutable( kCFAllocatorDefault, 0, & kCFTypeArrayCallBacks ); |
if ( hidMatchingCFMutableArrayRef ) { |
int idx; |
for ( idx = 0; idx < inNumDeviceTypes; idx++ ) { // for all usage and usage page types |
// Set up matching dictionary. returns NULL on error. |
CFMutableDictionaryRef hidMatchingCFDictRef = hu_SetUpMatchingDictionary( inUsagePages[idx], inUsages[idx] ); |
if ( hidMatchingCFDictRef ) { |
CFArrayAppendValue( hidMatchingCFMutableArrayRef, (void*) hidMatchingCFDictRef ); |
CFRelease( hidMatchingCFDictRef ); |
} else { |
fprintf( stderr, "%s: CouldnÕt create a matching dictionary.", __PRETTY_FUNCTION__ ); |
} |
} |
} else { |
fprintf( stderr, "%s: CouldnÕt create a matching array.", __PRETTY_FUNCTION__ ); |
} |
} |
// set it for IOHIDManager to use to match against |
IOHIDManagerSetDeviceMatchingMultiple( gIOHIDManagerRef, hidMatchingCFMutableArrayRef ); |
if ( hidMatchingCFMutableArrayRef ) { |
CFRelease( hidMatchingCFMutableArrayRef ); |
} |
if ( first ) { |
// open it |
IOReturn tIOReturn = IOHIDManagerOpen( gIOHIDManagerRef, kIOHIDOptionsTypeNone ); |
if ( kIOReturnSuccess != tIOReturn ) { |
fprintf( stderr, "%s: CouldnÕt open IOHIDManager.", __PRETTY_FUNCTION__ ); |
goto Oops; |
} |
} |
HIDRebuildDevices( ); |
result = TRUE; |
} else { |
fprintf( stderr, "%s: CouldnÕt create a IOHIDManager.", __PRETTY_FUNCTION__ ); |
} |
Oops: ; |
return result; |
} // HIDBuildMultiDeviceList |
//************************************************************************* |
// |
// HIDRebuildDevices( ) |
// |
// Purpose: rebuilds the (internal) list of IOHIDDevices |
// |
// Inputs: none |
// |
// Returns: none |
// |
void HIDRebuildDevices( void ) { |
// get the set of devices from the IOHID manager |
CFSetRef devCFSetRef = IOHIDManagerCopyDevices( gIOHIDManagerRef ); |
if ( devCFSetRef ) { |
// if the existing array isn't empty... |
if ( gDeviceCFArrayRef ) { |
// release it |
CFRelease( gDeviceCFArrayRef ); |
} |
// create an empty array |
gDeviceCFArrayRef = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); |
// now copy the set to the array |
CFSetApplyFunction( devCFSetRef, CFSetApplierFunctionCopyToCFArray, ( void * ) gDeviceCFArrayRef ); |
// now sort the array by location ID's |
CFIndex cnt = CFArrayGetCount( gDeviceCFArrayRef ); |
CFArraySortValues( gDeviceCFArrayRef, CFRangeMake( 0, cnt ), CFDeviceArrayComparatorFunction, NULL ); |
// and release the set we copied from the IOHID manager |
CFRelease( devCFSetRef ); |
} |
} // HIDRebuildDevices |
//************************************************************************* |
// |
// 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 HIDConfigureAction( IOHIDDeviceRef* outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef, float inTimeout ) |
{ |
if ( !outIOHIDDeviceRef || !outIOHIDElementRef ) { |
return FALSE; |
} |
if ( !gDeviceCFArrayRef ) { // if we do not have a device list |
// and we can't build another list |
if ( !HIDBuildMultiDeviceList( nil, nil, 0 ) || ! gDeviceCFArrayRef ) { |
return FALSE; |
} |
} |
// duration timers |
clock_t start, stop; |
// determine the maximum number of elements |
CFIndex maxElements = 0; |
CFIndex devIndex, devCount = CFArrayGetCount( gDeviceCFArrayRef ); |
for ( devIndex = 0; devIndex < devCount; devIndex++ ) { |
IOHIDDeviceRef tIOHIDDeviceRef = ( IOHIDDeviceRef ) CFArrayGetValueAtIndex( gDeviceCFArrayRef, devIndex ); |
if ( !tIOHIDDeviceRef) continue; |
gElementCFArrayRef = IOHIDDeviceCopyMatchingElements( tIOHIDDeviceRef, NULL, 0 ); |
if ( gElementCFArrayRef ) { |
CFIndex numElements = CFArrayGetCount( gElementCFArrayRef ); |
if ( numElements > maxElements ) { |
maxElements = numElements; |
} |
CFRelease( gElementCFArrayRef ); |
gElementCFArrayRef = NULL; |
} |
} |
// allocate an array of doubles to store devCount * maxElements values |
double* saveValueArray = ( double * ) calloc( sizeof( double ), devCount * maxElements ); // clear 2D array to save values |
// on the first pass store the initial values |
Boolean found = FALSE, done = FALSE, first = TRUE; |
// poll all devices and elements, compare current value to the initial values |
while ( !found && !done ) { |
for ( devIndex = 0; devIndex < devCount; devIndex++ ) { |
IOHIDDeviceRef tIOHIDDeviceRef = ( IOHIDDeviceRef ) CFArrayGetValueAtIndex( gDeviceCFArrayRef, devIndex ); |
if ( !tIOHIDDeviceRef) continue; |
gElementCFArrayRef = IOHIDDeviceCopyMatchingElements( tIOHIDDeviceRef, NULL, 0 ); |
if ( gElementCFArrayRef ) { |
CFIndex eleIndex, eleCount = CFArrayGetCount( gElementCFArrayRef ); |
for ( eleIndex = 0; eleIndex < eleCount; eleIndex++ ) { |
IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, eleIndex ); |
IOHIDElementType tIOHIDElementType = IOHIDElementGetType( tIOHIDElementRef ); |
// only care about inputs (no outputs or features) |
if ( tIOHIDElementType <= kIOHIDElementTypeInput_ScanCodes ) { |
uint32_t usagePage = IOHIDElementGetUsagePage( tIOHIDElementRef ); |
uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef ); |
// ignore PID elements and arrays |
if ( ( kHIDPage_PID != usagePage ) && ( -1 != usage ) ) { |
IOHIDValueRef tIOHIDValueRef; |
IOHIDDeviceGetValue( tIOHIDDeviceRef, tIOHIDElementRef, &tIOHIDValueRef ); |
double 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 delta = fabs( ( valueMax - valueMin ) * kPercentMove * 0.01f ); |
if ( fabs( initialValue - value ) > delta ) { |
found = TRUE; |
*outIOHIDDeviceRef = tIOHIDDeviceRef; |
*outIOHIDElementRef = tIOHIDElementRef; |
break; |
} |
} // if first |
} // if usage |
} // if type |
} // for elements... |
CFRelease( gElementCFArrayRef ); |
gElementCFArrayRef = NULL; |
} // for devices |
} // while !( found || done) |
if ( first ) { |
start = clock( ); |
first = FALSE; |
} else { |
// are we done? |
stop = clock( ); |
double secs; |
secs = ( double ) ( stop - start ) / CLOCKS_PER_SEC; |
if ( secs > inTimeout ) { |
done = TRUE; |
break; |
} |
} |
} // while ( !found && !done ); |
// return device and element moved |
if ( !found ) { |
*outIOHIDDeviceRef = NULL; |
*outIOHIDElementRef = NULL; |
} |
return found; |
} // HIDConfigureAction |
//************************************************************************* |
// |
// HIDSaveElementPref( inKeyCFStringRef, inAppCFStringRef, inIOHIDDeviceRef, inIOHIDElementRef ) |
// |
// Purpose: Save the device & element values into the specified key in the specified applications preferences |
// |
// Inputs: inKeyCFStringRef - the preference key |
// inAppCFStringRef - the application identifier |
// inIOHIDDeviceRef - the device |
// inIOHIDElementRef - the element |
// Returns: Boolean - if successful |
// |
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 devUsagePage = IOHIDDevice_GetUsagePage( inIOHIDDeviceRef ); |
uint32_t devUsage = IOHIDDevice_GetUsage( inIOHIDDeviceRef ); |
if ( !devUsagePage || !devUsage ) { |
devUsagePage = IOHIDDevice_GetPrimaryUsagePage( inIOHIDDeviceRef ); |
devUsage = IOHIDDevice_GetPrimaryUsage( inIOHIDDeviceRef ); |
} |
require( devUsagePage && devUsage, Oops); |
IOHIDElementType eleType = IOHIDElementGetType( inIOHIDElementRef ); |
uint32_t eleUsagePage = IOHIDElementGetUsagePage( inIOHIDElementRef ); |
uint32_t eleUsage = IOHIDElementGetUsage( inIOHIDElementRef ); |
IOHIDElementCookie eleCookie = IOHIDElementGetCookie( inIOHIDElementRef ); |
CFStringRef prefCFStringRef = CFStringCreateWithFormat( kCFAllocatorDefault, NULL, |
CFSTR( "d:{v:%ld, p:%ld, l:%ld, p:%ld, u:%ld}, e:{t:%ld, p:%ld, u:%ld, c:%ld}" ), |
vendorID, |
productID, |
locID, |
devUsagePage, |
devUsage, |
eleType, |
eleUsagePage, |
eleUsage, |
eleCookie ); |
if ( prefCFStringRef ) { |
CFPreferencesSetAppValue( inKeyCFStringRef, prefCFStringRef, inAppCFStringRef ); |
CFRelease( prefCFStringRef ); |
success = TRUE; |
} |
} |
Oops: ; |
return success; |
} // HIDSaveElementPref |
//************************************************************************* |
// |
// HIDRestoreElementPref( inKeyCFStringRef, inAppCFStringRef, outIOHIDDeviceRef, outIOHIDElementRef ) |
// |
// Purpose: Find the specified preference in the specified application |
// |
// Inputs: inKeyCFStringRef - the preference key |
// inAppCFStringRef - the application identifier |
// outIOHIDDeviceRef - address where to restore the device |
// outIOHIDElementRef - address where to restore the element |
// Returns: Boolean - if successful |
// outIOHIDDeviceRef - the device |
// outIOHIDElementRef - the element |
// |
Boolean HIDRestoreElementPref( CFStringRef inKeyCFStringRef, CFStringRef inAppCFStringRef, IOHIDDeviceRef* outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef ) |
{ |
Boolean found = FALSE; |
if ( inKeyCFStringRef && inAppCFStringRef && outIOHIDDeviceRef && outIOHIDElementRef ) { |
CFPropertyListRef prefCFPropertyListRef = CFPreferencesCopyAppValue( inKeyCFStringRef, inAppCFStringRef ); |
if ( prefCFPropertyListRef ) { |
if ( CFStringGetTypeID( ) == CFGetTypeID( prefCFPropertyListRef ) ) { |
char buffer[256]; |
if ( CFStringGetCString( ( CFStringRef ) prefCFPropertyListRef, buffer, sizeof( buffer ), kCFStringEncodingUTF8 ) ) { |
hu_InfoRec_t searchInfo; |
int count = sscanf( buffer, "d:{v:%ld, p:%ld, l:%ld, p:%d, u:%d}, e:{t:%ld, p:%d, u:%d, c:%ld}", |
&searchInfo.vendorID, &searchInfo.productID, &searchInfo.locID, &searchInfo.devUsagePage, &searchInfo.devUsage, |
&searchInfo.type, &searchInfo.eleUsagePage, &searchInfo.eleUsage,( long* ) &searchInfo.cookie ); |
if ( 9 == count ) { // if we found all nine parametersÉ |
// and can find a device & element that matches theseÉ |
if ( HIDFindDeviceAndElement( &searchInfo, outIOHIDDeviceRef, outIOHIDElementRef ) ) { |
found = TRUE; |
} |
} |
} |
} else { |
// We found the entry with this key but it's the wrong type; delete it. |
CFPreferencesSetAppValue( inKeyCFStringRef, NULL, inAppCFStringRef ); |
( void ) CFPreferencesAppSynchronize( inAppCFStringRef ); |
} |
CFRelease( prefCFPropertyListRef ); |
} |
} |
return found; |
} // HIDRestoreElementPref |
//************************************************************************* |
// |
// HIDFindDeviceAndElement( inSearchInfo, outFoundDevice, outFoundElement ) |
// |
// Purpose: find the closest matching device and element for this action |
// |
// Notes: matches device: serial, vendorID, productID, location, inUsagePage, usage |
// matches element: cookie, inUsagePage, usage, |
// |
// Inputs: inSearchInfo - the device & element info we searching for |
// outFoundDevice - the address of the best matching device |
// outFoundElement - the address of the best matching element |
// |
// Returns: Boolean - TRUE if we find a match |
// outFoundDevice - the best matching device |
// outFoundElement - the best matching element |
// |
Boolean HIDFindDeviceAndElement( const hu_InfoRec_t* inSearchInfo, IOHIDDeviceRef* outFoundDevice, IOHIDElementRef *outFoundElement ) |
{ |
Boolean result = FALSE; |
IOHIDDeviceRef bestIOHIDDeviceRef = NULL; |
IOHIDElementRef bestIOHIDElementRef = NULL; |
long bestScore = 0; |
CFIndex devIndex, devCount = CFArrayGetCount( gDeviceCFArrayRef ); |
for ( devIndex = 0; devIndex < devCount; devIndex++ ) { |
long deviceScore = 1; |
IOHIDDeviceRef tIOHIDDeviceRef = ( IOHIDDeviceRef ) CFArrayGetValueAtIndex( gDeviceCFArrayRef, devIndex ); |
if ( !tIOHIDDeviceRef) continue; |
// match vendorID, productID (+10, +8) |
if ( inSearchInfo->vendorID ) { |
long vendorID = IOHIDDevice_GetVendorID( tIOHIDDeviceRef); |
if ( vendorID ) { |
if ( inSearchInfo->vendorID == vendorID ) { |
deviceScore += 10; |
if ( inSearchInfo->productID ) { |
long productID = IOHIDDevice_GetProductID( tIOHIDDeviceRef); |
if ( productID ) { |
if ( inSearchInfo->productID == productID ) { |
deviceScore += 8; |
} // if ( inSearchInfo->productID == productID ) |
} // if ( productID ) |
} // if ( inSearchInfo->productID ) |
} // if (inSearchInfo->vendorID == vendorID) |
} // if vendorID |
} // if search->vendorID |
// match usagePage & usage (+9) |
if ( inSearchInfo->devUsagePage && inSearchInfo->devUsage ) { |
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->devUsagePage == usagePage ) { |
if ( usage ) { |
if ( inSearchInfo->devUsage == 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->locID ) { |
long locID = IOHIDDevice_GetLocationID( tIOHIDDeviceRef ); |
if ( locID ) { |
if ( inSearchInfo->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; |
long score = deviceScore; |
// match type, usage page, usage & cookie |
if ( inSearchInfo->type == IOHIDElementGetType( tIOHIDElementRef ) ) { |
if ( inSearchInfo->eleUsagePage && inSearchInfo->eleUsage ) { |
uint32_t usagePage = IOHIDElementGetUsagePage( tIOHIDElementRef ); |
if ( inSearchInfo->eleUsagePage == usagePage ) { |
uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef ); |
if ( inSearchInfo->eleUsage == usage ) { |
score += 5; |
IOHIDElementCookie cookie = IOHIDElementGetCookie( tIOHIDElementRef ); |
if ( inSearchInfo->cookie == cookie ) { |
score += 4; |
} // cookies match |
} else { |
score = 0; |
} // usages match |
} else { |
score = 0; |
} // usage pages match |
} // if ( search usage page & usage ) |
} // if type |
#if LOG_SCORING |
if ( kHIDPage_KeyboardOrKeypad != tElementRef->usagePage ) { // skip keyboards here |
printf( "%s: ( %ld:%ld )-I-Debug, score: %ld\t", __PRETTY_FUNCTION__, inSearchInfo->eleUsagePage, inSearchInfo->eleUsage, 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->eleUsagePage, inSearchInfo->eleUsage, 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->eleUsagePage, inSearchInfo->eleUsage, bestScore ); |
HIDPrintElement( bestIOHIDElementRef ); |
#endif LOG_SCORING |
result = TRUE; |
} |
return result; |
} // HIDFindDeviceAndElement |
//************************************************************************* |
// |
// HIDGetUsageName( inUsagePage, inUsage ) |
// |
// Purpose: return a CFStringRef string for a given usage page & usage( see IOUSBHIDParser.h ) |
// |
// Notes: returns usage page and usage values in CFString form for unknown values |
// |
// Inputs: inUsagePage - the usage page |
// inUsage - the usage |
// |
// Returns: CFStringRef - the resultant string |
// |
CFStringRef HIDCopyUsageName( long inUsagePage, long inUsage ) |
{ |
static CFPropertyListRef tCFPropertyListRef = NULL; |
CFStringRef result = NULL; |
if ( !tCFPropertyListRef ) |
tCFPropertyListRef = hu_XMLLoad( CFSTR( "HID_usage_strings" ), CFSTR( "plist" ) ); |
if ( tCFPropertyListRef ) { |
if ( CFDictionaryGetTypeID( ) == CFGetTypeID( tCFPropertyListRef ) ) { |
CFStringRef pageKeyCFStringRef = CFStringCreateWithFormat( kCFAllocatorDefault, NULL, CFSTR( "0x%4.4lX" ), inUsagePage ); |
if ( pageKeyCFStringRef ) { |
CFDictionaryRef pageCFDictionaryRef; |
if ( CFDictionaryGetValueIfPresent( tCFPropertyListRef, pageKeyCFStringRef, ( const void** ) &pageCFDictionaryRef ) ) { |
CFStringRef pageCFStringRef; |
if ( CFDictionaryGetValueIfPresent( pageCFDictionaryRef, kNameKeyCFStringRef, ( const void** ) &pageCFStringRef ) ) { |
CFStringRef usageKeyCFStringRef = CFStringCreateWithFormat( kCFAllocatorDefault, NULL, CFSTR( "0x%4.4lX" ), inUsage ); |
if ( usageKeyCFStringRef ) { |
CFStringRef usageCFStringRef; |
if ( CFDictionaryGetValueIfPresent( pageCFDictionaryRef, usageKeyCFStringRef, ( const void** ) &usageCFStringRef ) ) { |
result = CFStringCreateWithFormat( kCFAllocatorDefault, NULL, CFSTR( "%@ %@" ), pageCFStringRef, usageCFStringRef ); |
} |
#if FAKE_MISSING_NAMES |
else { |
result = CFStringCreateWithFormat( kCFAllocatorDefault, NULL, CFSTR( "%@ #%@" ), pageCFStringRef, usageKeyCFStringRef ); |
} |
#endif |
CFRelease( usageKeyCFStringRef ); |
} |
} else { |
// no name data for this page |
} |
} else { |
// no data for this page |
} |
CFRelease( pageKeyCFStringRef ); |
} |
} |
// CFRelease( tCFPropertyListRef ); // Leak this! |
// tCFPropertyListRef = NULL; |
} |
return result; |
} // HIDCopyUsageName |
// utility routine to dump device info |
void HIDDumpDeviceInfo( IOHIDDeviceRef inIOHIDDeviceRef ) |
{ |
printf( "Device: %p = { ", inIOHIDDeviceRef ); |
char manufacturer[256] = ""; // name of manufacturer |
CFStringRef tCFStringRef = IOHIDDevice_GetManufacturer( inIOHIDDeviceRef ); |
if ( tCFStringRef ) { |
verify( CFStringGetCString( tCFStringRef, manufacturer, sizeof( manufacturer ), kCFStringEncodingUTF8 ) ); |
} |
char product[256] = ""; // name of product |
tCFStringRef = IOHIDDevice_GetProduct( inIOHIDDeviceRef ); |
if ( tCFStringRef ) { |
verify( CFStringGetCString( tCFStringRef, product, sizeof( product ), kCFStringEncodingUTF8 ) ); |
} |
printf( "%s - %s, ", manufacturer, product ); |
long vendorID = IOHIDDevice_GetVendorID( inIOHIDDeviceRef ); |
if ( vendorID ) { |
#if 1 |
printf( " vendorID: 0x%04lX, ", vendorID ); |
#else |
char string[256]; |
if ( HIDGetVendorNameFromVendorID( vendorID, string ) ) { |
printf( " vendorID: 0x%04lX (\"%s\"), ", vendorID, string ); |
} else { |
printf( " vendorID: 0x%04lX, ", vendorID ); |
} |
#endif |
} |
long productID = IOHIDDevice_GetProductID( inIOHIDDeviceRef ); |
if ( productID ) { |
#if 1 |
printf( " productID: 0x%04lX, ", productID ); |
#else |
if ( HIDGetProductNameFromVendorProductID( vendorID, productID, string ) ) { |
printf( " productID: 0x%04lX (\"%s\"), ", productID, string ); |
} else { |
printf( " productID: 0x%04lX, ", productID ); |
} |
#endif |
} |
uint32_t devUsagePage = IOHIDDevice_GetUsagePage( inIOHIDDeviceRef ); |
uint32_t devUsage = IOHIDDevice_GetUsage( inIOHIDDeviceRef ); |
if ( !devUsagePage || !devUsage ) { |
devUsagePage = IOHIDDevice_GetPrimaryUsagePage( inIOHIDDeviceRef ); |
devUsage = IOHIDDevice_GetPrimaryUsage( inIOHIDDeviceRef ); |
} |
printf( "usage: 0x%04lX:0x%04lX, ", (long unsigned int) devUsagePage, (long unsigned int) devUsage ); |
#if 1 |
tCFStringRef = HIDCopyUsageName( devUsagePage, devUsage ); |
if ( tCFStringRef ) { |
char usageString[256] = ""; |
verify( CFStringGetCString( tCFStringRef, usageString, sizeof( usageString ), kCFStringEncodingUTF8 ) ); |
printf( "\"%s\"", usageString ); |
CFRelease( tCFStringRef ); |
} |
#endif |
printf( "\n" ); |
fflush( stdout ); |
} // HIDDumpDeviceInfo |
// utility routine to dump element info |
void HIDDumpElementInfo( IOHIDElementRef inIOHIDElementRef ) |
{ |
if ( inIOHIDElementRef ) { |
printf( "Element: %p = { ", inIOHIDElementRef ); |
IOHIDDeviceRef tIOHIDDeviceRef = IOHIDElementGetDevice( inIOHIDElementRef ); |
printf( "Device: %p, ", tIOHIDDeviceRef ); |
IOHIDElementRef parentIOHIDElementRef = IOHIDElementGetParent( inIOHIDElementRef ); |
printf( "parent: %p, ", parentIOHIDElementRef ); |
#if 0 |
CFArrayRef childrenCFArrayRef = IOHIDElementGetChildren( inIOHIDElementRef ); |
printf( "children: %p: { ", childrenCFArrayRef ); fflush( stdout ); |
CFShow( childrenCFArrayRef ); fflush( stdout ); |
printf( " }, " ); |
#endif |
IOHIDElementCookie tIOHIDElementCookie = IOHIDElementGetCookie( inIOHIDElementRef ); |
printf( "cookie: %p, ", tIOHIDElementCookie ); |
IOHIDElementType tIOHIDElementType = IOHIDElementGetType( inIOHIDElementRef ); |
switch ( tIOHIDElementType ) { |
case kIOHIDElementTypeInput_Misc: { |
printf( "type: Misc, " ); |
break; |
} |
case kIOHIDElementTypeInput_Button: { |
printf( "type: Button, " ); |
break; |
} |
case kIOHIDElementTypeInput_Axis: { |
printf( "type: Axis, " ); |
break; |
} |
case kIOHIDElementTypeInput_ScanCodes: { |
printf( "type: ScanCodes, " ); |
break; |
} |
case kIOHIDElementTypeOutput: { |
printf( "type: Output, " ); |
break; |
} |
case kIOHIDElementTypeFeature: { |
printf( "type: Feature, " ); |
break; |
} |
case kIOHIDElementTypeCollection: { |
printf( "type: Collection, " ); |
break; |
} |
default: { |
printf( "type: %p, ", (void*) tIOHIDElementType ); |
break; |
} |
} |
IOHIDElementCollectionType tIOHIDElementCollectionType = IOHIDElementGetCollectionType( inIOHIDElementRef ); |
printf( "collection: %p, ", (void*) tIOHIDElementCollectionType ); |
uint32_t usagePage = IOHIDElementGetUsagePage( inIOHIDElementRef ); |
uint32_t usage = IOHIDElementGetUsage( inIOHIDElementRef ); |
printf( "usage: 0x%04lX:0x%04lX, ", (long unsigned int) usagePage, (long unsigned int) usage ); |
#if 1 |
CFStringRef tCFStringRef = HIDCopyUsageName( usagePage, usage ); |
if ( tCFStringRef ) { |
char usageString[256] = ""; |
verify( CFStringGetCString( tCFStringRef, usageString, sizeof( usageString ), kCFStringEncodingUTF8 ) ); |
printf( "\"%s\", ", usageString ); |
CFRelease( tCFStringRef ); |
} |
#endif |
CFStringRef nameCFStringRef = IOHIDElementGetName( inIOHIDElementRef ); |
char buffer[256]; |
if ( nameCFStringRef && CFStringGetCString( nameCFStringRef, buffer, sizeof( buffer ), kCFStringEncodingUTF8 ) ) { |
printf( "name: %s, ", buffer ); |
} |
uint32_t reportID = IOHIDElementGetReportID( inIOHIDElementRef ); |
printf( "report: { ID: %08lX, ", (long unsigned int) reportID ); |
uint32_t reportSize = IOHIDElementGetReportSize( inIOHIDElementRef ); |
printf( "Size: %lu, ", (long unsigned int) reportSize ); |
uint32_t reportCount = IOHIDElementGetReportCount( inIOHIDElementRef ); |
printf( "Count: %lu }, ", (long unsigned int) reportCount ); |
uint32_t unit = IOHIDElementGetUnit( inIOHIDElementRef ); |
printf( "unit: %lu, ", (long unsigned int) unit ); |
uint32_t unitExp = IOHIDElementGetUnitExponent( inIOHIDElementRef ); |
printf( "unitExp: %lu, ", (long unsigned int) unitExp ); |
CFIndex logicalMin = IOHIDElementGetLogicalMin( inIOHIDElementRef ); |
printf( "logicalMin: %ld, ", logicalMin ); |
CFIndex logicalMax = IOHIDElementGetLogicalMax( inIOHIDElementRef ); |
printf( "logicalMax: %ld, ", logicalMax ); |
CFIndex physicalMin = IOHIDElementGetPhysicalMin( inIOHIDElementRef ); |
printf( "physicalMin: %ld, ", physicalMin ); |
CFIndex physicalMax = IOHIDElementGetPhysicalMax( inIOHIDElementRef ); |
printf( "physicalMax: %ld, ", physicalMax ); |
#if 1 |
Boolean isVirtual = IOHIDElementIsVirtual( inIOHIDElementRef ); |
if ( isVirtual ) printf( "isVirtual, " ); |
Boolean isRelative = IOHIDElementIsRelative( inIOHIDElementRef ); |
if ( isRelative ) printf( "isRelative, " ); |
Boolean isWrapping = IOHIDElementIsWrapping( inIOHIDElementRef ); |
if ( isWrapping ) printf( "isWrapping, " ); |
Boolean isArray = IOHIDElementIsArray( inIOHIDElementRef ); |
if ( isArray ) printf( "isArray, " ); |
Boolean isNonLinear = IOHIDElementIsNonLinear( inIOHIDElementRef ); |
if ( isNonLinear ) printf( "isNonLinear, " ); |
Boolean hasPreferredState = IOHIDElementHasPreferredState( inIOHIDElementRef ); |
if ( hasPreferredState ) printf( "hasPreferredState, " ); |
Boolean hasNullState = IOHIDElementHasNullState( inIOHIDElementRef ); |
if ( hasNullState ) printf( "hasNullState, " ); |
#else |
Boolean isVirtual = IOHIDElementIsVirtual( inIOHIDElementRef ); |
printf( "isVirtual: %s, ", isVirtual ? "YES" : "NO" ); |
Boolean isRelative = IOHIDElementIsRelative( inIOHIDElementRef ); |
printf( "isRelative: %s, ", isRelative ? "YES" : "NO" ); |
Boolean isWrapping = IOHIDElementIsWrapping( inIOHIDElementRef ); |
printf( "isWrapping: %s, ", isWrapping ? "YES" : "NO" ); |
Boolean isArray = IOHIDElementIsArray( inIOHIDElementRef ); |
printf( "isArray: %s, ", isArray ? "YES" : "NO" ); |
Boolean isNonLinear = IOHIDElementIsNonLinear( inIOHIDElementRef ); |
printf( "isNonLinear: %s, ", isNonLinear ? "YES" : "NO" ); |
Boolean hasPreferredState = IOHIDElementHasPreferredState( inIOHIDElementRef ); |
printf( "hasPreferredState: %s, ", hasPreferredState ? "YES" : "NO" ); |
Boolean hasNullState = IOHIDElementHasNullState( inIOHIDElementRef ); |
printf( "hasNullState: %s, ", hasNullState ? "YES" : "NO" ); |
#endif |
printf( " }\n" ); |
} |
} // HIDDumpElementInfo |
//*************************************************** |
#pragma mark - local ( static ) function implementations |
//----------------------------------------------------- |
//************************************************************************* |
// |
// CFSetApplierFunctionCopyToCFArray( value, context ) |
// |
// Purpose: CFSetApplierFunction to copy the CFSet to a CFArray |
// |
// Notes: called one time for each item in the CFSet |
// |
// Inputs: value - the current element of the CFSet |
// context - the CFMutableArrayRef we're adding the CFSet elements to |
// |
// Returns: nothing |
// |
static void CFSetApplierFunctionCopyToCFArray(const void *value, void *context) |
{ |
// printf( "%s: 0x%08lX\n", __PRETTY_FUNCTION__, (long unsigned int) value ); |
CFArrayAppendValue( ( CFMutableArrayRef ) context, value ); |
} // CFSetApplierFunctionCopyToCFArray |
// --------------------------------- |
// used to sort the CFDevice array after copying it from the (unordered) (CF)set. |
// we compare based on the location ID's since they're consistant (across boots & launches). |
// |
static CFComparisonResult CFDeviceArrayComparatorFunction(const void *val1, const void *val2, void *context) |
{ |
#pragma unused( context ) |
CFComparisonResult result = kCFCompareEqualTo; |
long loc1 = IOHIDDevice_GetLocationID( ( IOHIDDeviceRef ) val1 ); |
long loc2 = IOHIDDevice_GetLocationID( ( IOHIDDeviceRef ) val2 ); |
if ( loc1 < loc2 ) { |
result = kCFCompareLessThan; |
} else if ( loc1 > loc2 ) { |
result = kCFCompareGreaterThan; |
} |
return result; |
} // CFDeviceArrayComparatorFunction |
//************************************************************************* |
// |
// hu_SetUpMatchingDictionary( inUsagePage, inUsage ) |
// |
// Purpose: builds a matching dictionary based on usage page and usage |
// |
// Notes: Only called by HIDBuildMultiDeviceList |
// |
// Inputs: inUsagePage - usage page |
// inUsage - usages |
// |
// Returns: CFMutableDictionaryRef - the matching dictionary |
// |
static CFMutableDictionaryRef hu_SetUpMatchingDictionary( UInt32 inUsagePage, UInt32 inUsage ) |
{ |
// create a dictionary to add usage page/usages to |
CFMutableDictionaryRef refHIDMatchDictionary = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, |
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); |
if ( refHIDMatchDictionary ) { |
if ( inUsagePage ) { |
// Add key for device type to refine the matching dictionary. |
CFNumberRef pageCFNumberRef = CFNumberCreate( kCFAllocatorDefault, kCFNumberIntType, &inUsagePage ); |
if ( pageCFNumberRef ) { |
CFDictionarySetValue( refHIDMatchDictionary, |
CFSTR( kIOHIDPrimaryUsagePageKey ), pageCFNumberRef ); |
CFRelease( pageCFNumberRef ); |
// note: the usage is only valid if the usage page is also defined |
if ( inUsage ) { |
CFNumberRef usageCFNumberRef = CFNumberCreate( |
kCFAllocatorDefault, kCFNumberIntType, &inUsage ); |
if ( usageCFNumberRef ) { |
CFDictionarySetValue( refHIDMatchDictionary, |
CFSTR( kIOHIDPrimaryUsageKey ), usageCFNumberRef ); |
CFRelease( usageCFNumberRef ); |
} else { |
fprintf( stderr, "%s: CFNumberCreate( usage ) failed.", __PRETTY_FUNCTION__ ); |
} |
} |
} else { |
fprintf( stderr, "%s: CFNumberCreate( usage page ) failed.", __PRETTY_FUNCTION__ ); |
} |
} |
} else { |
fprintf( stderr, "%s: CFDictionaryCreateMutable failed.", __PRETTY_FUNCTION__ ); |
} |
return refHIDMatchDictionary; |
} // hu_SetUpMatchingDictionary |
//************************************************************************* |
// |
// hu_XMLLoad( inResourceName, inResourceExtension ) |
// |
// Purpose: Load a resource( XML ) file into a CFPropertyListRef |
// |
// Inputs: inResourceName - name of the resource file |
// inResourceExtension - extension of the resource file |
// |
// Returns: CFPropertyListRef - the data |
// |
static CFPropertyListRef hu_XMLLoad( CFStringRef inResourceName, CFStringRef inResourceExtension ) |
{ |
CFURLRef resFileCFURLRef; |
CFPropertyListRef tCFPropertyListRef = NULL; |
resFileCFURLRef = CFBundleCopyResourceURL( CFBundleGetMainBundle( ), inResourceName, inResourceExtension, NULL ); |
if ( resFileCFURLRef ) { |
tCFPropertyListRef = hu_LoadFromXMLFile( resFileCFURLRef ); |
CFRelease( resFileCFURLRef ); |
} |
return tCFPropertyListRef; |
} // hu_XMLLoad |
//************************************************************************* |
// |
// hu_LoadFromXMLFile( inCFURLRef ) |
// |
// Purpose: load a property list from an XML file |
// |
// Inputs: inCFURLRef - URL for the file |
// |
// Returns: CFPropertyListRef - the data |
// |
static CFPropertyListRef hu_LoadFromXMLFile( CFURLRef inCFURLRef ) |
{ |
CFDataRef xmlCFDataRef; |
CFPropertyListRef myCFPropertyListRef = NULL; |
// Read the XML file. |
SInt32 error; |
if ( CFURLCreateDataAndPropertiesFromResource( kCFAllocatorDefault, inCFURLRef, &xmlCFDataRef, NULL, NULL, &error ) ) { |
CFStringRef errorString; |
// Reconstitute the dictionary using the XML data. |
myCFPropertyListRef = CFPropertyListCreateFromXMLData( kCFAllocatorDefault, xmlCFDataRef, kCFPropertyListImmutable, &errorString ); |
// Release the XML data |
CFRelease( xmlCFDataRef ); |
} |
return myCFPropertyListRef; |
} // hu_LoadFromXMLFile |
Copyright © 2008 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2008-05-07