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; }
/* PsychHIDCountCollectionElements() Non-recursively count all elements of a collection which are of the specified type. HID element records hold three pointers to other element records: pPrevious, pChild and pSibling. PsychHIDCountCollectionElements() operates on the theory that the members of a collection are its child and all of that child's siblings. */ int PsychHIDCountCollectionElements(pRecElement collectionRecord, HIDElementTypeMask elementTypeMask) { pRecElement currentElement; int numElements = 0; CFIndex i, nmax; HIDElementTypeMask currentElementMaskValue; #ifdef __LP64__ CFArrayRef children = IOHIDElementGetChildren(collectionRecord); nmax = CFArrayGetCount(children); for (i = 0 ; i < nmax; i++) { currentElement = (pRecElement) CFArrayGetValueAtIndex(children, i); currentElementMaskValue = HIDConvertElementTypeToMask(IOHIDElementGetType(currentElement)); if(currentElementMaskValue & elementTypeMask) ++numElements; } #else for(currentElement=collectionRecord->pChild; currentElement != NULL; currentElement= currentElement->pSibling) { currentElementMaskValue=HIDConvertElementTypeToMask(currentElement->type); if(currentElementMaskValue & elementTypeMask) ++numElements; } #endif return(numElements); }
/* FindCollectionElements() Non-recursively return of a list of a collection's memember elements. HID element records hold three pointers to other element records: pPrevious, pChild and pSibling. FindCollectionElements() operates on the theory that the members of a collection are its child and all of that child's siblings. */ int PsychHIDFindCollectionElements(pRecElement collectionRecord, HIDElementTypeMask elementTypeMask, pRecElement *collectionMembers, int maxListElements) { pRecElement currentElement; int numElements = 0; CFIndex i, nmax; HIDElementTypeMask currentElementMaskValue; #ifdef __LP64__ CFArrayRef children = IOHIDElementGetChildren(collectionRecord); nmax = CFArrayGetCount(children); for (i = 0 ; i < nmax; i++) { currentElement = (pRecElement) CFArrayGetValueAtIndex(children, i); currentElementMaskValue = HIDConvertElementTypeToMask(IOHIDElementGetType(currentElement)); if(currentElementMaskValue & elementTypeMask) { if(numElements == maxListElements) PsychErrorExitMsg(PsychError_internal, "Number of collection elements exceeds allocated storage space." ); collectionMembers[numElements] = currentElement; ++numElements; } } #else for(currentElement=collectionRecord->pChild; currentElement != NULL; currentElement= currentElement->pSibling) { currentElementMaskValue=HIDConvertElementTypeToMask(currentElement->type); if(currentElementMaskValue & elementTypeMask){ if(numElements == maxListElements) PsychErrorExitMsg(PsychError_internal, "Number of collection elements exceeds allocated storage space." ); collectionMembers[numElements]=currentElement; ++numElements; } } #endif return(numElements); }
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; }
// NOTE: I pieced this together through trial and error, any corrections are welcome static void hid_device_input_callback(void* context, IOReturn result, void* sender, IOHIDValueRef value) { struct apple_pad_connection* connection = (struct apple_pad_connection*)context; IOHIDElementRef element = IOHIDValueGetElement(value); uint32_t type = IOHIDElementGetType(element); uint32_t page = IOHIDElementGetUsagePage(element); uint32_t use = IOHIDElementGetUsage(element); // Joystick handler: TODO: Can GamePad work the same? if (type == kIOHIDElementTypeInput_Button && page == kHIDPage_Button) { CFIndex state = IOHIDValueGetIntegerValue(value); if (state) g_current_input_data.pad_buttons[connection->slot] |= (1 << (use - 1)); else g_current_input_data.pad_buttons[connection->slot] &= ~(1 << (use - 1)); } else if (type == kIOHIDElementTypeInput_Misc && page == kHIDPage_GenericDesktop) { static const uint32_t axis_use_ids[4] = { 48, 49, 50, 53 }; for (int i = 0; i < 4; i ++) { if (use == axis_use_ids[i]) { CFIndex min = IOHIDElementGetPhysicalMin(element); CFIndex max = IOHIDElementGetPhysicalMax(element) - min; CFIndex state = IOHIDValueGetIntegerValue(value) - min; float val = (float)state / (float)max; g_current_input_data.pad_axis[connection->slot][i] = ((val * 2.0f) - 1.0f) * 32767.0f; } } } }
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)); } }
/* PsychHIDCountCollectionElements() Non-recursively count all elements of a collection which are of the specified type. HID element records hold three pointers to other element records: pPrevious, pChild and pSibling. PsychHIDCountCollectionElements() operates on the theory that the members of a collection are its child and all of that child's siblings. */ int PsychHIDCountCollectionElements(pRecElement collectionRecord, HIDElementTypeMask elementTypeMask) { pRecElement currentElement; int numElements = 0; CFIndex i, nmax; HIDElementTypeMask currentElementMaskValue; CFArrayRef children = IOHIDElementGetChildren(collectionRecord); nmax = CFArrayGetCount(children); for (i = 0; i < nmax; i++) { currentElement = (pRecElement)CFArrayGetValueAtIndex(children, i); currentElementMaskValue = HIDConvertElementTypeToMask(IOHIDElementGetType(currentElement)); if (currentElementMaskValue & elementTypeMask) ++numElements; } return numElements; }
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; }
/************************************************************************** * 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; }
static void get_element_children(IOHIDElementRef tElement, CFArrayRef childElements) { CFIndex idx, cnt; CFArrayRef tElementChildrenArray = IOHIDElementGetChildren(tElement); cnt = CFArrayGetCount(tElementChildrenArray); if (cnt < 1) return; /* Either add the element to the array or grab its children */ for (idx=0; idx<cnt; idx++) { IOHIDElementRef tChildElementRef; tChildElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(tElementChildrenArray, idx); if (IOHIDElementGetType(tChildElementRef) == kIOHIDElementTypeCollection) get_element_children(tChildElementRef, childElements); else CFArrayAppendValue((CFMutableArrayRef)childElements, tChildElementRef); } }
void CInputProviderMacOsHid::SetInitialBindValues(IOHIDDeviceRef device) { CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, nullptr, 0); for(int i = 0; i < CFArrayGetCount(elements); i++) { IOHIDElementRef elementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i); uint32 usagePage = IOHIDElementGetUsagePage(elementRef); if( (usagePage != kHIDPage_GenericDesktop) && (usagePage != kHIDPage_Button)) { continue; } IOHIDValueRef valueRef; if(IOHIDDeviceGetValue(device, elementRef, &valueRef) != kIOReturnSuccess) { continue; } CFIndex value = IOHIDValueGetIntegerValue(valueRef); IOHIDElementType type = IOHIDElementGetType(elementRef); uint32 usage = IOHIDElementGetUsage(elementRef); BINDINGTARGET tgt; tgt.providerId = PROVIDER_ID; tgt.deviceId = GetDeviceID(device); tgt.keyId = usage; tgt.keyType = GetKeyType(usage, type); switch(type) { case kIOHIDElementTypeInput_Misc: case kIOHIDElementTypeInput_Button: case kIOHIDElementTypeInput_Axis: OnInput(tgt, value); break; default: break; } } }
void CInputProviderMacOsHid::InputValueCallback(DEVICE_INFO* deviceInfo, IOReturn result, void* sender, IOHIDValueRef valueRef) { if(!OnInput) return; IOHIDElementRef elementRef = IOHIDValueGetElement(valueRef); uint32 usagePage = IOHIDElementGetUsagePage(elementRef); if( (usagePage != kHIDPage_GenericDesktop) && (usagePage != kHIDPage_Button)) { return; } uint32 usage = IOHIDElementGetUsage(elementRef); CFIndex value = IOHIDValueGetIntegerValue(valueRef); IOHIDElementType type = IOHIDElementGetType(elementRef); BINDINGTARGET tgt; tgt.providerId = PROVIDER_ID; tgt.deviceId = deviceInfo->deviceId; tgt.keyId = usage; tgt.keyType = GetKeyType(usage, type); OnInput(tgt, value); }
/* See if we care about this HID element, and if so, note it in our recDevice. */ static void AddHIDElement(const void *value, void *parameter) { recDevice *pDevice = (recDevice *) parameter; IOHIDElementRef refElement = (IOHIDElementRef) value; const CFTypeID elementTypeID = refElement ? CFGetTypeID(refElement) : 0; if (refElement && (elementTypeID == IOHIDElementGetTypeID())) { const IOHIDElementCookie cookie = IOHIDElementGetCookie(refElement); const uint32_t usagePage = IOHIDElementGetUsagePage(refElement); const uint32_t usage = IOHIDElementGetUsage(refElement); recElement *element = NULL; recElement **headElement = NULL; /* look at types of interest */ switch (IOHIDElementGetType(refElement)) { case kIOHIDElementTypeInput_Misc: case kIOHIDElementTypeInput_Button: case kIOHIDElementTypeInput_Axis: { switch (usagePage) { /* only interested in kHIDPage_GenericDesktop and kHIDPage_Button */ case kHIDPage_GenericDesktop: switch (usage) { case kHIDUsage_GD_X: case kHIDUsage_GD_Y: case kHIDUsage_GD_Z: case kHIDUsage_GD_Rx: case kHIDUsage_GD_Ry: case kHIDUsage_GD_Rz: case kHIDUsage_GD_Slider: case kHIDUsage_GD_Dial: case kHIDUsage_GD_Wheel: if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) { element = (recElement *) SDL_calloc(1, sizeof (recElement)); if (element) { pDevice->axes++; headElement = &(pDevice->firstAxis); } } break; case kHIDUsage_GD_Hatswitch: if (!ElementAlreadyAdded(cookie, pDevice->firstHat)) { element = (recElement *) SDL_calloc(1, sizeof (recElement)); if (element) { pDevice->hats++; headElement = &(pDevice->firstHat); } } break; } break; case kHIDPage_Simulation: switch (usage) { case kHIDUsage_Sim_Rudder: case kHIDUsage_Sim_Throttle: if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) { element = (recElement *) SDL_calloc(1, sizeof (recElement)); if (element) { pDevice->axes++; headElement = &(pDevice->firstAxis); } } break; default: break; } break; case kHIDPage_Button: if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) { element = (recElement *) SDL_calloc(1, sizeof (recElement)); if (element) { pDevice->buttons++; headElement = &(pDevice->firstButton); } } break; default: break; } } break; case kIOHIDElementTypeCollection: { CFArrayRef array = IOHIDElementGetChildren(refElement); if (array) { AddHIDElements(array, pDevice); } } break; default: break; } if (element && headElement) { /* add to list */ recElement *elementPrevious = NULL; recElement *elementCurrent = *headElement; while (elementCurrent && usage >= elementCurrent->usage) { elementPrevious = elementCurrent; elementCurrent = elementCurrent->pNext; } if (elementPrevious) { elementPrevious->pNext = element; } else { *headElement = element; } element->elementRef = refElement; element->usagePage = usagePage; element->usage = usage; element->pNext = elementCurrent; element->minReport = element->min = (SInt32) IOHIDElementGetLogicalMin(refElement); element->maxReport = element->max = (SInt32) IOHIDElementGetLogicalMax(refElement); element->cookie = IOHIDElementGetCookie(refElement); pDevice->elements++; } } }
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; }
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
// 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
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; }
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 apple_hid_device_input_callback(void *data, IOReturn result, void* sender, IOHIDValueRef value) { driver_t *driver = driver_get_ptr(); apple_input_data_t *apple = (apple_input_data_t*)driver->input_data; struct apple_hid_adapter *adapter = (struct apple_hid_adapter*)data; IOHIDElementRef element = IOHIDValueGetElement(value); uint32_t type = IOHIDElementGetType(element); uint32_t page = IOHIDElementGetUsagePage(element); uint32_t use = IOHIDElementGetUsage(element); if (type != kIOHIDElementTypeInput_Misc) if (type != kIOHIDElementTypeInput_Button) if (type != kIOHIDElementTypeInput_Axis) return; /* Joystick handler. * TODO: Can GamePad work the same? */ switch (page) { case kHIDPage_GenericDesktop: switch (type) { case kIOHIDElementTypeInput_Misc: switch (use) { case kHIDUsage_GD_Hatswitch: break; default: { int i; static const uint32_t axis_use_ids[4] = { 48, 49, 50, 53 }; for (i = 0; i < 4; i ++) { CFIndex min = IOHIDElementGetPhysicalMin(element); CFIndex max = IOHIDElementGetPhysicalMax(element) - min; CFIndex state = IOHIDValueGetIntegerValue(value) - min; float val = (float)state / (float)max; if (use != axis_use_ids[i]) continue; apple->axes[adapter->slot][i] = ((val * 2.0f) - 1.0f) * 32767.0f; } } break; } break; } break; case kHIDPage_Button: switch (type) { case kIOHIDElementTypeInput_Button: { CFIndex state = IOHIDValueGetIntegerValue(value); unsigned id = use - 1; if (state) BIT64_SET(apple->buttons[adapter->slot], id); else BIT64_CLEAR(apple->buttons[adapter->slot], id); } break; } break; } }
static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface) { JoystickImpl *device = (JoystickImpl*)iface; IOHIDElementRef tIOHIDTopElementRef; IOHIDDeviceRef tIOHIDDeviceRef; CFArrayRef gElementCFArrayRef = device->elementCFArrayRef; TRACE("polling device %i\n",device->id); if (!gCollections) return; tIOHIDTopElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gCollections, device->id); tIOHIDDeviceRef = IOHIDElementGetDevice(tIOHIDTopElementRef); if (!tIOHIDDeviceRef) return; if (gElementCFArrayRef) { int button_idx = 0; int pov_idx = 0; int slider_idx = 0; CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef ); for ( idx = 0; idx < cnt; idx++ ) { IOHIDValueRef valueRef; int val; IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx ); int eleType = IOHIDElementGetType( tIOHIDElementRef ); switch(eleType) { case kIOHIDElementTypeInput_Button: if(button_idx < 128) { IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef); val = IOHIDValueGetIntegerValue(valueRef); device->generic.js.rgbButtons[button_idx] = val ? 0x80 : 0x00; button_idx ++; } break; case kIOHIDElementTypeInput_Misc: { uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef ); switch(usage) { case kHIDUsage_GD_Hatswitch: { IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef); val = IOHIDValueGetIntegerValue(valueRef); if (val >= 8) device->generic.js.rgdwPOV[pov_idx] = -1; else device->generic.js.rgdwPOV[pov_idx] = val * 4500; pov_idx ++; break; } case kHIDUsage_GD_X: case kHIDUsage_GD_Y: case kHIDUsage_GD_Z: case kHIDUsage_GD_Rx: case kHIDUsage_GD_Ry: case kHIDUsage_GD_Rz: case kHIDUsage_GD_Slider: { IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef); val = IOHIDValueGetIntegerValue(valueRef); switch (usage) { case kHIDUsage_GD_X: device->generic.js.lX = joystick_map_axis(&device->generic.props[idx], val); break; case kHIDUsage_GD_Y: device->generic.js.lY = joystick_map_axis(&device->generic.props[idx], val); break; case kHIDUsage_GD_Z: device->generic.js.lZ = joystick_map_axis(&device->generic.props[idx], val); break; case kHIDUsage_GD_Rx: device->generic.js.lRx = joystick_map_axis(&device->generic.props[idx], val); break; case kHIDUsage_GD_Ry: device->generic.js.lRy = joystick_map_axis(&device->generic.props[idx], val); break; case kHIDUsage_GD_Rz: device->generic.js.lRz = joystick_map_axis(&device->generic.props[idx], val); break; case kHIDUsage_GD_Slider: device->generic.js.rglSlider[slider_idx] = joystick_map_axis(&device->generic.props[idx], val); slider_idx ++; break; } break; } default: FIXME("unhandled usage %i\n",usage); } break; } default: FIXME("Unhandled type %i\n",eleType); } } } }
/************************************************************************** * collect_joystick_elements */ static void collect_joystick_elements(joystick_t* joystick, IOHIDElementRef collection) { CFIndex i, count; CFArrayRef children = IOHIDElementGetChildren(collection); TRACE("collection %s\n", debugstr_element(collection)); count = CFArrayGetCount(children); for (i = 0; i < count; i++) { IOHIDElementRef child; int type; child = (IOHIDElementRef)CFArrayGetValueAtIndex(children, i); TRACE("child %s\n", debugstr_element(child)); type = IOHIDElementGetType(child); switch (type) { case kIOHIDElementTypeCollection: collect_joystick_elements(joystick, child); break; case kIOHIDElementTypeInput_Button: { int usage_page = IOHIDElementGetUsagePage(child); TRACE("kIOHIDElementTypeInput_Button usage_page %d\n", usage_page); /* avoid strange elements found on the 360 controller */ if (usage_page == kHIDPage_Button) CFArrayAppendValue(joystick->buttons, child); break; } case kIOHIDElementTypeInput_Axis: { TRACE("kIOHIDElementTypeInput_Axis; ignoring\n"); break; } case kIOHIDElementTypeInput_Misc: { uint32_t usage = IOHIDElementGetUsage( child ); switch(usage) { case kHIDUsage_GD_Hatswitch: { TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n"); if (joystick->hatswitch) TRACE(" ignoring additional hatswitch\n"); else joystick->hatswitch = (IOHIDElementRef)CFRetain(child); break; } case kHIDUsage_GD_X: case kHIDUsage_GD_Y: case kHIDUsage_GD_Z: case kHIDUsage_GD_Rx: case kHIDUsage_GD_Ry: case kHIDUsage_GD_Rz: { int axis = axis_for_usage(usage); TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_<axis> (%d) axis %d\n", usage, axis); if (axis < 0 || joystick->axes[axis].element) TRACE(" ignoring\n"); else { joystick->axes[axis].element = (IOHIDElementRef)CFRetain(child); joystick->axes[axis].min_value = IOHIDElementGetLogicalMin(child); joystick->axes[axis].max_value = IOHIDElementGetLogicalMax(child); } break; } case kHIDUsage_GD_Slider: TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Slider; ignoring\n"); break; default: FIXME("kIOHIDElementTypeInput_Misc / Unhandled usage %d\n", usage); break; } break; } default: FIXME("Unhandled type %i\n",type); break; } } }
// ************************************************************************* // // 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
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
// 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 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; }
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 get_osx_device_elements(JoystickImpl *device, int axis_map[8]) { IOHIDElementRef tIOHIDElementRef; CFArrayRef gElementCFArrayRef; DWORD axes = 0; DWORD sliders = 0; DWORD buttons = 0; DWORD povs = 0; device->elementCFArrayRef = NULL; if (!gCollections) return; tIOHIDElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(gCollections, device->id); if (!tIOHIDElementRef) return; gElementCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); get_element_children(tIOHIDElementRef, gElementCFArrayRef); if (gElementCFArrayRef) { CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef ); /* build our element array in the order that dinput expects */ device->elementCFArrayRef = CFArrayCreateMutable(NULL,0,NULL); for ( idx = 0; idx < cnt; idx++ ) { IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx ); int eleType = IOHIDElementGetType( tIOHIDElementRef ); switch(eleType) { case kIOHIDElementTypeInput_Button: { int usagePage = IOHIDElementGetUsagePage( tIOHIDElementRef ); if (usagePage != kHIDPage_Button) { /* avoid strange elements found on the 360 controller */ continue; } if (buttons < 128) { CFArrayInsertValueAtIndex(device->elementCFArrayRef, (axes+povs+buttons), tIOHIDElementRef); buttons++; } break; } case kIOHIDElementTypeInput_Axis: { CFArrayInsertValueAtIndex(device->elementCFArrayRef, axes, tIOHIDElementRef); axes++; break; } case kIOHIDElementTypeInput_Misc: { uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef ); switch(usage) { case kHIDUsage_GD_Hatswitch: { CFArrayInsertValueAtIndex(device->elementCFArrayRef, (axes+povs), tIOHIDElementRef); povs++; break; } case kHIDUsage_GD_Slider: sliders ++; if (sliders > 2) break; /* fallthrough, sliders are axis */ case kHIDUsage_GD_X: case kHIDUsage_GD_Y: case kHIDUsage_GD_Z: case kHIDUsage_GD_Rx: case kHIDUsage_GD_Ry: case kHIDUsage_GD_Rz: { CFArrayInsertValueAtIndex(device->elementCFArrayRef, axes, tIOHIDElementRef); axis_map[axes]=usage; axes++; break; } default: FIXME("Unhandled usage %i\n",usage); } break; } default: FIXME("Unhandled type %i\n",eleType); } } } device->generic.devcaps.dwAxes = axes; device->generic.devcaps.dwButtons = buttons; device->generic.devcaps.dwPOVs = povs; /* Sort buttons into correct order */ for (buttons = 0; buttons < device->generic.devcaps.dwButtons; buttons++) { IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( device->elementCFArrayRef, axes+povs+buttons); uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef ); usage --; /* usage is 1 indexed we need 0 indexed */ if (usage == buttons) continue; insert_sort_button(axes+povs, tIOHIDElementRef, device->elementCFArrayRef,buttons,usage); } }
static const char* debugstr_element(IOHIDElementRef element) { return wine_dbg_sprintf("<IOHIDElement %p type %d usage %u/%u device %p>", element, IOHIDElementGetType(element), IOHIDElementGetUsagePage(element), IOHIDElementGetUsage(element), IOHIDElementGetDevice(element)); }
//int main( int argc, const char * argv[] ) int manipulate_led( int which_led, int led_value ) { #pragma unused ( argc, argv ) IOHIDDeviceRef * tIOHIDDeviceRefs = nil; // create a IO HID Manager reference IOHIDManagerRef tIOHIDManagerRef = IOHIDManagerCreate( kCFAllocatorDefault, kIOHIDOptionsTypeNone ); require( tIOHIDManagerRef, Oops ); // Create a device matching dictionary CFDictionaryRef matchingCFDictRef = hu_CreateMatchingDictionaryUsagePageUsage( TRUE, kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard ); require( matchingCFDictRef, Oops ); // set the HID device matching dictionary IOHIDManagerSetDeviceMatching( tIOHIDManagerRef, matchingCFDictRef ); if ( matchingCFDictRef ) { CFRelease( matchingCFDictRef ); } // Now open the IO HID Manager reference IOReturn tIOReturn = IOHIDManagerOpen( tIOHIDManagerRef, kIOHIDOptionsTypeNone ); require_noerr( tIOReturn, Oops ); // and copy out its devices CFSetRef deviceCFSetRef = IOHIDManagerCopyDevices( tIOHIDManagerRef ); require( deviceCFSetRef, Oops ); // how many devices in the set? CFIndex deviceIndex, deviceCount = CFSetGetCount( deviceCFSetRef ); // allocate a block of memory to extact the device ref's from the set into tIOHIDDeviceRefs = malloc( sizeof( IOHIDDeviceRef ) * deviceCount ); if (!tIOHIDDeviceRefs) { CFRelease(deviceCFSetRef); deviceCFSetRef = NULL; goto Oops; } // now extract the device ref's from the set CFSetGetValues( deviceCFSetRef, (const void **) tIOHIDDeviceRefs ); CFRelease(deviceCFSetRef); deviceCFSetRef = NULL; // before we get into the device loop we'll setup our element matching dictionary matchingCFDictRef = hu_CreateMatchingDictionaryUsagePageUsage( FALSE, kHIDPage_LEDs, 0 ); require( matchingCFDictRef, Oops ); int pass; // do 256 passes //for ( pass = 0; pass < 256; pass++ ) { Boolean delayFlag = FALSE; // if we find an LED element we'll set this to TRUE //printf( "pass = %d.\n", pass ); for ( deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++ ) { // if this isn't a keyboard device... if ( !IOHIDDeviceConformsTo( tIOHIDDeviceRefs[deviceIndex], kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard ) ) { continue; // ...skip it } //printf( " device = %p.\n", tIOHIDDeviceRefs[deviceIndex] ); // copy all the elements CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements( tIOHIDDeviceRefs[deviceIndex], matchingCFDictRef, kIOHIDOptionsTypeNone ); require( elementCFArrayRef, next_device ); // for each device on the system these values are divided by the value ranges of all LED elements found // for example, if the first four LED element have a range of 0-1 then the four least significant bits of // this value will be sent to these first four LED elements, etc. int device_value = pass; // iterate over all the elements CFIndex elementIndex, elementCount = CFArrayGetCount( elementCFArrayRef ); for ( elementIndex = 0; elementIndex < elementCount; elementIndex++ ) { IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( elementCFArrayRef, elementIndex ); require( tIOHIDElementRef, next_element ); uint32_t usagePage = IOHIDElementGetUsagePage( tIOHIDElementRef ); // if this isn't an LED element... if ( kHIDPage_LEDs != usagePage ) { continue; // ...skip it } uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef ); IOHIDElementType tIOHIDElementType = IOHIDElementGetType( tIOHIDElementRef ); //printf( " element = %p (page: %d, usage: %d, type: %d ).\n", // tIOHIDElementRef, usagePage, usage, tIOHIDElementType ); // get the logical mix/max for this LED element CFIndex minCFIndex = IOHIDElementGetLogicalMin( tIOHIDElementRef ); CFIndex maxCFIndex = IOHIDElementGetLogicalMax( tIOHIDElementRef ); // calculate the range CFIndex modCFIndex = maxCFIndex - minCFIndex + 1; // compute the value for this LED element //CFIndex tCFIndex = minCFIndex + ( device_value % modCFIndex ); CFIndex tCFIndex = led_value; device_value /= modCFIndex; //printf( " value = 0x%08lX.\n", tCFIndex ); uint64_t timestamp = 0; // create the IO HID Value to be sent to this LED element IOHIDValueRef tIOHIDValueRef = IOHIDValueCreateWithIntegerValue( kCFAllocatorDefault, tIOHIDElementRef, timestamp, tCFIndex ); if ( tIOHIDValueRef ) { // now set it on the device tIOReturn = IOHIDDeviceSetValue( tIOHIDDeviceRefs[deviceIndex], tIOHIDElementRef, tIOHIDValueRef ); CFRelease( tIOHIDValueRef ); require_noerr( tIOReturn, next_element ); delayFlag = TRUE; // set this TRUE so we'll delay before changing our LED values again } next_element: ; continue; } CFRelease( elementCFArrayRef ); next_device: ; continue; } // if we found an LED we'll delay before continuing //if ( delayFlag ) { // usleep( 500000 ); // sleep one half second //} // if the mouse is down… //if (GetCurrentButtonState()) { // break; // abort pass loop //} //} // next pass if ( matchingCFDictRef ) { CFRelease( matchingCFDictRef ); } Oops: ; if ( tIOHIDDeviceRefs ) { free(tIOHIDDeviceRefs); } if ( tIOHIDManagerRef ) { CFRelease( tIOHIDManagerRef ); } return 0; } /* main */
static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface) { JoystickImpl *device = impl_from_IDirectInputDevice8A(iface); IOHIDElementRef tIOHIDTopElementRef; IOHIDDeviceRef tIOHIDDeviceRef; CFArrayRef gElementCFArrayRef = device->elementCFArrayRef; TRACE("polling device %i\n",device->id); if (!gCollections) return; tIOHIDTopElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gCollections, device->id); tIOHIDDeviceRef = IOHIDElementGetDevice(tIOHIDTopElementRef); if (!tIOHIDDeviceRef) return; if (gElementCFArrayRef) { int button_idx = 0; int pov_idx = 0; int slider_idx = 0; int inst_id; CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef ); for ( idx = 0; idx < cnt; idx++ ) { IOHIDValueRef valueRef; int val, oldVal, newVal; IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx ); int eleType = IOHIDElementGetType( tIOHIDElementRef ); switch(eleType) { case kIOHIDElementTypeInput_Button: if(button_idx < 128) { IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef); val = IOHIDValueGetIntegerValue(valueRef); newVal = val ? 0x80 : 0x0; oldVal = device->generic.js.rgbButtons[button_idx]; device->generic.js.rgbButtons[button_idx] = newVal; if (oldVal != newVal) { inst_id = DIDFT_MAKEINSTANCE(button_idx) | DIDFT_PSHBUTTON; queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++); } button_idx ++; } break; case kIOHIDElementTypeInput_Misc: { uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef ); switch(usage) { case kHIDUsage_GD_Hatswitch: { IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef); val = IOHIDValueGetIntegerValue(valueRef); oldVal = device->generic.js.rgdwPOV[pov_idx]; if (val >= 8) newVal = -1; else newVal = val * 4500; device->generic.js.rgdwPOV[pov_idx] = newVal; if (oldVal != newVal) { inst_id = DIDFT_MAKEINSTANCE(pov_idx) | DIDFT_POV; queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++); } pov_idx ++; break; } case kHIDUsage_GD_X: case kHIDUsage_GD_Y: case kHIDUsage_GD_Z: case kHIDUsage_GD_Rx: case kHIDUsage_GD_Ry: case kHIDUsage_GD_Rz: case kHIDUsage_GD_Slider: { int wine_obj = -1; IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef); val = IOHIDValueGetIntegerValue(valueRef); newVal = joystick_map_axis(&device->generic.props[idx], val); switch (usage) { case kHIDUsage_GD_X: wine_obj = 0; oldVal = device->generic.js.lX; device->generic.js.lX = newVal; break; case kHIDUsage_GD_Y: wine_obj = 1; oldVal = device->generic.js.lY; device->generic.js.lY = newVal; break; case kHIDUsage_GD_Z: wine_obj = 2; oldVal = device->generic.js.lZ; device->generic.js.lZ = newVal; break; case kHIDUsage_GD_Rx: wine_obj = 3; oldVal = device->generic.js.lRx; device->generic.js.lRx = newVal; break; case kHIDUsage_GD_Ry: wine_obj = 4; oldVal = device->generic.js.lRy; device->generic.js.lRy = newVal; break; case kHIDUsage_GD_Rz: wine_obj = 5; oldVal = device->generic.js.lRz; device->generic.js.lRz = newVal; break; case kHIDUsage_GD_Slider: wine_obj = 6 + slider_idx; oldVal = device->generic.js.rglSlider[slider_idx]; device->generic.js.rglSlider[slider_idx] = newVal; slider_idx ++; break; } if ((wine_obj != -1) && (oldVal != newVal)) { inst_id = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS; queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++); } break; } default: FIXME("unhandled usage %i\n",usage); } break; } default: FIXME("Unhandled type %i\n",eleType); } } } }