// // HIDAppDelegate.h // HIDcontrol // #import #import "HID_Utilities_External.h" typedef struct action_struct { IOHIDDeviceRef fDeviceRef; IOHIDElementRef fElementRef; double fValue; double fOldValue; } action_rec; enum {kActionXAxis, kActionYAxis, kActionThrust, kActionFire, kActionStart}; #define kNumActions 5 @interface HIDAppDelegate : NSObject { action_rec actionRecs[5]; } @property (assign) IBOutlet NSWindow *window; - (void) initHID ; - (double) calibrateElementValue: (IOHIDValueRef) inIOHIDValueRef; - (void) inputValueResult: (IOReturn) inResult sender: (void *) inSender value: (IOHIDValueRef) inIOHIDValueRef; - (void) termHID; @end #import "HIDAppDelegate.h" @implementation HIDAppDelegate - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { // Insert code here to initialize your application [self initHID]; } - (void) initHID { // create the manager gIOHIDManagerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); // No IOHIDManagerSetDeviceMatching, because it is called in HIDBuildDeviceList. // Register HID device matching and removal callback IOHIDManagerRegisterDeviceMatchingCallback(gIOHIDManagerRef, Handle_DeviceMatchingCallback, (__bridge void *)self); IOHIDManagerRegisterDeviceRemovalCallback(gIOHIDManagerRef, Handle_DeviceRemovalCallback, (__bridge void *)self); // schedule with runloop IOHIDManagerScheduleWithRunLoop(gIOHIDManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); // open it IOHIDManagerOpen(gIOHIDManagerRef, kIOHIDOptionsTypeNone); // Inside HIDBuildDeviceList, IOHIDManagerDeviceMathing is called and matched. // gDeviceCFArrayRef is also created by using IOHIDManagerCopyDevices (in HIDRebuildDevices) HIDBuildDeviceList(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick); // No IOMIDManagerRegisterInputValueCallback, because IOHIDDeviceRegisterValueCallback is defined. // by using gDeviceCFArrayRef, 1 device will selected by using HIDGetFirstDevice // tIOHIDDeviceRef shows the device. IOHIDDeviceRef tIOHIDDeviceRef = NULL; tIOHIDDeviceRef = HIDGetFirstDevice(); // The device is opend. IOHIDDeviceOpen(tIOHIDDeviceRef, kIOHIDOptionsTypeNone); // NSLog(@"tIOHIDDeviceRef:%@", tIOHIDDeviceRef); // No IOHIDDeviceCopyMatchingElement, because it is called in HIDFindDeviceAndElement // Device and Element Matching HID_info_rec searchHIDInfo; long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); long locID = IOHIDDevice_GetLocationID(tIOHIDDeviceRef); // uint32_t usagePage = IOHIDDevice_GetPrimaryUsagePage(tIOHIDDeviceRef); // uint32_t usage = IOHIDDevice_GetPrimaryUsage(tIOHIDDeviceRef); // X AXIS searchHIDInfo.device.vendorID = (uint32_t)vendorID; searchHIDInfo.device.productID = (uint32_t)productID; searchHIDInfo.device.locID = (uint32_t)locID; searchHIDInfo.device.usagePage = 0x01; searchHIDInfo.device.usage = 0x04; searchHIDInfo.element.usagePage = 0x01; searchHIDInfo.element.usage = 0x30; searchHIDInfo.element.cookie = 0x11; IOHIDDeviceRef outIOHIDDeviceRef = NULL; IOHIDElementRef outIOHIDElementRef = NULL; HIDFindDeviceAndElement(&searchHIDInfo, &outIOHIDDeviceRef, &outIOHIDElementRef); // if the calibration parameters haven't been set yet… double_t granularity = IOHIDElement_GetCalibrationGranularity(outIOHIDElementRef); if (granularity < 0) { // … do it now HIDSetupElementCalibration(outIOHIDElementRef); } IOHIDValueRef tIOHIDValueRef; if (kIOReturnSuccess == IOHIDDeviceGetValue(IOHIDElementGetDevice(outIOHIDElementRef), outIOHIDElementRef, &tIOHIDValueRef)) { actionRecs[kActionXAxis].fValue = [self calibrateElementValue:tIOHIDValueRef]; actionRecs[kActionXAxis].fOldValue = actionRecs[kActionXAxis].fValue; } actionRecs[kActionXAxis].fDeviceRef = outIOHIDDeviceRef; actionRecs[kActionXAxis].fElementRef = outIOHIDElementRef; // YAXIS searchHIDInfo.element.usagePage = 0x01; searchHIDInfo.element.usage = 0x31; searchHIDInfo.element.cookie = 0x12; HIDFindDeviceAndElement(&searchHIDInfo, &outIOHIDDeviceRef, &outIOHIDElementRef); // if the calibration parameters haven't been set yet… granularity = IOHIDElement_GetCalibrationGranularity(outIOHIDElementRef); if (granularity < 0) { // … do it now HIDSetupElementCalibration(outIOHIDElementRef); } if (kIOReturnSuccess == IOHIDDeviceGetValue(IOHIDElementGetDevice(outIOHIDElementRef), outIOHIDElementRef, &tIOHIDValueRef)) { actionRecs[kActionYAxis].fValue = [self calibrateElementValue:tIOHIDValueRef]; actionRecs[kActionYAxis].fOldValue = actionRecs[kActionYAxis].fValue; } actionRecs[kActionYAxis].fDeviceRef = outIOHIDDeviceRef; actionRecs[kActionYAxis].fElementRef = outIOHIDElementRef; // Button1 = Fire searchHIDInfo.element.usagePage = 0x09; searchHIDInfo.element.usage = 0x01; searchHIDInfo.element.cookie = 0x04; HIDFindDeviceAndElement(&searchHIDInfo, &outIOHIDDeviceRef, &outIOHIDElementRef); // if the calibration parameters haven't been set yet… granularity = IOHIDElement_GetCalibrationGranularity(outIOHIDElementRef); if (granularity < 0) { // … do it now HIDSetupElementCalibration(outIOHIDElementRef); } if (kIOReturnSuccess == IOHIDDeviceGetValue(IOHIDElementGetDevice(outIOHIDElementRef), outIOHIDElementRef, &tIOHIDValueRef)) { actionRecs[kActionFire].fValue = [self calibrateElementValue:tIOHIDValueRef]; actionRecs[kActionFire].fOldValue = actionRecs[kActionFire].fValue; } actionRecs[kActionFire].fDeviceRef = outIOHIDDeviceRef; actionRecs[kActionFire].fElementRef = outIOHIDElementRef; // Button3 = THRUST searchHIDInfo.element.usagePage = 0x09; searchHIDInfo.element.usage = 0x03; searchHIDInfo.element.cookie = 0x06; HIDFindDeviceAndElement(&searchHIDInfo, &outIOHIDDeviceRef, &outIOHIDElementRef); // if the calibration parameters haven't been set yet… granularity = IOHIDElement_GetCalibrationGranularity(outIOHIDElementRef); if (granularity < 0) { // … do it now HIDSetupElementCalibration(outIOHIDElementRef); } if (kIOReturnSuccess == IOHIDDeviceGetValue(IOHIDElementGetDevice(outIOHIDElementRef), outIOHIDElementRef, &tIOHIDValueRef)) { actionRecs[kActionThrust].fValue = [self calibrateElementValue:tIOHIDValueRef]; actionRecs[kActionThrust].fOldValue = actionRecs[kActionThrust].fValue; } actionRecs[kActionThrust].fDeviceRef = outIOHIDDeviceRef; actionRecs[kActionThrust].fElementRef = outIOHIDElementRef; // Button12 = Start searchHIDInfo.element.usagePage = 0x09; searchHIDInfo.element.usage = 0x0c; searchHIDInfo.element.cookie = 0x0f; HIDFindDeviceAndElement(&searchHIDInfo, &outIOHIDDeviceRef, &outIOHIDElementRef); // if the calibration parameters haven't been set yet… granularity = IOHIDElement_GetCalibrationGranularity(outIOHIDElementRef); if (granularity < 0) { // … do it now HIDSetupElementCalibration(outIOHIDElementRef); } if (kIOReturnSuccess == IOHIDDeviceGetValue(IOHIDElementGetDevice(outIOHIDElementRef), outIOHIDElementRef, &tIOHIDValueRef)) { actionRecs[kActionStart].fValue = [self calibrateElementValue:tIOHIDValueRef]; actionRecs[kActionStart].fOldValue = actionRecs[kActionStart].fValue; } actionRecs[kActionStart].fDeviceRef = outIOHIDDeviceRef; actionRecs[kActionStart].fElementRef = outIOHIDElementRef; // collect matching dictionarys for up to four devices CFMutableArrayRef inputValueMatchingArrays[kNumActions] = {NULL, NULL, NULL, NULL, NULL}; for (int actionIndex = 0; actionIndex < kNumActions; actionIndex++) { // register input value callback for this device (dups don't hurt) IOHIDDeviceRegisterInputValueCallback(actionRecs[actionIndex].fDeviceRef, Handle_InputValueCallback, (__bridge void *)self); // Now create a matching dictionary for this element IOHIDElementCookie cookie = IOHIDElementGetCookie(actionRecs[actionIndex].fElementRef); uint32_t usagePage = IOHIDElementGetUsagePage(actionRecs[actionIndex].fElementRef); uint32_t usage = IOHIDElementGetUsage(actionRecs[actionIndex].fElementRef); const void *keys[] = { CFSTR(kIOHIDElementCookieKey), CFSTR(kIOHIDElementUsagePageKey), CFSTR(kIOHIDElementUsageKey) }; const void *vals[] = { CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &cookie), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usagePage), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usage) }; CFDictionaryRef elementMatchingDict = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); int i; // append this matching dict to the first matching arrays for the same device for (i = 0; i <= actionIndex; i++) { if (actionRecs[actionIndex].fDeviceRef == actionRecs[i].fDeviceRef) { if (!inputValueMatchingArrays[i]) { inputValueMatchingArrays[i] = CFArrayCreateMutable(kCFAllocatorDefault, kNumActions, &kCFTypeArrayCallBacks); } CFArrayAppendValue(inputValueMatchingArrays[i], elementMatchingDict); break; } } // release everything were done with CFRelease(elementMatchingDict); CFRelease(vals[0]); CFRelease(vals[1]); CFRelease(vals[2]); } for (int actionIndex = 0; actionIndex < kNumActions; actionIndex++) { if (inputValueMatchingArrays[actionIndex]) { IOHIDDeviceSetInputValueMatchingMultiple(actionRecs[actionIndex].fDeviceRef, inputValueMatchingArrays[actionIndex]); CFRelease(inputValueMatchingArrays[actionIndex]); } } } - (void) termHID { if (gIOHIDManagerRef) { for (int actionIndex = 0; actionIndex < kNumActions; actionIndex++) { if (actionRecs[actionIndex].fDeviceRef) { IOHIDDeviceUnscheduleFromRunLoop(actionRecs[actionIndex].fDeviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); IOHIDDeviceRegisterInputValueCallback(actionRecs[actionIndex].fDeviceRef, NULL, NULL); } } IOHIDManagerRegisterDeviceMatchingCallback(gIOHIDManagerRef, NULL, NULL); IOHIDManagerRegisterDeviceRemovalCallback(gIOHIDManagerRef, NULL, NULL); IOHIDManagerUnscheduleFromRunLoop(gIOHIDManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); } if (gElementCFArrayRef) { CFRelease(gElementCFArrayRef); gElementCFArrayRef = NULL; } if (gDeviceCFArrayRef) { CFRelease(gDeviceCFArrayRef); gDeviceCFArrayRef = NULL; } if (gIOHIDManagerRef) { IOHIDManagerClose(gIOHIDManagerRef, 0); gIOHIDManagerRef = NULL; } } // termHID static void Handle_DeviceMatchingCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef) { // NSLog(@"result: %d, sender: %p, device %p", inResult, inSender, inIOHIDDeviceRef); #if DEBUG // HIDDumpDeviceInfo(inIOHIDDeviceRef); #endif // DEBUG // HIDRebuildDevices(); } static void Handle_DeviceRemovalCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef) { // NSLog(@"result: %d, sender: %p, device %p", inResult, inSender, inIOHIDDeviceRef); #if DEBUG HIDDumpDeviceInfo(inIOHIDDeviceRef); #endif // DEBUG HIDRebuildDevices(); } static void Handle_InputValueCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef inIOHIDValueRef) { // call the class method [(__bridge HIDAppDelegate *) inContext inputValueResult:inResult sender:inSender value:inIOHIDValueRef]; } - (void) inputValueResult: (IOReturn) inResult sender: (void *) inSender value: (IOHIDValueRef) inIOHIDValueRef { #pragma unused (inResult, inSender, inIOHIDValueRef) for (int actionIndex = 0; actionIndex < kNumActions; actionIndex++) { if (!actionRecs[actionIndex].fDeviceRef || !actionRecs[actionIndex].fElementRef) { continue; } if (actionRecs[actionIndex].fElementRef != IOHIDValueGetElement(inIOHIDValueRef)) { continue; } actionRecs[actionIndex].fValue = [self calibrateElementValue:inIOHIDValueRef]; switch (actionIndex) { case kActionXAxis: { if (actionRecs[kActionXAxis].fOldValue != actionRecs[kActionXAxis].fValue) { NSLog(@"x:%lf", actionRecs[kActionXAxis].fValue); actionRecs[kActionXAxis].fOldValue = actionRecs[kActionXAxis].fValue; } break; } case kActionYAxis: { if (actionRecs[kActionYAxis].fOldValue != actionRecs[kActionYAxis].fValue) { NSLog(@"y:%lf", actionRecs[kActionYAxis].fValue); actionRecs[kActionYAxis].fOldValue = actionRecs[kActionYAxis].fValue; } break; } case kActionThrust: { if (actionRecs[kActionThrust].fOldValue != actionRecs[kActionThrust].fValue) { NSLog(@"t:%lf", actionRecs[kActionThrust].fValue); actionRecs[kActionThrust].fOldValue = actionRecs[kActionThrust].fValue; } break; } case kActionFire: { if (actionRecs[kActionFire].fOldValue != actionRecs[kActionFire].fValue) { NSLog(@"f:%lf", actionRecs[kActionFire].fValue); actionRecs[kActionFire].fOldValue = actionRecs[kActionFire].fValue; } break; } case kActionStart: { if (actionRecs[kActionStart].fOldValue != actionRecs[kActionStart].fValue) { NSLog(@"s:%lf", actionRecs[kActionStart].fValue); actionRecs[kActionStart].fOldValue = actionRecs[kActionStart].fValue; } break; } default: { break; } } // switch } inResult = kIOReturnSuccess; } - (double) calibrateElementValue: (IOHIDValueRef) inIOHIDValueRef { double result = 0.; if (inIOHIDValueRef) { result = IOHIDValueGetScaledValue(inIOHIDValueRef, kIOHIDValueScaleTypePhysical); IOHIDElementRef tIOHIDElementRef = IOHIDValueGetElement(inIOHIDValueRef); if (tIOHIDElementRef) { #if 0 double_t granularity = IOHIDElement_GetCalibrationGranularity(tIOHIDElementRef); if (granularity < 0.0) { printf("%s, BAD granularity!\n", __PRETTY_FUNCTION__); HIDSetupElementCalibration(tIOHIDElementRef); granularity = IOHIDElement_GetCalibrationGranularity(tIOHIDElementRef); if (granularity < 0.0) { printf("%s, VERY BAD granularity!\n", __PRETTY_FUNCTION__); } } #endif // if 0 if (result < IOHIDElement_GetCalibrationSaturationMin(tIOHIDElementRef)) { IOHIDElement_SetCalibrationSaturationMin(tIOHIDElementRef, result); } if (result > IOHIDElement_GetCalibrationSaturationMax(tIOHIDElementRef)) { IOHIDElement_SetCalibrationSaturationMax(tIOHIDElementRef, result); } result = IOHIDValueGetScaledValue(inIOHIDValueRef, kIOHIDValueScaleTypeCalibrated); } } return (result); } @end