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)); } }
//************************************************************************* // // HIDIsValidElement( inIOHIDElementRef ) // // Purpose: validate this element // // Inputs: inIOHIDElementRef - the element // // Returns: Boolean - TRUE if this is a valid element ref // Boolean HIDIsValidElement( IOHIDElementRef inIOHIDElementRef ) { Boolean result = FALSE; // assume failure (pessimist!) if ( inIOHIDElementRef ) { if ( CFGetTypeID( inIOHIDElementRef ) == IOHIDElementGetTypeID() ) { result = TRUE; } } return result; }
// ************************************************************************* // // HIDIsValidElement(inIOHIDElementRef) // // Purpose: validate this element // // Inputs: inIOHIDElementRef - the element // // Returns: Boolean - true if this is a valid element ref // Boolean HIDIsValidElement(IOHIDElementRef inIOHIDElementRef) { bool result = false; // assume failure (pessimist!) if (inIOHIDElementRef) { if (CFGetTypeID(inIOHIDElementRef) == IOHIDElementGetTypeID()) { result = true; } } return (result); } // HIDIsValidElement
// --------------------------------- // removes element for queue, if last element in queue will release queue and closes device interface int HIDDequeueElement( IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef ) { IOReturn result = kIOReturnSuccess; if ( inIOHIDDeviceRef ) { assert( IOHIDDeviceGetTypeID() == CFGetTypeID( inIOHIDDeviceRef ) ); if ( inIOHIDElementRef ) { assert( IOHIDElementGetTypeID() == CFGetTypeID( inIOHIDElementRef ) ); IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue( inIOHIDDeviceRef ); if ( tIOHIDQueueRef ) { // stop queue IOHIDQueueStop( tIOHIDQueueRef ); // de-queue element if ( IOHIDQueueContainsElement( tIOHIDQueueRef, inIOHIDElementRef ) ) { IOHIDQueueRemoveElement( tIOHIDQueueRef, inIOHIDElementRef ); } // release device queue and close interface if queue empty if ( HIDIsDeviceQueueEmpty( inIOHIDDeviceRef ) ) { result = HIDDisposeReleaseQueue( inIOHIDDeviceRef ); if ( kIOReturnSuccess != result ) { HIDReportErrorNum( "Failed to dispose and release queue.", result ); } } else { // not empty so restart queue IOHIDQueueStart( tIOHIDQueueRef ); } } else { HIDReportError( "No queue for device passed to HIDDequeueElement." ); if ( kIOReturnSuccess == result ) { result = kIOReturnError; } } } else { HIDReportError( "NULL element passed to HIDDequeueElement." ); result = kIOReturnBadArgument; } } else { HIDReportError( "NULL device passed to HIDDequeueElement." ); result = kIOReturnBadArgument; } return result; } /* HIDDequeueElement */
// queues specific element, performing any device queue set up required // queue is started and ready to return events on exit from this function int HIDQueueElement( IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef ) { IOReturn result = kIOReturnSuccess; if ( inIOHIDDeviceRef ) { assert( IOHIDDeviceGetTypeID() == CFGetTypeID( inIOHIDDeviceRef ) ); if ( inIOHIDElementRef ) { assert( IOHIDElementGetTypeID() == CFGetTypeID( inIOHIDElementRef ) ); IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue( inIOHIDDeviceRef ); if ( !tIOHIDQueueRef ) { // if no queue create queue result = HIDCreateQueue( inIOHIDDeviceRef ); if ( kIOReturnSuccess == result ) { tIOHIDQueueRef = IOHIDDevice_GetQueue( inIOHIDDeviceRef ); } } if ( tIOHIDQueueRef ) { // stop queue IOHIDQueueStop( tIOHIDQueueRef ); // queue element if ( !IOHIDQueueContainsElement( tIOHIDQueueRef, inIOHIDElementRef ) ) { IOHIDQueueAddElement( tIOHIDQueueRef, inIOHIDElementRef ); } // restart queue IOHIDQueueStart( tIOHIDQueueRef ); } else { HIDReportError( "No queue for device passed to HIDQueueElement." ); if ( kIOReturnSuccess == result ) { result = kIOReturnError; } } } else { HIDReportError( "NULL element passed to HIDQueueElement." ); result = kIOReturnBadArgument; } } else { HIDReportError( "NULL device passed to HIDQueueElement." ); result = kIOReturnBadArgument; } return result; } /* HIDQueueElement */
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>(); } } }
/* 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++; } } }
// 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 */