// Set up a config record for saving // takes an input records, returns record user can save as they want // Note: the save rec must be pre-allocated by the calling app and will be filled out void HIDSetElementConfig (pRecSaveHID pConfigRec, IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef, int actionCookie) { // must save: // actionCookie // Device: serial,vendorID, productID, location, usagePage, usage // Element: cookie, usagePage, usage, pConfigRec->actionCookie = actionCookie; // device // need to add serial number when I have a test case if (inIOHIDDeviceRef && inIOHIDElementRef) { pConfigRec->device.vendorID = IOHIDDevice_GetVendorID( inIOHIDDeviceRef ); pConfigRec->device.productID = IOHIDDevice_GetProductID( inIOHIDDeviceRef ); pConfigRec->device.locID = IOHIDDevice_GetLocationID( inIOHIDDeviceRef ); pConfigRec->device.usage = IOHIDDevice_GetUsage( inIOHIDDeviceRef ); pConfigRec->device.usagePage = IOHIDDevice_GetUsagePage( inIOHIDDeviceRef ); pConfigRec->element.usagePage = IOHIDElementGetUsagePage( inIOHIDElementRef ); pConfigRec->element.usage = IOHIDElementGetUsage( inIOHIDElementRef ); pConfigRec->element.minReport = IOHIDElement_GetCalibrationSaturationMin( inIOHIDElementRef ); pConfigRec->element.maxReport = IOHIDElement_GetCalibrationSaturationMax( inIOHIDElementRef ); pConfigRec->element.cookie = IOHIDElementGetCookie( inIOHIDElementRef ); } else { pConfigRec->device.vendorID = 0; pConfigRec->device.productID = 0; pConfigRec->device.locID = 0; pConfigRec->device.usage = 0; pConfigRec->device.usagePage = 0; pConfigRec->element.usagePage = 0; pConfigRec->element.usage = 0; pConfigRec->element.minReport = 0; pConfigRec->element.maxReport = 0; pConfigRec->element.cookie = 0; } }
Boolean HIDSaveElementPref( const CFStringRef inKeyCFStringRef, CFStringRef inAppCFStringRef, IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef ) { Boolean success = FALSE; if ( inKeyCFStringRef && inAppCFStringRef && inIOHIDDeviceRef && inIOHIDElementRef ) { long vendorID = IOHIDDevice_GetVendorID( inIOHIDDeviceRef ); require( vendorID, Oops ); long productID = IOHIDDevice_GetProductID( inIOHIDDeviceRef ); require( productID, Oops ); long locID = IOHIDDevice_GetLocationID( inIOHIDDeviceRef ); require( locID, Oops ); uint32_t usagePage = IOHIDDevice_GetUsagePage( inIOHIDDeviceRef ); uint32_t usage = IOHIDDevice_GetUsage( inIOHIDDeviceRef ); if ( !usagePage || !usage ) { usagePage = IOHIDDevice_GetPrimaryUsagePage( inIOHIDDeviceRef ); usage = IOHIDDevice_GetPrimaryUsage( inIOHIDDeviceRef ); } require( usagePage && usage, Oops ); uint32_t usagePageE = IOHIDElementGetUsagePage( inIOHIDElementRef ); uint32_t usageE = IOHIDElementGetUsage( inIOHIDElementRef ); IOHIDElementCookie eleCookie = IOHIDElementGetCookie( inIOHIDElementRef ); CFStringRef prefCFStringRef = CFStringCreateWithFormat( kCFAllocatorDefault, NULL, CFSTR( "d:{v:%ld, p:%ld, l:%ld, p:%ld, u:%ld}, e:{p:%ld, u:%ld, c:%ld}" ), vendorID, productID, locID, usagePage, usage, usagePageE, usageE, eleCookie ); if ( prefCFStringRef ) { CFPreferencesSetAppValue( inKeyCFStringRef, prefCFStringRef, inAppCFStringRef ); CFRelease( prefCFStringRef ); success = TRUE; } } Oops: ; return success; } // HIDSaveElementPref
/* PsychHIDGetDeviceListByUsage() */ void PsychHIDGetDeviceListByUsage(long usagePage, long usage, int *numDeviceIndices, int *deviceIndices, pRecDevice *deviceRecords) { pRecDevice currentDevice; int currentDeviceIndex; PsychHIDVerifyInit(); currentDeviceIndex = 0; *numDeviceIndices = 0; for (currentDevice = HIDGetFirstDevice(); currentDevice != NULL; currentDevice = HIDGetNextDevice(currentDevice)) { ++currentDeviceIndex; if (IOHIDDevice_GetUsagePage(currentDevice) == usagePage && IOHIDDevice_GetUsage(currentDevice) == usage) { deviceRecords[*numDeviceIndices] = currentDevice; deviceIndices[*numDeviceIndices] = currentDeviceIndex; //the array is 0-indexed, devices are 1-indexed. ++(*numDeviceIndices); } } }
// Set up a config record for saving // takes an input records, returns record user can save as they want // Note: the save rec must be pre-allocated by the calling app and will be filled out void HIDSetElementConfig(HID_info_ptr inHIDInfoPtr, IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef, IOHIDElementCookie actionCookie) { // must save: // actionCookie // Device: serial,vendorID, productID, location, usagePage, usage // Element: cookie, usagePage, usage, inHIDInfoPtr->actionCookie = actionCookie; // device // need to add serial number when I have a test case if (inIOHIDDeviceRef && inIOHIDElementRef) { inHIDInfoPtr->device.vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); inHIDInfoPtr->device.productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); inHIDInfoPtr->device.locID = IOHIDDevice_GetLocationID(inIOHIDDeviceRef); inHIDInfoPtr->device.usage = IOHIDDevice_GetUsage(inIOHIDDeviceRef); inHIDInfoPtr->device.usagePage = IOHIDDevice_GetUsagePage(inIOHIDDeviceRef); if (!inHIDInfoPtr->device.usagePage || !inHIDInfoPtr->device.usage) { inHIDInfoPtr->device.usagePage = IOHIDDevice_GetPrimaryUsagePage(inIOHIDDeviceRef); inHIDInfoPtr->device.usage = IOHIDDevice_GetPrimaryUsage(inIOHIDDeviceRef); } inHIDInfoPtr->element.usagePage = IOHIDElementGetUsagePage(inIOHIDElementRef); inHIDInfoPtr->element.usage = IOHIDElementGetUsage(inIOHIDElementRef); inHIDInfoPtr->element.minReport = IOHIDElement_GetCalibrationSaturationMin(inIOHIDElementRef); inHIDInfoPtr->element.maxReport = IOHIDElement_GetCalibrationSaturationMax(inIOHIDElementRef); inHIDInfoPtr->element.cookie = IOHIDElementGetCookie(inIOHIDElementRef); } else { inHIDInfoPtr->device.vendorID = 0; inHIDInfoPtr->device.productID = 0; inHIDInfoPtr->device.locID = 0; inHIDInfoPtr->device.usage = 0; inHIDInfoPtr->device.usagePage = 0; inHIDInfoPtr->element.usagePage = 0; inHIDInfoPtr->element.usage = 0; inHIDInfoPtr->element.minReport = 0; inHIDInfoPtr->element.maxReport = 0; inHIDInfoPtr->element.cookie = 0; } } // HIDSetElementConfig
Boolean HIDFindDeviceAndElement(const HID_info_rec *inSearchInfo, IOHIDDeviceRef * outFoundDevice, IOHIDElementRef * outFoundElement) { Boolean result = false; IOHIDDeviceRef bestIOHIDDeviceRef = NULL; IOHIDElementRef bestIOHIDElementRef = NULL; int bestScore = 0; CFIndex devIndex, devCount = CFArrayGetCount(gDeviceCFArrayRef); for (devIndex = 0; devIndex < devCount; devIndex++) { int deviceScore = 1; IOHIDDeviceRef tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex); if (!tIOHIDDeviceRef) { continue; } // match vendorID, productID (+10, +8) if (inSearchInfo->device.vendorID) { uint32_t vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); if (vendorID) { if (inSearchInfo->device.vendorID == vendorID) { deviceScore += 10; if (inSearchInfo->device.productID) { uint32_t productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); if (productID) { if (inSearchInfo->device.productID == productID) { deviceScore += 8; } // if (inSearchInfo->device.productID == productID) } // if (productID) } // if (inSearchInfo->device.productID) } // if (inSearchInfo->device.vendorID == vendorID) } // if vendorID } // if search->device.vendorID // match usagePage & usage (+9) if (inSearchInfo->device.usagePage && inSearchInfo->device.usage) { uint32_t usagePage = IOHIDDevice_GetUsagePage(tIOHIDDeviceRef); uint32_t usage = IOHIDDevice_GetUsage(tIOHIDDeviceRef); if (!usagePage || !usage) { usagePage = IOHIDDevice_GetPrimaryUsagePage(tIOHIDDeviceRef); usage = IOHIDDevice_GetPrimaryUsage(tIOHIDDeviceRef); } if (usagePage) { if (inSearchInfo->device.usagePage == usagePage) { if (usage) { if (inSearchInfo->device.usage == usage) { deviceScore += 9; } // if (inSearchInfo->usage == usage) } // if (usage) } // if (inSearchInfo->usagePage == usagePage) } // if (usagePage) } // if (inSearchInfo->usagePage && // inSearchInfo->usage) // match location ID (+5) if (inSearchInfo->device.locID) { uint32_t locID = IOHIDDevice_GetLocationID(tIOHIDDeviceRef); if (locID) { if (inSearchInfo->device.locID == locID) { deviceScore += 5; } } } // iterate over all elements of this device gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, 0); if (gElementCFArrayRef) { CFIndex eleIndex, eleCount = CFArrayGetCount(gElementCFArrayRef); for (eleIndex = 0; eleIndex < eleCount; eleIndex++) { IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, eleIndex); if (!tIOHIDElementRef) { continue; } int score = deviceScore; // match usage page, usage & cookie if (inSearchInfo->element.usagePage && inSearchInfo->element.usage) { uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); if (inSearchInfo->element.usagePage == usagePage) { uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); if (inSearchInfo->element.usage == usage) { score += 5; IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); if (inSearchInfo->element.cookie == cookie) { score += 4; } // cookies match } else { score = 0; } // usages match } else { score = 0; } // usage pages match } // if (search usage page & usage) #if LOG_SCORING if (kHIDPage_KeyboardOrKeypad != tElementRef->usagePage) { // skip keyboards here printf("%s: (%ld:%ld)-I-Debug, score: %ld\t", __PRETTY_FUNCTION__, inSearchInfo->element.usagePage, inSearchInfo->element.usage, score); HIDPrintElement(tIOHIDElementRef); } #endif // LOG_SCORING if (score > bestScore) { bestIOHIDDeviceRef = tIOHIDDeviceRef; bestIOHIDElementRef = tIOHIDElementRef; bestScore = score; #if LOG_SCORING printf("%s: (%ld:%ld)-I-Debug, better score: %ld\t", __PRETTY_FUNCTION__, inSearchInfo->element.usagePage, inSearchInfo->element.usage, score); HIDPrintElement(bestIOHIDElementRef); #endif // LOG_SCORING } } // for elements... CFRelease(gElementCFArrayRef); gElementCFArrayRef = NULL; } // if (gElementCFArrayRef) } // for (devIndex = 0; devIndex < devCount; // devIndex++) if (bestIOHIDDeviceRef || bestIOHIDElementRef) { *outFoundDevice = bestIOHIDDeviceRef; *outFoundElement = bestIOHIDElementRef; #if LOG_SCORING printf("%s: (%ld:%ld)-I-Debug, best score: %ld\t", __PRETTY_FUNCTION__, inSearchInfo->element.usagePage, inSearchInfo->element.usage, bestScore); HIDPrintElement(bestIOHIDElementRef); #endif // LOG_SCORING result = true; } return (result); } // HIDFindDeviceAndElement
void PsychHIDOSKbTriggerWait(int deviceIndex, int numScankeys, int* scanKeys) { pRecDevice deviceRecord; int i, numDeviceIndices; long KeysUsagePage; // This is the usage page of the target element: A key on a keyboard/keypad or a button. long KeysUsage; // This will contain the key code of the trigger key long KbDeviceUsagePages[NUMDEVICEUSAGES]= {kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop}; long KbDeviceUsages[NUMDEVICEUSAGES]={kHIDUsage_GD_Keyboard, kHIDUsage_GD_Keypad, kHIDUsage_GD_Mouse, kHIDUsage_GD_Pointer, kHIDUsage_GD_Joystick, kHIDUsage_GD_GamePad, kHIDUsage_GD_MultiAxisController}; int numDeviceUsages=NUMDEVICEUSAGES; int deviceIndices[PSYCH_HID_MAX_KEYBOARD_DEVICES]; pRecDevice deviceRecords[PSYCH_HID_MAX_KEYBOARD_DEVICES]; psych_bool isDeviceSpecified, foundUserSpecifiedDevice; double *timeValueOutput; IOHIDQueueInterface **queue; HRESULT result; IOHIDDeviceInterface122** interface=NULL; // This requires Mac OS X 10.3 or higher IOReturn success; IOHIDElementCookie triggerCookie; uint32_t usage; // Assign single supported trigger key: if (numScankeys != 1) PsychErrorExitMsg(PsychError_user, "Sorry, the OS/X version of KbTriggerWait only supports one trigger key."); KeysUsage = (long) scanKeys[0]; // Optional deviceIndex specified? isDeviceSpecified = (deviceIndex >= 0) ? TRUE : FALSE; PsychHIDVerifyInit(); if(hidDataRef!=NULL) PsychErrorExitMsg(PsychError_user, "A queue is already running, you must call KbQueueRelease() before invoking KbTriggerWait."); //Choose the device index and its record PsychHIDGetDeviceListByUsages(numDeviceUsages, KbDeviceUsagePages, KbDeviceUsages, &numDeviceIndices, deviceIndices, deviceRecords); if(isDeviceSpecified){ //make sure that the device number provided by the user is really a keyboard or keypad. for(i=0;i<numDeviceIndices;i++){ if(foundUserSpecifiedDevice=(deviceIndices[i]==deviceIndex)) break; } if(!foundUserSpecifiedDevice) PsychErrorExitMsg(PsychError_user, "Specified device number is not a keyboard or keypad device."); }else{ // set the keyboard or keypad device to be the first keyboard device or, if no keyboard, the first keypad i=0; if(numDeviceIndices==0) PsychErrorExitMsg(PsychError_user, "No keyboard or keypad devices detected."); else{ deviceIndex=deviceIndices[i]; } } deviceRecord=deviceRecords[i]; #ifndef __LP64__ usage = deviceRecord->usage; #else usage = IOHIDDevice_GetUsage(deviceRecord); #endif KeysUsagePage = ((usage == kHIDUsage_GD_Keyboard) || (usage == kHIDUsage_GD_Keypad)) ? kHIDPage_KeyboardOrKeypad : kHIDPage_Button; //Allocate and init out return arguments. PsychAllocOutDoubleArg(1, FALSE, &timeValueOutput); if(!timeValueOutput) PsychErrorExitMsg(PsychError_system, "Failed to allocate memory for output."); interface = PsychHIDGetDeviceInterfacePtrFromIndex(deviceIndex); if(!interface) PsychErrorExitMsg(PsychError_system, "Could not get interface to device."); // The following bracketed clause will get a cookie corresponding to the // trigger. If multiple keys were of interest, the code could be modified // trivially to iterate over an array of KeysUsage to generate an array of // corresponding cookies { CFArrayRef elements=NULL; psych_bool usedDictionary=FALSE; { CFDictionaryRef dict=NULL; // The next few lines define a dictionary describing the key of interest // If they are omitted, the code will still work, but all elements will match // initially rather than just the one key of interest, so the code will be // slower since it will iterate through hundreds of keys CFStringRef keys[2] = {CFSTR("UsagePage"), CFSTR("Usage")}; CFNumberRef values[2]; values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &KeysUsagePage); values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &KeysUsage); if(values[0]!=NULL && values[1]!=NULL){ // Even if they are NULL, it will be ok since dict can be NULL at the expense of some loss of efficiency dict = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys, (const void**)values, 2, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } // copyMatchinfElements requires IOHIDDeviceInterface122, thus Mac OS X 10.3 or higher // elements would have to be obtained directly from IORegistry for 10.2 or earlier // If dict==NULL, all elements will match, leading to some inefficiency success = (*interface)->copyMatchingElements(interface, dict, &elements); if(dict){ usedDictionary=TRUE; CFRelease(dict); } if(values[0]) CFRelease(values[0]); if(values[1]) CFRelease(values[1]); if(!elements){ PsychErrorExitMsg(PsychError_user, "Specified key code not found on device (I)."); } } { // elements will only contain one element in this implementation, but has the // advantage of generalizing to future derived implementations that listen // for multiple keys CFIndex i; for (i=0; i<CFArrayGetCount(elements); i++) { long number; CFDictionaryRef element= CFArrayGetValueAtIndex(elements, i); CFTypeRef object; if(!element) continue; if(!usedDictionary){ // Verify tht we are dealing with a keypad or keyboard object = CFDictionaryGetValue(element, CFSTR(kIOHIDElementUsageKey)); if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue; if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType,&number)) continue; if(number!=KeysUsage) continue; // See if element corresponds to the desired key object = CFDictionaryGetValue(element, CFSTR(kIOHIDElementUsagePageKey)); if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue; if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number)) continue; if(number!=KeysUsagePage) continue; } // Get the cookie for this element object= (CFDictionaryGetValue(element, CFSTR(kIOHIDElementCookieKey))); if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue; if(!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number)) continue; triggerCookie = (IOHIDElementCookie) number; break; } if(CFArrayGetCount(elements)==i){ CFRelease(elements); PsychErrorExitMsg(PsychError_user, "Specified key code not found on device (II)."); } CFRelease(elements); } } // Allocate for the queue queue=(*interface)->allocQueue(interface); if(!queue){ PsychErrorExitMsg(PsychError_system, "Failed to allocate event queue for detecting key press."); } // Create the queue result = (*queue)->create(queue, 0, 8); // 8 events can be stored before the earliest event is lost if (kIOReturnSuccess != result){ (*queue)->Release(queue); (*queue)=NULL; PsychErrorExitMsg(PsychError_system, "Failed to create event queue for detecting key press."); } // Add the trigger to the queue // If multiple keys were of interest, their cookies could be added in turn result = (*queue)->addElement(queue, triggerCookie, 0); if (kIOReturnSuccess != result){ result = (*queue)->dispose(queue); (*queue)->Release(queue); (*queue)=NULL; PsychErrorExitMsg(PsychError_system, "Failed to add trigger key to event queues."); } // Start the queue result = (*queue)->start(queue); if (kIOReturnSuccess != result){ result = (*queue)->dispose(queue); (*queue)->Release(queue); (*queue)=NULL; PsychErrorExitMsg(PsychError_system, "Failed to start event queues."); } // Watch for the trigger { IOHIDEventStruct event; while(1){ AbsoluteTime zeroTime = {0,0}; result = (*queue)->getNextEvent(queue, &event, zeroTime, 0); if(kIOReturnSuccess==result) break; PsychWaitIntervalSeconds((double)0.004); //surrender some time to other processes // If it were of interest to trigger selectively on key press or key release, // this could be evaluated by checking event.value (zero versus non-zero) // but this would put more code inside the loop // If multiple keys are registered via addElement (not the case here), the // cookie for the key responsible for the event can be obtained from // event.elementCookie } // If event.longValue is not NULL, the documentation indicates that it is up // to the caller to deallocate it. The circumstances under which a non-NULL // value would be generated are not specified. My guess is that some devices // might return a 64-bit value (e.g., a tracking device coordinate). // Keys, having only two states, shouldn't need this, but check and free to // be safe if ((event.longValueSize != 0) && (event.longValue != NULL)) free(event.longValue); // Set the time, using the same strategy as PsychTimeGlue's PsychGetPrecisionTimerSeconds // For code maintainability, it would be better if this conversion were performed // by a function in PsychTimeGlue { Nanoseconds timeNanoseconds=AbsoluteToNanoseconds(event.timestamp); UInt64 timeUInt64=UnsignedWideToUInt64(timeNanoseconds); double timeDouble=(double)timeUInt64; *timeValueOutput=timeDouble / 1000000000; } } // Clean up result = (*queue)->stop(queue); // Code from Apple sometimes removes elements from queue before disposing and sometimes does not // I can't see any reason to do so for a queue that's one line of code away from destruction // and therefore haven't result = (*queue)->dispose(queue); (*queue)->Release(queue); (*queue)=NULL; // Just in case queue is redefined as static in the future return; }
// utility routine to dump device info void HIDDumpDeviceInfo(IOHIDDeviceRef inIOHIDDeviceRef) { char cstring[256]; printf("Device: %p = { ", inIOHIDDeviceRef); char manufacturer[256] = ""; // name of manufacturer CFStringRef tCFStringRef = IOHIDDevice_GetManufacturer(inIOHIDDeviceRef); if (tCFStringRef) { (void) CFStringGetCString(tCFStringRef, manufacturer, sizeof(manufacturer), kCFStringEncodingUTF8); } char product[256] = ""; // name of product tCFStringRef = IOHIDDevice_GetProduct(inIOHIDDeviceRef); if (tCFStringRef) { (void) CFStringGetCString(tCFStringRef, product, sizeof(product), kCFStringEncodingUTF8); } printf("%s - %s, ", manufacturer, product); long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); if (vendorID) { if ( HIDGetVendorNameFromVendorID(vendorID, cstring) ) { printf(" vendorID: 0x%04lX (\"%s\"), ", vendorID, cstring); } else { printf(" vendorID: 0x%04lX, ", vendorID); } } long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); if (productID) { if ( HIDGetProductNameFromVendorProductID(vendorID, productID, cstring) ) { printf(" productID: 0x%04lX (\"%s\"), ", productID, cstring); } else { printf(" productID: 0x%04lX, ", productID); } } uint32_t usagePage = IOHIDDevice_GetUsagePage(inIOHIDDeviceRef); uint32_t usage = IOHIDDevice_GetUsage(inIOHIDDeviceRef); if (!usagePage || !usage) { usagePage = IOHIDDevice_GetPrimaryUsagePage(inIOHIDDeviceRef); usage = IOHIDDevice_GetPrimaryUsage(inIOHIDDeviceRef); } printf("usage: 0x%04lX:0x%04lX, ", (long unsigned int) usagePage, (long unsigned int) usage); tCFStringRef = HIDCopyUsageName(usagePage, usage); if (tCFStringRef) { (void) CFStringGetCString(tCFStringRef, cstring, sizeof(cstring), kCFStringEncodingUTF8); printf("\"%s\", ", cstring); CFRelease(tCFStringRef); } tCFStringRef = IOHIDDevice_GetTransport(inIOHIDDeviceRef); if (tCFStringRef) { (void) CFStringGetCString(tCFStringRef, cstring, sizeof(cstring), kCFStringEncodingUTF8); printf("Transport: \"%s\", ", cstring); } long vendorIDSource = IOHIDDevice_GetVendorIDSource(inIOHIDDeviceRef); if (vendorIDSource) { printf("VendorIDSource: %ld, ", vendorIDSource); } long version = IOHIDDevice_GetVersionNumber(inIOHIDDeviceRef); if (version) { printf("version: %ld, ", version); } tCFStringRef = IOHIDDevice_GetSerialNumber(inIOHIDDeviceRef); if (tCFStringRef) { (void) CFStringGetCString(tCFStringRef, cstring, sizeof(cstring), kCFStringEncodingUTF8); printf("SerialNumber: \"%s\", ", cstring); } long country = IOHIDDevice_GetCountryCode(inIOHIDDeviceRef); if (country) { printf("CountryCode: %ld, ", country); } long locationID = IOHIDDevice_GetLocationID(inIOHIDDeviceRef); if (locationID) { printf("locationID: 0x%08lX, ", locationID); } #if false CFArrayRef pairs = IOHIDDevice_GetUsagePairs(inIOHIDDeviceRef); if (pairs) { CFIndex idx, cnt = CFArrayGetCount(pairs); for (idx = 0; idx < cnt; idx++) { const void * pair = CFArrayGetValueAtIndex(pairs, idx); CFShow(pair); } } #endif // if false long maxInputReportSize = IOHIDDevice_GetMaxInputReportSize(inIOHIDDeviceRef); if (maxInputReportSize) { printf("MaxInputReportSize: %ld, ", maxInputReportSize); } long maxOutputReportSize = IOHIDDevice_GetMaxOutputReportSize(inIOHIDDeviceRef); if (maxOutputReportSize) { printf("MaxOutputReportSize: %ld, ", maxOutputReportSize); } long maxFeatureReportSize = IOHIDDevice_GetMaxFeatureReportSize(inIOHIDDeviceRef); if (maxFeatureReportSize) { printf("MaxFeatureReportSize: %ld, ", maxOutputReportSize); } long reportInterval = IOHIDDevice_GetReportInterval(inIOHIDDeviceRef); if (reportInterval) { printf("ReportInterval: %ld, ", reportInterval); } IOHIDQueueRef queueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); if (queueRef) { printf("queue: %p, ", queueRef); } IOHIDTransactionRef transactionRef = IOHIDDevice_GetTransaction(inIOHIDDeviceRef); if (transactionRef) { printf("transaction: %p, ", transactionRef); } printf("}\n"); fflush(stdout); } // HIDDumpDeviceInfo
/* PsychHIDGetDeviceListByUsage() */ void PsychHIDGetDeviceListByUsage(long usagePage, long usage, int *numDeviceIndices, int *deviceIndices, pRecDevice *deviceRecords) { pRecDevice currentDevice; int currentDeviceIndex; PsychHIDVerifyInit(); currentDeviceIndex=0; *numDeviceIndices=0; for(currentDevice=HIDGetFirstDevice(); currentDevice != NULL; currentDevice=HIDGetNextDevice(currentDevice)){ ++currentDeviceIndex; #ifndef __LP64__ if(currentDevice->usagePage==usagePage && currentDevice->usage==usage){ #else if(IOHIDDevice_GetUsagePage(currentDevice) == usagePage && IOHIDDevice_GetUsage(currentDevice) == usage){ #endif deviceRecords[*numDeviceIndices]=currentDevice; deviceIndices[*numDeviceIndices]=currentDeviceIndex; //the array is 0-indexed, devices are 1-indexed. ++(*numDeviceIndices); } } } /* PsychHIDGetDeviceListByUsages() */ void PsychHIDGetDeviceListByUsages(int numUsages, long *usagePages, long *usages, int *numDeviceIndices, int *deviceIndices, pRecDevice *deviceRecords) { pRecDevice currentDevice; int currentDeviceIndex; int currentUsage; long *usagePage; long *usage; PsychHIDVerifyInit(); *numDeviceIndices=0; for(usagePage=usagePages, usage=usages, currentUsage=0; currentUsage<numUsages; usagePage++, usage++, currentUsage++){ currentDeviceIndex=0; for(currentDevice=HIDGetFirstDevice(); currentDevice != NULL; currentDevice=HIDGetNextDevice(currentDevice)){ ++currentDeviceIndex; #ifndef __LP64__ if(currentDevice->usagePage==*usagePage && currentDevice->usage==*usage){ #else if(IOHIDDevice_GetPrimaryUsagePage(currentDevice) == *usagePage && IOHIDDevice_GetPrimaryUsage(currentDevice) == *usage){ #endif deviceRecords[*numDeviceIndices]=currentDevice; deviceIndices[*numDeviceIndices]=currentDeviceIndex; //the array is 0-indexed, devices are 1-indexed. ++(*numDeviceIndices); } } } } /* PsychHIDGetIndexFromRecord() The inverse of PsychHIDGetDeviceRecordPtrFromIndex. This O(n) where n is the number of device elements. We could make it O(1) if we modified the element structure in the HID Utilities library to include a field specifying the index of the element or collection. Note that if PsychHIDGetIndexFromRecord() is O(n) then its caller, PsychHIDGetCollections, is O(n^2) for each device, whereas if PsychHIDGetIndexFromRecord() is O(1) then psychHIDGetCollections becomes O(n) for each device. */ int PsychHIDGetIndexFromRecord(pRecDevice deviceRecord, pRecElement elementRecord, HIDElementTypeMask typeMask) { int elementIndex; pRecElement currentElement; if(elementRecord==NULL) return(0); elementIndex=1; for(currentElement=HIDGetFirstDeviceElement(deviceRecord, typeMask); currentElement != elementRecord && currentElement != NULL; currentElement=HIDGetNextDeviceElement(currentElement, typeMask)) ++elementIndex; if(currentElement==elementRecord) return(elementIndex); else{ PsychErrorExitMsg(PsychError_internal, "Element record not found within device record"); return(0); //make the compiler happy } }