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.c
// |
// File: HID_Utilities.c |
// |
// 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. |
// |
// Copyright ( C ) 2001-2008 Apple Inc. All Rights Reserved. |
// |
#include "HID_Utilities_Internal.h" |
#include "HID_Utilities_External.h" |
// ================================== |
// private functions |
// path to follow to next element |
static char MapChar (char c); |
static void CleanString (char * targetString); |
static void HIDGetElementInfo (CFTypeRef refElement, pRecElement pElement); |
static void HIDAddElement (CFTypeRef refElement, pRecElement * ppElementCurrent); |
static void HIDGetElementsCFArrayHandler (const void * value, void * parameter); |
static void HIDGetElements (CFTypeRef refElementCurrent, pRecElement * ppElementCurrent); |
static void HIDGetCollectionElements (CFMutableDictionaryRef deviceProperties, pRecElement * ppElementCurrent); |
static void HIDGetDeviceInfo (io_object_t hidDevice, CFMutableDictionaryRef hidProperties, pRecDevice pDevice); |
static void HIDAddDevice (pRecDevice *ppListDeviceHead, pRecDevice pNewDevice); |
static pRecDevice HIDMoveDevice (pRecDevice *ppListDeviceHead, pRecDevice pNewDevice, pRecDevice *ppOldListDeviceHead); |
static pRecDevice HIDBuildDevice (io_object_t hidDevice); |
static pRecDevice HIDCreateSingleTypeDeviceList (io_iterator_t hidObjectIterator); |
static pRecDevice HIDCreateMultiTypeDeviceList (UInt32 *usagePage, UInt32 *usage, UInt32 numDeviceTypes); |
static Boolean HIDFindDeviceInList (pRecDevice pDeviceList, pRecDevice pFindDevice); |
static Boolean HIDCompareUpdateDeviceList (pRecDevice *ppListDeviceHead, pRecDevice *ppNewDeviceList); |
static void HIDMergeDeviceList (pRecDevice *ppNewDeviceList, pRecDevice *ppDeviceList); |
static io_iterator_t HIDGetIterator (const mach_port_t masterPort, UInt32 usagePage, UInt32 usage); |
static CFMutableDictionaryRef HIDSetUpMatchingDictionary (UInt32 usagePage, UInt32 usage); |
static void HIDDisposeDeviceElements (pRecElement pElement); |
static pRecDevice HIDDisposeDevice (pRecDevice *ppDevice); |
static UInt32 HIDCountCurrentDevices (pRecDevice pDeviceList); |
static Boolean HIDMatchElementTypeMask (IOHIDElementType type, HIDElementTypeMask typeMask); |
static pRecElement HIDGetDeviceElement (pRecElement pElement, HIDElementTypeMask typeMask); |
// ================================== |
// globals |
// for element retrieval |
pRecDevice gCurrentGetDevice = NULL; |
Boolean gfAddAsChild = true; |
pRecDevice gpDeviceList = NULL; |
int gNumDevices = 0; |
// ================================== |
// private functions |
// Maps bad chars to good chars for html/printing ASCII |
static char MapChar (char c) |
{ |
unsigned char uc = (unsigned char) c; |
switch (uc) |
{ |
case 0x01: return ' '; |
case 0x02: return ' '; |
case 0x03: return ' '; |
case 0x04: return ' '; |
case 0x05: return ' '; |
case 0x06: return ' '; |
case 0x07: return ' '; |
case 0x08: return ' '; |
case 0x09: return ' '; |
case 0x0A: return ' '; |
case 0x0B: return ' '; |
case 0x0C: return ' '; |
case 0x0D: return ' '; |
case 0x0E: return ' '; |
case 0x0F: return ' '; |
case 0x10: return ' '; |
case 0x11: return ' '; |
case 0x12: return ' '; |
case 0x13: return ' '; |
case 0x14: return ' '; |
case 0x15: return ' '; |
case 0x16: return ' '; |
case 0x17: return ' '; |
case 0x18: return ' '; |
case 0x19: return ' '; |
case 0x1A: return ' '; |
case 0x1B: return ' '; |
case 0x1C: return ' '; |
case 0x1D: return ' '; |
case 0x1E: return ' '; |
case 0x1F: return ' '; |
case '/': return '-'; // use dash instead of slash |
case 0x7F: return ' '; |
case 0x80: return 'A'; |
case 0x81: return 'A'; |
case 0x82: return 'C'; |
case 0x83: return 'E'; |
case 0x84: return 'N'; |
case 0x85: return 'O'; |
case 0x86: return 'U'; |
case 0x87: return 'a'; |
case 0x88: return 'a'; |
case 0x89: return 'a'; |
case 0x8A: return 'a'; |
case 0x8B: return 'a'; |
case 0x8C: return 'a'; |
case 0x8D: return 'c'; |
case 0x8E: return 'e'; |
case 0x8F: return 'e'; |
case 0x90: return ' '; |
case 0x91: return ' '; // ? ' |
case 0x92: return ' '; // ? ' |
case 0x93: return ' '; // ? " |
case 0x94: return ' '; // ? " |
case 0x95: return ' '; |
case 0x96: return ' '; |
case 0x97: return ' '; |
case 0x98: return ' '; |
case 0x99: return ' '; |
case 0x9A: return ' '; |
case 0x9B: return 0x27; |
case 0x9C: return 0x22; |
case 0x9D: return ' '; |
case 0x9E: return ' '; |
case 0x9F: return ' '; |
case 0xA0: return ' '; |
case 0xA1: return ' '; |
case 0xA2: return ' '; |
case 0xA3: return ' '; |
case 0xA4: return ' '; |
case 0xA5: return ' '; |
case 0xA6: return ' '; |
case 0xA7: return ' '; |
case 0xA8: return ' '; |
case 0xA9: return ' '; |
case 0xAA: return ' '; |
case 0xAB: return ' '; |
case 0xAC: return ' '; |
case 0xAD: return ' '; |
case 0xAE: return ' '; |
case 0xAF: return ' '; |
case 0xB0: return ' '; |
case 0xB1: return ' '; |
case 0xB2: return ' '; |
case 0xB3: return ' '; |
case 0xB4: return ' '; |
case 0xB5: return ' '; |
case 0xB6: return ' '; |
case 0xB7: return ' '; |
case 0xB8: return ' '; |
case 0xB9: return ' '; |
case 0xBA: return ' '; |
case 0xBB: return ' '; |
case 0xBC: return ' '; |
case 0xBD: return ' '; |
case 0xBE: return ' '; |
case 0xBF: return ' '; |
case 0xC0: return ' '; |
case 0xC1: return ' '; |
case 0xC2: return ' '; |
case 0xC3: return ' '; |
case 0xC4: return ' '; |
case 0xC5: return ' '; |
case 0xC6: return ' '; |
case 0xC7: return ' '; |
case 0xC8: return ' '; |
case 0xC9: return ' '; |
case 0xCA: return ' '; |
case 0xCB: return 'A'; |
case 0xCC: return 'A'; |
case 0xCD: return 'O'; |
case 0xCE: return ' '; |
case 0xCF: return ' '; |
case 0xD0: return '-'; |
case 0xD1: return '-'; |
case 0xD2: return 0x22; |
case 0xD3: return 0x22; |
case 0xD4: return 0x27; |
case 0xD5: return 0x27; |
case 0xD6: return '-'; // use dash instead of slash |
case 0xD7: return ' '; |
case 0xD8: return 'y'; |
case 0xD9: return 'Y'; |
case 0xDA: return '-'; // use dash instead of slash |
case 0xDB: return ' '; |
case 0xDC: return '<'; |
case 0xDD: return '>'; |
case 0xDE: return ' '; |
case 0xDF: return ' '; |
case 0xE0: return ' '; |
case 0xE1: return ' '; |
case 0xE2: return ','; |
case 0xE3: return ','; |
case 0xE4: return ' '; |
case 0xE5: return 'A'; |
case 0xE6: return 'E'; |
case 0xE7: return 'A'; |
case 0xE8: return 'E'; |
case 0xE9: return 'E'; |
case 0xEA: return 'I'; |
case 0xEB: return 'I'; |
case 0xEC: return 'I'; |
case 0xED: return 'I'; |
case 0xEE: return 'O'; |
case 0xEF: return 'O'; |
case 0xF0: return ' '; |
case 0xF1: return 'O'; |
case 0xF2: return 'U'; |
case 0xF3: return 'U'; |
case 0xF4: return 'U'; |
case 0xF5: return '|'; |
case 0xF6: return ' '; |
case 0xF7: return ' '; |
case 0xF8: return ' '; |
case 0xF9: return ' '; |
case 0xFA: return '.'; |
case 0xFB: return ' '; |
case 0xFC: return ' '; |
case 0xFD: return 0x22; |
case 0xFE: return ' '; |
case 0xFF: return ' '; |
} |
return c; |
} |
// --------------------------------- |
// ensures the string only contains printable ASCII characters |
// using varition of my html conversion source |
// input is null terminated string, change is made in place |
static void CleanString (char * targetString) |
{ |
char * charIt = targetString; |
while (*charIt) { |
*charIt = MapChar (*charIt); |
charIt++; |
} |
} |
// --------------------------------- |
// extracts actual specific element information from each element CF dictionary entry |
static void HIDGetElementInfo (CFTypeRef refElement, pRecElement pElement) |
{ |
int number; |
CFTypeRef refType; |
// type, usagePage, usage already stored |
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementCookieKey)); |
if (refType && CFNumberGetValue (refType, kCFNumberIntType, &number)) |
pElement->cookie = (IOHIDElementCookie) number; |
else |
pElement->cookie = (IOHIDElementCookie) 0; |
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementMinKey)); |
if (refType && CFNumberGetValue (refType, kCFNumberIntType, &number)) |
pElement->min = number; |
else |
pElement->min = 0; |
pElement->maxReport = pElement->min; |
pElement->userMin = kDefaultUserMin; |
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementMaxKey)); |
if (refType && CFNumberGetValue (refType, kCFNumberIntType, &number)) |
pElement->max = number; |
else |
pElement->max = 0; |
pElement->minReport = pElement->max; |
pElement->userMax = kDefaultUserMax; |
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMinKey)); |
if (refType && CFNumberGetValue (refType, kCFNumberIntType, &number)) |
pElement->scaledMin = number; |
else |
pElement->scaledMin = 0; |
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMaxKey)); |
if (refType && CFNumberGetValue (refType, kCFNumberIntType, &number)) |
pElement->scaledMax = number; |
else |
pElement->scaledMax = 0; |
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementSizeKey)); |
if (refType && CFNumberGetValue (refType, kCFNumberIntType, &number)) |
pElement->size = number; |
else |
pElement->size = 0; |
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsRelativeKey)); |
if (refType) |
pElement->relative = CFBooleanGetValue (refType); |
else |
pElement->relative = 0; |
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsWrappingKey)); |
if (refType) |
pElement->wrapping = CFBooleanGetValue (refType); |
else |
pElement->wrapping = false; |
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsNonLinearKey)); |
if (refType) |
pElement->nonLinear = CFBooleanGetValue (refType); |
else |
pElement->nonLinear = false; |
#ifdef kIOHIDElementHasPreferredStateKey |
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasPreferredStateKey)); |
#else // Mac OS X 10.0 has spelling error |
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasPreferedStateKey)); |
#endif |
if (refType) |
pElement->preferredState = CFBooleanGetValue (refType); |
else |
pElement->preferredState = false; |
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasNullStateKey)); |
if (refType) |
pElement->nullState = CFBooleanGetValue (refType); |
else |
pElement->nullState = false; |
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUnitKey)); |
if (refType && CFNumberGetValue (refType, kCFNumberIntType, &number)) |
pElement->units = number; |
else |
pElement->units = 0; |
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUnitExponentKey)); |
if (refType && CFNumberGetValue (refType, kCFNumberIntType, &number)) |
pElement->unitExp = number; |
else |
pElement->unitExp = 0; |
refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementNameKey)); |
if (refType) |
if (!CFStringGetCString (refType, pElement->name, 256, CFStringGetSystemEncoding ())) |
HIDReportError ("CFStringGetCString error retrieving pElement->name."); |
CleanString (pElement->name); |
if (!*pElement->name) { // set name from vendor id/product id look up |
GetElementNameFromVendorProduct (gCurrentGetDevice->vendorID, gCurrentGetDevice->productID, (int) pElement->cookie, pElement->name); |
if (!*pElement->name) { // if no name |
HIDGetUsageName (pElement->usagePage, pElement->usage, pElement->name); |
if (!*pElement->name) // if not usage |
sprintf (pElement->name, "Element"); |
} |
} |
} |
// --------------------------------- |
// examines CF dictionary value in device element hierarchy to determine if it is element of interest or a collection of more elements |
// if element of interest allocate storage, add to list and retrieve element specific info |
// if collection then pass on to deconstruction collection into additional individual elements |
static void HIDAddElement (CFTypeRef refElement, pRecElement * ppElementCurrent) |
{ |
pRecDevice pDevice = gCurrentGetDevice; |
pRecElement pElement = NULL; |
int elementType, usagePage, usage; |
CFTypeRef refElementType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementTypeKey)); |
CFTypeRef refUsagePage = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUsagePageKey)); |
CFTypeRef refUsage = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUsageKey)); |
if (refElementType) |
CFNumberGetValue (refElementType, kCFNumberIntType, &elementType); |
if (refUsagePage) |
CFNumberGetValue (refUsagePage, kCFNumberIntType, &usagePage); |
if (refUsage) |
CFNumberGetValue (refUsage, kCFNumberIntType, &usage); |
if (NULL == pDevice) |
return; |
if (elementType) |
{ |
// look at types of interest |
if (elementType != kIOHIDElementTypeCollection) |
{ |
switch (usagePage) // only interested in kHIDPage_GenericDesktop and kHIDPage_Button |
{ |
case kHIDPage_GenericDesktop: |
{ |
switch (usage) // look at usage to determine function |
{ |
case kHIDUsage_GD_X: |
case kHIDUsage_GD_Y: |
case kHIDUsage_GD_Z: |
case kHIDUsage_GD_Rx: |
case kHIDUsage_GD_Ry: |
case kHIDUsage_GD_Rz: |
pElement = (pRecElement) malloc (sizeof (recElement)); |
if (pElement) |
pDevice->axis++; |
break; |
case kHIDUsage_GD_Slider: |
pElement = (pRecElement) malloc (sizeof (recElement)); |
if (pElement) |
pDevice->sliders++; |
break; |
case kHIDUsage_GD_Dial: |
pElement = (pRecElement) malloc (sizeof (recElement)); |
if (pElement) |
pDevice->dials++; |
break; |
case kHIDUsage_GD_Wheel: |
pElement = (pRecElement) malloc (sizeof (recElement)); |
if (pElement) |
pDevice->wheels++; |
break; |
case kHIDUsage_GD_Hatswitch: |
pElement = (pRecElement) malloc (sizeof (recElement)); |
if (pElement) |
pDevice->hats++; |
break; |
} |
} |
break; |
case kHIDPage_Button: |
pElement = (pRecElement) malloc (sizeof (recElement)); |
if (pElement) |
pDevice->buttons++; |
break; |
default: |
// just add a generic element |
pElement = (pRecElement) malloc (sizeof (recElement)); |
break; |
} |
} |
else // collection |
pElement = (pRecElement) malloc (sizeof (recElement)); |
} |
else |
HIDReportError ("CFNumberGetValue error when getting value for refElementType."); |
if (pElement) // add to list |
{ |
// this code builds a binary tree based on the collection hierarchy of inherent in the device element layout |
// it preserves the structure of the lements as collections have children and lements are siblings to each other |
// clear record |
int i; |
char * temp = (char *) pElement; |
for (i = 0; i < (int) sizeof (recElement); i++) |
*temp++ = 0x00; |
// get element info |
pElement->type = elementType; |
pElement->usagePage = usagePage; |
pElement->usage = usage; |
HIDGetElementInfo (refElement, pElement); |
// count elements |
pDevice->totalElements++; |
switch (pElement->type) |
{ |
case kIOHIDElementTypeInput_Misc: |
case kIOHIDElementTypeInput_Button: |
case kIOHIDElementTypeInput_Axis: |
case kIOHIDElementTypeInput_ScanCodes: |
pDevice->inputs++; |
break; |
case kIOHIDElementTypeOutput: |
pDevice->outputs++; |
break; |
case kIOHIDElementTypeFeature: |
pDevice->features++; |
break; |
case kIOHIDElementTypeCollection: |
pDevice->collections++; |
break; |
default: |
HIDReportErrorNum ("Unknown element type : ", pElement->type); |
} |
// if a type that is normally an axis and has a preferred state and is not relative |
pElement->hasCenter = false; |
if ((pElement->preferredState) && (!pElement->relative) && (pElement->usagePage == kHIDPage_GenericDesktop)) |
switch (pElement->usage) { |
case kHIDUsage_GD_X: |
case kHIDUsage_GD_Y: |
case kHIDUsage_GD_Z: |
case kHIDUsage_GD_Rx: |
case kHIDUsage_GD_Ry: |
case kHIDUsage_GD_Rz: |
// case kHIDUsage_GD_Slider: // should not have center |
// case kHIDUsage_GD_Dial: // should not have center |
case kHIDUsage_GD_Wheel: |
case kHIDUsage_GD_Hatswitch: |
pElement->hasCenter = true; // respect center |
pElement->initialCenter = HIDGetElementValue (pDevice, pElement); |
} |
if (NULL == *ppElementCurrent) // if at list head |
{ |
pDevice->pListElements = pElement; // add current element |
*ppElementCurrent = pElement; // set current element to element we just added |
if (elementType == kIOHIDElementTypeCollection) // if this element is a collection of other elements |
{ |
gfAddAsChild = true; // the next element is a child of this element |
HIDGetCollectionElements ((CFMutableDictionaryRef) refElement, &pElement); // recursively process the collection |
} |
gfAddAsChild = false; // when returning from the recursive processing or the element is not a collection, add the next as a sibling |
} |
else // have existing structure |
{ |
if (gfAddAsChild) // if the previous element was a collection, let's add this as a child of the previous |
{ |
// this iteration should not be needed but there maybe some untested degenerate case which this code will ensure works |
while ((*ppElementCurrent)->pChild) // step down tree until free child node found |
*ppElementCurrent = (*ppElementCurrent)->pChild; |
(*ppElementCurrent)->pChild = pElement; // insert there |
} |
else // add as sibling |
{ |
// this iteration should not be needed but there maybe some untested degenerate case which this code will ensure works |
while ((*ppElementCurrent)->pSibling) // step down tree until free sibling node found |
*ppElementCurrent = (*ppElementCurrent)->pSibling; |
(*ppElementCurrent)->pSibling = pElement; // insert there |
} |
pElement->pPrevious = *ppElementCurrent; // point to previous |
*ppElementCurrent = pElement; // set current to our collection |
if (elementType == kIOHIDElementTypeCollection) // if this element is a collection of other elements |
{ |
gfAddAsChild = true; // add next set as children to this element |
HIDGetCollectionElements ((CFMutableDictionaryRef) refElement, &pElement); // recursively process the collection |
} |
gfAddAsChild = false; // add next as this elements sibling (when return from a collection or with non-collections) |
} |
} |
} |
// --------------------------------- |
// collects information from each array member in device element list (each array memeber = element) |
static void HIDGetElementsCFArrayHandler (const void * value, void * parameter) |
{ |
if (CFGetTypeID (value) == CFDictionaryGetTypeID ()) |
HIDAddElement ((CFTypeRef) value, (pRecElement *) parameter); |
} |
// --------------------------------- |
// handles retrieval of element information from arrays of elements in device IO registry information |
static void HIDGetElements (CFTypeRef refElementCurrent, pRecElement *ppCurrentElement) |
{ |
CFTypeID type = CFGetTypeID (refElementCurrent); |
if (type == CFArrayGetTypeID()) // if element is an array |
{ |
CFRange range = {0, CFArrayGetCount (refElementCurrent)}; |
// CountElementsCFArrayHandler called for each array member |
CFArrayApplyFunction (refElementCurrent, range, HIDGetElementsCFArrayHandler, ppCurrentElement); |
} |
} |
// --------------------------------- |
// handles extracting element information from element collection CF types |
// used from top level element decoding and hierarchy deconstruction to flatten device element list |
static void HIDGetCollectionElements (CFMutableDictionaryRef deviceProperties, pRecElement *ppCurrentCollection) |
{ |
CFTypeRef refElementTop = CFDictionaryGetValue (deviceProperties, CFSTR(kIOHIDElementKey)); |
if (refElementTop) |
HIDGetElements (refElementTop, ppCurrentCollection); |
else |
HIDReportError ("CFDictionaryGetValue error when creating CFTypeRef for kIOHIDElementKey."); |
} |
// --------------------------------- |
// use top level element usage page and usage to discern device usage page and usage setting appropriate values in device record |
static void HIDTopLevelElementHandler (const void * value, void * parameter) |
{ |
CFTypeRef refCF = 0; |
if ((NULL == value) || (NULL == parameter)) |
return; // (paramErr) |
if (CFGetTypeID (value) != CFDictionaryGetTypeID ()) |
return; |
refCF = CFDictionaryGetValue (value, CFSTR(kIOHIDElementUsagePageKey)); |
if (!CFNumberGetValue (refCF, kCFNumberIntType, &((pRecDevice) parameter)->usagePage)) |
HIDReportError ("CFNumberGetValue error retrieving pDevice->usagePage."); |
refCF = CFDictionaryGetValue (value, CFSTR(kIOHIDElementUsageKey)); |
if (!CFNumberGetValue (refCF, kCFNumberIntType, &((pRecDevice) parameter)->usage)) |
HIDReportError ("CFNumberGetValue error retrieving pDevice->usage."); |
} |
// --------------------------------- |
// extracts device info from CF dictionary records in IO registry |
static void HIDGetDeviceInfo (io_object_t hidDevice, CFMutableDictionaryRef hidProperties, pRecDevice pDevice) |
{ |
CFMutableDictionaryRef usbProperties = 0; |
io_registry_entry_t parent1, parent2; |
// Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also |
// get dictionary for usb properties: step up two levels and get CF dictionary for USB properties |
if ((KERN_SUCCESS == IORegistryEntryGetParentEntry (hidDevice, kIOServicePlane, &parent1)) && |
(KERN_SUCCESS == IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2)) && |
(KERN_SUCCESS == IORegistryEntryCreateCFProperties (parent2, &usbProperties, kCFAllocatorDefault, kNilOptions))) |
{ |
if (usbProperties) |
{ |
CFTypeRef refCF = 0; |
// get device info |
// try hid dictionary first, if fail then go to usb dictionary |
// get transport |
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDTransportKey)); |
if (refCF) |
{ |
if (!CFStringGetCString (refCF, pDevice->transport, 256, CFStringGetSystemEncoding ())) |
HIDReportError ("CFStringGetCString error retrieving pDevice->transport."); |
CleanString (pDevice->transport); |
} |
// get vendorID |
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDVendorIDKey)); |
if (!refCF) |
refCF = CFDictionaryGetValue (usbProperties, CFSTR("idVendor")); |
if (refCF) |
{ |
if (!CFNumberGetValue (refCF, kCFNumberIntType, &pDevice->vendorID)) |
HIDReportError ("CFNumberGetValue error retrieving pDevice->vendorID."); |
} |
// get product ID |
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDProductIDKey)); |
if (!refCF) |
refCF = CFDictionaryGetValue (usbProperties, CFSTR("idProduct")); |
if (refCF) |
{ |
if (!CFNumberGetValue (refCF, kCFNumberIntType, &pDevice->productID)) |
HIDReportError ("CFNumberGetValue error retrieving pDevice->productID."); |
} |
// get product version |
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDVersionNumberKey)); |
if (refCF) |
{ |
if (!CFNumberGetValue (refCF, kCFNumberIntType, &pDevice->version)) |
HIDReportError ("CFNumberGetValue error retrieving pDevice->version."); |
} |
// get manufacturer name |
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDManufacturerKey)); |
if (!refCF) |
refCF = CFDictionaryGetValue (usbProperties, CFSTR("USB Vendor Name")); |
if (refCF) |
{ |
if (!CFStringGetCString (refCF, pDevice->manufacturer, 256, CFStringGetSystemEncoding ())) |
HIDReportError ("CFStringGetCString error retrieving pDevice->manufacturer."); |
CleanString (pDevice->manufacturer); |
} |
// get product name |
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDProductKey)); |
if (!refCF) |
refCF = CFDictionaryGetValue (usbProperties, CFSTR("USB Product Name")); |
if (refCF) |
{ |
if (!CFStringGetCString (refCF, pDevice->product, 256, CFStringGetSystemEncoding ())) |
HIDReportError ("CFStringGetCString error retrieving pDevice->product."); |
CleanString (pDevice->product); |
} |
// get serial |
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDSerialNumberKey)); |
if (refCF) |
{ |
if (!CFStringGetCString (refCF, pDevice->serial, 256, CFStringGetSystemEncoding ())) |
HIDReportError ("CFStringGetCString error retrieving pDevice->serial."); |
CleanString (pDevice->serial); |
} |
// get location ID |
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDLocationIDKey)); |
if (!refCF) |
refCF = CFDictionaryGetValue (usbProperties, CFSTR("locationID")); |
if (refCF) |
{ |
if (!CFNumberGetValue (refCF, kCFNumberIntType, &pDevice->locID)) |
HIDReportError ("CFNumberGetValue error retrieving pDevice->locID."); |
} |
// get usage page and usage |
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDPrimaryUsagePageKey)); |
if (refCF) |
{ |
if (!CFNumberGetValue (refCF, kCFNumberIntType, &pDevice->usagePage)) |
HIDReportError ("CFNumberGetValue error retrieving pDevice->usagePage."); |
refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDPrimaryUsageKey)); |
if (refCF) |
if (!CFNumberGetValue (refCF, kCFNumberIntType, &pDevice->usage)) |
HIDReportError ("CFNumberGetValue error retrieving pDevice->usage."); |
} |
if (NULL == refCF) // get top level element HID usage page or usage |
{ |
// use top level element instead |
CFTypeRef refCFTopElement = 0; |
refCFTopElement = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDElementKey)); |
{ |
// refCFTopElement points to an array of element dictionaries |
CFRange range = {0, CFArrayGetCount (refCFTopElement)}; |
CFArrayApplyFunction (refCFTopElement, range, HIDTopLevelElementHandler, NULL); |
} |
} |
} |
else |
HIDReportError ("IORegistryEntryCreateCFProperties failed to create usbProperties."); |
CFRelease (usbProperties); |
if (kIOReturnSuccess != IOObjectRelease (parent2)) |
HIDReportError ("IOObjectRelease error with parent2."); |
if (kIOReturnSuccess != IOObjectRelease (parent1)) |
HIDReportError ("IOObjectRelease error with parent1."); |
} |
} |
// --------------------------------- |
// adds device to linked list of devices passed in (handles NULL lists properly) |
static void HIDAddDevice (pRecDevice *ppListDeviceHead, pRecDevice pNewDevice) |
{ |
if (NULL == *ppListDeviceHead) |
*ppListDeviceHead = pNewDevice; |
else |
{ |
pRecDevice pDevicePrevious = NULL, pDevice = *ppListDeviceHead; |
while (pDevice) |
{ |
pDevicePrevious = pDevice; |
pDevice = pDevicePrevious->pNext; |
} |
pDevicePrevious->pNext = pNewDevice; |
} |
pNewDevice->pNext = NULL; |
} |
// --------------------------------- |
// adds device to linked list of devices passed in (handles NULL lists properly) |
// returns next device is old list (for properly iterating) |
static pRecDevice HIDMoveDevice (pRecDevice *ppListDeviceHead, pRecDevice pNewDevice, pRecDevice *ppOldListDeviceHead) |
{ |
pRecDevice pDeviceNext = NULL; |
if (!pNewDevice || !ppOldListDeviceHead || !ppListDeviceHead) { // handle NULL pointers |
HIDReportError ("HIDMoveDevice, NULL input error."); |
return pDeviceNext; |
} |
// remove from old |
if (pNewDevice == *ppOldListDeviceHead) { // replacing head |
*ppOldListDeviceHead = pNewDevice->pNext; |
pDeviceNext = *ppOldListDeviceHead; |
} else { |
pRecDevice pDevicePrevious = NULL, pDevice = *ppOldListDeviceHead; |
while (pDevice && (pDevice != pNewDevice)) { // step through list until match or end |
pDevicePrevious = pDevice; |
pDevice = pDevicePrevious->pNext; |
} |
if (pDevice == pNewDevice) { // if there was a match |
pDevicePrevious->pNext = pDevice->pNext; // skip this device |
pDeviceNext = pDevice->pNext; |
} else |
HIDReportError ("HIDMoveDevice device not found when moving."); |
} |
// add to new list |
HIDAddDevice (ppListDeviceHead, pNewDevice); |
return pDeviceNext; // return next device |
} |
// --------------------------------- |
// given a IO device object build a flat device record including device info and elements |
static pRecDevice HIDBuildDevice (io_object_t hidDevice) |
{ |
pRecDevice pDevice = (pRecDevice) malloc (sizeof (recDevice)); |
if (pDevice) |
{ |
UInt32 i; |
// get dictionary for HID properties |
CFMutableDictionaryRef hidProperties = 0; |
kern_return_t result = IORegistryEntryCreateCFProperties (hidDevice, &hidProperties, kCFAllocatorDefault, kNilOptions); |
// clear record |
for (i = 0; i < sizeof (recDevice); i++) // clear array |
*((char *) pDevice + i) = 0x00; |
if ((result == KERN_SUCCESS) && hidProperties) |
{ |
pRecElement pCurrentElement = NULL; |
// create device interface |
result = HIDCreateOpenDeviceInterface (hidDevice, pDevice); |
if (kIOReturnSuccess != result) |
HIDReportErrorNum ("HIDCreateOpenDeviceInterface failed.", result); |
HIDGetDeviceInfo (hidDevice, hidProperties, pDevice); // hidDevice used to find parents in registry tree |
// set current device for use in getting elements |
gCurrentGetDevice = pDevice; |
HIDGetCollectionElements (hidProperties, &pCurrentElement); |
gCurrentGetDevice = NULL; |
CFRelease (hidProperties); |
} |
else |
HIDReportErrorNum ("IORegistryEntryCreateCFProperties error when creating deviceProperties.", result); |
} |
else |
HIDReportError ("malloc error when allocating pRecDevice."); |
return pDevice; |
} |
// --------------------------------- |
// build flat linked list of devices from device iterator |
static pRecDevice HIDCreateSingleTypeDeviceList (io_iterator_t hidObjectIterator) |
{ |
IOReturn result = kIOReturnSuccess; |
pRecDevice pListDeviceHead = NULL; |
pRecDevice pNewDevice = NULL; |
io_object_t ioHIDDeviceObject = 0; |
while ((ioHIDDeviceObject = IOIteratorNext (hidObjectIterator))) |
{ |
pNewDevice = HIDBuildDevice (ioHIDDeviceObject); |
// dump device object, it is no longer needed |
result = IOObjectRelease (ioHIDDeviceObject); |
if (KERN_SUCCESS != result) |
HIDReportErrorNum ("IOObjectRelease error with ioHIDDeviceObject.", result); |
HIDAddDevice (&pListDeviceHead, pNewDevice); |
} |
result = IOObjectRelease (hidObjectIterator); // release the iterator |
if (kIOReturnSuccess != result) |
HIDReportErrorNum ("IOObjectRelease error with hidObjectIterator.", result); |
return pListDeviceHead; |
} |
// --------------------------------- |
// build flat linked list of devices from list of usages and usagePages |
static pRecDevice HIDCreateMultiTypeDeviceList (UInt32 *usagePage, UInt32 *usage, UInt32 numDeviceTypes) |
{ |
mach_port_t masterPort = 0; |
io_iterator_t hidObjectIterator = 0; |
pRecDevice pNewDeviceList = NULL; // build new list |
UInt32 i; |
if (!usage || !usagePage || (numDeviceTypes == 0)) |
HIDReportError ("HIDCreateMultiTypeDeviceList: NULL usage, usagePage or numDeviceTypes."); |
else { |
if (kIOReturnSuccess != IOMasterPort (bootstrap_port, &masterPort)) |
HIDReportError ("HIDCreateMultiTypeDeviceList: IOMasterPort error with bootstrap_port."); |
else |
for (i = 0; i < numDeviceTypes; i++) { // for all usage and usage page types |
pRecDevice pDeviceList = NULL; |
hidObjectIterator = HIDGetIterator (masterPort, usagePage[i], usage[i]); // get iterator |
if (0 != hidObjectIterator) |
pDeviceList = HIDCreateSingleTypeDeviceList (hidObjectIterator); // build device list |
if (NULL != pDeviceList) // if there are devices to merge |
HIDMergeDeviceList (&pNewDeviceList, &pDeviceList); // merge into new list |
while (NULL != pDeviceList) // dump what is left of source list (head first no clean up needed) |
pDeviceList = HIDDisposeDevice (&pDeviceList); // dispose current device return next device, will set pDeviceList to NULL |
} |
} |
return pNewDeviceList; |
} |
// --------------------------------- |
// given a device list and a device find if device is in list |
// returns true if in list, false otherwise |
// this is problematic for generic devices as location id could be zero |
// also match number of elements |
static Boolean HIDFindDeviceInList (pRecDevice pDeviceList, pRecDevice pFindDevice) |
{ |
Boolean found = false; // not found |
pRecDevice pDevice = pDeviceList; // not really needed but is clearer this way |
while (pDevice && !found) { // while we still have device to look at and have not found the target device |
if ((pDevice->vendorID == pFindDevice->vendorID) && // if we match same vendor, product & location |
(pDevice->productID == pFindDevice->productID) && // this is not quite right for same tyes plugged into the same location but different physical devices |
(pDevice->locID == pFindDevice->locID) && // since this is a corner and impossible to detect without serial numbers case we will ignore it |
(pDevice->usage == pFindDevice->usage) && // ensure all device parameters really match (helps with generic devices) |
(pDevice->usagePage == pFindDevice->usagePage) && |
(pDevice->totalElements == pFindDevice->totalElements) && |
(pDevice->features == pFindDevice->features) && |
(pDevice->inputs == pFindDevice->inputs) && |
(pDevice->outputs == pFindDevice->outputs) && |
(pDevice->collections == pFindDevice->collections) && |
(pDevice->axis == pFindDevice->axis) && |
(pDevice->buttons == pFindDevice->buttons) && |
(pDevice->hats == pFindDevice->hats) && |
(pDevice->sliders == pFindDevice->sliders) && |
(pDevice->wheels == pFindDevice->wheels) && |
(pDevice->dials == pFindDevice->dials)) |
found = true; // found device |
pDevice = pDevice->pNext; // step to next device |
} |
return found; |
} |
// --------------------------------- |
// update main list with new devices and remove unplugged devices |
// startegy is to update based on location of device, if this changed then previous device was |
// at least unplugged and plugged in somewhere else, thus can't be considered the same |
// return true if any changes |
static Boolean HIDCompareUpdateDeviceList (pRecDevice *ppListDeviceHead, pRecDevice *ppNewDeviceList) |
{ |
Boolean changedList = false; |
pRecDevice pDevice = *ppListDeviceHead; |
pRecDevice pPrevDevice = NULL; |
while (pDevice) { // for all the devices in main list |
Boolean present = false; |
// ensure they are in new list |
present = HIDFindDeviceInList (*ppNewDeviceList, pDevice); |
// remove those that are not |
if (!present) { |
changedList = true; |
if (pDevice == *ppListDeviceHead) // if we are removing the list head |
*ppListDeviceHead = pDevice->pNext; // move list head |
pDevice = HIDDisposeDevice (&pDevice); // remove device from list and dispose device and step to next device |
if (pPrevDevice) // next device clean up required for HIDDisposeDevice |
pPrevDevice->pNext = pDevice; |
} else { |
pPrevDevice = pDevice; // only advance here since other case removes a device and prev stays constant |
pDevice = pDevice->pNext; // step to next device |
} |
} |
pDevice = *ppNewDeviceList; |
while (pDevice) { // for all the devices in new list |
Boolean present = false; |
present = HIDFindDeviceInList (*ppListDeviceHead, pDevice); // ensure they are in original list |
if (!present) { // not found in old list so move to old list from new |
changedList = true; |
pDevice = HIDMoveDevice (ppListDeviceHead, pDevice, ppNewDeviceList); // move those that are not |
} else // found |
pDevice = pDevice->pNext; // just step to next device |
} |
return changedList; |
} |
// --------------------------------- |
// merges two devicelist into single *ppNewDeviceList |
// note: ppNewDeviceList may have head device modified (such as if it is NULL) thus pointer to pointer to device |
// devices are matched on vendorID, productID, locID by HIDFindDeviceInList |
// device record in pNewDeviceList maintained |
static void HIDMergeDeviceList (pRecDevice *ppNewDeviceList, pRecDevice *ppDeviceList) |
{ |
pRecDevice pDevice = *ppDeviceList; |
while (pDevice) { // for all the devices in old list |
Boolean present = false; |
present = HIDFindDeviceInList (*ppNewDeviceList, pDevice); // ensure they are in new list |
if (!present) // not found in new list |
pDevice = HIDMoveDevice (ppNewDeviceList, pDevice, ppDeviceList); // move to new list and get next |
else // found in new list (so don't do anything |
pDevice = pDevice->pNext; // just step to next device |
} |
} |
// --------------------------------- |
// builds a matching dictionary based on usage page and usage |
static CFMutableDictionaryRef HIDSetUpMatchingDictionary (UInt32 usagePage, UInt32 usage) |
{ |
CFNumberRef refUsage = NULL, refUsagePage = NULL; |
CFMutableDictionaryRef refHIDMatchDictionary = NULL; |
// Set up a matching dictionary to search I/O Registry by class name for all HID class devices. |
refHIDMatchDictionary = IOServiceMatching (kIOHIDDeviceKey); |
if ((refHIDMatchDictionary != NULL) && (usagePage) && (usage)) |
{ |
// Add key for device type (joystick, in this case) to refine the matching dictionary. |
refUsagePage = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usagePage); |
CFDictionarySetValue (refHIDMatchDictionary, CFSTR (kIOHIDPrimaryUsagePageKey), refUsagePage); |
CFRelease (refUsagePage); |
refUsage = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usage); |
CFDictionarySetValue (refHIDMatchDictionary, CFSTR (kIOHIDPrimaryUsageKey), refUsage); |
CFRelease (refUsage); |
} |
else if (NULL == refHIDMatchDictionary) |
HIDReportError ("Failed to get HID CFMutableDictionaryRef via IOServiceMatching."); |
return refHIDMatchDictionary; |
} |
// --------------------------------- |
// sets up a matching dictionary based on usage page and usage passed in and uses it to get a device iterator |
// |
static io_iterator_t HIDGetIterator (const mach_port_t masterPort, UInt32 usagePage, UInt32 usage) |
{ |
IOReturn result = kIOReturnSuccess; |
CFMutableDictionaryRef hidMatchDictionary = NULL; |
io_iterator_t hidObjectIterator = 0; |
// Set up matching dictionary to search the I/O Registry for HID devices we are interested in. Dictionary reference is NULL if error. |
hidMatchDictionary = HIDSetUpMatchingDictionary (usagePage, usage); |
if (NULL == hidMatchDictionary) |
{ |
HIDReportError ("CouldnÕt create a matching dictionary."); |
return hidObjectIterator; |
} |
// Now search I/O Registry for matching devices. |
result = IOServiceGetMatchingServices (masterPort, hidMatchDictionary, &hidObjectIterator); |
if (kIOReturnSuccess != result) |
HIDReportErrorNum ("Failed to create IO object iterator, error:", result); |
// --- this is a "normal" failure condition (such as no devices of a certain type plugged in) so do not issue a warning |
// else if (NULL == hidObjectIterator) // likely no HID devices which matched selection criteria are connected |
// HIDReportError ("Warning: Could not find any matching devices, thus iterator creation failed."); |
// IOServiceGetMatchingServices consumes a reference to the dictionary, so we don't need to release the dictionary ref. |
hidMatchDictionary = NULL; |
return hidObjectIterator; |
} |
// --------------------------------- |
// disposes of the element list associated with a device and the memory associated with the list |
// uses depthwise recursion to dispose both collections and elements. |
static void HIDDisposeDeviceElements (pRecElement pElement) |
{ |
if (pElement) |
{ |
if (pElement->pChild) |
HIDDisposeDeviceElements (pElement->pChild); |
if (pElement->pSibling) |
HIDDisposeDeviceElements (pElement->pSibling); |
free (pElement); |
} |
} |
// --------------------------------- |
// disposes of a single device, closing and releasing interface, freeing memory for device and elements, setting device pointer to NULL |
// all your device no longer belong to us... (i.e., you do not 'own' the device anymore) |
// routines calling this need to fix up previous previous device next device pointer to point to the device returned by this routine |
static pRecDevice HIDDisposeDevice (pRecDevice *ppDevice) |
{ |
kern_return_t result = KERN_SUCCESS; |
pRecDevice pDeviceNext = NULL; |
if (*ppDevice) |
{ |
// save next device prior to disposing of this device |
pDeviceNext = (*ppDevice)->pNext; |
HIDDisposeDeviceElements ((*ppDevice)->pListElements); |
(*ppDevice)->pListElements = NULL; |
result = HIDCloseReleaseInterface (*ppDevice); // function sanity checks interface value (now application does not own device) |
if (kIOReturnSuccess != result) |
HIDReportErrorNum ("HIDCloseReleaseInterface failed when trying to dipose device.", result); |
free (*ppDevice); |
*ppDevice = NULL; |
} |
return pDeviceNext; |
} |
// --------------------------------- |
// count number of devices in global device list (gpDeviceList) |
static UInt32 HIDCountCurrentDevices (pRecDevice pDeviceList) |
{ |
pRecDevice pDevice = pDeviceList; |
UInt32 devices = 0; |
while (pDevice) |
{ |
devices++; |
pDevice = pDevice->pNext; |
} |
return devices; |
} |
// --------------------------------- |
// matches type masks passed in to actual element types (which are not set up to be used as a mask |
static Boolean HIDMatchElementTypeMask (IOHIDElementType type, HIDElementTypeMask typeMask) |
{ |
if (typeMask & kHIDElementTypeInput) |
if ((type == kIOHIDElementTypeInput_Misc) || (type == kIOHIDElementTypeInput_Button) || (type == kIOHIDElementTypeInput_Axis) || (type == kIOHIDElementTypeInput_ScanCodes)) |
return true; |
if (typeMask & kHIDElementTypeOutput) |
if (type == kIOHIDElementTypeOutput) |
return true; |
if (typeMask & kHIDElementTypeFeature) |
if (type == kIOHIDElementTypeFeature) |
return true; |
if (typeMask & kHIDElementTypeCollection) |
if (type == kIOHIDElementTypeCollection) |
return true; |
return false; |
} |
// --------------------------------- |
static pRecElement HIDGetDeviceElement (pRecElement pElement, HIDElementTypeMask typeMask) |
{ |
// we are asking for this element |
if (NULL != pElement) |
{ |
if (HIDMatchElementTypeMask (pElement->type, typeMask)) // if the type match what we are looking for |
return pElement; // return the element |
else |
return HIDGetNextDeviceElement (pElement, typeMask); // else get the next one |
} |
return NULL; |
} |
// --------------------------------- |
void HIDDumpDevice (pRecDevice pDevice) |
{ |
printf (" %s %s: 0x%p", pDevice->manufacturer, pDevice->product, pDevice); |
if (!pDevice->pListElements) |
printf (" (NO elements)"); |
else |
printf (" (%d elements)", pDevice->inputs); |
} |
// --------------------------------- |
void HIDDumpDeviceList (pRecDevice pDeviceList) |
{ |
printf ("HIDDumpDeviceList (0x%p):\n", pDeviceList); |
pRecDevice pDevice = pDeviceList; |
while (pDevice) { |
HIDDumpDevice (pDevice); |
printf("\n"); |
pDevice = pDevice->pNext; |
} |
} |
#pragma mark - |
// ================================= |
// public functions |
// builds list of devices with elements (allocates memory and captures devices) |
// in which the devices could be of different types/usages |
// list is allocated internally within HID Utilites and can be accessed via accessor functions |
// structures within list are considered flat and user accessable, but not user modifiable |
// can be called again to rebuild list to account for new devices (will do the right thing in case of disposing existing list) |
// usagePage, usage are each a numDeviceTypes sized array of matching usage and usage pages |
// returns true if succesful |
Boolean HIDBuildMultiDeviceList (UInt32 *pUsagePage, UInt32 *pUsage, UInt32 numDeviceTypes) |
{ |
gpDeviceList = HIDCreateMultiTypeDeviceList (pUsagePage, pUsage, numDeviceTypes); |
gNumDevices = HIDCountCurrentDevices (gpDeviceList); // set count of main list |
if (gpDeviceList) |
return true; |
else |
return false; |
} |
// --------------------------------- |
// same as above but this uses a single usagePage and usage |
Boolean HIDBuildDeviceList (UInt32 usagePage, UInt32 usage) |
{ |
return HIDBuildMultiDeviceList (&usagePage, &usage, 1); // call HIDBuildMultiDeviceList with a single usage |
} |
// --------------------------------- |
// updates the current device list for any new/removed devices |
// if this is called before HIDBuildDeviceList the it functions like HIDBuildMultiDeviceList |
// usagePage, usage are each a numDeviceTypes sized array of matching usage and usage pages |
// returns true if successful which means if any device were added or removed (the device config changed) |
Boolean HIDUpdateDeviceList (UInt32 *pUsagePage, UInt32 *pUsage, UInt32 numDeviceTypes) |
{ |
Boolean updateResult = false; |
pRecDevice pNewDeviceList = HIDCreateMultiTypeDeviceList (pUsagePage, pUsage, numDeviceTypes); // get current list |
if (gpDeviceList) { // if we have a list already |
updateResult = HIDCompareUpdateDeviceList (&gpDeviceList, &pNewDeviceList); // update main list |
while (NULL != pNewDeviceList) // dispose new list after compare and update of main list (heaad first distruction to no next device clean up) |
pNewDeviceList = HIDDisposeDevice (&pNewDeviceList); // dispose current device return next device, will set pNewDeviceList to NULL |
} else |
gpDeviceList = pNewDeviceList; |
gNumDevices = HIDCountCurrentDevices (gpDeviceList); // set count |
return updateResult; |
} |
// --------------------------------- |
// release list built by above function |
// MUST be called prior to application exit to properly release devices |
// if not called (or app crashes) devices can be recovered by pluging into different location in USB chain |
void HIDReleaseDeviceList (void) |
{ |
IOReturn result = kIOReturnSuccess; |
if (NULL != gpDeviceList) // release queues and device |
{ |
result = HIDReleaseAllDeviceQueues (); |
if (kIOReturnSuccess != result) |
HIDReportErrorNum ("Could not Dequeue Device elements and release devices.", result); |
} |
while (NULL != gpDeviceList) // head first destruction no clean needed |
gpDeviceList = HIDDisposeDevice (&gpDeviceList); // dispose current device return next device will set gpDeviceList to NULL |
gNumDevices = 0; |
} |
// --------------------------------- |
// does a device list exist |
Boolean HIDHaveDeviceList (void) |
{ |
if (NULL != gpDeviceList) |
return true; |
return false; |
} |
// --------------------------------- |
// how many HID devices have been found |
// returns 0 if no device list exist |
UInt32 HIDCountDevices (void) |
{ |
return gNumDevices; |
} |
// --------------------------------- |
// how many elements does a specific device have |
// returns 0 if device is invlaid or NULL |
UInt32 HIDCountDeviceElements (pRecDevice pDevice, HIDElementTypeMask typeMask) |
{ |
int count = 0; |
if (NULL != pDevice) |
{ |
if (typeMask & kHIDElementTypeInput) |
count += pDevice->inputs; |
if (typeMask & kHIDElementTypeOutput) |
count += pDevice->outputs; |
if (typeMask & kHIDElementTypeFeature) |
count += pDevice->features; |
if (typeMask & kHIDElementTypeCollection) |
count += pDevice->collections; |
} |
return count; |
} |
// --------------------------------- |
// get the first device in the device list |
// returns NULL if no list exists |
pRecDevice HIDGetFirstDevice (void) |
{ |
return gpDeviceList; |
} |
// --------------------------------- |
// get next device in list given current device as parameter |
// returns NULL if end of list |
pRecDevice HIDGetNextDevice (pRecDevice pDevice) |
{ |
if (NULL != pDevice) |
return pDevice->pNext; |
else |
return NULL; |
} |
// --------------------------------- |
// get the first element of device passed in as parameter |
// returns NULL if no list exists or device does not exists or is NULL |
pRecElement HIDGetFirstDeviceElement (pRecDevice pDevice, HIDElementTypeMask typeMask) |
{ |
if (NULL != pDevice) |
{ |
if (HIDMatchElementTypeMask (pDevice->pListElements->type, typeMask)) // ensure first type matches |
return pDevice->pListElements; |
else |
return HIDGetNextDeviceElement (pDevice->pListElements, typeMask); |
} |
else |
return NULL; |
} |
// --------------------------------- |
// get next element of given device in list given current element as parameter |
// will walk down each collection then to next element or collection (depthwise traverse) |
// returns NULL if end of list |
// uses mask of HIDElementTypeMask to restrict element found |
// use kHIDElementTypeIO to get previous HIDGetNextDeviceElement functionality |
pRecElement HIDGetNextDeviceElement (pRecElement pElement, HIDElementTypeMask typeMask) |
{ |
// should only have elements passed in (though someone could mix calls and pass us a collection) |
// collection means return the next child or sibling (in that order) |
// element means returnt he next sibling (as elements can't have children |
if (NULL != pElement) |
{ |
if (pElement->pChild) |
{ |
if (pElement->type != kIOHIDElementTypeCollection) |
HIDReportError ("Malformed element list: found child of element."); |
else |
return HIDGetDeviceElement (pElement->pChild, typeMask); // return the child of this element |
} |
else if (pElement->pSibling) |
{ |
return HIDGetDeviceElement (pElement->pSibling, typeMask); //return the sibling of this element |
} |
else // at end back up correctly |
{ |
pRecElement pPreviousElement = NULL; |
// malformed device ending in collection |
if (pElement->type == kIOHIDElementTypeCollection) |
HIDReportError ("Malformed device: found collection at end of element chain."); |
// walk back up tree to element prior to first collection ecountered and take next element |
while (pElement) |
{ |
pPreviousElement = pElement; |
pElement = pElement->pPrevious; // look at previous element |
if (!pElement) // if we are at the top and did not find another branch |
return NULL; |
// if the previous element is the branch element |
// which means the previous the we are coming back up the child path and we have a sibling |
if ((pPreviousElement != pElement->pSibling) && pElement->pSibling) |
break; |
} |
// now we must have been down the child route so go down the sibling route |
pElement = pElement->pSibling; // element of interest |
return HIDGetDeviceElement (pElement, typeMask); // otherwise return this element |
} |
} |
return NULL; |
} |
// --------------------------------- |
// get previous element of given device in list given current element as parameter |
// this wlaks directly up the tree to the top element and does not search at each level |
// returns NULL if beginning of list |
// uses mask of HIDElementTypeMask to restrict element found |
// use kHIDElementTypeIO to get non-collection elements |
pRecElement HIDGetPreviousDeviceElement (pRecElement pElement, HIDElementTypeMask typeMask) |
{ |
pRecElement pPreviousElement = pElement->pPrevious; |
// walk back up tree to element prior |
while (pPreviousElement && !HIDMatchElementTypeMask (pPreviousElement->type, typeMask)) |
{ |
pElement = pPreviousElement; // look at previous element |
pPreviousElement = pElement->pPrevious; |
} |
return pPreviousElement; // return this element |
} |
// --------------------------------- |
// returns C string type name given a type enumeration passed in as parameter (see IOHIDKeys.h) |
// returns "Unknown Type" for invalid types |
void HIDGetTypeName (int type, char * cstrName) |
{ |
switch (type) |
{ |
case kIOHIDElementTypeInput_Misc: |
sprintf(cstrName, "Miscellaneous Input"); |
break; |
case kIOHIDElementTypeInput_Button: |
sprintf(cstrName, "Button Input"); |
break; |
case kIOHIDElementTypeInput_Axis: |
sprintf(cstrName, "Axis Input"); |
break; |
case kIOHIDElementTypeInput_ScanCodes: |
sprintf(cstrName, "Scan Code Input"); |
break; |
case kIOHIDElementTypeOutput: |
sprintf(cstrName, "Output"); |
break; |
case kIOHIDElementTypeFeature: |
sprintf(cstrName, "Feature"); |
break; |
case kIOHIDElementTypeCollection: |
sprintf(cstrName, "Collection"); |
break; |
default: |
sprintf(cstrName, "Unknown Type"); |
break; |
} |
} |
// --------------------------------- |
// returns C string usage given usage page and usage passed in as parameters (see IOUSBHIDParser.h) |
// returns usage page and usage values in string form for unknown values |
void HIDGetUsageName (int valueUsagePage, int valueUsage, char * cstrName) |
{ |
switch (valueUsagePage) |
{ |
case kHIDPage_Undefined: |
switch (valueUsage) |
{ |
default: sprintf (cstrName, "Undefined Page, Usage 0x%x", valueUsage); break; |
} |
break; |
case kHIDPage_GenericDesktop: |
switch (valueUsage) |
{ |
case kHIDUsage_GD_Pointer: sprintf (cstrName, "Pointer"); break; |
case kHIDUsage_GD_Mouse: sprintf (cstrName, "Mouse"); break; |
case kHIDUsage_GD_Joystick: sprintf (cstrName, "Joystick"); break; |
case kHIDUsage_GD_GamePad: sprintf (cstrName, "GamePad"); break; |
case kHIDUsage_GD_Keyboard: sprintf (cstrName, "Keyboard"); break; |
case kHIDUsage_GD_Keypad: sprintf (cstrName, "Keypad"); break; |
case kHIDUsage_GD_MultiAxisController: sprintf (cstrName, "Multi-Axis Controller"); break; |
case kHIDUsage_GD_X: sprintf (cstrName, "X-Axis"); break; |
case kHIDUsage_GD_Y: sprintf (cstrName, "Y-Axis"); break; |
case kHIDUsage_GD_Z: sprintf (cstrName, "Z-Axis"); break; |
case kHIDUsage_GD_Rx: sprintf (cstrName, "X-Rotation"); break; |
case kHIDUsage_GD_Ry: sprintf (cstrName, "Y-Rotation"); break; |
case kHIDUsage_GD_Rz: sprintf (cstrName, "Z-Rotation"); break; |
case kHIDUsage_GD_Slider: sprintf (cstrName, "Slider"); break; |
case kHIDUsage_GD_Dial: sprintf (cstrName, "Dial"); break; |
case kHIDUsage_GD_Wheel: sprintf (cstrName, "Wheel"); break; |
case kHIDUsage_GD_Hatswitch: sprintf (cstrName, "Hatswitch"); break; |
case kHIDUsage_GD_CountedBuffer: sprintf (cstrName, "Counted Buffer"); break; |
case kHIDUsage_GD_ByteCount: sprintf (cstrName, "Byte Count"); break; |
case kHIDUsage_GD_MotionWakeup: sprintf (cstrName, "Motion Wakeup"); break; |
case kHIDUsage_GD_Start: sprintf (cstrName, "Start"); break; |
case kHIDUsage_GD_Select: sprintf (cstrName, "Select"); break; |
case kHIDUsage_GD_Vx: sprintf (cstrName, "X-Velocity"); break; |
case kHIDUsage_GD_Vy: sprintf (cstrName, "Y-Velocity"); break; |
case kHIDUsage_GD_Vz: sprintf (cstrName, "Z-Velocity"); break; |
case kHIDUsage_GD_Vbrx: sprintf (cstrName, "X-Rotation Velocity"); break; |
case kHIDUsage_GD_Vbry: sprintf (cstrName, "Y-Rotation Velocity"); break; |
case kHIDUsage_GD_Vbrz: sprintf (cstrName, "Z-Rotation Velocity"); break; |
case kHIDUsage_GD_Vno: sprintf (cstrName, "Vno"); break; |
case kHIDUsage_GD_SystemControl: sprintf (cstrName, "System Control"); break; |
case kHIDUsage_GD_SystemPowerDown: sprintf (cstrName, "System Power Down"); break; |
case kHIDUsage_GD_SystemSleep: sprintf (cstrName, "System Sleep"); break; |
case kHIDUsage_GD_SystemWakeUp: sprintf (cstrName, "System Wake Up"); break; |
case kHIDUsage_GD_SystemContextMenu: sprintf (cstrName, "System Context Menu"); break; |
case kHIDUsage_GD_SystemMainMenu: sprintf (cstrName, "System Main Menu"); break; |
case kHIDUsage_GD_SystemAppMenu: sprintf (cstrName, "System App Menu"); break; |
case kHIDUsage_GD_SystemMenuHelp: sprintf (cstrName, "System Menu Help"); break; |
case kHIDUsage_GD_SystemMenuExit: sprintf (cstrName, "System Menu Exit"); break; |
case kHIDUsage_GD_SystemMenu: sprintf (cstrName, "System Menu"); break; |
case kHIDUsage_GD_SystemMenuRight: sprintf (cstrName, "System Menu Right"); break; |
case kHIDUsage_GD_SystemMenuLeft: sprintf (cstrName, "System Menu Left"); break; |
case kHIDUsage_GD_SystemMenuUp: sprintf (cstrName, "System Menu Up"); break; |
case kHIDUsage_GD_SystemMenuDown: sprintf (cstrName, "System Menu Down"); break; |
case kHIDUsage_GD_DPadUp: sprintf (cstrName, "DPad Up"); break; |
case kHIDUsage_GD_DPadDown: sprintf (cstrName, "DPad Down"); break; |
case kHIDUsage_GD_DPadRight: sprintf (cstrName, "DPad Right"); break; |
case kHIDUsage_GD_DPadLeft: sprintf (cstrName, "DPad Left"); break; |
case kHIDUsage_GD_Reserved: sprintf (cstrName, "Reserved"); break; |
default: sprintf (cstrName, "Generic Desktop Usage 0x%x", valueUsage); break; |
} |
break; |
case kHIDPage_Simulation: |
switch (valueUsage) |
{ |
default: sprintf (cstrName, "Simulation Usage 0x%x", valueUsage); break; |
} |
break; |
case kHIDPage_VR: |
switch (valueUsage) |
{ |
default: sprintf (cstrName, "VR Usage 0x%x", valueUsage); break; |
} |
break; |
case kHIDPage_Sport: |
switch (valueUsage) |
{ |
default: sprintf (cstrName, "Sport Usage 0x%x", valueUsage); break; |
} |
break; |
case kHIDPage_Game: |
switch (valueUsage) |
{ |
default: sprintf (cstrName, "Game Usage 0x%x", valueUsage); break; |
} |
break; |
case kHIDPage_KeyboardOrKeypad: |
switch (valueUsage) |
{ |
case kHIDUsage_KeyboardErrorRollOver: sprintf (cstrName, "Error Roll Over"); break; |
case kHIDUsage_KeyboardPOSTFail: sprintf (cstrName, "POST Fail"); break; |
case kHIDUsage_KeyboardErrorUndefined: sprintf (cstrName, "Error Undefined"); break; |
case kHIDUsage_KeyboardA: sprintf (cstrName, "A"); break; |
case kHIDUsage_KeyboardB: sprintf (cstrName, "B"); break; |
case kHIDUsage_KeyboardC: sprintf (cstrName, "C"); break; |
case kHIDUsage_KeyboardD: sprintf (cstrName, "D"); break; |
case kHIDUsage_KeyboardE: sprintf (cstrName, "E"); break; |
case kHIDUsage_KeyboardF: sprintf (cstrName, "F"); break; |
case kHIDUsage_KeyboardG: sprintf (cstrName, "G"); break; |
case kHIDUsage_KeyboardH: sprintf (cstrName, "H"); break; |
case kHIDUsage_KeyboardI: sprintf (cstrName, "I"); break; |
case kHIDUsage_KeyboardJ: sprintf (cstrName, "J"); break; |
case kHIDUsage_KeyboardK: sprintf (cstrName, "K"); break; |
case kHIDUsage_KeyboardL: sprintf (cstrName, "L"); break; |
case kHIDUsage_KeyboardM: sprintf (cstrName, "M"); break; |
case kHIDUsage_KeyboardN: sprintf (cstrName, "N"); break; |
case kHIDUsage_KeyboardO: sprintf (cstrName, "O"); break; |
case kHIDUsage_KeyboardP: sprintf (cstrName, "P"); break; |
case kHIDUsage_KeyboardQ: sprintf (cstrName, "Q"); break; |
case kHIDUsage_KeyboardR: sprintf (cstrName, "R"); break; |
case kHIDUsage_KeyboardS: sprintf (cstrName, "S"); break; |
case kHIDUsage_KeyboardT: sprintf (cstrName, "T"); break; |
case kHIDUsage_KeyboardU: sprintf (cstrName, "U"); break; |
case kHIDUsage_KeyboardV: sprintf (cstrName, "V"); break; |
case kHIDUsage_KeyboardW: sprintf (cstrName, "W"); break; |
case kHIDUsage_KeyboardX: sprintf (cstrName, "X"); break; |
case kHIDUsage_KeyboardY: sprintf (cstrName, "Y"); break; |
case kHIDUsage_KeyboardZ: sprintf (cstrName, "Z"); break; |
case kHIDUsage_Keyboard1: sprintf (cstrName, "1"); break; |
case kHIDUsage_Keyboard2: sprintf (cstrName, "2"); break; |
case kHIDUsage_Keyboard3: sprintf (cstrName, "3"); break; |
case kHIDUsage_Keyboard4: sprintf (cstrName, "4"); break; |
case kHIDUsage_Keyboard5: sprintf (cstrName, "5"); break; |
case kHIDUsage_Keyboard6: sprintf (cstrName, "6"); break; |
case kHIDUsage_Keyboard7: sprintf (cstrName, "7"); break; |
case kHIDUsage_Keyboard8: sprintf (cstrName, "8"); break; |
case kHIDUsage_Keyboard9: sprintf (cstrName, "9"); break; |
case kHIDUsage_Keyboard0: sprintf (cstrName, "0"); break; |
case kHIDUsage_KeyboardReturnOrEnter: sprintf (cstrName, "Return"); break; |
case kHIDUsage_KeyboardEscape: sprintf (cstrName, "Escape"); break; |
case kHIDUsage_KeyboardDeleteOrBackspace: sprintf (cstrName, "Delete"); break; |
case kHIDUsage_KeyboardTab: sprintf (cstrName, "Tab"); break; |
case kHIDUsage_KeyboardSpacebar: sprintf (cstrName, "Spacebar"); break; |
case kHIDUsage_KeyboardHyphen: sprintf (cstrName, "Dash"); break; |
case kHIDUsage_KeyboardEqualSign: sprintf (cstrName, "Equal"); break; |
case kHIDUsage_KeyboardOpenBracket: sprintf (cstrName, "Left Square Bracket"); break; |
case kHIDUsage_KeyboardCloseBracket: sprintf (cstrName, "Right Square Bracket"); break; |
case kHIDUsage_KeyboardBackslash: sprintf (cstrName, "Slash"); break; |
case kHIDUsage_KeyboardNonUSPound: sprintf (cstrName, "Non-US #"); break; |
case kHIDUsage_KeyboardSemicolon: sprintf (cstrName, "Semi-Colon"); break; |
case kHIDUsage_KeyboardQuote: sprintf (cstrName, "Single Quote"); break; |
case kHIDUsage_KeyboardGraveAccentAndTilde: sprintf (cstrName, "Grave Accent"); break; |
case kHIDUsage_KeyboardComma: sprintf (cstrName, "Comma"); break; |
case kHIDUsage_KeyboardPeriod: sprintf (cstrName, "Period"); break; |
case kHIDUsage_KeyboardSlash: sprintf (cstrName, "Slash"); break; |
case kHIDUsage_KeyboardCapsLock: sprintf (cstrName, "Caps Lock"); break; |
case kHIDUsage_KeyboardF1: sprintf (cstrName, "F1"); break; |
case kHIDUsage_KeyboardF2: sprintf (cstrName, "F2"); break; |
case kHIDUsage_KeyboardF3: sprintf (cstrName, "F3"); break; |
case kHIDUsage_KeyboardF4: sprintf (cstrName, "F4"); break; |
case kHIDUsage_KeyboardF5: sprintf (cstrName, "F5"); break; |
case kHIDUsage_KeyboardF6: sprintf (cstrName, "F6"); break; |
case kHIDUsage_KeyboardF7: sprintf (cstrName, "F7"); break; |
case kHIDUsage_KeyboardF8: sprintf (cstrName, "F8"); break; |
case kHIDUsage_KeyboardF9: sprintf (cstrName, "F9"); break; |
case kHIDUsage_KeyboardF10: sprintf (cstrName, "F10"); break; |
case kHIDUsage_KeyboardF11: sprintf (cstrName, "F11"); break; |
case kHIDUsage_KeyboardF12: sprintf (cstrName, "F12"); break; |
case kHIDUsage_KeyboardPrintScreen: sprintf (cstrName, "Print Screen"); break; |
case kHIDUsage_KeyboardScrollLock: sprintf (cstrName, "Scroll Lock"); break; |
case kHIDUsage_KeyboardPause: sprintf (cstrName, "Pause"); break; |
case kHIDUsage_KeyboardInsert: sprintf (cstrName, "Insert"); break; |
case kHIDUsage_KeyboardHome: sprintf (cstrName, "Home"); break; |
case kHIDUsage_KeyboardPageUp: sprintf (cstrName, "Page Up"); break; |
case kHIDUsage_KeyboardDeleteForward: sprintf (cstrName, "Delete Forward"); break; |
case kHIDUsage_KeyboardEnd: sprintf (cstrName, "End"); break; |
case kHIDUsage_KeyboardPageDown: sprintf (cstrName, "Page Down"); break; |
case kHIDUsage_KeyboardRightArrow: sprintf (cstrName, "Right Arrow"); break; |
case kHIDUsage_KeyboardLeftArrow: sprintf (cstrName, "Left Arrow"); break; |
case kHIDUsage_KeyboardDownArrow: sprintf (cstrName, "Down Arrow"); break; |
case kHIDUsage_KeyboardUpArrow: sprintf (cstrName, "Up Arrow"); break; |
case kHIDUsage_KeypadNumLock: sprintf (cstrName, "Keypad NumLock"); break; |
case kHIDUsage_KeypadSlash: sprintf (cstrName, "Keypad Slash"); break; |
case kHIDUsage_KeypadAsterisk: sprintf (cstrName, "Keypad Asterisk"); break; |
case kHIDUsage_KeypadHyphen: sprintf (cstrName, "Keypad Dash"); break; |
case kHIDUsage_KeypadPlus: sprintf (cstrName, "Keypad Plus"); break; |
case kHIDUsage_KeypadEnter: sprintf (cstrName, "Keypad Enter"); break; |
case kHIDUsage_Keypad1: sprintf (cstrName, "Keypad 1"); break; |
case kHIDUsage_Keypad2: sprintf (cstrName, "Keypad 2"); break; |
case kHIDUsage_Keypad3: sprintf (cstrName, "Keypad 3"); break; |
case kHIDUsage_Keypad4: sprintf (cstrName, "Keypad 4"); break; |
case kHIDUsage_Keypad5: sprintf (cstrName, "Keypad 5"); break; |
case kHIDUsage_Keypad6: sprintf (cstrName, "Keypad 6"); break; |
case kHIDUsage_Keypad7: sprintf (cstrName, "Keypad 7"); break; |
case kHIDUsage_Keypad8: sprintf (cstrName, "Keypad 8"); break; |
case kHIDUsage_Keypad9: sprintf (cstrName, "Keypad 9"); break; |
case kHIDUsage_Keypad0: sprintf (cstrName, "Keypad 0"); break; |
case kHIDUsage_KeypadPeriod: sprintf (cstrName, "Keypad Period"); break; |
case kHIDUsage_KeyboardNonUSBackslash: sprintf (cstrName, "Non-US Backslash"); break; |
case kHIDUsage_KeyboardApplication: sprintf (cstrName, "Application"); break; |
case kHIDUsage_KeyboardPower: sprintf (cstrName, "Power"); break; |
case kHIDUsage_KeypadEqualSign: sprintf (cstrName, "Keypad Equal"); break; |
case kHIDUsage_KeyboardF13: sprintf (cstrName, "F13"); break; |
case kHIDUsage_KeyboardF14: sprintf (cstrName, "F14"); break; |
case kHIDUsage_KeyboardF15: sprintf (cstrName, "F15"); break; |
case kHIDUsage_KeyboardF16: sprintf (cstrName, "F16"); break; |
case kHIDUsage_KeyboardF17: sprintf (cstrName, "F17"); break; |
case kHIDUsage_KeyboardF18: sprintf (cstrName, "F18"); break; |
case kHIDUsage_KeyboardF19: sprintf (cstrName, "F19"); break; |
case kHIDUsage_KeyboardF20: sprintf (cstrName, "F20"); break; |
case kHIDUsage_KeyboardF21: sprintf (cstrName, "F21"); break; |
case kHIDUsage_KeyboardF22: sprintf (cstrName, "F22"); break; |
case kHIDUsage_KeyboardF23: sprintf (cstrName, "F23"); break; |
case kHIDUsage_KeyboardF24: sprintf (cstrName, "F24"); break; |
case kHIDUsage_KeyboardExecute: sprintf (cstrName, "Execute"); break; |
case kHIDUsage_KeyboardHelp: sprintf (cstrName, "Help"); break; |
case kHIDUsage_KeyboardMenu: sprintf (cstrName, "Menu"); break; |
case kHIDUsage_KeyboardSelect: sprintf (cstrName, "Select"); break; |
case kHIDUsage_KeyboardStop: sprintf (cstrName, "Stop"); break; |
case kHIDUsage_KeyboardAgain: sprintf (cstrName, "Again"); break; |
case kHIDUsage_KeyboardUndo: sprintf (cstrName, "Undo"); break; |
case kHIDUsage_KeyboardCut: sprintf (cstrName, "Cut"); break; |
case kHIDUsage_KeyboardCopy: sprintf (cstrName, "Copy"); break; |
case kHIDUsage_KeyboardPaste: sprintf (cstrName, "Paste"); break; |
case kHIDUsage_KeyboardFind: sprintf (cstrName, "Find"); break; |
case kHIDUsage_KeyboardMute: sprintf (cstrName, "Mute"); break; |
case kHIDUsage_KeyboardVolumeUp: sprintf (cstrName, "Volume Up"); break; |
case kHIDUsage_KeyboardVolumeDown: sprintf (cstrName, "Volume Down"); break; |
case kHIDUsage_KeyboardLockingCapsLock: sprintf (cstrName, "Locking Caps Lock"); break; |
case kHIDUsage_KeyboardLockingNumLock: sprintf (cstrName, "Locking Num Lock"); break; |
case kHIDUsage_KeyboardLockingScrollLock: sprintf (cstrName, "Locking Scroll Lock"); break; |
case kHIDUsage_KeypadComma: sprintf (cstrName, "Keypad Comma"); break; |
case kHIDUsage_KeypadEqualSignAS400: sprintf (cstrName, "Keypad Equal Sign for AS-400"); break; |
case kHIDUsage_KeyboardInternational1: sprintf (cstrName, "International1"); break; |
case kHIDUsage_KeyboardInternational2: sprintf (cstrName, "International2"); break; |
case kHIDUsage_KeyboardInternational3: sprintf (cstrName, "International3"); break; |
case kHIDUsage_KeyboardInternational4: sprintf (cstrName, "International4"); break; |
case kHIDUsage_KeyboardInternational5: sprintf (cstrName, "International5"); break; |
case kHIDUsage_KeyboardInternational6: sprintf (cstrName, "International6"); break; |
case kHIDUsage_KeyboardInternational7: sprintf (cstrName, "International7"); break; |
case kHIDUsage_KeyboardInternational8: sprintf (cstrName, "International8"); break; |
case kHIDUsage_KeyboardInternational9: sprintf (cstrName, "International9"); break; |
case kHIDUsage_KeyboardLANG1: sprintf (cstrName, "LANG1"); break; |
case kHIDUsage_KeyboardLANG2: sprintf (cstrName, "LANG2"); break; |
case kHIDUsage_KeyboardLANG3: sprintf (cstrName, "LANG3"); break; |
case kHIDUsage_KeyboardLANG4: sprintf (cstrName, "LANG4"); break; |
case kHIDUsage_KeyboardLANG5: sprintf (cstrName, "LANG5"); break; |
case kHIDUsage_KeyboardLANG6: sprintf (cstrName, "LANG6"); break; |
case kHIDUsage_KeyboardLANG7: sprintf (cstrName, "LANG7"); break; |
case kHIDUsage_KeyboardLANG8: sprintf (cstrName, "LANG8"); break; |
case kHIDUsage_KeyboardLANG9: sprintf (cstrName, "LANG9"); break; |
case kHIDUsage_KeyboardAlternateErase: sprintf (cstrName, "Alternate Erase"); break; |
case kHIDUsage_KeyboardSysReqOrAttention: sprintf (cstrName, "SysReq or Attention"); break; |
case kHIDUsage_KeyboardCancel: sprintf (cstrName, "Cancel"); break; |
case kHIDUsage_KeyboardClear: sprintf (cstrName, "Clear"); break; |
case kHIDUsage_KeyboardPrior: sprintf (cstrName, "Prior"); break; |
case kHIDUsage_KeyboardReturn: sprintf (cstrName, "Return"); break; |
case kHIDUsage_KeyboardSeparator: sprintf (cstrName, "Separator"); break; |
case kHIDUsage_KeyboardOut: sprintf (cstrName, "Out"); break; |
case kHIDUsage_KeyboardOper: sprintf (cstrName, "Oper"); break; |
case kHIDUsage_KeyboardClearOrAgain: sprintf (cstrName, "Clear or Again"); break; |
case kHIDUsage_KeyboardCrSelOrProps: sprintf (cstrName, "CrSel or Props"); break; |
case kHIDUsage_KeyboardExSel: sprintf (cstrName, "ExSel"); break; |
case kHIDUsage_KeyboardLeftControl: sprintf (cstrName, "Left Control"); break; |
case kHIDUsage_KeyboardLeftShift: sprintf (cstrName, "Left Shift"); break; |
case kHIDUsage_KeyboardLeftAlt: sprintf (cstrName, "Left Alt"); break; |
case kHIDUsage_KeyboardLeftGUI: sprintf (cstrName, "Left GUI"); break; |
case kHIDUsage_KeyboardRightControl: sprintf (cstrName, "Right Control"); break; |
case kHIDUsage_KeyboardRightShift: sprintf (cstrName, "Right Shift"); break; |
case kHIDUsage_KeyboardRightAlt: sprintf (cstrName, "Right Alt"); break; |
case kHIDUsage_KeyboardRightGUI: sprintf (cstrName, "Right GUI"); break; |
case kHIDUsage_Keyboard_Reserved: sprintf (cstrName, "Reserved"); break; |
default: sprintf (cstrName, "Keyboard Usage 0x%x", valueUsage); break; |
} |
break; |
case kHIDPage_LEDs: |
switch (valueUsage) |
{ |
// some LED usages |
case kHIDUsage_LED_IndicatorRed: sprintf (cstrName, "Red LED"); break; |
case kHIDUsage_LED_IndicatorGreen: sprintf (cstrName, "Green LED"); break; |
case kHIDUsage_LED_IndicatorAmber: sprintf (cstrName, "Amber LED"); break; |
case kHIDUsage_LED_GenericIndicator: sprintf (cstrName, "Generic LED"); break; |
case kHIDUsage_LED_SystemSuspend: sprintf (cstrName, "System Suspend LED"); break; |
case kHIDUsage_LED_ExternalPowerConnected: sprintf (cstrName, "External Power LED"); break; |
default: sprintf (cstrName, "LED Usage 0x%x", valueUsage); break; |
} |
break; |
case kHIDPage_Button: |
switch (valueUsage) |
{ |
default: sprintf (cstrName, "Button #%d", valueUsage); break; |
} |
break; |
case kHIDPage_Ordinal: |
switch (valueUsage) |
{ |
default: sprintf (cstrName, "Ordinal Instance %x", valueUsage); break; |
} |
break; |
case kHIDPage_Telephony: |
switch (valueUsage) |
{ |
default: sprintf (cstrName, "Telephony Usage 0x%x", valueUsage); break; |
} |
break; |
case kHIDPage_Consumer: |
switch (valueUsage) |
{ |
default: sprintf (cstrName, "Consumer Usage 0x%x", valueUsage); break; |
} |
break; |
case kHIDPage_Digitizer: |
switch (valueUsage) |
{ |
default: sprintf (cstrName, "Digitizer Usage 0x%x", valueUsage); break; |
} |
break; |
case kHIDPage_Unicode: |
switch (valueUsage) |
{ |
default: sprintf (cstrName, "Unicode Usage 0x%x", valueUsage); break; |
} |
break; |
case kHIDPage_AlphanumericDisplay: |
switch (valueUsage) |
{ |
default: sprintf (cstrName, "Alphanumeric Display Usage 0x%x", valueUsage); break; |
} |
break; |
case kHIDPage_BarCodeScanner: |
switch (valueUsage) |
{ |
default: sprintf (cstrName, "Bar Code Scanner Usage 0x%x", valueUsage); break; |
} |
break; |
case kHIDPage_Scale: |
switch (valueUsage) |
{ |
default: sprintf (cstrName, "Scale Usage 0x%x", valueUsage); break; |
} |
break; |
case kHIDPage_CameraControl: |
switch (valueUsage) |
{ |
default: sprintf (cstrName, "Camera Control Usage 0x%x", valueUsage); break; |
} |
break; |
case kHIDPage_Arcade: |
switch (valueUsage) |
{ |
default: sprintf (cstrName, "Arcade Usage 0x%x", valueUsage); break; |
} |
break; |
default: |
if (valueUsagePage > kHIDPage_VendorDefinedStart) |
sprintf (cstrName, "Vendor Defined Usage 0x%x", valueUsage); |
else |
sprintf (cstrName, "Page: 0x%x, Usage: 0x%x", valueUsagePage, valueUsage); |
break; |
} |
} |
// --------------------------------- |
// returns calibrated value given raw value passed in |
// calibrated value is equal to min and max values returned by HIDGetElementValue since device list built scaled to element reported min and max values |
// 2002-05-01: ggs: add support for center calibration |
int HIDCalibrateValue (int value, pRecElement pElement) |
{ |
if (NULL != pElement) |
{ |
float readScale, deviceScale = pElement->max - pElement->min; |
if (!pElement->hasCenter) { // no center for this element type |
if (pElement->maxReport == pElement->minReport) |
return value; // no scaling as |
else { |
readScale = pElement->maxReport - pElement->minReport; |
return ((value - pElement->minReport) * deviceScale / readScale) + pElement->min; |
} |
} else { // has center |
if (value < pElement->initialCenter) { |
readScale = pElement->initialCenter - pElement->minReport; |
if (readScale > 0.5f) { // if we have a positve range and reasonable value |
return ((value - pElement->minReport) * deviceScale * 0.5f / readScale ) + pElement->min; |
} else return value; // bad scaling |
} else if (value > pElement->initialCenter) { |
readScale = pElement->maxReport - pElement->initialCenter; |
if (readScale > 0.5f) { // if we have a positve range and reasonable value |
return ((value - pElement->initialCenter) * deviceScale * 0.5f / readScale ) + pElement->min + deviceScale / 2.0; |
} else return value; // bad scaling |
} else // at center so return center of range |
return (pElement->min + deviceScale / 2.0); |
} |
} |
else |
return 0; // bad element passed in |
} |
// --------------------------------- |
// returns scaled value given raw value passed in |
// scaled value is equal to current value assumed to be in the range of element reported min and max values scaled to user min and max scaled values |
int HIDScaleValue (int value, pRecElement pElement) |
{ |
float deviceScale = pElement->userMax - pElement->userMin; |
float readScale = pElement->max - pElement->min; |
if (readScale == 0) |
return value; |
return (value - pElement->min) * deviceScale / readScale + pElement->userMin; |
} |
Copyright © 2008 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2008-02-08