// NOTE: I pieced this together through trial and error, any corrections are welcome static void hid_device_input_callback(void* context, IOReturn result, void* sender, IOHIDValueRef value) { IOHIDElementRef element; uint32_t type, page, use; struct apple_pad_connection* connection = (struct apple_pad_connection*)context; element = IOHIDValueGetElement(value); type = IOHIDElementGetType(element); page = IOHIDElementGetUsagePage(element); use = IOHIDElementGetUsage(element); // Joystick handler: TODO: Can GamePad work the same? if (type == kIOHIDElementTypeInput_Button && page == kHIDPage_Button) { CFIndex state = IOHIDValueGetIntegerValue(value); if (state) g_current_input_data.pad_buttons[connection->slot] |= (1 << (use - 1)); else g_current_input_data.pad_buttons[connection->slot] &= ~(1 << (use - 1)); } else if (type == kIOHIDElementTypeInput_Misc && page == kHIDPage_GenericDesktop) { static const uint32_t axis_use_ids[4] = { 48, 49, 50, 53 }; int i; for (i = 0; i < 4; i ++) { if (use == axis_use_ids[i]) { CFIndex min, max, state; float val; min = IOHIDElementGetPhysicalMin(element); max = IOHIDElementGetPhysicalMax(element) - min; state = IOHIDValueGetIntegerValue(value) - min; val = (float)state / (float)max; g_current_input_data.pad_axis[connection->slot][i] = ((val * 2.0f) - 1.0f) * 32767.0f; } } } }
/************************************************************************** * find_top_level */ static CFIndex find_top_level(IOHIDDeviceRef hid_device, CFMutableArrayRef main_elements) { CFArrayRef elements; CFIndex total = 0; TRACE("hid_device %s\n", debugstr_device(hid_device)); if (!hid_device) return 0; elements = IOHIDDeviceCopyMatchingElements(hid_device, NULL, 0); if (elements) { CFIndex i, count = CFArrayGetCount(elements); for (i = 0; i < count; i++) { IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i); int type = IOHIDElementGetType(element); TRACE("element %s\n", debugstr_element(element)); /* Check for top-level gaming device collections */ if (type == kIOHIDElementTypeCollection && IOHIDElementGetParent(element) == 0) { int usage_page = IOHIDElementGetUsagePage(element); int usage = IOHIDElementGetUsage(element); if (usage_page == kHIDPage_GenericDesktop && (usage == kHIDUsage_GD_Joystick || usage == kHIDUsage_GD_GamePad)) { CFArrayAppendValue(main_elements, element); total++; } } } CFRelease(elements); } TRACE("-> total %d\n", (int)total); return total; }
bool HIDGamepad::maybeAddButton(IOHIDElementRef element) { uint32_t usagePage = IOHIDElementGetUsagePage(element); if (usagePage != kHIDPage_Button) return false; uint32_t usage = IOHIDElementGetUsage(element); if (!usage) return false; CFIndex min = IOHIDElementGetLogicalMin(element); CFIndex max = IOHIDElementGetLogicalMax(element); m_buttons.append(std::make_unique<HIDGamepadButton>(usage, min, max, element)); IOHIDElementCookie cookie = IOHIDElementGetCookie(element); m_elementMap.set(cookie, m_buttons.last().get()); return true; }
bool HIDGamepad::maybeAddDPad(IOHIDElementRef element) { uint32_t usagePage = IOHIDElementGetUsagePage(element); if (usagePage != kHIDPage_GenericDesktop) return false; uint32_t usage = IOHIDElementGetUsage(element); if (!usage || usage != kHIDUsage_GD_Hatswitch) return false; CFIndex min = IOHIDElementGetLogicalMin(element); CFIndex max = IOHIDElementGetLogicalMax(element); m_dPads.append(makeUniqueRef<HIDGamepadDPad>(min, max, element)); IOHIDElementCookie cookie = IOHIDElementGetCookie(element); m_elementMap.set(cookie, &m_dPads.last().get()); return true; }
// Put element into the dictionary and into the queue: PsychError PsychHIDOSKbElementAdd(IOHIDElementRef element, IOHIDQueueRef queue, int deviceIndex) { // If at least one keyboard style device is detected, mark this queue as keyboard queue: if (IOHIDElementGetUsagePage(element) == kHIDPage_KeyboardOrKeypad) queueIsAKeyboard[deviceIndex] = TRUE; // Avoid redundant assignment to same keycode: if (IOHIDQueueContainsElement(queue, element)) { if (getenv("PSYCHHID_TELLME")) printf("--> Key %i Already assigned --> Skipping.\n", IOHIDElementGetUsage(element) - 1); return(PsychError_none); } if (getenv("PSYCHHID_TELLME")) { printf("--> Accepting key %i as new KbQueue element%s.\n", IOHIDElementGetUsage(element) - 1, (queueIsAKeyboard) ? " for a keyboard" : ""); } // Put the element cookie into the queue: IOHIDQueueAddElement(queue, element); return(PsychError_none); }
bool HIDGamepad::maybeAddAxis(IOHIDElementRef element) { uint32_t usagePage = IOHIDElementGetUsagePage(element); if (usagePage != kHIDPage_GenericDesktop) return false; uint32_t usage = IOHIDElementGetUsage(element); // This range covers the standard axis usages. if (usage < kHIDUsage_GD_X || usage > kHIDUsage_GD_Rz) return false; CFIndex min = IOHIDElementGetPhysicalMin(element); CFIndex max = IOHIDElementGetPhysicalMax(element); m_axes.append(std::make_unique<HIDGamepadAxis>(min, max, element)); IOHIDElementCookie cookie = IOHIDElementGetCookie(element); m_elementMap.set(cookie, m_axes.last().get()); return true; }
void CInputProviderMacOsHid::SetInitialBindValues(IOHIDDeviceRef device) { CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, nullptr, 0); for(int i = 0; i < CFArrayGetCount(elements); i++) { IOHIDElementRef elementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i); uint32 usagePage = IOHIDElementGetUsagePage(elementRef); if( (usagePage != kHIDPage_GenericDesktop) && (usagePage != kHIDPage_Button)) { continue; } IOHIDValueRef valueRef; if(IOHIDDeviceGetValue(device, elementRef, &valueRef) != kIOReturnSuccess) { continue; } CFIndex value = IOHIDValueGetIntegerValue(valueRef); IOHIDElementType type = IOHIDElementGetType(elementRef); uint32 usage = IOHIDElementGetUsage(elementRef); BINDINGTARGET tgt; tgt.providerId = PROVIDER_ID; tgt.deviceId = GetDeviceID(device); tgt.keyId = usage; tgt.keyType = GetKeyType(usage, type); switch(type) { case kIOHIDElementTypeInput_Misc: case kIOHIDElementTypeInput_Button: case kIOHIDElementTypeInput_Axis: OnInput(tgt, value); break; default: break; } } }
void CInputProviderMacOsHid::InputValueCallback(DEVICE_INFO* deviceInfo, IOReturn result, void* sender, IOHIDValueRef valueRef) { if(!OnInput) return; IOHIDElementRef elementRef = IOHIDValueGetElement(valueRef); uint32 usagePage = IOHIDElementGetUsagePage(elementRef); if( (usagePage != kHIDPage_GenericDesktop) && (usagePage != kHIDPage_Button)) { return; } uint32 usage = IOHIDElementGetUsage(elementRef); CFIndex value = IOHIDValueGetIntegerValue(valueRef); IOHIDElementType type = IOHIDElementGetType(elementRef); BINDINGTARGET tgt; tgt.providerId = PROVIDER_ID; tgt.deviceId = deviceInfo->deviceId; tgt.keyId = usage; tgt.keyType = GetKeyType(usage, type); OnInput(tgt, value); }
/* See if we care about this HID element, and if so, note it in our recDevice. */ static void AddHIDElement(const void *value, void *parameter) { recDevice *pDevice = (recDevice *) parameter; IOHIDElementRef refElement = (IOHIDElementRef) value; const CFTypeID elementTypeID = refElement ? CFGetTypeID(refElement) : 0; if (refElement && (elementTypeID == IOHIDElementGetTypeID())) { const IOHIDElementCookie cookie = IOHIDElementGetCookie(refElement); const uint32_t usagePage = IOHIDElementGetUsagePage(refElement); const uint32_t usage = IOHIDElementGetUsage(refElement); recElement *element = NULL; recElement **headElement = NULL; /* look at types of interest */ switch (IOHIDElementGetType(refElement)) { case kIOHIDElementTypeInput_Misc: case kIOHIDElementTypeInput_Button: case kIOHIDElementTypeInput_Axis: { switch (usagePage) { /* only interested in kHIDPage_GenericDesktop and kHIDPage_Button */ case kHIDPage_GenericDesktop: switch (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: case kHIDUsage_GD_Dial: case kHIDUsage_GD_Wheel: if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) { element = (recElement *) SDL_calloc(1, sizeof (recElement)); if (element) { pDevice->axes++; headElement = &(pDevice->firstAxis); } } break; case kHIDUsage_GD_Hatswitch: if (!ElementAlreadyAdded(cookie, pDevice->firstHat)) { element = (recElement *) SDL_calloc(1, sizeof (recElement)); if (element) { pDevice->hats++; headElement = &(pDevice->firstHat); } } break; } break; case kHIDPage_Simulation: switch (usage) { case kHIDUsage_Sim_Rudder: case kHIDUsage_Sim_Throttle: if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) { element = (recElement *) SDL_calloc(1, sizeof (recElement)); if (element) { pDevice->axes++; headElement = &(pDevice->firstAxis); } } break; default: break; } break; case kHIDPage_Button: if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) { element = (recElement *) SDL_calloc(1, sizeof (recElement)); if (element) { pDevice->buttons++; headElement = &(pDevice->firstButton); } } break; default: break; } } break; case kIOHIDElementTypeCollection: { CFArrayRef array = IOHIDElementGetChildren(refElement); if (array) { AddHIDElements(array, pDevice); } } break; default: break; } if (element && headElement) { /* add to list */ recElement *elementPrevious = NULL; recElement *elementCurrent = *headElement; while (elementCurrent && usage >= elementCurrent->usage) { elementPrevious = elementCurrent; elementCurrent = elementCurrent->pNext; } if (elementPrevious) { elementPrevious->pNext = element; } else { *headElement = element; } element->elementRef = refElement; element->usagePage = usagePage; element->usage = usage; element->pNext = elementCurrent; element->minReport = element->min = (SInt32) IOHIDElementGetLogicalMin(refElement); element->maxReport = element->max = (SInt32) IOHIDElementGetLogicalMax(refElement); element->cookie = IOHIDElementGetCookie(refElement); pDevice->elements++; } } }
void osxHIDInputDevice::AddDevice(void *context, IOReturn /*result*/, void */*sender*/, IOHIDDeviceRef device) { osxHIDInputDevice *self = (osxHIDInputDevice*)context ; URI devUri = hidDeviceURI(device) ; // std::cerr << std::endl << self->uri.asString() << std::endl << devUri.asString() << std::endl << std::endl ; bool match = self->theDevice==0 && (self->uri.isEmpty() || self->uri.scheme=="any" || self->uri.resemble(devUri)) ; if (self->debugLevel>0) { std::cerr << (match?"+ ":" ") ; hidDebugDevice(device, std::cerr) ; std::cerr << std::endl ; } if (!match) return ; self->theDevice = new __device(device) ; self->uri = devUri ; CFDataRef descriptor = (CFDataRef)IOHIDDeviceGetProperty(self->theDevice->device, CFSTR(kIOHIDReportDescriptorKey)) ; if (descriptor) { const UInt8 *bytes = CFDataGetBytePtr(descriptor) ; CFIndex length = CFDataGetLength(descriptor) ; if (self->inputreport_callback && !self->parser->setDescriptor(bytes, length)) std::cerr << "osxHIDInputDevice::AddDevice: unable to parse the HID report descriptor" << std::endl; if (self->debugLevel > 1) { std::cerr << " HID report descriptor: [ " << std::flush ; for (int i=0; i<length; ++i) std::cerr << std::hex << std::setfill('0') << std::setw(2) << (int)bytes[i] << " " ; std::cerr << "]" << std::endl ; } } #if DEBUG_MODE std::cerr << "Setting up callbacks" << std::endl ; #endif // ---------------------------------------------------------------- if (self->inputreport_callback) { #if DEBUG_MODE std::cerr << "Setting up report callback" << std::endl ; #endif #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 IOHIDDeviceRegisterInputReportWithTimeStampCallback(device, self->theDevice->report, sizeof(self->theDevice->report), self->inputreport_callback, self->inputreport_context) ; #else IOHIDDeviceRegisterInputReportCallback(device, self->theDevice->report, sizeof(self->theDevice->report), self->inputreport_callback, self->inputreport_context) ; #endif } // ---------------------------------------------------------------- if (self->value_callback) { #if DEBUG_MODE std::cerr << "Setting up value callback" << std::endl ; #endif IOHIDDeviceSetInputValueMatchingMultiple(device, self->elements_match) ; IOHIDDeviceRegisterInputValueCallback(device, self->value_callback, self->value_context) ; } // ---------------------------------------------------------------- if (self->queue_callback) { #if DEBUG_MODE std::cerr << "Setting up queue callback" << std::endl ; #endif self->theDevice->queue = IOHIDQueueCreate(kCFAllocatorDefault, device, queueSize, kIOHIDOptionsTypeNone) ; if (self->elements_match) { #if DEBUG && DEBUG_MATCHING_ELEMENTS std::cerr << "Queue, elements_match" << std::endl ; #endif CFIndex mcount = CFArrayGetCount(self->elements_match) ; for (CFIndex mindex=0; mindex<mcount; ++mindex) { CFDictionaryRef matching = (CFDictionaryRef)CFArrayGetValueAtIndex(self->elements_match, mindex) ; CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, matching, kIOHIDOptionsTypeNone) ; if (!elements) continue ; CFIndex ecount = CFArrayGetCount(elements) ; for (CFIndex eindex=0; eindex<ecount; ++eindex) { IOHIDElementRef e = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, eindex) ; IOHIDQueueAddElement(self->theDevice->queue, e) ; #if DEBUG && DEBUG_MATCHING_ELEMENTS std::cerr << "elements_match EINDEX: " << eindex << ", usagepage: " << IOHIDElementGetUsagePage(e) << ", usage: " << IOHIDElementGetUsage(e) << std::endl ; #endif } } } else { #if DEBUG && DEBUG_MATCHING_ELEMENTS std::cerr << "Queue, no elements_match" << std::endl ; #endif CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, 0, kIOHIDOptionsTypeNone) ; if (elements) { CFIndex ecount = CFArrayGetCount(elements) ; for (CFIndex eindex=0; eindex<ecount; ++eindex) { IOHIDElementRef e = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, eindex) ; IOHIDQueueAddElement(self->theDevice->queue, e) ; #if DEBUG && DEBUG_MATCHING_ELEMENTS std::cerr << "!elements_match EINDEX: " << eindex << ", usagepage: " << IOHIDElementGetUsagePage(e) << ", usage: " << IOHIDElementGetUsage(e) << std::endl ; #endif } } } IOHIDQueueRegisterValueAvailableCallback(self->theDevice->queue, self->queue_callback, self->queue_context) ; IOHIDQueueScheduleWithRunLoop(self->theDevice->queue, CFRunLoopGetMain(), kCFRunLoopDefaultMode) ; IOHIDQueueStart(self->theDevice->queue) ; } // ---------------------------------------------------------------- }
void __deviceValueCallback (void * context, IOReturn result, void * sender, IOHIDValueRef value) { IOHIDElementRef element = IOHIDValueGetElement(value); printf("IOHIDDeviceRef[%p]: value=%p timestamp=%lld cookie=%d usagePage=0x%02X usage=0x%02X intValue=%ld\n", sender, value, IOHIDValueGetTimeStamp(value), (uint32_t)IOHIDElementGetCookie(element), IOHIDElementGetUsagePage(element), IOHIDElementGetUsage(element), IOHIDValueGetIntegerValue(value)); }
void joypad::add_hid_element(IOHIDElementRef p_element) { const CFTypeID elementTypeID = p_element ? CFGetTypeID(p_element) : 0; if (p_element && (elementTypeID == IOHIDElementGetTypeID())) { const IOHIDElementCookie cookie = IOHIDElementGetCookie(p_element); const uint32_t usagePage = IOHIDElementGetUsagePage(p_element); const uint32_t usage = IOHIDElementGetUsage(p_element); Vector<rec_element> *list = NULL; switch (IOHIDElementGetType(p_element)) { case kIOHIDElementTypeInput_Misc: case kIOHIDElementTypeInput_Button: case kIOHIDElementTypeInput_Axis: { switch (usagePage) { case kHIDPage_GenericDesktop: switch (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: case kHIDUsage_GD_Dial: case kHIDUsage_GD_Wheel: if (!has_element(cookie, &axis_elements)) { list = &axis_elements; } break; case kHIDUsage_GD_Hatswitch: if (!has_element(cookie, &hat_elements)) { list = &hat_elements; } break; case kHIDUsage_GD_DPadUp: case kHIDUsage_GD_DPadDown: case kHIDUsage_GD_DPadRight: case kHIDUsage_GD_DPadLeft: case kHIDUsage_GD_Start: case kHIDUsage_GD_Select: if (!has_element(cookie, &button_elements)) { list = &button_elements; } break; } break; case kHIDPage_Simulation: switch (usage) { case kHIDUsage_Sim_Rudder: case kHIDUsage_Sim_Throttle: if (!has_element(cookie, &axis_elements)) { list = &axis_elements; } break; default: break; } break; case kHIDPage_Button: case kHIDPage_Consumer: if (!has_element(cookie, &button_elements)) { list = &button_elements; } break; default: break; } } break; case kIOHIDElementTypeCollection: { CFArrayRef array = IOHIDElementGetChildren(p_element); if (array) { add_hid_elements(array); } } break; default: break; } if (list) { /* add to list */ rec_element element; element.ref = p_element; element.usage = usage; element.min = (SInt32)IOHIDElementGetLogicalMin(p_element); element.max = (SInt32)IOHIDElementGetLogicalMax(p_element); element.cookie = IOHIDElementGetCookie(p_element); list->push_back(element); list->sort_custom<rec_element::Comparator>(); } } }
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
static void get_osx_device_elements(JoystickImpl *device, int axis_map[8]) { IOHIDElementRef tIOHIDElementRef; CFArrayRef gElementCFArrayRef; DWORD axes = 0; DWORD sliders = 0; DWORD buttons = 0; DWORD povs = 0; device->elementCFArrayRef = NULL; if (!gCollections) return; tIOHIDElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(gCollections, device->id); if (!tIOHIDElementRef) return; gElementCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); get_element_children(tIOHIDElementRef, gElementCFArrayRef); if (gElementCFArrayRef) { CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef ); /* build our element array in the order that dinput expects */ device->elementCFArrayRef = CFArrayCreateMutable(NULL,0,NULL); for ( idx = 0; idx < cnt; idx++ ) { IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx ); int eleType = IOHIDElementGetType( tIOHIDElementRef ); switch(eleType) { case kIOHIDElementTypeInput_Button: { int usagePage = IOHIDElementGetUsagePage( tIOHIDElementRef ); if (usagePage != kHIDPage_Button) { /* avoid strange elements found on the 360 controller */ continue; } if (buttons < 128) { CFArrayInsertValueAtIndex(device->elementCFArrayRef, (axes+povs+buttons), tIOHIDElementRef); buttons++; } break; } case kIOHIDElementTypeInput_Axis: { CFArrayInsertValueAtIndex(device->elementCFArrayRef, axes, tIOHIDElementRef); axes++; break; } case kIOHIDElementTypeInput_Misc: { uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef ); switch(usage) { case kHIDUsage_GD_Hatswitch: { CFArrayInsertValueAtIndex(device->elementCFArrayRef, (axes+povs), tIOHIDElementRef); povs++; break; } case kHIDUsage_GD_Slider: sliders ++; if (sliders > 2) break; /* fallthrough, sliders are axis */ case kHIDUsage_GD_X: case kHIDUsage_GD_Y: case kHIDUsage_GD_Z: case kHIDUsage_GD_Rx: case kHIDUsage_GD_Ry: case kHIDUsage_GD_Rz: { CFArrayInsertValueAtIndex(device->elementCFArrayRef, axes, tIOHIDElementRef); axis_map[axes]=usage; axes++; break; } default: FIXME("Unhandled usage %i\n",usage); } break; } default: FIXME("Unhandled type %i\n",eleType); } } } device->generic.devcaps.dwAxes = axes; device->generic.devcaps.dwButtons = buttons; device->generic.devcaps.dwPOVs = povs; /* Sort buttons into correct order */ for (buttons = 0; buttons < device->generic.devcaps.dwButtons; buttons++) { IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( device->elementCFArrayRef, axes+povs+buttons); uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef ); usage --; /* usage is 1 indexed we need 0 indexed */ if (usage == buttons) continue; insert_sort_button(axes+povs, tIOHIDElementRef, device->elementCFArrayRef,buttons,usage); } }
/************************************************************************** * collect_joystick_elements */ static void collect_joystick_elements(joystick_t* joystick, IOHIDElementRef collection) { CFIndex i, count; CFArrayRef children = IOHIDElementGetChildren(collection); TRACE("collection %s\n", debugstr_element(collection)); count = CFArrayGetCount(children); for (i = 0; i < count; i++) { IOHIDElementRef child; int type; child = (IOHIDElementRef)CFArrayGetValueAtIndex(children, i); TRACE("child %s\n", debugstr_element(child)); type = IOHIDElementGetType(child); switch (type) { case kIOHIDElementTypeCollection: collect_joystick_elements(joystick, child); break; case kIOHIDElementTypeInput_Button: { int usage_page = IOHIDElementGetUsagePage(child); TRACE("kIOHIDElementTypeInput_Button usage_page %d\n", usage_page); /* avoid strange elements found on the 360 controller */ if (usage_page == kHIDPage_Button) CFArrayAppendValue(joystick->buttons, child); break; } case kIOHIDElementTypeInput_Axis: { TRACE("kIOHIDElementTypeInput_Axis; ignoring\n"); break; } case kIOHIDElementTypeInput_Misc: { uint32_t usage = IOHIDElementGetUsage( child ); switch(usage) { case kHIDUsage_GD_Hatswitch: { TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n"); if (joystick->hatswitch) TRACE(" ignoring additional hatswitch\n"); else joystick->hatswitch = (IOHIDElementRef)CFRetain(child); break; } case kHIDUsage_GD_X: case kHIDUsage_GD_Y: case kHIDUsage_GD_Z: case kHIDUsage_GD_Rx: case kHIDUsage_GD_Ry: case kHIDUsage_GD_Rz: { int axis = axis_for_usage(usage); TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_<axis> (%d) axis %d\n", usage, axis); if (axis < 0 || joystick->axes[axis].element) TRACE(" ignoring\n"); else { joystick->axes[axis].element = (IOHIDElementRef)CFRetain(child); joystick->axes[axis].min_value = IOHIDElementGetLogicalMin(child); joystick->axes[axis].max_value = IOHIDElementGetLogicalMax(child); } break; } case kHIDUsage_GD_Slider: TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Slider; ignoring\n"); break; default: FIXME("kIOHIDElementTypeInput_Misc / Unhandled usage %d\n", usage); break; } break; } default: FIXME("Unhandled type %i\n",type); break; } } }
PsychError PsychHIDOSKbQueueCreate(int deviceIndex, int numScankeys, int* scanKeys) { pRecDevice deviceRecord; // Valid number of keys? if (scanKeys && (numScankeys != 256)) { PsychErrorExitMsg(PsychError_user, "Second argument to KbQueueCreate must be a vector with 256 elements."); } // Do we finally have a valid keyboard or other suitable input device? // PsychHIDOSGetKbQueueDevice() will error out if no suitable device // for deviceIndex can be found. Otherwise it will return the HID // device record and remapped deviceIndex for use with our KbQueues: deviceIndex = PsychHIDOSGetKbQueueDevice(deviceIndex, &deviceRecord); // Keyboard queue for this deviceIndex already created? if (psychHIDKbQueueFirstPress[deviceIndex]) { // Yep. Release it, so we can start from scratch: PsychHIDOSKbQueueRelease(deviceIndex); } // Allocate and zero-init memory for tracking key presses and key releases: psychHIDKbQueueFirstPress[deviceIndex] = calloc(256, sizeof(double)); psychHIDKbQueueFirstRelease[deviceIndex] = calloc(256, sizeof(double)); psychHIDKbQueueLastPress[deviceIndex] = calloc(256, sizeof(double)); psychHIDKbQueueLastRelease[deviceIndex] = calloc(256, sizeof(double)); psychHIDKbQueueScanKeys[deviceIndex] = calloc(256, sizeof(int)); // Assign scanKeys vector, if any: if (scanKeys) { // Copy it: memcpy(psychHIDKbQueueScanKeys[deviceIndex], scanKeys, 256 * sizeof(int)); } else { // None provided. Enable all keys by default: memset(psychHIDKbQueueScanKeys[deviceIndex], 1, 256 * sizeof(int)); } // Create HIDQueue for device: queue[deviceIndex] = IOHIDQueueCreate(kCFAllocatorDefault, deviceRecord, 30, 0); if (NULL == queue[deviceIndex]) PsychErrorExitMsg(PsychError_system, "Failed to create event queue for detecting key press."); // Mark as a non-keyboard device, to start with: queueIsAKeyboard[deviceIndex] = FALSE; // Parse HID device to add all detected and selected keys: { // Add deviceRecord's elements to our queue, filtering unwanted keys via 'scanList'. // This code is almost identical to the enumeration code in PsychHIDKbCheck, to make sure we get // matching performance and behaviour and hopefully that it works on the latest problematic Apple // hardware, e.g., late 2013 MacBookAir and OSX 10.9: { uint32_t usage, usagePage; pRecElement currentElement, lastElement = NULL; // Step through the elements of the device and add matching ones: for (currentElement = HIDGetFirstDeviceElement(deviceRecord, kHIDElementTypeInput | kHIDElementTypeCollection); (currentElement != NULL) && (currentElement != lastElement); currentElement = HIDGetNextDeviceElement(currentElement, kHIDElementTypeInput | kHIDElementTypeCollection)) { // Keep track of last queried element: lastElement = currentElement; usage = IOHIDElementGetUsage(currentElement); usagePage = IOHIDElementGetUsagePage(currentElement); if (getenv("PSYCHHID_TELLME")) { printf("PTB-DEBUG: [KbQueueCreate]: ce %p page %d usage: %d isArray: %d\n", currentElement, usagePage, usage, IOHIDElementIsArray(currentElement)); } if (IOHIDElementGetType(currentElement) == kIOHIDElementTypeCollection) { CFArrayRef children = IOHIDElementGetChildren(currentElement); if (!children) continue; CFIndex idx, cnt = CFArrayGetCount(children); for (idx = 0; idx < cnt; idx++) { IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(children, idx); if (tIOHIDElementRef && ((IOHIDElementGetType(tIOHIDElementRef) == kIOHIDElementTypeInput_Button) || (IOHIDElementGetType(tIOHIDElementRef) == kIOHIDElementTypeInput_ScanCodes))) { usage = IOHIDElementGetUsage(tIOHIDElementRef); if ((usage <= 256) && (usage >= 1) && ( (scanKeys == NULL) || (scanKeys[usage - 1] > 0) )) { // Add it for use in keyboard queue: PsychHIDOSKbElementAdd(tIOHIDElementRef, queue[deviceIndex], deviceIndex); } } } // Done with this currentElement, which was a collection of buttons/keys. // Iterate to next currentElement: continue; } // Classic path for non-collection elements: if(((usagePage == kHIDPage_KeyboardOrKeypad) || (usagePage == kHIDPage_Button)) && (usage <= 256) && (usage >= 1) && ( (scanKeys == NULL) || (scanKeys[usage - 1] > 0) ) ) { // Add it for use in keyboard queue: PsychHIDOSKbElementAdd(currentElement, queue[deviceIndex], deviceIndex); } } } } // Register "queue empty -> non-empty transition" callback: TODO Replace queue by reference to our keyboard queue struct: IOHIDQueueRegisterValueAvailableCallback(queue[deviceIndex], (IOHIDCallback) PsychHIDKbQueueCallbackFunction, (void*) (long) deviceIndex); // Create event buffer: PsychHIDCreateEventBuffer(deviceIndex); // Start the processing thread for this queue: PsychLockMutex(&KbQueueMutex); if (PsychCreateThread(&KbQueueThread[deviceIndex], NULL, KbQueueWorkerThreadMain, (void*) (long) deviceIndex)) { // We are so screwed: // Cleanup the mess: psychHIDKbQueueActive[deviceIndex] = FALSE; PsychUnlockMutex(&KbQueueMutex); // Whine a little bit: printf("PsychHID-ERROR: Start of keyboard queue processing for deviceIndex %i failed!\n", deviceIndex); PsychErrorExitMsg(PsychError_system, "Creation of keyboard queue background processing thread failed!"); } PsychUnlockMutex(&KbQueueMutex); // Ready to use this keybord queue. return(PsychError_none); }
static const char* debugstr_element(IOHIDElementRef element) { return wine_dbg_sprintf("<IOHIDElement %p type %d usage %u/%u device %p>", element, IOHIDElementGetType(element), IOHIDElementGetUsagePage(element), IOHIDElementGetUsage(element), IOHIDElementGetDevice(element)); }
// ************************************************************************* // // HIDConfigureAction(outIOHIDDeviceRef, outIOHIDElementRef, inTimeout) // // Purpose: polls all devices and elements for a change greater than kPercentMove. // Times out after given time returns 1 and pointer to device and element // if found; returns 0 and NULL for both parameters if not found // // Inputs: outIOHIDDeviceRef - address where to store the device // outIOHIDElementRef - address where to store the element // inTimeout - the timeout // Returns: Boolean - if successful // outIOHIDDeviceRef - the device // outIOHIDElementRef - the element // Boolean HIDConfigureActionOfType(actionTypeMask inActionTypeMask, double inTimeout, IOHIDDeviceRef * outIOHIDDeviceRef, IOHIDElementRef * outIOHIDElementRef) { // param error? if (!outIOHIDDeviceRef || !outIOHIDElementRef) { return (false); } if (!gDeviceCFArrayRef) { // if we do not have a device list // and we can't build another list if (!HIDBuildDeviceList(0, 0) || !gDeviceCFArrayRef) { return (false); // bail } } IOHIDDeviceRef tIOHIDDeviceRef; IOHIDElementRef tIOHIDElementRef; IOHIDElementType elementType = 0; switch (inActionTypeMask) { case kActionTypeButton: { elementType = kIOHIDElementTypeInput_Button; break; } case kActionTypeAxis: { elementType = kIOHIDElementTypeInput_Misc; break; } case kActionTypeAll: default: { elementType = 0; break; } } // switch // determine the maximum number of elements CFIndex maxElements = 0; CFIndex devIndex, devCount = CFArrayGetCount(gDeviceCFArrayRef); for (devIndex = 0; devIndex < devCount; devIndex++) { tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex); if (!tIOHIDDeviceRef) { continue; // skip this one } // HIDDumpDeviceInfo(tIOHIDDeviceRef); CFIndex count = HIDCountDeviceElementsOfType(tIOHIDDeviceRef, elementType); if (count > maxElements) { maxElements = count; } } if (!(devCount * maxElements)) { return (false); } #if true // NSDictionary * matchDictionary = @{@(kIOHIDElementTypeKey): @(elementType)}; const void *keys[] = {CFSTR(kIOHIDElementTypeKey)}; const void *vals[] = {CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &elementType)}; CFDictionaryRef matchingDict = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFRelease(vals[0]); #endif // if 1 // allocate an array of int's in which to store devCount * maxElements values double *saveValueArray = (double *) calloc(devCount * maxElements, sizeof(double)); // clear 2D array to save values // remember when we start; used to calculate timeout clock_t start = clock(), end; // on first pass store initial values / compare current values to initial values on subsequent passes Boolean found = false, first = true; while (!found) { double maxDeltaPercent = 0; // we want to find the one that moves the most // (percentage wise) for (devIndex = 0; devIndex < devCount; devIndex++) { tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex); if (!tIOHIDDeviceRef) { continue; // skip this one } gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, matchingDict, kIOHIDOptionsTypeNone); if (gElementCFArrayRef) { CFIndex eleIndex, eleCount = CFArrayGetCount(gElementCFArrayRef); for (eleIndex = 0; eleIndex < eleCount; eleIndex++) { tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, eleIndex); if (!tIOHIDElementRef) { continue; } IOHIDElementType tIOHIDElementType = IOHIDElementGetType(tIOHIDElementRef); // only care about inputs (no outputs or features) if (tIOHIDElementType <= kIOHIDElementTypeInput_ScanCodes) { if (IOHIDElementIsArray(tIOHIDElementRef)) { // printf("ARRAY!\n"); continue; // skip array elements } if (elementType && ((tIOHIDElementType != elementType))) { continue; } uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); uint32_t reportCount = IOHIDElementGetReportCount(tIOHIDElementRef); #ifdef DEBUG if (first) { HIDDumpElementInfo(tIOHIDElementRef); fflush(stdout); uint32_t vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); uint32_t productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); if ((0x054C == vendorID) && (0x0268 == productID) && (0x001E == (uint32_t) cookie)) { // printf("DING!\n"); } } #endif // ifdef DEBUG #if true // work-around for IOHIDValueGetScaledValue crash // (when element report count > 1) if (reportCount > 1) { // printf("REPORT!\n"); continue; // skip reports } #endif // if 1 // ignore PID elements and arrays if ((kHIDPage_PID != usagePage) && ((-1) != usage)) { // get this elements current value double value = 0.0; // default value is zero IOHIDValueRef tIOHIDValueRef; IOReturn ioReturn = IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &tIOHIDValueRef); if (kIOReturnSuccess == ioReturn) { value = IOHIDValueGetScaledValue(tIOHIDValueRef, kIOHIDValueScaleTypePhysical); } if (first) { saveValueArray[(devIndex * maxElements) + eleIndex] = value; } else { double initialValue = saveValueArray[(devIndex * maxElements) + eleIndex]; CFIndex valueMin = IOHIDElementGetPhysicalMin(tIOHIDElementRef); CFIndex valueMax = IOHIDElementGetPhysicalMax(tIOHIDElementRef); double deltaPercent = fabs((initialValue - value) * 100.0 / (valueMax - valueMin)); #if false // debug code useful to dump out value info for // specific (vendorID, productID, usagePage and // usage) device if (!first) { // Device: 0x13b6a0 = { Logitech Inc. - WingMan Force 3D, vendorID: 0x046D, productID: 0xC283, // usage: 0x0001:0x0004, "Generic Desktop Joystick" if ((vendorID == 0x046D) && (productID == 0xC283)) { if ((kHIDPage_GenericDesktop == usagePage) && (kHIDUsage_GD_Rz == usage)) { printf("initial: %6.2f, value: %6.2f, diff: %6.2f, delta percent: %6.2f!\n", initialValue, value, fabs(initialValue - value), deltaPercent); } } } deltaPercent = 0.0; #endif // if false if (deltaPercent >= kPercentMove) { found = true; if (deltaPercent > maxDeltaPercent) { maxDeltaPercent = deltaPercent; *outIOHIDDeviceRef = tIOHIDDeviceRef; *outIOHIDElementRef = tIOHIDElementRef; } break; } } // if first } // if usage } // if type } // for elements... CFRelease(gElementCFArrayRef); gElementCFArrayRef = NULL; } // if (gElementCFArrayRef) if (found) { // HIDDumpElementInfo(tIOHIDElementRef); break; // DONE! } } // for devices first = false; // no longer the first pass // are we done? end = clock(); double secs = (double) (end - start) / CLOCKS_PER_SEC; if (secs > inTimeout) { break; // (yes) timeout } } // while (!found) if (saveValueArray) { free(saveValueArray); } // return device and element moved if (!found) { *outIOHIDDeviceRef = NULL; *outIOHIDElementRef = NULL; } CFRelease(matchingDict); return (found); } // HIDConfigureAction
Boolean HIDSaveElementPref(const CFStringRef inKeyCFStringRef, CFStringRef inAppCFStringRef, IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef) { Boolean success = false; if (inKeyCFStringRef && inAppCFStringRef && inIOHIDDeviceRef && inIOHIDElementRef) { uint32_t vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); require(vendorID, Oops); uint32_t productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); require(productID, Oops); uint32_t 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:%d, p:%d, l:%d, p:%d, u:%d}, e:{p:%d, u:%d, c:%d}"), vendorID, productID, locID, usagePage, usage, usagePageE, usageE, eleCookie); if (prefCFStringRef) { CFPreferencesSetAppValue(inKeyCFStringRef, prefCFStringRef, inAppCFStringRef); CFRelease(prefCFStringRef); success = true; } } Oops:; return (success); } // HIDSaveElementPref
/************************************************************************* * * hu_AddDeviceElementToUsageXML( inDevice, inElement ) * * Purpose: add a device and it's elements to our usage( XML ) file * * Inputs: inDevice - the device * inElement - the element * * Returns: Boolean - if successful */ static Boolean hu_AddDeviceElementToUsageXML(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef) { Boolean results = FALSE; if ( gUsageCFPropertyListRef ) { CFRelease(gUsageCFPropertyListRef); } gUsageCFPropertyListRef = hu_XMLLoad( CFSTR( "HID_device_usage_strings"), CFSTR("plist") ); if ( gUsageCFPropertyListRef ) { CFMutableDictionaryRef tCFMutableDictionaryRef = CFDictionaryCreateMutableCopy( kCFAllocatorDefault, 0, gUsageCFPropertyListRef); if ( tCFMutableDictionaryRef ) { CFMutableDictionaryRef vendorCFMutableDictionaryRef; CFMutableDictionaryRef productCFMutableDictionaryRef; CFStringRef productKeyCFStringRef; CFStringRef usageKeyCFStringRef; // if the vendor dictionary exists... long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); CFStringRef vendorKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), vendorID); if ( vendorKeyCFStringRef ) { if ( CFDictionaryGetValueIfPresent(tCFMutableDictionaryRef, vendorKeyCFStringRef, (const void **) &vendorCFMutableDictionaryRef) ) { // ...copy it... vendorCFMutableDictionaryRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, vendorCFMutableDictionaryRef); } else { // ...otherwise... // ...create it. vendorCFMutableDictionaryRef = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); results = TRUE; } // if the vendor name key doesn't exist... if ( !CFDictionaryContainsKey(vendorCFMutableDictionaryRef, kNameKeyCFStringRef) ) { CFStringRef manCFStringRef = IOHIDDevice_GetManufacturer(inIOHIDDeviceRef); // ...create it. CFDictionaryAddValue(vendorCFMutableDictionaryRef, kNameKeyCFStringRef, manCFStringRef); results = TRUE; } // if the product key exists in the vendor dictionary... long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); productKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), productID); if ( CFDictionaryGetValueIfPresent(vendorCFMutableDictionaryRef, productKeyCFStringRef, (const void **) &productCFMutableDictionaryRef) ) { // ...copy it... productCFMutableDictionaryRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, productCFMutableDictionaryRef); } else { // ...otherwise... // ...create it. productCFMutableDictionaryRef = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); results = TRUE; } // if the product name key doesn't exist... if ( !CFDictionaryContainsKey(productCFMutableDictionaryRef, kNameKeyCFStringRef) ) { CFStringRef productCFStringRef = IOHIDDevice_GetProduct(inIOHIDDeviceRef); // ...create it. CFDictionaryAddValue(productCFMutableDictionaryRef, kNameKeyCFStringRef, productCFStringRef); results = TRUE; } // if the usage key doesn't exist in the product dictionary... uint32_t usagePage = IOHIDElementGetUsagePage(inIOHIDElementRef); uint32_t usage = IOHIDElementGetUsagePage(inIOHIDElementRef); usageKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld:%ld"), usagePage, usage); if ( usageKeyCFStringRef ) { if ( !CFDictionaryContainsKey(productCFMutableDictionaryRef, usageKeyCFStringRef) ) { // find it's generic name CFStringRef usageCFStringRef = HIDCopyUsageName(usagePage, usage); if ( usageCFStringRef ) { // and add that. CFDictionaryAddValue(productCFMutableDictionaryRef, usageKeyCFStringRef, usageCFStringRef); results = TRUE; CFRelease(usageCFStringRef); } } CFRelease(usageKeyCFStringRef); } if ( vendorCFMutableDictionaryRef ) { if ( productCFMutableDictionaryRef ) { if ( results ) { CFDictionarySetValue(vendorCFMutableDictionaryRef, productKeyCFStringRef, productCFMutableDictionaryRef); } CFRelease(productCFMutableDictionaryRef); } if ( results ) { CFDictionarySetValue(tCFMutableDictionaryRef, vendorKeyCFStringRef, vendorCFMutableDictionaryRef); } CFRelease(vendorCFMutableDictionaryRef); } CFRelease(vendorKeyCFStringRef); } if ( productKeyCFStringRef ) { CFRelease(productKeyCFStringRef); } if ( results ) { hu_XMLSave( tCFMutableDictionaryRef, CFSTR( "HID_device_usage_strings"), CFSTR("plist") ); } CFRelease( tCFMutableDictionaryRef); } } return (results); } // hu_AddDeviceElementToUsageXML
// Get matching element from config record // takes a pre-allocated and filled out config record // search for matching device // return IOHIDDeviceRef, IOHIDElementRef and cookie for action IOHIDElementCookie HIDGetElementConfig(HID_info_ptr inHIDInfoPtr, IOHIDDeviceRef * outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef) { if (!inHIDInfoPtr->device.locID && !inHIDInfoPtr->device.vendorID && !inHIDInfoPtr->device.productID && !inHIDInfoPtr->device.usage && !inHIDInfoPtr->device.usagePage) // { // // early out *outIOHIDDeviceRef = NULL; *outIOHIDElementRef = NULL; return (inHIDInfoPtr->actionCookie); } IOHIDDeviceRef tIOHIDDeviceRef = NULL, foundIOHIDDeviceRef = NULL; IOHIDElementRef tIOHIDElementRef = NULL, foundIOHIDElementRef = NULL; CFIndex devIdx, devCnt, idx, cnt; // compare to current device list for matches // look for device if (inHIDInfoPtr->device.locID && inHIDInfoPtr->device.vendorID && inHIDInfoPtr->device.productID) // look for specific { // device // type plug in to same // port devCnt = CFArrayGetCount(gDeviceCFArrayRef); for (devIdx = 0; devIdx < devCnt; devIdx++) { tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIdx); if (!tIOHIDDeviceRef) { continue; // skip this device } uint32_t locID = IOHIDDevice_GetLocationID(tIOHIDDeviceRef); uint32_t vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); uint32_t productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); if ((inHIDInfoPtr->device.locID == locID) && (inHIDInfoPtr->device.vendorID == vendorID) && (inHIDInfoPtr->device.productID == productID)) { foundIOHIDDeviceRef = tIOHIDDeviceRef; } if (foundIOHIDDeviceRef) { break; } } // next devIdx if (foundIOHIDDeviceRef) { CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(foundIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); if (elementCFArrayRef) { cnt = CFArrayGetCount(elementCFArrayRef); for (idx = 0; idx < cnt; idx++) { tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); if (!tIOHIDElementRef) { continue; // skip this element } IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); if (inHIDInfoPtr->element.cookie == cookie) { foundIOHIDElementRef = tIOHIDElementRef; } if (foundIOHIDElementRef) { break; } } if (!foundIOHIDElementRef) { cnt = CFArrayGetCount(elementCFArrayRef); for (idx = 0; idx < cnt; idx++) { tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); if (!tIOHIDElementRef) { continue; // skip this element } uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); if ((inHIDInfoPtr->element.usage == usage) && (inHIDInfoPtr->element.usagePage == usagePage)) { foundIOHIDElementRef = tIOHIDElementRef; } if (foundIOHIDElementRef) { break; } } // next idx } // if (!foundIOHIDElementRef) if (foundIOHIDElementRef) { // if same device // setup the calibration IOHIDElement_SetupCalibration(tIOHIDElementRef); IOHIDElement_SetCalibrationSaturationMin(tIOHIDElementRef, inHIDInfoPtr->element.minReport); IOHIDElement_SetCalibrationSaturationMax(tIOHIDElementRef, inHIDInfoPtr->element.maxReport); } CFRelease(elementCFArrayRef); } // if (elementCFArrayRef) } // if (foundIOHIDDeviceRef) // if we have not found a match, look at just vendor // and product if ((!foundIOHIDDeviceRef) && (inHIDInfoPtr->device.vendorID && inHIDInfoPtr->device.productID)) { devCnt = CFArrayGetCount(gDeviceCFArrayRef); for (devIdx = 0; devIdx < devCnt; devIdx++) { tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIdx); if (!tIOHIDDeviceRef) { continue; // skip this device } uint32_t vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); uint32_t productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); if ((inHIDInfoPtr->device.vendorID == vendorID) && (inHIDInfoPtr->device.productID == productID)) { foundIOHIDDeviceRef = tIOHIDDeviceRef; } if (foundIOHIDDeviceRef) { break; } } // match elements by cookie since same device type if (foundIOHIDDeviceRef) { CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(foundIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); if (elementCFArrayRef) { cnt = CFArrayGetCount(elementCFArrayRef); for (idx = 0; idx < cnt; idx++) { tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); if (!tIOHIDElementRef) { continue; // skip this element } IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); if (inHIDInfoPtr->element.cookie == cookie) { foundIOHIDElementRef = tIOHIDElementRef; } if (foundIOHIDElementRef) { break; } } // if no cookie match (should NOT occur) match on usage if (!foundIOHIDElementRef) { cnt = CFArrayGetCount(elementCFArrayRef); for (idx = 0; idx < cnt; idx++) { tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); if (!tIOHIDElementRef) { continue; // skip this element } uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); if ((inHIDInfoPtr->element.usage == usage) && (inHIDInfoPtr->element.usagePage == usagePage)) { foundIOHIDElementRef = tIOHIDElementRef; } if (foundIOHIDElementRef) { break; } } // next idx } // if (!foundIOHIDElementRef) if (foundIOHIDElementRef) { // if same device // setup the calibration IOHIDElement_SetupCalibration(tIOHIDElementRef); IOHIDElement_SetCalibrationSaturationMin(tIOHIDElementRef, inHIDInfoPtr->element.minReport); IOHIDElement_SetCalibrationSaturationMax(tIOHIDElementRef, inHIDInfoPtr->element.maxReport); } CFRelease(elementCFArrayRef); } // if (elementCFArrayRef) } // if (foundIOHIDDeviceRef) } // if (device not found & vendorID & productID) } // if (inHIDInfoPtr->locID && // inHIDInfoPtr->device.vendorID && // inHIDInfoPtr->device.productID) // can't find matching device return NULL, do not // return first device if ((!foundIOHIDDeviceRef) || (!foundIOHIDElementRef)) { // no HID device *outIOHIDDeviceRef = NULL; *outIOHIDElementRef = NULL; return (inHIDInfoPtr->actionCookie); } else { // HID device *outIOHIDDeviceRef = foundIOHIDDeviceRef; *outIOHIDElementRef = foundIOHIDElementRef; return (inHIDInfoPtr->actionCookie); } } // HIDGetElementConfig
bool JoystickImpl::open(unsigned int index) { m_index = index; m_hat = NULL; Location deviceLoc = m_locationIDs[index]; // The device we need to load // Get all devices CFSetRef devices = HIDJoystickManager::getInstance().copyJoysticks(); if (devices == NULL) return false; // Get a usable copy of the joysticks devices. CFIndex joysticksCount = CFSetGetCount(devices); CFTypeRef devicesArray[joysticksCount]; CFSetGetValues(devices, devicesArray); // Get the desired joystick. IOHIDDeviceRef self = 0; for (CFIndex i(0); self == 0 && i < joysticksCount; ++i) { IOHIDDeviceRef d = (IOHIDDeviceRef)devicesArray[i]; if (deviceLoc == HIDInputManager::getLocationID(d)) self = d; } if (self == 0) { CFRelease(devices); return false; } m_identification.name = getDeviceString(self, CFSTR(kIOHIDProductKey), m_index); m_identification.vendorId = getDeviceUint(self, CFSTR(kIOHIDVendorIDKey), m_index); m_identification.productId = getDeviceUint(self, CFSTR(kIOHIDProductIDKey), m_index); // Get a list of all elements attached to the device. CFArrayRef elements = IOHIDDeviceCopyMatchingElements(self, NULL, kIOHIDOptionsTypeNone); if (elements == NULL) { CFRelease(devices); return false; } // Go through all connected elements. CFIndex elementsCount = CFArrayGetCount(elements); for (int i = 0; i < elementsCount; ++i) { IOHIDElementRef element = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, i); switch (IOHIDElementGetUsagePage(element)) { case kHIDPage_GenericDesktop: switch (IOHIDElementGetUsage(element)) { case kHIDUsage_GD_X: m_axis[Joystick::X] = element; break; case kHIDUsage_GD_Y: m_axis[Joystick::Y] = element; break; case kHIDUsage_GD_Z: m_axis[Joystick::Z] = element; break; case kHIDUsage_GD_Rx: m_axis[Joystick::U] = element; break; case kHIDUsage_GD_Ry: m_axis[Joystick::V] = element; break; case kHIDUsage_GD_Rz: m_axis[Joystick::R] = element; break; case kHIDUsage_GD_Hatswitch: // From §4.3 MiscellaneousControls of HUT v1.12: // // > Hat Switch: // > A typical example is four switches that are capable of generating // > information about four possible directions in which the knob can be // > tilted. Intermediate positions can also be decoded if the hardware // > allows two switches to be reported simultaneously. // // We assume this model here as well. Hence, with 4 switches and intermediate // positions we have 8 values (0-7) plus the "null" state (8). { CFIndex min = IOHIDElementGetLogicalMin(element); CFIndex max = IOHIDElementGetLogicalMax(element); if (min != 0 || max != 7) { sf::err() << std::hex << "Joystick (vendor/product id: 0x" << m_identification.vendorId << "/0x" << m_identification.productId << std::dec << ") range is an unexpected one: [" << min << ", " << max << "]" << std::endl; } else { m_hat = element; } } break; case kHIDUsage_GD_GamePad: // We assume a game pad is an application collection, meaning it doesn't hold // any values per say. They kind of "emit" the joystick's usages. // See §3.4.3 Usage Types (Collection) of HUT v1.12 if (IOHIDElementGetCollectionType(element) != kIOHIDElementCollectionTypeApplication) { sf::err() << std::hex << "Gamepage (vendor/product id: 0x" << m_identification.vendorId << "/0x" << m_identification.productId << ") is not an CA but a 0x" << IOHIDElementGetCollectionType(element) << std::dec << std::endl; } break; default: #ifdef SFML_DEBUG sf::err() << "Unexpected usage for element of Page Generic Desktop: 0x" << std::hex << IOHIDElementGetUsage(element) << std::dec << std::endl; #endif break; } break; case kHIDPage_Button: if (m_buttons.size() < Joystick::ButtonCount) // If we have free slot... m_buttons.push_back(element); // ...we add this element to the list // Else: too many buttons. We ignore this one. break; default: /* No other page is expected because of the mask applied by the HID manager. */ break; } } // Ensure that the buttons will be indexed in the same order as their // HID Usage (assigned by manufacturer and/or a driver). std::sort(m_buttons.begin(), m_buttons.end(), JoystickButtonSortPredicate); // Retain all these objects for personal use for (ButtonsVector::iterator it(m_buttons.begin()); it != m_buttons.end(); ++it) CFRetain(*it); for (AxisMap::iterator it(m_axis.begin()); it != m_axis.end(); ++it) CFRetain(it->second); if (m_hat != NULL) CFRetain(m_hat); // Note: we didn't retain element in the switch because we might have multiple // Axis X (for example) and we want to keep only the last one. To prevent // leaking we retain objects 'only' now. CFRelease(devices); CFRelease(elements); return true; }
bool Joystick::init() { SYNC; if(!p->hidManager) { VERIFY(p->hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone)); IOHIDManagerSetDeviceMatching((IOHIDManagerRef) p->hidManager, 0); IOHIDManagerOpen((IOHIDManagerRef) p->hidManager, kIOHIDOptionsTypeNone); p->nextDevice = 0; } CFSetRef copyOfDevices = IOHIDManagerCopyDevices((IOHIDManagerRef) p->hidManager); CFMutableArrayRef devArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); CFSetApplyFunction(copyOfDevices, Joystick::Private::copyCallBack, (void*) devArray); CFRelease(copyOfDevices); while(!p->deviceId && p->nextDevice < (unsigned) CFArrayGetCount(devArray)) { p->deviceId = CFArrayGetValueAtIndex(devArray, p->nextDevice++); if(p->deviceId) { CFArrayRef elemAry = IOHIDDeviceCopyMatchingElements((IOHIDDeviceRef) p->deviceId, 0, 0); bool isJoystick = false; for(int i = 0; !isJoystick && i < (int) CFArrayGetCount(elemAry); ++i) { IOHIDElementRef elem = (IOHIDElementRef) CFArrayGetValueAtIndex(elemAry, i); isJoystick = IOHIDElementGetUsagePage(elem) == kHIDPage_GenericDesktop && (IOHIDElementGetUsage(elem) == kHIDUsage_GD_Joystick || IOHIDElementGetUsage(elem) == kHIDUsage_GD_GamePad); } if(isJoystick) { CFRetain((IOHIDDeviceRef) p->deviceId); ++(p->usedJoysticks); for(int i = 0; i < (int) CFArrayGetCount(elemAry); ++i) { IOHIDElementRef elem = (IOHIDElementRef) CFArrayGetValueAtIndex(elemAry, i); IOHIDElementType elemType = IOHIDElementGetType(elem); if(elemType == kIOHIDElementTypeInput_Misc || elemType == kIOHIDElementTypeInput_Button || elemType == kIOHIDElementTypeInput_Axis || elemType == kIOHIDElementTypeInput_ScanCodes) { if(IOHIDElementGetUsagePage(elem) == kHIDPage_GenericDesktop) switch(IOHIDElementGetUsage(elem)) { case kHIDUsage_GD_X: case kHIDUsage_GD_Y: case kHIDUsage_GD_Z: case kHIDUsage_GD_Rx: case kHIDUsage_GD_Ry: case kHIDUsage_GD_Rz: { CFRetain(elem); int axis = IOHIDElementGetUsage(elem) - kHIDUsage_GD_X; p->axisIds[axis] = elem; p->axisMin[axis] = (int) IOHIDElementGetLogicalMin(elem); p->axisMax[axis] = (int) IOHIDElementGetLogicalMax(elem); break; } case kHIDUsage_GD_Hatswitch: CFRetain(elem); p->hatId = elem; p->axisMin[6] = p->axisMin[7] = -1; p->axisMax[6] = p->axisMax[7] = 1; break; } else if(IOHIDElementGetUsagePage(elem) == kHIDPage_Button) { int button = IOHIDElementGetUsage(elem) - 1; if(button >= 0 && button < numOfButtons) { CFRetain(elem); p->buttonIds[button] = elem; } } } } } else p->deviceId = 0; CFRelease(elemAry); } } CFRelease(devArray); return p->deviceId != 0; }
//int main( int argc, const char * argv[] ) int manipulate_led( int which_led, int led_value ) { #pragma unused ( argc, argv ) IOHIDDeviceRef * tIOHIDDeviceRefs = nil; // create a IO HID Manager reference IOHIDManagerRef tIOHIDManagerRef = IOHIDManagerCreate( kCFAllocatorDefault, kIOHIDOptionsTypeNone ); require( tIOHIDManagerRef, Oops ); // Create a device matching dictionary CFDictionaryRef matchingCFDictRef = hu_CreateMatchingDictionaryUsagePageUsage( TRUE, kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard ); require( matchingCFDictRef, Oops ); // set the HID device matching dictionary IOHIDManagerSetDeviceMatching( tIOHIDManagerRef, matchingCFDictRef ); if ( matchingCFDictRef ) { CFRelease( matchingCFDictRef ); } // Now open the IO HID Manager reference IOReturn tIOReturn = IOHIDManagerOpen( tIOHIDManagerRef, kIOHIDOptionsTypeNone ); require_noerr( tIOReturn, Oops ); // and copy out its devices CFSetRef deviceCFSetRef = IOHIDManagerCopyDevices( tIOHIDManagerRef ); require( deviceCFSetRef, Oops ); // how many devices in the set? CFIndex deviceIndex, deviceCount = CFSetGetCount( deviceCFSetRef ); // allocate a block of memory to extact the device ref's from the set into tIOHIDDeviceRefs = malloc( sizeof( IOHIDDeviceRef ) * deviceCount ); if (!tIOHIDDeviceRefs) { CFRelease(deviceCFSetRef); deviceCFSetRef = NULL; goto Oops; } // now extract the device ref's from the set CFSetGetValues( deviceCFSetRef, (const void **) tIOHIDDeviceRefs ); CFRelease(deviceCFSetRef); deviceCFSetRef = NULL; // before we get into the device loop we'll setup our element matching dictionary matchingCFDictRef = hu_CreateMatchingDictionaryUsagePageUsage( FALSE, kHIDPage_LEDs, 0 ); require( matchingCFDictRef, Oops ); int pass; // do 256 passes //for ( pass = 0; pass < 256; pass++ ) { Boolean delayFlag = FALSE; // if we find an LED element we'll set this to TRUE //printf( "pass = %d.\n", pass ); for ( deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++ ) { // if this isn't a keyboard device... if ( !IOHIDDeviceConformsTo( tIOHIDDeviceRefs[deviceIndex], kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard ) ) { continue; // ...skip it } //printf( " device = %p.\n", tIOHIDDeviceRefs[deviceIndex] ); // copy all the elements CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements( tIOHIDDeviceRefs[deviceIndex], matchingCFDictRef, kIOHIDOptionsTypeNone ); require( elementCFArrayRef, next_device ); // for each device on the system these values are divided by the value ranges of all LED elements found // for example, if the first four LED element have a range of 0-1 then the four least significant bits of // this value will be sent to these first four LED elements, etc. int device_value = pass; // iterate over all the elements CFIndex elementIndex, elementCount = CFArrayGetCount( elementCFArrayRef ); for ( elementIndex = 0; elementIndex < elementCount; elementIndex++ ) { IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( elementCFArrayRef, elementIndex ); require( tIOHIDElementRef, next_element ); uint32_t usagePage = IOHIDElementGetUsagePage( tIOHIDElementRef ); // if this isn't an LED element... if ( kHIDPage_LEDs != usagePage ) { continue; // ...skip it } uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef ); IOHIDElementType tIOHIDElementType = IOHIDElementGetType( tIOHIDElementRef ); //printf( " element = %p (page: %d, usage: %d, type: %d ).\n", // tIOHIDElementRef, usagePage, usage, tIOHIDElementType ); // get the logical mix/max for this LED element CFIndex minCFIndex = IOHIDElementGetLogicalMin( tIOHIDElementRef ); CFIndex maxCFIndex = IOHIDElementGetLogicalMax( tIOHIDElementRef ); // calculate the range CFIndex modCFIndex = maxCFIndex - minCFIndex + 1; // compute the value for this LED element //CFIndex tCFIndex = minCFIndex + ( device_value % modCFIndex ); CFIndex tCFIndex = led_value; device_value /= modCFIndex; //printf( " value = 0x%08lX.\n", tCFIndex ); uint64_t timestamp = 0; // create the IO HID Value to be sent to this LED element IOHIDValueRef tIOHIDValueRef = IOHIDValueCreateWithIntegerValue( kCFAllocatorDefault, tIOHIDElementRef, timestamp, tCFIndex ); if ( tIOHIDValueRef ) { // now set it on the device tIOReturn = IOHIDDeviceSetValue( tIOHIDDeviceRefs[deviceIndex], tIOHIDElementRef, tIOHIDValueRef ); CFRelease( tIOHIDValueRef ); require_noerr( tIOReturn, next_element ); delayFlag = TRUE; // set this TRUE so we'll delay before changing our LED values again } next_element: ; continue; } CFRelease( elementCFArrayRef ); next_device: ; continue; } // if we found an LED we'll delay before continuing //if ( delayFlag ) { // usleep( 500000 ); // sleep one half second //} // if the mouse is down… //if (GetCurrentButtonState()) { // break; // abort pass loop //} //} // next pass if ( matchingCFDictRef ) { CFRelease( matchingCFDictRef ); } Oops: ; if ( tIOHIDDeviceRefs ) { free(tIOHIDDeviceRefs); } if ( tIOHIDManagerRef ) { CFRelease( tIOHIDManagerRef ); } return 0; } /* main */
static void input_callback(void *context, IOReturn result, void *sender, IOHIDValueRef value) { struct input_data *input = (struct input_data*)context; IOHIDElementRef elem = IOHIDValueGetElement(value); uint32_t page = IOHIDElementGetUsagePage(elem); uint32_t usage = IOHIDElementGetUsage(elem); uint32_t val = IOHIDValueGetIntegerValue(value); if (page == kHIDPage_GenericDesktop) { if (input->ignore_mouse) { return; } switch (usage) { case kHIDUsage_GD_X: pthread_mutex_lock(&input->mouse_mutex); input->mouse_x += val; pthread_mutex_unlock(&input->mouse_mutex); break; case kHIDUsage_GD_Y: pthread_mutex_lock(&input->mouse_mutex); input->mouse_y += val; pthread_mutex_unlock(&input->mouse_mutex); break; case kHIDUsage_GD_Wheel: if ((int32_t)val > 0) { add_to_event_queue(input, K_MWHEELUP, true); add_to_event_queue(input, K_MWHEELUP, false); } else if ((int32_t)val < 0) { add_to_event_queue(input, K_MWHEELDOWN, true); add_to_event_queue(input, K_MWHEELDOWN, false); } break; default: break; } } else if (page == kHIDPage_Button) { if (input->ignore_mouse) { return; } if (usage < 1 || usage > 10) { usage = 10; } add_to_event_queue(input, K_MOUSE1 + usage - 1, val ? true : false); } else if (page == kHIDPage_KeyboardOrKeypad) { if (usage == kHIDUsage_KeyboardLeftGUI) { input->left_cmd_key_active = val ? true : false; } else if (usage == kHIDUsage_KeyboardRightGUI) { input->right_cmd_key_active = val ? true : false; } if (usage < sizeof(keytable) && (input->left_cmd_key_active || input->right_cmd_key_active)) { if (keytable[usage] == 'c' && val) { add_to_event_queue(input, K_COPY, true); add_to_event_queue(input, K_COPY, false); } else if (keytable[usage] == 'v' && val) { add_to_event_queue(input, K_PASTE, true); add_to_event_queue(input, K_PASTE, false); } return; } if (usage < sizeof(keytable)) { add_to_event_queue(input, keytable[usage], val ? true : false); pthread_mutex_lock(&input->key_mutex); if (val) { input->repeatkey = keytable[usage]; input->nextrepeattime = Sys_IntTime() + input->key_repeat_initial_delay; } else { input->repeatkey = 0; input->nextrepeattime = 0; } pthread_mutex_unlock(&input->key_mutex); } } else if (page == 0xFF) { if (usage == kHIDUsage_KeyboardErrorUndefined) { input->fn_key_active = val ? true : false; } } }
void GamepadManager::onDeviceValueChanged(IOHIDValueRef value) { IOHIDElementRef element = IOHIDValueGetElement(value); IOHIDDeviceRef device = IOHIDElementGetDevice(element); int vendorID = getIntDeviceProperty(device, CFSTR(kIOHIDVendorIDKey)); int productID = getIntDeviceProperty(device, CFSTR(kIOHIDProductIDKey)); uint32_t usagePage = IOHIDElementGetUsagePage(element); uint32_t usage = IOHIDElementGetUsage(element); // The following controller mapping is based on the Logitech F710, however we use it for // all Logitech devices on the assumption that they're likely to share the same mapping. if (vendorID == Logitech_F710_VendorID) { // Logitech F710 mapping. if (usagePage == kHIDPage_Button) { bool buttonState = IOHIDValueGetIntegerValue(value); switch(usage) { case kHIDUsage_Button_1: manipulateBitField(State.Buttons, Gamepad_X, buttonState); break; case kHIDUsage_Button_2: manipulateBitField(State.Buttons, Gamepad_A, buttonState); break; case kHIDUsage_Button_3: manipulateBitField(State.Buttons, Gamepad_B, buttonState); break; case kHIDUsage_Button_4: manipulateBitField(State.Buttons, Gamepad_Y, buttonState); break; case 0x05: manipulateBitField(State.Buttons, Gamepad_L1, buttonState); break; case 0x06: manipulateBitField(State.Buttons, Gamepad_R1, buttonState); break; case 0x07: State.LT = buttonState ? 1.0f:0.0f; break; case 0x08: State.RT = buttonState ? 1.0f:0.0f; break; case 0x09: manipulateBitField(State.Buttons, Gamepad_Back, buttonState); break; case 0x0A: manipulateBitField(State.Buttons, Gamepad_Start, buttonState); break; case 0x0B: manipulateBitField(State.Buttons, Gamepad_LStick, buttonState); break; case 0x0C: manipulateBitField(State.Buttons, Gamepad_RStick, buttonState); break; default: return; } } else if (usagePage == kHIDPage_GenericDesktop) { float v; switch(usage) { case kHIDUsage_GD_X: v = mapAnalogAxis(value, element); if (!setStateIfDifferent(State.LX, v)) return; break; case kHIDUsage_GD_Y: v = mapAnalogAxis(value, element); if (!setStateIfDifferent(State.LY, -v)) return; break; case kHIDUsage_GD_Z: v = mapAnalogAxis(value, element); if (!setStateIfDifferent(State.RX, v)) return; break; case kHIDUsage_GD_Rz: v = mapAnalogAxis(value, element); if (!setStateIfDifferent(State.RY, -v)) return; break; case kHIDUsage_GD_Hatswitch: { CFIndex integerValue = IOHIDValueGetIntegerValue(value); manipulateBitField(State.Buttons, Gamepad_Up, integerValue == 7 || integerValue == 0 || integerValue == 1); manipulateBitField(State.Buttons, Gamepad_Down, integerValue == 3 || integerValue == 4 || integerValue == 5); manipulateBitField(State.Buttons, Gamepad_Left, integerValue == 5 || integerValue == 6 || integerValue == 7); manipulateBitField(State.Buttons, Gamepad_Right, integerValue == 1 || integerValue == 2 || integerValue == 3); } break; default: return; } } } // The following controller mapping is based on the Sony DualShock3, however we use it for // all Sony devices on the assumption that they're likely to share the same mapping. else if (vendorID == Sony_DualShock3_VendorID) { // PS3 Controller. if (usagePage == kHIDPage_Button) { bool buttonState = IOHIDValueGetIntegerValue(value); switch(usage) { case kHIDUsage_Button_1: manipulateBitField(State.Buttons, Gamepad_Back, buttonState); break; case kHIDUsage_Button_2: manipulateBitField(State.Buttons, Gamepad_LStick, buttonState); break; case kHIDUsage_Button_3: manipulateBitField(State.Buttons, Gamepad_RStick, buttonState); break; case kHIDUsage_Button_4: manipulateBitField(State.Buttons, Gamepad_Start, buttonState); break; case 0x05: manipulateBitField(State.Buttons, Gamepad_Up, buttonState); break; case 0x06: manipulateBitField(State.Buttons, Gamepad_Right, buttonState); break; case 0x07: manipulateBitField(State.Buttons, Gamepad_Down, buttonState); break; case 0x08: manipulateBitField(State.Buttons, Gamepad_Left, buttonState); break; case 0x09: State.LT = buttonState ? 1.0f:0.0f; break; case 0x0A: State.RT = buttonState ? 1.0f:0.0f; break; case 0x0B: manipulateBitField(State.Buttons, Gamepad_L1, buttonState); break; case 0x0C: manipulateBitField(State.Buttons, Gamepad_R1, buttonState); break; case 0x0D: // PS3 Triangle. manipulateBitField(State.Buttons, Gamepad_TRIANGLE, buttonState); break; case 0x0E: // PS3 Circle manipulateBitField(State.Buttons, Gamepad_CIRCLE, buttonState); break; case 0x0F: // PS3 Cross manipulateBitField(State.Buttons, Gamepad_CROSS, buttonState); break; case 0x10: // PS3 Square manipulateBitField(State.Buttons, Gamepad_SQUARE, buttonState); break; default: return; } } else if (usagePage == kHIDPage_GenericDesktop) { float v; switch(usage) { case kHIDUsage_GD_X: v = mapAnalogAxis(value, element); if (!setStateIfDifferent(State.LX, v)) return; break; case kHIDUsage_GD_Y: v = mapAnalogAxis(value, element); if (!setStateIfDifferent(State.LY, -v)) return; break; case kHIDUsage_GD_Z: v = mapAnalogAxis(value, element); if (!setStateIfDifferent(State.RX, v)) return; break; case kHIDUsage_GD_Rz: v = mapAnalogAxis(value, element); if (!setStateIfDifferent(State.RY, -v)) return; break; default: return; } } } bStateChanged = true; }
static void apple_hid_device_input_callback(void *data, IOReturn result, void* sender, IOHIDValueRef value) { driver_t *driver = driver_get_ptr(); apple_input_data_t *apple = (apple_input_data_t*)driver->input_data; struct apple_hid_adapter *adapter = (struct apple_hid_adapter*)data; IOHIDElementRef element = IOHIDValueGetElement(value); uint32_t type = IOHIDElementGetType(element); uint32_t page = IOHIDElementGetUsagePage(element); uint32_t use = IOHIDElementGetUsage(element); if (type != kIOHIDElementTypeInput_Misc) if (type != kIOHIDElementTypeInput_Button) if (type != kIOHIDElementTypeInput_Axis) return; /* Joystick handler. * TODO: Can GamePad work the same? */ switch (page) { case kHIDPage_GenericDesktop: switch (type) { case kIOHIDElementTypeInput_Misc: switch (use) { case kHIDUsage_GD_Hatswitch: break; default: { int i; static const uint32_t axis_use_ids[4] = { 48, 49, 50, 53 }; for (i = 0; i < 4; i ++) { CFIndex min = IOHIDElementGetPhysicalMin(element); CFIndex max = IOHIDElementGetPhysicalMax(element) - min; CFIndex state = IOHIDValueGetIntegerValue(value) - min; float val = (float)state / (float)max; if (use != axis_use_ids[i]) continue; apple->axes[adapter->slot][i] = ((val * 2.0f) - 1.0f) * 32767.0f; } } break; } break; } break; case kHIDPage_Button: switch (type) { case kIOHIDElementTypeInput_Button: { CFIndex state = IOHIDValueGetIntegerValue(value); unsigned id = use - 1; if (state) BIT64_SET(apple->buttons[adapter->slot], id); else BIT64_CLEAR(apple->buttons[adapter->slot], id); } break; } break; } }
// utility routine to dump element info void HIDDumpElementInfo(IOHIDElementRef inIOHIDElementRef) { if (inIOHIDElementRef) { printf(" Element: %p = { ", inIOHIDElementRef); #if false IOHIDDeviceRef tIOHIDDeviceRef = IOHIDElementGetDevice(inIOHIDElementRef); printf("Device: %p, ", tIOHIDDeviceRef); #endif // if 0 IOHIDElementRef parentIOHIDElementRef = IOHIDElementGetParent(inIOHIDElementRef); printf("parent: %p, ", parentIOHIDElementRef); #if false CFArrayRef childrenCFArrayRef = IOHIDElementGetChildren(inIOHIDElementRef); printf("children: %p: { ", childrenCFArrayRef); fflush(stdout); CFShow(childrenCFArrayRef); fflush(stdout); printf(" }, "); #endif // if 0 IOHIDElementCookie tIOHIDElementCookie = IOHIDElementGetCookie(inIOHIDElementRef); printf("cookie: 0x%08lX, ", (long unsigned int) tIOHIDElementCookie); IOHIDElementType tIOHIDElementType = IOHIDElementGetType(inIOHIDElementRef); switch (tIOHIDElementType) { case kIOHIDElementTypeInput_Misc: { printf("type: Misc, "); break; } case kIOHIDElementTypeInput_Button: { printf("type: Button, "); break; } case kIOHIDElementTypeInput_Axis: { printf("type: Axis, "); break; } case kIOHIDElementTypeInput_ScanCodes: { printf("type: ScanCodes, "); break; } case kIOHIDElementTypeOutput: { printf("type: Output, "); break; } case kIOHIDElementTypeFeature: { printf("type: Feature, "); break; } case kIOHIDElementTypeCollection: { IOHIDElementCollectionType tIOHIDElementCollectionType = IOHIDElementGetCollectionType(inIOHIDElementRef); switch (tIOHIDElementCollectionType) { case kIOHIDElementCollectionTypePhysical: { printf("type: Physical Collection, "); break; } case kIOHIDElementCollectionTypeApplication: { printf("type: Application Collection, "); break; } case kIOHIDElementCollectionTypeLogical: { printf("type: Logical Collection, "); break; } case kIOHIDElementCollectionTypeReport: { printf("type: Report Collection, "); break; } case kIOHIDElementCollectionTypeNamedArray: { printf("type: Named Array Collection, "); break; } case kIOHIDElementCollectionTypeUsageSwitch: { printf("type: Usage Switch Collection, "); break; } case kIOHIDElementCollectionTypeUsageModifier: { printf("type: Usage Modifier Collection, "); break; } default: { printf("type: %p Collection, ", (void *) tIOHIDElementCollectionType); break; } } // switch break; } default: { printf("type: %p, ", (void *) tIOHIDElementType); break; } } /* switch */ uint32_t usagePage = IOHIDElementGetUsagePage(inIOHIDElementRef); uint32_t usage = IOHIDElementGetUsage(inIOHIDElementRef); printf("usage: 0x%04lX:0x%04lX, ", (long unsigned int) usagePage, (long unsigned int) usage); CFStringRef tCFStringRef = HIDCopyUsageName(usagePage, usage); if (tCFStringRef) { char usageString[256] = ""; (void) CFStringGetCString(tCFStringRef, usageString, sizeof(usageString), kCFStringEncodingUTF8); printf("\"%s\", ", usageString); CFRelease(tCFStringRef); } CFStringRef nameCFStringRef = IOHIDElementGetName(inIOHIDElementRef); char buffer[256]; if ( nameCFStringRef && CFStringGetCString(nameCFStringRef, buffer, sizeof(buffer), kCFStringEncodingUTF8) ) { printf("name: %s, ", buffer); } uint32_t reportID = IOHIDElementGetReportID(inIOHIDElementRef); uint32_t reportSize = IOHIDElementGetReportSize(inIOHIDElementRef); uint32_t reportCount = IOHIDElementGetReportCount(inIOHIDElementRef); printf("report: { ID: %lu, Size: %lu, Count: %lu }, ", (long unsigned int) reportID, (long unsigned int) reportSize, (long unsigned int) reportCount); uint32_t unit = IOHIDElementGetUnit(inIOHIDElementRef); uint32_t unitExp = IOHIDElementGetUnitExponent(inIOHIDElementRef); if (unit || unitExp) { printf("unit: %lu * 10^%lu, ", (long unsigned int) unit, (long unsigned int) unitExp); } CFIndex logicalMin = IOHIDElementGetLogicalMin(inIOHIDElementRef); CFIndex logicalMax = IOHIDElementGetLogicalMax(inIOHIDElementRef); if (logicalMin != logicalMax) { printf("logical: {min: %ld, max: %ld}, ", logicalMin, logicalMax); } CFIndex physicalMin = IOHIDElementGetPhysicalMin(inIOHIDElementRef); CFIndex physicalMax = IOHIDElementGetPhysicalMax(inIOHIDElementRef); if (physicalMin != physicalMax) { printf("physical: {min: %ld, max: %ld}, ", physicalMin, physicalMax); } Boolean isVirtual = IOHIDElementIsVirtual(inIOHIDElementRef); if (isVirtual) { printf("isVirtual, "); } Boolean isRelative = IOHIDElementIsRelative(inIOHIDElementRef); if (isRelative) { printf("isRelative, "); } Boolean isWrapping = IOHIDElementIsWrapping(inIOHIDElementRef); if (isWrapping) { printf("isWrapping, "); } Boolean isArray = IOHIDElementIsArray(inIOHIDElementRef); if (isArray) { printf("isArray, "); } Boolean isNonLinear = IOHIDElementIsNonLinear(inIOHIDElementRef); if (isNonLinear) { printf("isNonLinear, "); } Boolean hasPreferredState = IOHIDElementHasPreferredState(inIOHIDElementRef); if (hasPreferredState) { printf("hasPreferredState, "); } Boolean hasNullState = IOHIDElementHasNullState(inIOHIDElementRef); if (hasNullState) { printf("hasNullState, "); } printf(" }\n"); } } // HIDDumpElementInfo
void Burger::Mouse::InputCallback(void *pData, int iReturn,void * /* pSender */,IOHIDValueRef pValue) { if (iReturn == kIOReturnSuccess) { Mouse *pMouse = static_cast<Mouse *>(pData); Word uCount = pMouse->m_uMiceCount; if (uCount) { IOHIDElementRef pElement = IOHIDValueGetElement(pValue); IOHIDDeviceRef pDevice = IOHIDElementGetDevice(pElement); #if 0 Word uRatNumber = BURGER_MAXUINT; const DeviceStruct *pRat = pMouse->m_Mice; do { if (pRat->m_pDevice == pDevice) { uRatNumber = pMouse->m_uMiceCount-uCount; break; } ++pRat; } while (--uCount); if (uRatNumber==BURGER_MAXUINT) { } #endif Word32 uTime = static_cast<Word32>(IOHIDValueGetTimeStamp(pValue)); CFIndex iValue = IOHIDValueGetIntegerValue(pValue); uint32_t uPage = IOHIDElementGetUsagePage(pElement); uint32_t uUsage = IOHIDElementGetUsage(pElement); switch (uPage) { case kHIDPage_GenericDesktop: if (iValue) { switch (uUsage) { case kHIDUsage_GD_X: pMouse->PostMouseMotion(static_cast<Int32>(iValue),0,uTime); break; case kHIDUsage_GD_Y: pMouse->PostMouseMotion(0,static_cast<Int32>(iValue),uTime); break; case kHIDUsage_GD_Wheel: pMouse->PostMouseWheel(0,static_cast<Int32>(iValue),uTime); break; default: printf("Unknown usage %u\n",uUsage); break; } } break; case kHIDPage_Button: // iValue == down // Usage = which 1.2.3.4 if (iValue) { pMouse->PostMouseDown(1<<(uUsage-1)); } else { pMouse->PostMouseUp(1<<(uUsage-1)); } break; // Ignore this one case kHIDPage_Consumer: break; default: printf("Unknown page found %u\n",uPage); break; } } } }
Boolean HIDConfigureAction( IOHIDDeviceRef* outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef, float inTimeout ) { // param error? if ( !outIOHIDDeviceRef || !outIOHIDElementRef ) { return 0; } if ( !gDeviceCFArrayRef ) { // if we do not have a device list // and we can't build another list if ( !HIDBuildDeviceList( 0, 0 ) || !gDeviceCFArrayRef ) { return FALSE; // bail } } IOHIDDeviceRef tIOHIDDeviceRef; IOHIDElementRef tIOHIDElementRef; // remember when we start; used to calculate timeout clock_t start = clock(), end; // determine the maximum number of elements CFIndex maxElements = 0; CFIndex devIndex, devCount = CFArrayGetCount( gDeviceCFArrayRef ); for ( devIndex = 0; devIndex < devCount; devIndex++ ) { tIOHIDDeviceRef = ( IOHIDDeviceRef ) CFArrayGetValueAtIndex( gDeviceCFArrayRef, devIndex ); if ( !tIOHIDDeviceRef ) { continue; // skip this one } UInt32 count = HIDCountDeviceElements( tIOHIDDeviceRef, kHIDElementTypeInput ); if ( count > maxElements ) { maxElements = count; } } // allocate an array of int's in which to store devCount * maxElements values double* saveValueArray = ( double * ) calloc( devCount * maxElements, sizeof( double ) ); // clear 2D array to save values // on first pass store initial values / compare current values to initial values on subsequent passes Boolean found = FALSE, first = TRUE; while ( !found ) { double maxDeltaPercent = 0; // we want to find the one that moves the most ( percentage wise ) for ( devIndex = 0; devIndex < devCount; devIndex++ ) { IOHIDDeviceRef tIOHIDDeviceRef = ( IOHIDDeviceRef ) CFArrayGetValueAtIndex( gDeviceCFArrayRef, devIndex ); if ( !tIOHIDDeviceRef ) { continue; // skip this one } #ifdef DEBUG long vendorID = IOHIDDevice_GetVendorID( tIOHIDDeviceRef ); long productID = IOHIDDevice_GetProductID( tIOHIDDeviceRef ); #endif gElementCFArrayRef = IOHIDDeviceCopyMatchingElements( tIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone ); if ( gElementCFArrayRef ) { CFIndex eleIndex, eleCount = CFArrayGetCount( gElementCFArrayRef ); for ( eleIndex = 0; eleIndex < eleCount; eleIndex++ ) { tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, eleIndex ); if ( !tIOHIDElementRef ) { continue; } IOHIDElementType tIOHIDElementType = IOHIDElementGetType( tIOHIDElementRef ); // only care about inputs (no outputs or features) if ( tIOHIDElementType <= kIOHIDElementTypeInput_ScanCodes ) { if ( IOHIDElementIsArray( tIOHIDElementRef ) ) { //printf( "ARRAY!\n" ); continue; // skip array elements } uint32_t usagePage = IOHIDElementGetUsagePage( tIOHIDElementRef ); uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef ); uint32_t reportCount = IOHIDElementGetReportCount( tIOHIDElementRef ); #ifdef DEBUG if ( first ) { IOHIDElementCookie cookie = IOHIDElementGetCookie( tIOHIDElementRef ); printf( "%s, dev: {ref:%p, ven: 0x%08lX, pro: 0x%08lX}, ele: {ref:%p, cookie: %p, usage:%04lX:%08lX}\n", __PRETTY_FUNCTION__, tIOHIDDeviceRef, vendorID, productID, tIOHIDElementRef, cookie, (long unsigned int) usagePage, (long unsigned int) usage ); fflush( stdout ); if ( ( 0x054C == vendorID ) && ( 0x0268 == productID ) && ( 0x001E == (UInt32) cookie ) ) { //printf( "DING!\n" ); } } #endif #if 1 // work-around for IOHIDValueGetScaledValue crash (when element report count > 1) if ( reportCount > 1 ) { //printf( "REPORT!\n" ); continue; // skip reports } #endif // ignore PID elements and arrays if ( ( kHIDPage_PID != usagePage ) && ( -1 != usage ) ) { // get this elements current value double value = 0.0; // default value is zero IOHIDValueRef tIOHIDValueRef; IOReturn ioReturn = IOHIDDeviceGetValue( tIOHIDDeviceRef, tIOHIDElementRef, &tIOHIDValueRef ); if ( kIOReturnSuccess == ioReturn ) { value = IOHIDValueGetScaledValue( tIOHIDValueRef, kIOHIDValueScaleTypePhysical ); } if ( first ) { saveValueArray[( devIndex * maxElements ) + eleIndex] = value; } else { double initialValue = saveValueArray[( devIndex * maxElements ) + eleIndex]; CFIndex valueMin = IOHIDElementGetPhysicalMin( tIOHIDElementRef ); CFIndex valueMax = IOHIDElementGetPhysicalMax( tIOHIDElementRef ); double deltaPercent = fabs( ( initialValue - value ) * 100.0 / (valueMax - valueMin) ); #if 0 if ( !first ) { // Device: 0x13b6a0 = { Logitech Inc. - WingMan Force 3D, vendorID: 0x046D, productID: 0xC283, usage: 0x0001:0x0004, "Generic Desktop Joystick" if ( ( vendorID == 0x046D ) && ( productID == 0xC283 ) ) { if ( ( kHIDPage_GenericDesktop == usagePage ) && ( kHIDUsage_GD_Rz == usage ) ) { printf( "initial: %6.2f, value: %6.2f, diff: %6.2f, delta percent: %6.2f!\n", initialValue, value, fabs( initialValue - value ), deltaPercent ); } } } deltaPercent = 0.0; #endif if ( deltaPercent >= kPercentMove ) { found = TRUE; if ( deltaPercent > maxDeltaPercent ) { maxDeltaPercent = deltaPercent; *outIOHIDDeviceRef = tIOHIDDeviceRef; *outIOHIDElementRef = tIOHIDElementRef; } break; } } // if first } // if usage } // if type } // for elements... CFRelease( gElementCFArrayRef ); gElementCFArrayRef = NULL; } // if ( gElementCFArrayRef ) if ( found ) { // HIDDumpElementInfo( tIOHIDElementRef ); break; // DONE! } } // for devices first = FALSE; // no longer the first pass // are we done? end = clock(); double secs = (double)( end - start ) / CLOCKS_PER_SEC; if ( secs > inTimeout ) { break; // ( yes ) timeout } } // while ( !found ) // return device and element moved if ( !found ) { *outIOHIDDeviceRef = NULL; *outIOHIDElementRef = NULL; } return found; } // HIDConfigureAction