void os_updateindicators(usbdevice* kb, int force){ if(!IS_CONNECTED(kb) || NEEDS_FW_UPDATE(kb)) return; // Set NumLock on permanently char ileds = 1; // Set Caps Lock if enabled. Unlike Linux, OSX keyboards have independent caps lock states, so // we use the last-assigned value rather than fetching it from the system if(kb->eventflags & kCGEventFlagMaskAlphaShift) ileds |= 2; usbmode* mode = kb->profile.currentmode; if(mode && kb->active) ileds = (ileds & ~mode->ioff) | mode->ion; if(force || ileds != kb->ileds){ kb->ileds = ileds; // Set the LEDs CFArrayRef leds = IOHIDDeviceCopyMatchingElements(kb->handles[0], 0, kIOHIDOptionsTypeNone); CFIndex count = CFArrayGetCount(leds); for(CFIndex i = 0; i < count; i++){ IOHIDElementRef led = (void*)CFArrayGetValueAtIndex(leds, i); uint32_t page = IOHIDElementGetUsagePage(led); if(page != kHIDPage_LEDs) continue; uint32_t usage = IOHIDElementGetUsage(led); IOHIDValueRef value = IOHIDValueCreateWithIntegerValue(kCFAllocatorDefault, led, 0, !!(ileds & (1 << (usage - 1)))); IOHIDDeviceSetValue(kb->handles[0], led, value); CFRelease(value); } CFRelease(leds); } }
void updateindicators(usbdevice* kb, int force){ // Set NumLock on permanently char ileds = 1; // Set Caps Lock if enabled. Unlike Linux, OSX keyboards have independent caps lock states, so // we use the last-assigned value rather than fetching it from the system if(kb->eflags & kCGEventFlagMaskAlphaShift) ileds |= 2; if(force || ileds != kb->ileds){ kb->ileds = ileds; // Set the LEDs CFArrayRef leds = IOHIDDeviceCopyMatchingElements(kb->handles[0], 0, kIOHIDOptionsTypeNone); CFIndex count = CFArrayGetCount(leds); for(CFIndex i = 0; i < count; i++){ IOHIDElementRef led = (void*)CFArrayGetValueAtIndex(leds, i); uint32_t page = IOHIDElementGetUsagePage(led); if(page != kHIDPage_LEDs) continue; uint32_t usage = IOHIDElementGetUsage(led); IOHIDValueRef value = IOHIDValueCreateWithIntegerValue(kCFAllocatorDefault, led, 0, !!(ileds & (1 << (usage - 1)))); IOHIDDeviceSetValue(kb->handles[0], led, value); CFRelease(value); } CFRelease(leds); IOHIDDeviceSetReport(kb->handles[1], kIOHIDReportTypeOutput, 0, &kb->ileds, 1); } }
static CFIndex find_top_level(IOHIDDeviceRef tIOHIDDeviceRef, CFArrayRef topLevels) { CFArrayRef gElementCFArrayRef; CFIndex numTops = 0; if (!tIOHIDDeviceRef) return 0; gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, 0); if (gElementCFArrayRef) { CFIndex idx, cnt = CFArrayGetCount(gElementCFArrayRef); for (idx=0; idx<cnt; idx++) { IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(gElementCFArrayRef, idx); int eleType = IOHIDElementGetType(tIOHIDElementRef); /* Check for top-level gaming device collections */ if (eleType == kIOHIDElementTypeCollection && IOHIDElementGetParent(tIOHIDElementRef) == 0) { int tUsagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); int tUsage = IOHIDElementGetUsage(tIOHIDElementRef); if (tUsagePage == kHIDPage_GenericDesktop && (tUsage == kHIDUsage_GD_Joystick || tUsage == kHIDUsage_GD_GamePad)) { CFArrayAppendValue((CFMutableArrayRef)topLevels, tIOHIDElementRef); numTops++; } } } } return numTops; }
IOHIDElementRef GetFirstOutputElement(IOHIDDeviceRef device) { IOHIDElementRef output = NULL; CFArrayRef arrayElements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); CFIndex elementCount = CFArrayGetCount(arrayElements); for(CFIndex i = 0; i < elementCount; i++) { IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(arrayElements, i); if(!tIOHIDElementRef) { continue; } IOHIDElementType type = IOHIDElementGetType(tIOHIDElementRef); switch ( type ) { case kIOHIDElementTypeOutput: { output = tIOHIDElementRef; break; } default: break; } if(output) { break; } } CFRelease(arrayElements); return output; }
int joy_hidlib_enumerate_elements(joy_hid_device_t *device) { IOHIDDeviceRef dev = device->internal_device; if(dev == NULL) { return -1; } /* get all elements of device */ CFArrayRef internal_elements = IOHIDDeviceCopyMatchingElements( dev, NULL, 0 ); if(!internal_elements) { return -1; } /* get number of elements */ CFIndex cnt = CFArrayGetCount( internal_elements ); device->num_elements = (int)cnt; /* create elements array */ joy_hid_element_t *elements = (joy_hid_element_t *) lib_malloc(sizeof(joy_hid_element_t) * cnt); if(elements == NULL) { CFRelease(internal_elements); internal_elements = NULL; return -1; } /* enumerate and convert all elements */ CFIndex i; joy_hid_element_t *e = elements; for(i=0;i<cnt;i++) { IOHIDElementRef internal_element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( internal_elements, i ); if ( internal_element ) { uint32_t usage_page = IOHIDElementGetUsagePage( internal_element ); uint32_t usage = IOHIDElementGetUsage( internal_element ); CFIndex min = IOHIDElementGetPhysicalMin( internal_element ); CFIndex max = IOHIDElementGetPhysicalMax( internal_element ); e->usage_page = (int)usage_page; e->usage = (int)usage; e->min_value = (int)min; e->max_value = (int)max; e->internal_element = internal_element; } else { e->usage_page = -1; e->usage = -1; e->min_value = -1; e->max_value = -1; e->internal_element = NULL; } e++; } /* keep the reference until the elements are free'ed again */ device->internal_elements = internal_elements; device->elements = elements; return (int)cnt; }
bool JoystickOSX::configure_joystick(IOHIDDeviceRef p_device_ref, joystick *p_joy) { CFTypeRef refCF = NULL; p_joy->device_ref = p_device_ref; /* get device name */ String name; char c_name[256]; refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDProductKey)); if (!refCF) { refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDManufacturerKey)); } if ((!refCF) || (!CFStringGetCString((CFStringRef)refCF, c_name, sizeof(c_name), kCFStringEncodingUTF8))) { name = "Unidentified Joystick"; } name = c_name; int id = input->get_unused_joy_id(); ERR_FAIL_COND_V(id == -1, false); p_joy->id = id; int vendor = 0; refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDVendorIDKey)); if (refCF) { CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &vendor); } int product_id = 0; refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDProductIDKey)); if (refCF) { CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &product_id); } if (vendor && product_id) { char uid[128]; sprintf(uid, "%04x%08x%04x%08x", OSSwapHostToBigInt32(vendor), 0, OSSwapHostToBigInt32(product_id), 0); input->joy_connection_changed(id, true, name, uid); } else { //bluetooth device String guid = "05000000"; for (int i = 0; i < 12; i++) { if (i < name.size()) guid += _hex_str(name[i]); else guid += "00"; } input->joy_connection_changed(id, true, name, guid); } CFArrayRef array = NULL; array = IOHIDDeviceCopyMatchingElements(p_device_ref, NULL, kIOHIDOptionsTypeNone); if (array) { p_joy->add_hid_elements(array); CFRelease(array); } return true; }
void setKeyboard(struct __IOHIDDevice *device, CFDictionaryRef keyboardDictionary, LedState changes[]) { CFStringRef deviceNameRef = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); if (!deviceNameRef) return; const char * deviceName = CFStringGetCStringPtr(deviceNameRef, kCFStringEncodingUTF8); if (nameMatch && fnmatch(nameMatch, deviceName, 0) != 0) return; if (verbose) printf("\n \"%s\" ", deviceName); CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, keyboardDictionary, kIOHIDOptionsTypeNone); if (elements) { for (CFIndex elementIndex = 0; elementIndex < CFArrayGetCount(elements); elementIndex++) { IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, elementIndex); if (element && kHIDPage_LEDs == IOHIDElementGetUsagePage(element)) { uint32_t led = IOHIDElementGetUsage(element); if (led >= maxLeds) break; // Get current keyboard led status IOHIDValueRef currentValue = 0; if (IOHIDDeviceGetValue(device, element, ¤tValue) == kIOReturnSuccess && IOHIDValueGetLength(currentValue) < 3) { long current = IOHIDValueGetIntegerValue(currentValue); CFRelease(currentValue); // Should we try to set the led? if (changes[led] != NoChange && changes[led] != current) { IOHIDValueRef newValue = IOHIDValueCreateWithIntegerValue(kCFAllocatorDefault, element, 0, changes[led]); if (newValue) { IOReturn changeResult = IOHIDDeviceSetValue(device, element, newValue); // Was the change successful? if (kIOReturnSuccess == changeResult && verbose) { printf("%s%s ", stateSymbol[changes[led]], ledNames[led - 1]); } CFRelease(newValue); } } else if (verbose) { printf("%s%s ", stateSymbol[current], ledNames[led - 1]); } } } } CFRelease(elements); } if (verbose) printf("\n"); }
IOHIDElementRef GetNextOutputElement(IOHIDDeviceRef device, IOHIDElementRef previousElement) { bool found = false; IOHIDElementRef output = NULL; CFArrayRef arrayElements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); CFIndex elementCount = CFArrayGetCount(arrayElements); for(CFIndex i = 0; i < elementCount; i++) { IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(arrayElements, i); if(!tIOHIDElementRef) { continue; } if(!found) { if(previousElement == tIOHIDElementRef) { CFRelease(previousElement); // do we need this ? found = true; } continue; } IOHIDElementType type = IOHIDElementGetType(tIOHIDElementRef); switch ( type ) { case kIOHIDElementTypeOutput: { output = tIOHIDElementRef; break; } default: break; } if(output) { break; } } CFRelease(arrayElements); return output; }
void HIDGamepad::initElements() { RetainPtr<CFArrayRef> elements = adoptCF(IOHIDDeviceCopyMatchingElements(m_hidDevice.get(), NULL, kIOHIDOptionsTypeNone)); initElementsFromArray(elements.get()); // Buttons are specified to appear highest priority first in the array. std::sort(m_buttons.begin(), m_buttons.end(), [](const std::unique_ptr<HIDGamepadButton>& a, const std::unique_ptr<HIDGamepadButton>& b) { return a->priority < b->priority; }); m_axisValues.resize(m_axes.size()); m_buttonValues.resize(m_buttons.size()); for (auto& button : m_buttons) getCurrentValueForElement(*button); for (auto& axis : m_axes) getCurrentValueForElement(*axis); }
/************************************************************************** * 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; }
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; } } }
unsigned char HIDConfigureSingleDeviceAction(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef * outIOHIDElementRef, double timeout) { if (!inIOHIDDeviceRef) { return (0); } if (0 == HIDHaveDeviceList()) { // if we do not have a device list return (0); // return 0 } Boolean found = false; // build list of device and elements to save current values CFIndex maxElements = HIDCountDeviceElements(inIOHIDDeviceRef, kHIDElementTypeInput); double *saveValueArray = (double *) calloc(maxElements, sizeof(double)); // 2D array to save values // store initial values on first pass / compare to initial value on subsequent passes Boolean first = true; // get all the elements from this device CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(inIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); // if that worked... if (elementCFArrayRef) { clock_t start = clock(), end; // poll all devices and elements while (!found) { uint32_t currElementIndex = 0; CFIndex idx, cnt = CFArrayGetCount(elementCFArrayRef); for (idx = 0; idx < cnt; idx++) { *outIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); if (!*outIOHIDElementRef) { continue; } // is this an input element? IOHIDElementType type = IOHIDElementGetType(*outIOHIDElementRef); switch (type) { // these types are inputs case kIOHIDElementTypeInput_Misc: case kIOHIDElementTypeInput_Button: case kIOHIDElementTypeInput_Axis: case kIOHIDElementTypeInput_ScanCodes: default: { break; } case kIOHIDElementTypeOutput: case kIOHIDElementTypeFeature: case kIOHIDElementTypeCollection: { *outIOHIDElementRef = NULL; // these types are not (Skip them) break; } } /* switch */ if (!*outIOHIDElementRef) { continue; // skip this element } // get this elements current value double value = 0; // default value is zero IOHIDValueRef tIOHIDValueRef; IOReturn ioReturn = IOHIDDeviceGetValue(inIOHIDDeviceRef, *outIOHIDElementRef, &tIOHIDValueRef); if (kIOReturnSuccess == ioReturn) { value = IOHIDValueGetScaledValue(tIOHIDValueRef, kIOHIDValueScaleTypePhysical); } if (first) { saveValueArray[currElementIndex] = value; } else { CFIndex min = IOHIDElementGetLogicalMin(*outIOHIDElementRef); CFIndex max = IOHIDElementGetLogicalMax(*outIOHIDElementRef); double initialValue = saveValueArray[currElementIndex]; double delta = (double) (max - min) * kPercentMove * 0.01f; // is the new value within +/- delta of the initial value? if (((initialValue + delta) < value) || ((initialValue - delta) > value)) { found = 1; // (yes!) mark as found break; } } // if (first) currElementIndex++; // bump element index } // next idx if (first) { first = false; // no longer the first pass } else { // are we done? end = clock(); double secs = (double) (end - start) / CLOCKS_PER_SEC; if (secs > timeout) { break; // (yes) timeout } } } // while (!found) CFRelease(elementCFArrayRef); } // if (elementCFArrayRef) if (saveValueArray) { free(saveValueArray); } // return device and element moved if (found) { return (1); } else { *outIOHIDElementRef = NULL; return (0); } } // HIDConfigureSingleDeviceAction
// ************************************************************************* // // 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
static SDL_bool GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice) { Uint32 *guid32 = NULL; CFTypeRef refCF = NULL; CFArrayRef array = NULL; /* get usage page and usage */ refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsagePageKey)); if (refCF) { CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usagePage); } if (pDevice->usagePage != kHIDPage_GenericDesktop) { return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */ } refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsageKey)); if (refCF) { CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usage); } if ((pDevice->usage != kHIDUsage_GD_Joystick && pDevice->usage != kHIDUsage_GD_GamePad && pDevice->usage != kHIDUsage_GD_MultiAxisController)) { return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */ } pDevice->deviceRef = hidDevice; /* get device name */ refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductKey)); if (!refCF) { /* Maybe we can't get "AwesomeJoystick2000", but we can get "Logitech"? */ refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey)); } if ((!refCF) || (!CFStringGetCString(refCF, pDevice->product, sizeof (pDevice->product), kCFStringEncodingUTF8))) { SDL_strlcpy(pDevice->product, "Unidentified joystick", sizeof (pDevice->product)); } refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey)); if (refCF) { CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->guid.data[0]); } refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductIDKey)); if (refCF) { CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->guid.data[8]); } /* Check to make sure we have a vendor and product ID If we don't, use the same algorithm as the Linux code for Bluetooth devices */ guid32 = (Uint32*)pDevice->guid.data; if (!guid32[0] && !guid32[1]) { /* If we don't have a vendor and product ID this is probably a Bluetooth device */ const Uint16 BUS_BLUETOOTH = 0x05; Uint16 *guid16 = (Uint16 *)guid32; *guid16++ = BUS_BLUETOOTH; *guid16++ = 0; SDL_strlcpy((char*)guid16, pDevice->product, sizeof(pDevice->guid.data) - 4); } array = IOHIDDeviceCopyMatchingElements(hidDevice, NULL, kIOHIDOptionsTypeNone); if (array) { AddHIDElements(array, pDevice); CFRelease(array); } return SDL_TRUE; }
bool JoystickImpl::open(unsigned int index) { m_index = index; 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); i < joysticksCount; ++i) { IOHIDDeviceRef d = (IOHIDDeviceRef)devicesArray[i]; if (deviceLoc == HIDInputManager::getLocationID(d)) { self = d; break; // We found it so we stop looping. } } if (self == 0) { // This shouldn't happen! 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; } // How many elements are there? CFIndex elementsCount = CFArrayGetCount(elements); if (elementsCount == 0) { // What is a joystick with no element? CFRelease(elements); CFRelease(devices); return false; } // Go through all connected elements. for (int i = 0; i < elementsCount; ++i) { IOHIDElementRef element = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, i); switch (IOHIDElementGetType(element)) { case kIOHIDElementTypeInput_Misc: 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; // kHIDUsage_GD_Vx, kHIDUsage_GD_Vy, kHIDUsage_GD_Vz are ignored. } break; case kIOHIDElementTypeInput_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: // Make compiler happy 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); // Note: Joy::AxisPovX/Y are not supported (yet). // Maybe kIOHIDElementTypeInput_Axis is the corresponding type but I can't test. // 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); // 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. So to prevent // leaking we retain objects 'only' now. CFRelease(devices); CFRelease(elements); return true; }
bool GPUSB_Open() { // VID = 4938 PID = 36897 IOHIDManagerRef managerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); pGPUSB = FindDevice(managerRef, 4938, 36896); if(!pGPUSB) { CFRelease(managerRef); return false; } GPUSB_Model = 0; CFArrayRef arrayElements = IOHIDDeviceCopyMatchingElements(pGPUSB, NULL, kIOHIDOptionsTypeNone); CFIndex elementCount = CFArrayGetCount(arrayElements); int countInputElements = 0; for(int i = 0; i < elementCount; i++) { IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(arrayElements, i); if(!tIOHIDElementRef) { continue; } IOHIDElementType type = IOHIDElementGetType(tIOHIDElementRef); switch ( type ) { case kIOHIDElementTypeInput_Misc: case kIOHIDElementTypeInput_Button: case kIOHIDElementTypeInput_Axis: case kIOHIDElementTypeInput_ScanCodes: { countInputElements++; break; } default: break; } CFRelease(tIOHIDElementRef); } CFRelease(arrayElements); if(countInputElements == 1) GPUSB_Model = 1; // now opening the device for communication IOReturn ioReturnValue = IOHIDDeviceOpen(pGPUSB, kIOHIDOptionsTypeSeizeDevice); if(ioReturnValue != kIOReturnSuccess) { CFRelease(pGPUSB); pGPUSB = NULL; CFRelease(managerRef); return false; } CFRelease(managerRef); // check if this is ok return true; }
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; }
//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 */
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; }
static SDL_bool GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice) { const Uint16 BUS_USB = 0x03; const Uint16 BUS_BLUETOOTH = 0x05; Sint32 vendor = 0; Sint32 product = 0; Sint32 version = 0; CFTypeRef refCF = NULL; CFArrayRef array = NULL; Uint16 *guid16 = (Uint16 *)pDevice->guid.data; /* get usage page and usage */ refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsagePageKey)); if (refCF) { CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usagePage); } if (pDevice->usagePage != kHIDPage_GenericDesktop) { return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */ } refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsageKey)); if (refCF) { CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usage); } if ((pDevice->usage != kHIDUsage_GD_Joystick && pDevice->usage != kHIDUsage_GD_GamePad && pDevice->usage != kHIDUsage_GD_MultiAxisController)) { return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */ } pDevice->deviceRef = hidDevice; /* get device name */ refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductKey)); if (!refCF) { /* Maybe we can't get "AwesomeJoystick2000", but we can get "Logitech"? */ refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey)); } if ((!refCF) || (!CFStringGetCString(refCF, pDevice->product, sizeof (pDevice->product), kCFStringEncodingUTF8))) { SDL_strlcpy(pDevice->product, "Unidentified joystick", sizeof (pDevice->product)); } refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey)); if (refCF) { CFNumberGetValue(refCF, kCFNumberSInt32Type, &vendor); } refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductIDKey)); if (refCF) { CFNumberGetValue(refCF, kCFNumberSInt32Type, &product); } refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVersionNumberKey)); if (refCF) { CFNumberGetValue(refCF, kCFNumberSInt32Type, &version); } SDL_memset(pDevice->guid.data, 0, sizeof(pDevice->guid.data)); if (vendor && product) { *guid16++ = SDL_SwapLE16(BUS_USB); *guid16++ = 0; *guid16++ = SDL_SwapLE16((Uint16)vendor); *guid16++ = 0; *guid16++ = SDL_SwapLE16((Uint16)product); *guid16++ = 0; *guid16++ = SDL_SwapLE16((Uint16)version); *guid16++ = 0; } else { *guid16++ = SDL_SwapLE16(BUS_BLUETOOTH); *guid16++ = 0; SDL_strlcpy((char*)guid16, pDevice->product, sizeof(pDevice->guid.data) - 4); } array = IOHIDDeviceCopyMatchingElements(hidDevice, NULL, kIOHIDOptionsTypeNone); if (array) { AddHIDElements(array, pDevice); CFRelease(array); } return SDL_TRUE; }
// ---------------------------------------------------- int main(int argc, const char * argv[]) { #pragma unused ( argc, argv ) IOHIDManagerRef tIOHIDManagerRef = NULL; CFSetRef deviceCFSetRef = NULL; IOHIDDeviceRef *tIOHIDDeviceRefs = nil; do { tIOHIDManagerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); if (!tIOHIDManagerRef) { break; } IOHIDManagerSetDeviceMatching(tIOHIDManagerRef, NULL); IOReturn tIOReturn = IOHIDManagerOpen(tIOHIDManagerRef, kIOHIDOptionsTypeNone); if (noErr != tIOReturn) { break; } deviceCFSetRef = IOHIDManagerCopyDevices(tIOHIDManagerRef); if (!deviceCFSetRef) { break; } CFIndex deviceIndex, deviceCount = CFSetGetCount(deviceCFSetRef); tIOHIDDeviceRefs = malloc(sizeof(IOHIDDeviceRef) * deviceCount); if (!tIOHIDDeviceRefs) { break; } CFSetGetValues(deviceCFSetRef, (const void **)tIOHIDDeviceRefs); CFRelease(deviceCFSetRef); deviceCFSetRef = NULL; for (deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) { if (!tIOHIDDeviceRefs[deviceIndex]) { continue; } HIDDumpDeviceInfo(tIOHIDDeviceRefs[deviceIndex]); #if true // and copy all the elements CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRefs[deviceIndex], NULL /* matchingCFDictRef */, kIOHIDOptionsTypeNone); if (elementCFArrayRef) { // iterate over all the elements CFIndex elementIndex, elementCount = CFArrayGetCount(elementCFArrayRef); for (elementIndex = 0; elementIndex < elementCount; elementIndex++) { IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(elementCFArrayRef, elementIndex); if (tIOHIDElementRef) { HIDDumpElementInfo(tIOHIDElementRef); } } CFRelease(elementCFArrayRef); } #endif } } while (false); if (tIOHIDDeviceRefs) { free(tIOHIDDeviceRefs); } if (deviceCFSetRef) { CFRelease(deviceCFSetRef); deviceCFSetRef = NULL; } if (tIOHIDManagerRef) { CFRelease(tIOHIDManagerRef); } return (0); } // main
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
// 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
// get the first element of device passed in as parameter // returns NULL if no list exists or device does not exists or is NULL IOHIDElementRef HIDGetFirstDeviceElement( IOHIDDeviceRef inIOHIDDeviceRef, HIDElementTypeMask typeMask ) { IOHIDElementRef result = NULL; if ( inIOHIDDeviceRef ) { assert( IOHIDDeviceGetTypeID() == CFGetTypeID( inIOHIDDeviceRef ) ); gElementCFArrayRef = IOHIDDeviceCopyMatchingElements( inIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone ); if ( gElementCFArrayRef ) { CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef ); for ( idx = 0; idx < cnt; idx++ ) { IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx ); if ( !tIOHIDElementRef ) { continue; } IOHIDElementType type = IOHIDElementGetType( tIOHIDElementRef ); switch ( type ) { case kIOHIDElementTypeInput_Misc: case kIOHIDElementTypeInput_Button: case kIOHIDElementTypeInput_Axis: case kIOHIDElementTypeInput_ScanCodes: { if ( typeMask & kHIDElementTypeInput ) { result = tIOHIDElementRef; } break; } case kIOHIDElementTypeOutput: { if ( typeMask & kHIDElementTypeOutput ) { result = tIOHIDElementRef; } break; } case kIOHIDElementTypeFeature: { if ( typeMask & kHIDElementTypeFeature ) { result = tIOHIDElementRef; } break; } case kIOHIDElementTypeCollection: { if ( typeMask & kHIDElementTypeCollection ) { result = tIOHIDElementRef; } break; } } // switch ( type ) if ( result ) { break; // DONE! } } // next idx CFRelease( gElementCFArrayRef ); gElementCFArrayRef = NULL; } // if ( gElementCFArrayRef ) } // if ( inIOHIDDeviceRef ) return result; } /* HIDGetFirstDeviceElement */
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; }
// get next element of given device in list given current element as parameter // will walk down each collection then to next element or collection (depthwise traverse) // returns NULL if end of list // uses mask of HIDElementTypeMask to restrict element found // use kHIDElementTypeIO to get previous HIDGetNextDeviceElement functionality IOHIDElementRef HIDGetNextDeviceElement( IOHIDElementRef inIOHIDElementRef, HIDElementTypeMask typeMask ) { IOHIDElementRef result = NULL; if ( inIOHIDElementRef ) { assert( IOHIDElementGetTypeID() == CFGetTypeID( inIOHIDElementRef ) ); IOHIDDeviceRef tIOHIDDeviceRef = IOHIDElementGetDevice( inIOHIDElementRef ); if ( tIOHIDDeviceRef ) { Boolean found = FALSE; gElementCFArrayRef = IOHIDDeviceCopyMatchingElements( tIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone ); if ( gElementCFArrayRef ) { CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef ); for ( idx = 0; idx < cnt; idx++ ) { IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx ); if ( !tIOHIDElementRef ) { continue; } if ( !found ) { if ( inIOHIDElementRef == tIOHIDElementRef ) { found = TRUE; } continue; // next element } else { // we've found the current element; now find the next one of the right type IOHIDElementType type = IOHIDElementGetType( tIOHIDElementRef ); switch ( type ) { case kIOHIDElementTypeInput_Misc: case kIOHIDElementTypeInput_Button: case kIOHIDElementTypeInput_Axis: case kIOHIDElementTypeInput_ScanCodes: { if ( typeMask & kHIDElementTypeInput ) { result = tIOHIDElementRef; } break; } case kIOHIDElementTypeOutput: { if ( typeMask & kHIDElementTypeOutput ) { result = tIOHIDElementRef; } break; } case kIOHIDElementTypeFeature: { if ( typeMask & kHIDElementTypeFeature ) { result = tIOHIDElementRef; } break; } case kIOHIDElementTypeCollection: { if ( typeMask & kHIDElementTypeCollection ) { result = tIOHIDElementRef; } break; } } // switch ( type ) if ( result ) { break; // DONE! } } // if ( !found ) } // next idx CFRelease( gElementCFArrayRef ); gElementCFArrayRef = NULL; } // if ( gElementCFArrayRef ) } // if ( inIOHIDDeviceRef ) } // if ( inIOHIDElementRef ) return result; } /* HIDGetNextDeviceElement */
static void __deviceCallback(void * context, IOReturn result, void * sender, IOHIDDeviceRef device) { boolean_t terminated = context == 0; CFStringRef debugString = CFCopyDescription(device); char * c_debug_string = NULL; if( debugString ){ // DG: Bluetooth "Product" strings have Unicode encoding and no nul terminator and CFStringGetCStringPtr returns NULL // Need to manually copy to C string CFIndex length = CFStringGetLength(debugString); CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, CFStringGetSystemEncoding()) + 1; c_debug_string = (char *)malloc(maxSize); CFStringGetCString(debugString, c_debug_string, maxSize, CFStringGetSystemEncoding()); CFRelease(debugString); } static CFMutableDictionaryRef s_timers = NULL; if ( !s_timers ) s_timers = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); uint64_t uuid = 0; CFNumberRef temp = IOHIDDeviceGetProperty( device, CFSTR(kIOHIDUniqueIDKey) ); if ( temp && CFGetTypeID(temp) == CFNumberGetTypeID() ) CFNumberGetValue( temp, kCFNumberLongLongType, &uuid ); CFNumberGetValue( IOHIDDeviceGetProperty( device, CFSTR(kIOHIDUniqueIDKey) ), kCFNumberLongLongType, &uuid ); printf("%-10.10s: %s UniqueID %llu\n", terminated ? "terminated" : "matched", c_debug_string ? c_debug_string : "", uuid ); if ( c_debug_string ) free(c_debug_string); if ( terminated ) { CFDictionaryRemoveValue(gOutputElements, device); CFRunLoopTimerRef timer = (CFRunLoopTimerRef)CFDictionaryGetValue(s_timers, device); if ( timer ) { CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes); } CFDictionaryRemoveValue(s_timers, device); } else { CFArrayRef outputElements = NULL; CFMutableDictionaryRef matching = NULL; if ( gPrintDescriptor ) { CFDataRef descriptor = NULL; descriptor = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDReportDescriptorKey)); if ( descriptor ) { PrintHIDDescriptor(CFDataGetBytePtr(descriptor), CFDataGetLength(descriptor)); } } if ( gPollInterval != 0.0 ) { CFRunLoopTimerContext context = {.info=device}; CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent(), gPollInterval, 0, 0, __timerCallback, &context); CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes); CFDictionaryAddValue(s_timers, device, timer); CFRelease(timer); printf("Adding polling timer @ %4.6f s\n", gPollInterval); } matching = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if ( matching ) { uint32_t value = kIOHIDElementTypeOutput; CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); if ( number ) { CFDictionarySetValue(matching, CFSTR(kIOHIDElementTypeKey), number); outputElements = IOHIDDeviceCopyMatchingElements(device, matching, 0); if ( outputElements ) { CFDictionarySetValue(gOutputElements, device, outputElements); CFRelease(outputElements); } CFRelease(number); } CFRelease(matching); } }
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) ; } // ---------------------------------------------------------------- }
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
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); } }