void HIDGamepad::valueChanged(IOHIDValueRef value) { IOHIDElementCookie cookie = IOHIDElementGetCookie(IOHIDValueGetElement(value)); HIDGamepadElement* element = m_elementMap.get(cookie); // This might be an element we don't currently handle as input so we can skip it. if (!element) return; element->rawValue = IOHIDValueGetScaledValue(value, kIOHIDValueScaleTypePhysical); if (element->isButton()) { for (unsigned i = 0; i < m_buttons.size(); ++i) { if (m_buttons[i].get() == element) m_buttonValues[i] = element->normalizedValue(); } } else if (element->isAxis()) { for (unsigned i = 0; i < m_axes.size(); ++i) { if (m_axes[i].get() == element) m_axisValues[i] = element->normalizedValue(); } } else ASSERT_NOT_REACHED(); m_lastUpdateTime = monotonicallyIncreasingTime(); }
// ************************************************************************* // // IOHIDElement_GetValue(inIOHIDElementRef, inIOHIDValueScaleType) // // Purpose: returns the current value for an element(polling) // // Notes: will return 0 on error conditions which should be accounted for by application // // Inputs: inIOHIDElementRef - the element // inIOHIDValueScaleType - scale type (calibrated or physical) // // Returns: double - current value for element // double IOHIDElement_GetValue(IOHIDElementRef inIOHIDElementRef, IOHIDValueScaleType inIOHIDValueScaleType) { double result = NAN; IOHIDValueRef tIOHIDValueRef; if (kIOReturnSuccess == IOHIDDeviceGetValue(IOHIDElementGetDevice(inIOHIDElementRef), inIOHIDElementRef, &tIOHIDValueRef)) { result = IOHIDValueGetScaledValue(tIOHIDValueRef, inIOHIDValueScaleType); } return (result); } // IOHIDElement_GetValue
HIDInputType HIDGamepad::valueChanged(IOHIDValueRef value) { IOHIDElementCookie cookie = IOHIDElementGetCookie(IOHIDValueGetElement(value)); HIDGamepadElement* element = m_elementMap.get(cookie); // This might be an element we don't currently handle as input so we can skip it. if (!element) return HIDInputType::NotAButtonPress; element->rawValue = IOHIDValueGetScaledValue(value, kIOHIDValueScaleTypePhysical); if (element->isButton()) { for (unsigned i = 0; i < m_buttons.size(); ++i) { if (&m_buttons[i].get() == element) { m_buttonValues[i] = element->normalizedValue(); break; } } } else if (element->isAxis()) { for (unsigned i = 0; i < m_axes.size(); ++i) { if (&m_axes[i].get() == element) { m_axisValues[i] = element->normalizedValue(); break; } } } else if (element->isDPad()) { int intValue = IOHIDValueGetIntegerValue(value) - element->min; for (unsigned i = 0; i < m_dPads.size(); ++i) { if (&m_dPads[i].get() != element) continue; // Each DPad represents 4 button values which are tacked on to the end of the values from non-DPad buttons. unsigned firstButtonValue = m_buttons.size() + i * 4; ASSERT(m_buttonValues.size() > firstButtonValue + 3); fillInButtonValues(intValue, m_buttonValues[firstButtonValue], m_buttonValues[firstButtonValue + 1], m_buttonValues[firstButtonValue + 2], m_buttonValues[firstButtonValue + 3]); } } else ASSERT_NOT_REACHED(); m_lastUpdateTime = monotonicallyIncreasingTime(); return element->isButton() ? HIDInputType::ButtonPress : HIDInputType::NotAButtonPress; }
int joy_hidlib_get_value(joy_hid_device_t *device, joy_hid_element_t *element, int *value, int phys) { IOHIDValueRef value_ref; IOReturn result = IOHIDDeviceGetValue( device->internal_device, element->internal_element, &value_ref ); if(result == kIOReturnSuccess) { if(phys) { *value = (int)IOHIDValueGetScaledValue( value_ref, kIOHIDValueScaleTypePhysical ); } else { *value = (int)IOHIDValueGetIntegerValue( value_ref ); } return 0; } else { return -1; } }
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
JoystickState JoystickImpl::update() { static const JoystickState disconnectedState; // return this if joystick was disconnected JoystickState state; // otherwise return that state.connected = true; // Note: free up is done in close() which is called, if required, // by the joystick manager. So we don't release buttons nor axes here. // First, let's determine if the joystick is still connected Location selfLoc = m_locationIDs[m_index]; // Get all devices CFSetRef devices = HIDJoystickManager::getInstance().copyJoysticks(); if (devices == NULL) return disconnectedState; // Get a usable copy of the joysticks devices. CFIndex joysticksCount = CFSetGetCount(devices); CFTypeRef devicesArray[joysticksCount]; CFSetGetValues(devices, devicesArray); // Search for it bool found = false; for (CFIndex i(0); i < joysticksCount; ++i) { IOHIDDeviceRef d = (IOHIDDeviceRef)devicesArray[i]; if (selfLoc == HIDInputManager::getLocationID(d)) { found = true; break; // Stop looping } } // Release unused stuff CFRelease(devices); // If not found we consider it disconnected if (!found) return disconnectedState; // Update buttons' state unsigned int i = 0; for (ButtonsVector::iterator it(m_buttons.begin()); it != m_buttons.end(); ++it, ++i) { IOHIDValueRef value = 0; IOHIDDeviceGetValue(IOHIDElementGetDevice(*it), *it, &value); // Check for plug out. if (!value) { // No value? Hum... Seems like the joystick is gone return disconnectedState; } // 1 means pressed, others mean released state.buttons[i] = IOHIDValueGetIntegerValue(value) == 1; } // Update axes' state for (AxisMap::iterator it = m_axis.begin(); it != m_axis.end(); ++it) { IOHIDValueRef value = 0; IOHIDDeviceGetValue(IOHIDElementGetDevice(it->second), it->second, &value); // Check for plug out. if (!value) { // No value? Hum... Seems like the joystick is gone return disconnectedState; } // We want to bind [physicalMin,physicalMax] to [-100=min,100=max]. // // General formula to bind [a,b] to [c,d] with a linear progression: // // f: [a, b] -> [c, d] // x |-> (x-a)(d-c)/(b-a)+c // // This method might not be very accurate (the "0 position" can be // slightly shift with some device) but we don't care because most // of devices are so sensitive that this is not relevant. double physicalMax = IOHIDElementGetPhysicalMax(it->second); double physicalMin = IOHIDElementGetPhysicalMin(it->second); double scaledMin = -100; double scaledMax = 100; double physicalValue = IOHIDValueGetScaledValue(value, kIOHIDValueScaleTypePhysical); float scaledValue = (((physicalValue - physicalMin) * (scaledMax - scaledMin)) / (physicalMax - physicalMin)) + scaledMin; state.axes[it->first] = scaledValue; } return state; }
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