// 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; } }
bool HIDGamepad::maybeAddButton(IOHIDElementRef element) { uint32_t usagePage = IOHIDElementGetUsagePage(element); if (usagePage != kHIDPage_Button && usagePage != kHIDPage_GenericDesktop) return false; uint32_t usage = IOHIDElementGetUsage(element); if (usagePage == kHIDPage_GenericDesktop) { if (usage < kHIDUsage_GD_DPadUp || usage > kHIDUsage_GD_DPadLeft) return false; usage = std::numeric_limits<uint32_t>::max(); } else if (!usage) return false; CFIndex min = IOHIDElementGetLogicalMin(element); CFIndex max = IOHIDElementGetLogicalMax(element); m_buttons.append(makeUniqueRef<HIDGamepadButton>(usage, min, max, element)); IOHIDElementCookie cookie = IOHIDElementGetCookie(element); m_elementMap.set(cookie, &m_buttons.last().get()); return true; }
void HIDGamepad::valueChanged(IOHIDValueRef value) { IOHIDElementCookie cookie = IOHIDElementGetCookie(IOHIDValueGetElement(value)); HIDGamepadElement* element = m_elementMap.get(cookie); // This might be an element we don't currently handle as input so we can skip it. if (!element) return; element->rawValue = IOHIDValueGetScaledValue(value, kIOHIDValueScaleTypePhysical); if (element->isButton()) { for (unsigned i = 0; i < m_buttons.size(); ++i) { if (m_buttons[i].get() == element) m_buttonValues[i] = element->normalizedValue(); } } else if (element->isAxis()) { for (unsigned i = 0; i < m_axes.size(); ++i) { if (m_axes[i].get() == element) m_axisValues[i] = element->normalizedValue(); } } else ASSERT_NOT_REACHED(); m_lastUpdateTime = monotonicallyIncreasingTime(); }
void HIDGamepad::initElementsFromArray(CFArrayRef elements) { for (CFIndex i = 0, count = CFArrayGetCount(elements); i < count; ++i) { IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i); if (CFGetTypeID(element) != IOHIDElementGetTypeID()) continue; // As a physical element can appear in the device twice (in different collections) and can be // represented by different IOHIDElementRef objects, we look at the IOHIDElementCookie which // is meant to be unique for each physical element. IOHIDElementCookie cookie = IOHIDElementGetCookie(element); if (m_elementMap.contains(cookie)) continue; IOHIDElementType type = IOHIDElementGetType(element); if ((type == kIOHIDElementTypeInput_Misc || type == kIOHIDElementTypeInput_Button) && maybeAddButton(element)) continue; if ((type == kIOHIDElementTypeInput_Misc || type == kIOHIDElementTypeInput_Axis) && maybeAddAxis(element)) continue; if (type == kIOHIDElementTypeCollection) initElementsFromArray(IOHIDElementGetChildren(element)); } }
CFComparisonResult iohidmanager_sort_elements(const void *val1, const void *val2, void *context) { uint32_t page1 = (uint32_t)IOHIDElementGetUsagePage((IOHIDElementRef)val1); uint32_t page2 = (uint32_t)IOHIDElementGetUsagePage((IOHIDElementRef)val2); uint32_t use1 = (uint32_t)IOHIDElementGetUsage((IOHIDElementRef)val1); uint32_t use2 = (uint32_t)IOHIDElementGetUsage((IOHIDElementRef)val2); uint32_t cookie1 = (uint32_t)IOHIDElementGetCookie((IOHIDElementRef)val1); uint32_t cookie2 = (uint32_t)IOHIDElementGetCookie((IOHIDElementRef)val2); if (page1 != page2) return (CFComparisonResult)(page1 > page2); if(use1 != use2) return (CFComparisonResult)(use1 > use2); return (CFComparisonResult)(cookie1 > cookie2); }
Boolean HIDSaveElementPref( const CFStringRef inKeyCFStringRef, CFStringRef inAppCFStringRef, IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef ) { Boolean success = FALSE; if ( inKeyCFStringRef && inAppCFStringRef && inIOHIDDeviceRef && inIOHIDElementRef ) { long vendorID = IOHIDDevice_GetVendorID( inIOHIDDeviceRef ); require( vendorID, Oops ); long productID = IOHIDDevice_GetProductID( inIOHIDDeviceRef ); require( productID, Oops ); long locID = IOHIDDevice_GetLocationID( inIOHIDDeviceRef ); require( locID, Oops ); uint32_t usagePage = IOHIDDevice_GetUsagePage( inIOHIDDeviceRef ); uint32_t usage = IOHIDDevice_GetUsage( inIOHIDDeviceRef ); if ( !usagePage || !usage ) { usagePage = IOHIDDevice_GetPrimaryUsagePage( inIOHIDDeviceRef ); usage = IOHIDDevice_GetPrimaryUsage( inIOHIDDeviceRef ); } require( usagePage && usage, Oops ); uint32_t usagePageE = IOHIDElementGetUsagePage( inIOHIDElementRef ); uint32_t usageE = IOHIDElementGetUsage( inIOHIDElementRef ); IOHIDElementCookie eleCookie = IOHIDElementGetCookie( inIOHIDElementRef ); CFStringRef prefCFStringRef = CFStringCreateWithFormat( kCFAllocatorDefault, NULL, CFSTR( "d:{v:%ld, p:%ld, l:%ld, p:%ld, u:%ld}, e:{p:%ld, u:%ld, c:%ld}" ), vendorID, productID, locID, usagePage, usage, usagePageE, usageE, eleCookie ); if ( prefCFStringRef ) { CFPreferencesSetAppValue( inKeyCFStringRef, prefCFStringRef, inAppCFStringRef ); CFRelease( prefCFStringRef ); success = TRUE; } } Oops: ; return success; } // HIDSaveElementPref
// Set up a config record for saving // takes an input records, returns record user can save as they want // Note: the save rec must be pre-allocated by the calling app and will be filled out void HIDSetElementConfig(HID_info_ptr inHIDInfoPtr, IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef, IOHIDElementCookie actionCookie) { // must save: // actionCookie // Device: serial,vendorID, productID, location, usagePage, usage // Element: cookie, usagePage, usage, inHIDInfoPtr->actionCookie = actionCookie; // device // need to add serial number when I have a test case if (inIOHIDDeviceRef && inIOHIDElementRef) { inHIDInfoPtr->device.vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); inHIDInfoPtr->device.productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); inHIDInfoPtr->device.locID = IOHIDDevice_GetLocationID(inIOHIDDeviceRef); inHIDInfoPtr->device.usage = IOHIDDevice_GetUsage(inIOHIDDeviceRef); inHIDInfoPtr->device.usagePage = IOHIDDevice_GetUsagePage(inIOHIDDeviceRef); if (!inHIDInfoPtr->device.usagePage || !inHIDInfoPtr->device.usage) { inHIDInfoPtr->device.usagePage = IOHIDDevice_GetPrimaryUsagePage(inIOHIDDeviceRef); inHIDInfoPtr->device.usage = IOHIDDevice_GetPrimaryUsage(inIOHIDDeviceRef); } inHIDInfoPtr->element.usagePage = IOHIDElementGetUsagePage(inIOHIDElementRef); inHIDInfoPtr->element.usage = IOHIDElementGetUsage(inIOHIDElementRef); inHIDInfoPtr->element.minReport = IOHIDElement_GetCalibrationSaturationMin(inIOHIDElementRef); inHIDInfoPtr->element.maxReport = IOHIDElement_GetCalibrationSaturationMax(inIOHIDElementRef); inHIDInfoPtr->element.cookie = IOHIDElementGetCookie(inIOHIDElementRef); } else { inHIDInfoPtr->device.vendorID = 0; inHIDInfoPtr->device.productID = 0; inHIDInfoPtr->device.locID = 0; inHIDInfoPtr->device.usage = 0; inHIDInfoPtr->device.usagePage = 0; inHIDInfoPtr->element.usagePage = 0; inHIDInfoPtr->element.usage = 0; inHIDInfoPtr->element.minReport = 0; inHIDInfoPtr->element.maxReport = 0; inHIDInfoPtr->element.cookie = 0; } } // HIDSetElementConfig
HIDInputType HIDGamepad::valueChanged(IOHIDValueRef value) { IOHIDElementCookie cookie = IOHIDElementGetCookie(IOHIDValueGetElement(value)); HIDGamepadElement* element = m_elementMap.get(cookie); // This might be an element we don't currently handle as input so we can skip it. if (!element) return HIDInputType::NotAButtonPress; element->rawValue = IOHIDValueGetScaledValue(value, kIOHIDValueScaleTypePhysical); if (element->isButton()) { for (unsigned i = 0; i < m_buttons.size(); ++i) { if (&m_buttons[i].get() == element) { m_buttonValues[i] = element->normalizedValue(); break; } } } else if (element->isAxis()) { for (unsigned i = 0; i < m_axes.size(); ++i) { if (&m_axes[i].get() == element) { m_axisValues[i] = element->normalizedValue(); break; } } } else if (element->isDPad()) { int intValue = IOHIDValueGetIntegerValue(value) - element->min; for (unsigned i = 0; i < m_dPads.size(); ++i) { if (&m_dPads[i].get() != element) continue; // Each DPad represents 4 button values which are tacked on to the end of the values from non-DPad buttons. unsigned firstButtonValue = m_buttons.size() + i * 4; ASSERT(m_buttonValues.size() > firstButtonValue + 3); fillInButtonValues(intValue, m_buttonValues[firstButtonValue], m_buttonValues[firstButtonValue + 1], m_buttonValues[firstButtonValue + 2], m_buttonValues[firstButtonValue + 3]); } } else ASSERT_NOT_REACHED(); m_lastUpdateTime = monotonicallyIncreasingTime(); return element->isButton() ? HIDInputType::ButtonPress : HIDInputType::NotAButtonPress; }
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; }
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; }
static void iohidmanager_hid_device_input_callback(void *data, IOReturn result, void* sender, IOHIDValueRef value) { iohidmanager_hid_t *hid = (iohidmanager_hid_t*)hid_driver_get_data(); struct iohidmanager_hid_adapter *adapter = (struct iohidmanager_hid_adapter*)data; IOHIDElementRef element = IOHIDValueGetElement(value); uint32_t type = (uint32_t)IOHIDElementGetType(element); uint32_t page = (uint32_t)IOHIDElementGetUsagePage(element); uint32_t use = (uint32_t)IOHIDElementGetUsage(element); uint32_t cookie = (uint32_t)IOHIDElementGetCookie(element); apple_input_rec_t *tmp = NULL; if (type != kIOHIDElementTypeInput_Misc) if (type != kIOHIDElementTypeInput_Button) if (type != kIOHIDElementTypeInput_Axis) return; /* Joystick handler. * TODO: Can GamePad work the same? */ int pushed_button = 0; switch (page) { case kHIDPage_GenericDesktop: switch (type) { case kIOHIDElementTypeInput_Misc: switch (use) { case kHIDUsage_GD_Hatswitch: { tmp = adapter->hats; while(tmp && tmp->cookie != (IOHIDElementCookie)cookie) tmp = tmp->next; if(tmp->cookie == (IOHIDElementCookie)cookie) { CFIndex range = IOHIDElementGetLogicalMax(element) - IOHIDElementGetLogicalMin(element); CFIndex val = IOHIDValueGetIntegerValue(value); if(range == 3) val *= 2; switch(val) { case 0: /* pos = up */ hid->hats[adapter->slot][0] = 0; hid->hats[adapter->slot][1] = -1; break; case 1: /* pos = up+right */ hid->hats[adapter->slot][0] = 1; hid->hats[adapter->slot][1] = -1; break; case 2: /* pos = right */ hid->hats[adapter->slot][0] = 1; hid->hats[adapter->slot][1] = 0; break; case 3: /* pos = down+right */ hid->hats[adapter->slot][0] = 1; hid->hats[adapter->slot][1] = 1; break; case 4: /* pos = down */ hid->hats[adapter->slot][0] = 0; hid->hats[adapter->slot][1] = 1; break; case 5: /* pos = down+left */ hid->hats[adapter->slot][0] = -1; hid->hats[adapter->slot][1] = 1; break; case 6: /* pos = left */ hid->hats[adapter->slot][0] = -1; hid->hats[adapter->slot][1] = 0; break; case 7: /* pos = up_left */ hid->hats[adapter->slot][0] = -1; hid->hats[adapter->slot][1] = -1; break; default: /* pos = centered */ hid->hats[adapter->slot][0] = 0; hid->hats[adapter->slot][1] = 0; break; } } } break; default: tmp = adapter->axes; while(tmp && tmp->cookie != (IOHIDElementCookie)cookie) tmp = tmp->next; if (tmp) { if(tmp->cookie == (IOHIDElementCookie)cookie) { CFIndex min = IOHIDElementGetPhysicalMin(element); CFIndex state = IOHIDValueGetIntegerValue(value) - min; CFIndex max = IOHIDElementGetPhysicalMax(element) - min; float val = (float)state / (float)max; hid->axes[adapter->slot][tmp->id] = ((val * 2.0f) - 1.0f) * 32767.0f; } } else pushed_button = 1; break; } break; } break; case kHIDPage_Consumer: case kHIDPage_Button: switch (type) { case kIOHIDElementTypeInput_Misc: case kIOHIDElementTypeInput_Button: pushed_button = 1; break; } break; } if (pushed_button) { tmp = adapter->buttons; uint8_t bit = 0; while(tmp && tmp->cookie != (IOHIDElementCookie)cookie) { bit++; tmp = tmp->next; } if(tmp && tmp->cookie == (IOHIDElementCookie)cookie) { CFIndex state = IOHIDValueGetIntegerValue(value); if (state) BIT64_SET(hid->buttons[adapter->slot], bit); else BIT64_CLEAR(hid->buttons[adapter->slot], bit); } } }
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)); }
// 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
static void onDeviceValueChanged(void * context, IOReturn result, void * sender, IOHIDValueRef value) { struct Gamepad_device * deviceRecord; struct Gamepad_devicePrivate * hidDeviceRecord; IOHIDElementRef element; IOHIDElementCookie cookie; unsigned int axisIndex, buttonIndex; static mach_timebase_info_data_t timebaseInfo; if (timebaseInfo.denom == 0) { mach_timebase_info(&timebaseInfo); } deviceRecord = context; hidDeviceRecord = deviceRecord->privateData; element = IOHIDValueGetElement(value); cookie = IOHIDElementGetCookie(element); for (axisIndex = 0; axisIndex < deviceRecord->numAxes; axisIndex++) { if (!hidDeviceRecord->axisElements[axisIndex].isHatSwitchSecondAxis && hidDeviceRecord->axisElements[axisIndex].cookie == cookie) { CFIndex integerValue; if (IOHIDValueGetLength(value) > 4) { // Workaround for a strange crash that occurs with PS3 controller; was getting lengths of 39 (!) continue; } integerValue = IOHIDValueGetIntegerValue(value); if (hidDeviceRecord->axisElements[axisIndex].isHatSwitch) { int x, y; // Fix for Saitek X52 if (!hidDeviceRecord->axisElements[axisIndex].hasNullState) { if (integerValue < hidDeviceRecord->axisElements[axisIndex].logicalMin) { integerValue = hidDeviceRecord->axisElements[axisIndex].logicalMax - hidDeviceRecord->axisElements[axisIndex].logicalMin + 1; } else { integerValue--; } } hatValueToXY(integerValue, hidDeviceRecord->axisElements[axisIndex].logicalMax - hidDeviceRecord->axisElements[axisIndex].logicalMin + 1, &x, &y); if (x != deviceRecord->axisStates[axisIndex]) { queueAxisEvent(deviceRecord, IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, axisIndex, x, deviceRecord->axisStates[axisIndex]); deviceRecord->axisStates[axisIndex] = x; } if (y != deviceRecord->axisStates[axisIndex + 1]) { queueAxisEvent(deviceRecord, IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, axisIndex + 1, y, deviceRecord->axisStates[axisIndex + 1]); deviceRecord->axisStates[axisIndex + 1] = y; } } else { float floatValue; if (integerValue < hidDeviceRecord->axisElements[axisIndex].logicalMin) { hidDeviceRecord->axisElements[axisIndex].logicalMin = integerValue; } if (integerValue > hidDeviceRecord->axisElements[axisIndex].logicalMax) { hidDeviceRecord->axisElements[axisIndex].logicalMax = integerValue; } floatValue = (integerValue - hidDeviceRecord->axisElements[axisIndex].logicalMin) / (float) (hidDeviceRecord->axisElements[axisIndex].logicalMax - hidDeviceRecord->axisElements[axisIndex].logicalMin) * 2.0f - 1.0f; queueAxisEvent(deviceRecord, IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, axisIndex, floatValue, deviceRecord->axisStates[axisIndex]); deviceRecord->axisStates[axisIndex] = floatValue; } return; } } for (buttonIndex = 0; buttonIndex < deviceRecord->numButtons; buttonIndex++) { if (hidDeviceRecord->buttonElements[buttonIndex].cookie == cookie) { bool down; down = IOHIDValueGetIntegerValue(value); queueButtonEvent(deviceRecord, IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, buttonIndex, down); deviceRecord->buttonStates[buttonIndex] = down; return; } } }
static void onDeviceMatched(void * context, IOReturn result, void * sender, IOHIDDeviceRef device) { CFArrayRef elements; CFIndex elementIndex; IOHIDElementRef element; CFStringRef cfProductName; struct Gamepad_device * deviceRecord; struct Gamepad_devicePrivate * hidDeviceRecord; IOHIDElementType type; char * description; struct Gamepad_queuedEvent queuedEvent; deviceRecord = malloc(sizeof(struct Gamepad_device)); deviceRecord->deviceID = nextDeviceID++; deviceRecord->vendorID = IOHIDDeviceGetVendorID(device); deviceRecord->productID = IOHIDDeviceGetProductID(device); deviceRecord->deviceMap = Gamepad_deviceMap(deviceRecord->vendorID, deviceRecord->productID); deviceRecord->numAxes = 0; deviceRecord->numButtons = 0; devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1)); devices[numDevices++] = deviceRecord; hidDeviceRecord = malloc(sizeof(struct Gamepad_devicePrivate)); hidDeviceRecord->deviceRef = device; hidDeviceRecord->axisElements = NULL; hidDeviceRecord->buttonElements = NULL; deviceRecord->privateData = hidDeviceRecord; cfProductName = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); if (cfProductName == NULL || CFGetTypeID(cfProductName) != CFStringGetTypeID()) { description = malloc(strlen("[Unknown]" + 1)); strcpy(description, "[Unknown]"); } else { CFIndex length; CFStringGetBytes(cfProductName, CFRangeMake(0, CFStringGetLength(cfProductName)), kCFStringEncodingUTF8, '?', false, NULL, 100, &length); description = malloc(length + 1); CFStringGetBytes(cfProductName, CFRangeMake(0, CFStringGetLength(cfProductName)), kCFStringEncodingUTF8, '?', false, (UInt8 *) description, length + 1, NULL); description[length] = '\x00'; } deviceRecord->description = description; elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); for (elementIndex = 0; elementIndex < CFArrayGetCount(elements); elementIndex++) { element = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, elementIndex); type = IOHIDElementGetType(element); // All of the axis elements I've ever detected have been kIOHIDElementTypeInput_Misc. kIOHIDElementTypeInput_Axis is only included for good faith... if (type == kIOHIDElementTypeInput_Misc || type == kIOHIDElementTypeInput_Axis) { hidDeviceRecord->axisElements = realloc(hidDeviceRecord->axisElements, sizeof(struct HIDGamepadAxis) * (deviceRecord->numAxes + 1)); hidDeviceRecord->axisElements[deviceRecord->numAxes].cookie = IOHIDElementGetCookie(element); hidDeviceRecord->axisElements[deviceRecord->numAxes].logicalMin = IOHIDElementGetLogicalMin(element); hidDeviceRecord->axisElements[deviceRecord->numAxes].logicalMax = IOHIDElementGetLogicalMax(element); hidDeviceRecord->axisElements[deviceRecord->numAxes].hasNullState = !!IOHIDElementHasNullState(element); hidDeviceRecord->axisElements[deviceRecord->numAxes].isHatSwitch = IOHIDElementGetUsage(element) == kHIDUsage_GD_Hatswitch; hidDeviceRecord->axisElements[deviceRecord->numAxes].isHatSwitchSecondAxis = false; deviceRecord->numAxes++; if (hidDeviceRecord->axisElements[deviceRecord->numAxes - 1].isHatSwitch) { hidDeviceRecord->axisElements = realloc(hidDeviceRecord->axisElements, sizeof(struct HIDGamepadAxis) * (deviceRecord->numAxes + 1)); hidDeviceRecord->axisElements[deviceRecord->numAxes].isHatSwitchSecondAxis = true; deviceRecord->numAxes++; } } else if (type == kIOHIDElementTypeInput_Button) { hidDeviceRecord->buttonElements = realloc(hidDeviceRecord->buttonElements, sizeof(struct HIDGamepadButton) * (deviceRecord->numButtons + 1)); hidDeviceRecord->buttonElements[deviceRecord->numButtons].cookie = IOHIDElementGetCookie(element); deviceRecord->numButtons++; } } CFRelease(elements); deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes); deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons); IOHIDDeviceRegisterInputValueCallback(device, onDeviceValueChanged, deviceRecord); queuedEvent.deviceID = deviceRecord->deviceID; queuedEvent.eventType = GAMEPAD_EVENT_DEVICE_ATTACHED; queuedEvent.eventData = deviceRecord; if (deviceEventCount >= deviceEventQueueSize) { deviceEventQueueSize = deviceEventQueueSize == 0 ? 1 : deviceEventQueueSize * 2; deviceEventQueue = realloc(deviceEventQueue, sizeof(struct Gamepad_queuedEvent) * deviceEventQueueSize); } deviceEventQueue[deviceEventCount++] = queuedEvent; }
// 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
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>(); } } }
static void iohidmanager_hid_device_add(void *data, IOReturn result, void* sender, IOHIDDeviceRef device) { int i; IOReturn ret; uint16_t dev_vid, dev_pid; CFArrayRef elements_raw; int count; CFMutableArrayRef elements; CFRange range; bool found_axis[6] = { false, false, false, false, false, false }; apple_input_rec_t *tmp = NULL; apple_input_rec_t *tmpButtons = NULL; apple_input_rec_t *tmpAxes = NULL; iohidmanager_hid_t *hid = (iohidmanager_hid_t*) hid_driver_get_data(); struct iohidmanager_hid_adapter *adapter = (struct iohidmanager_hid_adapter*) calloc(1, sizeof(*adapter)); if (!adapter) return; if (!hid) goto error; adapter->handle = device; ret = IOHIDDeviceOpen(device, kIOHIDOptionsTypeNone); if (ret != kIOReturnSuccess) goto error; /* Move the device's run loop to this thread. */ IOHIDDeviceScheduleWithRunLoop(device, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); IOHIDDeviceRegisterRemovalCallback(device, iohidmanager_hid_device_remove, adapter); #ifndef IOS iohidmanager_hid_device_get_product_string(device, adapter->name, sizeof(adapter->name)); #endif dev_vid = iohidmanager_hid_device_get_vendor_id (device); dev_pid = iohidmanager_hid_device_get_product_id (device); adapter->slot = pad_connection_pad_init(hid->slots, adapter->name, dev_vid, dev_pid, adapter, &iohidmanager_hid); if (adapter->slot == -1) goto error; if (pad_connection_has_interface(hid->slots, adapter->slot)) IOHIDDeviceRegisterInputReportCallback(device, adapter->data + 1, sizeof(adapter->data) - 1, iohidmanager_hid_device_report, adapter); else IOHIDDeviceRegisterInputValueCallback(device, iohidmanager_hid_device_input_callback, adapter); if (string_is_empty(adapter->name)) goto error; /* scan for buttons, axis, hats */ elements_raw = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); count = (int)CFArrayGetCount(elements_raw); elements = CFArrayCreateMutableCopy( kCFAllocatorDefault,(CFIndex)count,elements_raw); range = CFRangeMake(0,count); CFArraySortValues(elements, range, iohidmanager_sort_elements, NULL); for (i = 0; i < count; i++) { IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i); if (!element) continue; IOHIDElementType type = IOHIDElementGetType(element); uint32_t page = (uint32_t)IOHIDElementGetUsagePage(element); uint32_t use = (uint32_t)IOHIDElementGetUsage(element); uint32_t cookie = (uint32_t)IOHIDElementGetCookie(element); int detected_button = 0; switch (page) { case kHIDPage_GenericDesktop: switch (type) { case kIOHIDElementTypeCollection: case kIOHIDElementTypeInput_ScanCodes: case kIOHIDElementTypeFeature: case kIOHIDElementTypeInput_Button: case kIOHIDElementTypeOutput: case kIOHIDElementTypeInput_Axis: /* TODO/FIXME */ break; case kIOHIDElementTypeInput_Misc: switch (use) { case kHIDUsage_GD_Hatswitch: { /* as far as I can tell, OSX only reports one Hat */ apple_input_rec_t *hat = (apple_input_rec_t *)malloc(sizeof(apple_input_rec_t)); hat->id = 0; hat->cookie = (IOHIDElementCookie)cookie; hat->next = NULL; adapter->hats = hat; } break; default: { uint32_t i = 0; static const uint32_t axis_use_ids[6] = { 48, 49, 51, 52, 50, 53 }; while (i < 6 && axis_use_ids[i] != use) i++; if (i < 6) { apple_input_rec_t *axis = (apple_input_rec_t *)malloc(sizeof(apple_input_rec_t)); axis->id = i; axis->cookie = (IOHIDElementCookie)cookie; axis->next = NULL; if(iohidmanager_check_for_id(adapter->axes,i)) { /* axis ID already exists, save to tmp for appending later */ if(tmpAxes) iohidmanager_append_record(tmpAxes, axis); else tmpAxes = axis; } else { found_axis[axis->id] = true; if(adapter->axes) iohidmanager_append_record(adapter->axes, axis); else adapter->axes = axis; } } else detected_button = 1; } break; } break; } break; case kHIDPage_Consumer: case kHIDPage_Button: switch (type) { case kIOHIDElementTypeCollection: case kIOHIDElementTypeFeature: case kIOHIDElementTypeInput_ScanCodes: case kIOHIDElementTypeInput_Axis: case kIOHIDElementTypeOutput: /* TODO/FIXME */ break; case kIOHIDElementTypeInput_Misc: case kIOHIDElementTypeInput_Button: detected_button = 1; break; } break; } if (detected_button) { apple_input_rec_t *btn = (apple_input_rec_t *)malloc(sizeof(apple_input_rec_t)); btn->id = (uint32_t)use; btn->cookie = (IOHIDElementCookie)cookie; btn->next = NULL; if(iohidmanager_check_for_id(adapter->buttons,btn->id)) { if(tmpButtons) iohidmanager_append_record_ordered(&tmpButtons, btn); else tmpButtons = btn; } else { if(adapter->buttons) iohidmanager_append_record_ordered(&adapter->buttons, btn); else adapter->buttons = btn; } } } /* take care of buttons/axes with duplicate 'use' values */ for (i = 0; i < 6; i++) { if(found_axis[i] == false && tmpAxes) { apple_input_rec_t *next = tmpAxes->next; tmpAxes->id = i; tmpAxes->next = NULL; iohidmanager_append_record(adapter->axes, tmpAxes); tmpAxes = next; } } tmp = adapter->buttons; if (tmp) { while(tmp->next) tmp = tmp->next; } while(tmpButtons) { apple_input_rec_t *next = tmpButtons->next; tmpButtons->id = tmp->id; tmpButtons->next = NULL; tmp->next = tmpButtons; tmp = tmp->next; tmpButtons = next; } iohidmanager_hid_device_add_autodetect(adapter->slot, adapter->name, iohidmanager_hid.ident, dev_vid, dev_pid); return; error: { apple_input_rec_t *tmp = NULL; while(adapter->hats != NULL) { tmp = adapter->hats; adapter->hats = adapter->hats->next; free(tmp); } while(adapter->axes != NULL) { tmp = adapter->axes; adapter->axes = adapter->axes->next; free(tmp); } while(adapter->buttons != NULL) { tmp = adapter->buttons; adapter->buttons = adapter->buttons->next; free(tmp); } while(tmpAxes != NULL) { tmp = tmpAxes; tmpAxes = tmpAxes->next; free(tmp); } while(tmpButtons != NULL) { tmp = tmpButtons; tmpButtons = tmpButtons->next; free(tmp); } free(adapter); } }
Boolean HIDFindDeviceAndElement(const HID_info_rec *inSearchInfo, IOHIDDeviceRef * outFoundDevice, IOHIDElementRef * outFoundElement) { Boolean result = false; IOHIDDeviceRef bestIOHIDDeviceRef = NULL; IOHIDElementRef bestIOHIDElementRef = NULL; int bestScore = 0; CFIndex devIndex, devCount = CFArrayGetCount(gDeviceCFArrayRef); for (devIndex = 0; devIndex < devCount; devIndex++) { int deviceScore = 1; IOHIDDeviceRef tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex); if (!tIOHIDDeviceRef) { continue; } // match vendorID, productID (+10, +8) if (inSearchInfo->device.vendorID) { uint32_t vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); if (vendorID) { if (inSearchInfo->device.vendorID == vendorID) { deviceScore += 10; if (inSearchInfo->device.productID) { uint32_t productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); if (productID) { if (inSearchInfo->device.productID == productID) { deviceScore += 8; } // if (inSearchInfo->device.productID == productID) } // if (productID) } // if (inSearchInfo->device.productID) } // if (inSearchInfo->device.vendorID == vendorID) } // if vendorID } // if search->device.vendorID // match usagePage & usage (+9) if (inSearchInfo->device.usagePage && inSearchInfo->device.usage) { uint32_t usagePage = IOHIDDevice_GetUsagePage(tIOHIDDeviceRef); uint32_t usage = IOHIDDevice_GetUsage(tIOHIDDeviceRef); if (!usagePage || !usage) { usagePage = IOHIDDevice_GetPrimaryUsagePage(tIOHIDDeviceRef); usage = IOHIDDevice_GetPrimaryUsage(tIOHIDDeviceRef); } if (usagePage) { if (inSearchInfo->device.usagePage == usagePage) { if (usage) { if (inSearchInfo->device.usage == usage) { deviceScore += 9; } // if (inSearchInfo->usage == usage) } // if (usage) } // if (inSearchInfo->usagePage == usagePage) } // if (usagePage) } // if (inSearchInfo->usagePage && // inSearchInfo->usage) // match location ID (+5) if (inSearchInfo->device.locID) { uint32_t locID = IOHIDDevice_GetLocationID(tIOHIDDeviceRef); if (locID) { if (inSearchInfo->device.locID == locID) { deviceScore += 5; } } } // iterate over all elements of this device gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, 0); if (gElementCFArrayRef) { CFIndex eleIndex, eleCount = CFArrayGetCount(gElementCFArrayRef); for (eleIndex = 0; eleIndex < eleCount; eleIndex++) { IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, eleIndex); if (!tIOHIDElementRef) { continue; } int score = deviceScore; // match usage page, usage & cookie if (inSearchInfo->element.usagePage && inSearchInfo->element.usage) { uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); if (inSearchInfo->element.usagePage == usagePage) { uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); if (inSearchInfo->element.usage == usage) { score += 5; IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); if (inSearchInfo->element.cookie == cookie) { score += 4; } // cookies match } else { score = 0; } // usages match } else { score = 0; } // usage pages match } // if (search usage page & usage) #if LOG_SCORING if (kHIDPage_KeyboardOrKeypad != tElementRef->usagePage) { // skip keyboards here printf("%s: (%ld:%ld)-I-Debug, score: %ld\t", __PRETTY_FUNCTION__, inSearchInfo->element.usagePage, inSearchInfo->element.usage, score); HIDPrintElement(tIOHIDElementRef); } #endif // LOG_SCORING if (score > bestScore) { bestIOHIDDeviceRef = tIOHIDDeviceRef; bestIOHIDElementRef = tIOHIDElementRef; bestScore = score; #if LOG_SCORING printf("%s: (%ld:%ld)-I-Debug, better score: %ld\t", __PRETTY_FUNCTION__, inSearchInfo->element.usagePage, inSearchInfo->element.usage, score); HIDPrintElement(bestIOHIDElementRef); #endif // LOG_SCORING } } // for elements... CFRelease(gElementCFArrayRef); gElementCFArrayRef = NULL; } // if (gElementCFArrayRef) } // for (devIndex = 0; devIndex < devCount; // devIndex++) if (bestIOHIDDeviceRef || bestIOHIDElementRef) { *outFoundDevice = bestIOHIDDeviceRef; *outFoundElement = bestIOHIDElementRef; #if LOG_SCORING printf("%s: (%ld:%ld)-I-Debug, best score: %ld\t", __PRETTY_FUNCTION__, inSearchInfo->element.usagePage, inSearchInfo->element.usage, bestScore); HIDPrintElement(bestIOHIDElementRef); #endif // LOG_SCORING result = true; } return (result); } // HIDFindDeviceAndElement
// ************************************************************************* // // HIDConfigureAction(outIOHIDDeviceRef, outIOHIDElementRef, inTimeout) // // Purpose: polls all devices and elements for a change greater than kPercentMove. // Times out after given time returns 1 and pointer to device and element // if found; returns 0 and NULL for both parameters if not found // // Inputs: outIOHIDDeviceRef - address where to store the device // outIOHIDElementRef - address where to store the element // inTimeout - the timeout // Returns: Boolean - if successful // outIOHIDDeviceRef - the device // outIOHIDElementRef - the element // Boolean HIDConfigureActionOfType(actionTypeMask inActionTypeMask, double inTimeout, IOHIDDeviceRef * outIOHIDDeviceRef, IOHIDElementRef * outIOHIDElementRef) { // param error? if (!outIOHIDDeviceRef || !outIOHIDElementRef) { return (false); } if (!gDeviceCFArrayRef) { // if we do not have a device list // and we can't build another list if (!HIDBuildDeviceList(0, 0) || !gDeviceCFArrayRef) { return (false); // bail } } IOHIDDeviceRef tIOHIDDeviceRef; IOHIDElementRef tIOHIDElementRef; IOHIDElementType elementType = 0; switch (inActionTypeMask) { case kActionTypeButton: { elementType = kIOHIDElementTypeInput_Button; break; } case kActionTypeAxis: { elementType = kIOHIDElementTypeInput_Misc; break; } case kActionTypeAll: default: { elementType = 0; break; } } // switch // determine the maximum number of elements CFIndex maxElements = 0; CFIndex devIndex, devCount = CFArrayGetCount(gDeviceCFArrayRef); for (devIndex = 0; devIndex < devCount; devIndex++) { tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex); if (!tIOHIDDeviceRef) { continue; // skip this one } // HIDDumpDeviceInfo(tIOHIDDeviceRef); CFIndex count = HIDCountDeviceElementsOfType(tIOHIDDeviceRef, elementType); if (count > maxElements) { maxElements = count; } } if (!(devCount * maxElements)) { return (false); } #if true // NSDictionary * matchDictionary = @{@(kIOHIDElementTypeKey): @(elementType)}; const void *keys[] = {CFSTR(kIOHIDElementTypeKey)}; const void *vals[] = {CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &elementType)}; CFDictionaryRef matchingDict = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFRelease(vals[0]); #endif // if 1 // allocate an array of int's in which to store devCount * maxElements values double *saveValueArray = (double *) calloc(devCount * maxElements, sizeof(double)); // clear 2D array to save values // remember when we start; used to calculate timeout clock_t start = clock(), end; // on first pass store initial values / compare current values to initial values on subsequent passes Boolean found = false, first = true; while (!found) { double maxDeltaPercent = 0; // we want to find the one that moves the most // (percentage wise) for (devIndex = 0; devIndex < devCount; devIndex++) { tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex); if (!tIOHIDDeviceRef) { continue; // skip this one } gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, matchingDict, kIOHIDOptionsTypeNone); if (gElementCFArrayRef) { CFIndex eleIndex, eleCount = CFArrayGetCount(gElementCFArrayRef); for (eleIndex = 0; eleIndex < eleCount; eleIndex++) { tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, eleIndex); if (!tIOHIDElementRef) { continue; } IOHIDElementType tIOHIDElementType = IOHIDElementGetType(tIOHIDElementRef); // only care about inputs (no outputs or features) if (tIOHIDElementType <= kIOHIDElementTypeInput_ScanCodes) { if (IOHIDElementIsArray(tIOHIDElementRef)) { // printf("ARRAY!\n"); continue; // skip array elements } if (elementType && ((tIOHIDElementType != elementType))) { continue; } uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); uint32_t reportCount = IOHIDElementGetReportCount(tIOHIDElementRef); #ifdef DEBUG if (first) { HIDDumpElementInfo(tIOHIDElementRef); fflush(stdout); uint32_t vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); uint32_t productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); if ((0x054C == vendorID) && (0x0268 == productID) && (0x001E == (uint32_t) cookie)) { // printf("DING!\n"); } } #endif // ifdef DEBUG #if true // work-around for IOHIDValueGetScaledValue crash // (when element report count > 1) if (reportCount > 1) { // printf("REPORT!\n"); continue; // skip reports } #endif // if 1 // ignore PID elements and arrays if ((kHIDPage_PID != usagePage) && ((-1) != usage)) { // get this elements current value double value = 0.0; // default value is zero IOHIDValueRef tIOHIDValueRef; IOReturn ioReturn = IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &tIOHIDValueRef); if (kIOReturnSuccess == ioReturn) { value = IOHIDValueGetScaledValue(tIOHIDValueRef, kIOHIDValueScaleTypePhysical); } if (first) { saveValueArray[(devIndex * maxElements) + eleIndex] = value; } else { double initialValue = saveValueArray[(devIndex * maxElements) + eleIndex]; CFIndex valueMin = IOHIDElementGetPhysicalMin(tIOHIDElementRef); CFIndex valueMax = IOHIDElementGetPhysicalMax(tIOHIDElementRef); double deltaPercent = fabs((initialValue - value) * 100.0 / (valueMax - valueMin)); #if false // debug code useful to dump out value info for // specific (vendorID, productID, usagePage and // usage) device if (!first) { // Device: 0x13b6a0 = { Logitech Inc. - WingMan Force 3D, vendorID: 0x046D, productID: 0xC283, // usage: 0x0001:0x0004, "Generic Desktop Joystick" if ((vendorID == 0x046D) && (productID == 0xC283)) { if ((kHIDPage_GenericDesktop == usagePage) && (kHIDUsage_GD_Rz == usage)) { printf("initial: %6.2f, value: %6.2f, diff: %6.2f, delta percent: %6.2f!\n", initialValue, value, fabs(initialValue - value), deltaPercent); } } } deltaPercent = 0.0; #endif // if false if (deltaPercent >= kPercentMove) { found = true; if (deltaPercent > maxDeltaPercent) { maxDeltaPercent = deltaPercent; *outIOHIDDeviceRef = tIOHIDDeviceRef; *outIOHIDElementRef = tIOHIDElementRef; } break; } } // if first } // if usage } // if type } // for elements... CFRelease(gElementCFArrayRef); gElementCFArrayRef = NULL; } // if (gElementCFArrayRef) if (found) { // HIDDumpElementInfo(tIOHIDElementRef); break; // DONE! } } // for devices first = false; // no longer the first pass // are we done? end = clock(); double secs = (double) (end - start) / CLOCKS_PER_SEC; if (secs > inTimeout) { break; // (yes) timeout } } // while (!found) if (saveValueArray) { free(saveValueArray); } // return device and element moved if (!found) { *outIOHIDDeviceRef = NULL; *outIOHIDElementRef = NULL; } CFRelease(matchingDict); return (found); } // HIDConfigureAction
/* 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++; } } }
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