HID_Config_SaveAppDelegate.m
// File: HID_Config_SaveAppDelegate.m |
// Abstract: Implementation file for HID_Config_SaveAppDelegate class of HID_Config_Save project |
// Version: 5.3 |
// |
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
// Inc. ("Apple") in consideration of your agreement to the following |
// terms, and your use, installation, modification or redistribution of |
// this Apple software constitutes acceptance of these terms. If you do |
// not agree with these terms, please do not use, install, modify or |
// redistribute this Apple software. |
// |
// In consideration of your agreement to abide by the following terms, and |
// subject to these terms, Apple grants you a personal, non-exclusive |
// license, under Apple's copyrights in this original Apple software (the |
// "Apple Software"), to use, reproduce, modify and redistribute the Apple |
// Software, with or without modifications, in source and/or binary forms; |
// provided that if you redistribute the Apple Software in its entirety and |
// without modifications, you must retain this notice and the following |
// text and disclaimers in all such redistributions of the Apple Software. |
// Neither the name, trademarks, service marks or logos of Apple Inc. may |
// be used to endorse or promote products derived from the Apple Software |
// without specific prior written permission from Apple. Except as |
// expressly stated in this notice, no other rights or licenses, express or |
// implied, are granted by Apple herein, including but not limited to any |
// patent rights that may be infringed by your derivative works or by other |
// works in which the Apple Software may be incorporated. |
// |
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
// |
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
// POSSIBILITY OF SUCH DAMAGE. |
// |
// Copyright (C) 2014 Apple Inc. All Rights Reserved. |
// |
#import "PlayView.h" |
#import "HID_Config_SaveAppDelegate.h" |
// **************************************************** |
#pragma mark - * (private) @interface * |
// ---------------------------------------------------- |
@interface HID_Config_SaveAppDelegate () |
- (OSStatus) initHID; |
- (OSStatus) termHID; |
- (void) deviceMatchingResult:(IOReturn)inResult sender:(void *)inSender device:(IOHIDDeviceRef)inIOHIDDeviceRef; |
- (void) deviceRemovalResult:(IOReturn)inResult sender:(void *)inSender device:(IOHIDDeviceRef)inIOHIDDeviceRef; |
#if USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
- (void) valueAvailableResult:(IOReturn)inResult sender:(void *)inSender; |
#elif USE_INPUT_VALUE_CALLBACK |
- (void) inputValueResult:(IOReturn)inResult sender:(void *)inSender value:(IOHIDValueRef)inIOHIDValueRef; |
#else // if USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
- (void) idleTimer:(NSTimer *)inTimer; |
#endif // if USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
- (double) calibrateElementValue:(IOHIDValueRef)inIOHIDValueRef; |
@end; |
// **************************************************** |
#pragma mark - * static (local) function prototypes * |
// ---------------------------------------------------- |
static CFStringRef Copy_DeviceName(IOHIDDeviceRef inIOHIDDeviceRef); |
static NSString *Copy_DeviceElementNameString(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef); |
static void Handle_DeviceMatchingCallback(void * inContext, |
IOReturn inResult, |
void * inSender, |
IOHIDDeviceRef inIOHIDDeviceRef); |
static void Handle_DeviceRemovalCallback(void * inContext, |
IOReturn inResult, |
void * inSender, |
IOHIDDeviceRef inIOHIDDeviceRef); |
#if USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
static void Handle_ValueAvailableCallback(void * inContext, |
IOReturn inResult, |
void * inSender); |
#elif USE_INPUT_VALUE_CALLBACK |
static void Handle_InputValueCallback(void *inContext, |
IOReturn inResult, |
void *inSender, |
IOHIDValueRef inIOHIDValueRef); |
#endif // USE_INPUT_VALUE_CALLBACK |
// **************************************************** |
#pragma mark - * @implementation * |
// ---------------------------------------------------- |
@implementation HID_Config_SaveAppDelegate |
// **************************************************** |
#pragma mark - * public class methods * |
// ---------------------------------------------------- |
- (BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender { |
#pragma unused (sender) |
return (YES); |
} // applicationShouldTerminateAfterLastWindowClosed |
// -------------------------------------------------------------------------------------------- |
- (IBAction) configureXAxis:(id)inSender { |
NSLogDebug(@"sender: <%@>", inSender); |
IOHIDDeviceRef tIOHIDDeviceRef = NULL; |
IOHIDElementRef tIOHIDElementRef = NULL; |
if (HIDConfigureActionOfType(kActionTypeAxis, |
10.0, |
&tIOHIDDeviceRef, |
&tIOHIDElementRef)) |
{ |
#if DEBUG |
HIDDumpElementInfo(tIOHIDElementRef); |
#endif |
actionRecs[kActionXAxis].fDeviceRef = tIOHIDDeviceRef; |
actionRecs[kActionXAxis].fElementRef = tIOHIDElementRef; |
NSString *devEleName = Copy_DeviceElementNameString(tIOHIDDeviceRef, |
tIOHIDElementRef); |
if (devEleName) { |
[xAxisTextField setStringValue:devEleName]; |
} |
// if the calibration parameters haven't been set yet… |
double_t granularity = IOHIDElement_GetCalibrationGranularity(tIOHIDElementRef); |
if (isnan(granularity)) { // … do it now |
IOHIDElement_SetupCalibration(tIOHIDElementRef); |
} |
IOHIDValueRef tIOHIDValueRef; |
if (kIOReturnSuccess == IOHIDDeviceGetValue(IOHIDElementGetDevice(tIOHIDElementRef), |
tIOHIDElementRef, |
&tIOHIDValueRef)) |
{ |
actionRecs[kActionXAxis].fValue = [self calibrateElementValue:tIOHIDValueRef]; |
} |
playView.x = actionRecs[kActionXAxis].fValue; |
} |
} // configureXAxis |
// -------------------------------------------------------------------------------------------- |
- (IBAction) configureYAxis:(id)inSender { |
NSLogDebug(@"sender: <%@>", inSender); |
IOHIDDeviceRef tIOHIDDeviceRef = NULL; |
IOHIDElementRef tIOHIDElementRef = NULL; |
if (HIDConfigureActionOfType(kActionTypeAxis, |
10.0, |
&tIOHIDDeviceRef, |
&tIOHIDElementRef)) |
{ |
#if DEBUG |
HIDDumpElementInfo(tIOHIDElementRef); |
#endif |
actionRecs[kActionYAxis].fDeviceRef = tIOHIDDeviceRef; |
actionRecs[kActionYAxis].fElementRef = tIOHIDElementRef; |
NSString *devEleName = Copy_DeviceElementNameString(tIOHIDDeviceRef, |
tIOHIDElementRef); |
if (devEleName) { |
[yAxisTextField setStringValue:devEleName]; |
} |
// if the calibration parameters haven't been set yet… |
double_t granularity = IOHIDElement_GetCalibrationGranularity(tIOHIDElementRef); |
if (isnan(granularity)) { // … do it now |
IOHIDElement_SetupCalibration(tIOHIDElementRef); |
} |
IOHIDValueRef tIOHIDValueRef; |
if (kIOReturnSuccess == IOHIDDeviceGetValue(IOHIDElementGetDevice(tIOHIDElementRef), |
tIOHIDElementRef, |
&tIOHIDValueRef)) |
{ |
actionRecs[kActionYAxis].fValue = [self calibrateElementValue:tIOHIDValueRef]; |
} |
playView.y = actionRecs[kActionYAxis].fValue; |
} |
} // configureYAxis |
// -------------------------------------------------------------------------------------------- |
- (IBAction) configureThrust:(id)inSender { |
NSLogDebug(@"sender: <%@>", inSender); |
IOHIDDeviceRef tIOHIDDeviceRef = NULL; |
IOHIDElementRef tIOHIDElementRef = NULL; |
if (HIDConfigureActionOfType(kActionTypeAll, |
10.0, |
&tIOHIDDeviceRef, |
&tIOHIDElementRef)) |
{ |
#if DEBUG |
HIDDumpElementInfo(tIOHIDElementRef); |
#endif |
actionRecs[kActionThrust].fDeviceRef = tIOHIDDeviceRef; |
actionRecs[kActionThrust].fElementRef = tIOHIDElementRef; |
NSString *devEleName = Copy_DeviceElementNameString(tIOHIDDeviceRef, |
tIOHIDElementRef); |
if (devEleName) { |
[thrustTextField setStringValue:devEleName]; |
} |
// if the calibration parameters haven't been set yet… |
double_t granularity = IOHIDElement_GetCalibrationGranularity(tIOHIDElementRef); |
if (isnan(granularity)) { // … do it now |
IOHIDElement_SetupCalibration(tIOHIDElementRef); |
} |
IOHIDValueRef tIOHIDValueRef; |
if (kIOReturnSuccess == IOHIDDeviceGetValue(IOHIDElementGetDevice(tIOHIDElementRef), |
tIOHIDElementRef, |
&tIOHIDValueRef)) |
{ |
actionRecs[kActionThrust].fValue = [self calibrateElementValue:tIOHIDValueRef]; |
} |
playView.thrust = actionRecs[kActionThrust].fValue; |
} |
} // configureThrust |
// -------------------------------------------------------------------------------------------- |
- (IBAction) configureFire:(id)inSender { |
NSLogDebug(@"sender: <%@>", inSender); |
IOHIDDeviceRef tIOHIDDeviceRef = NULL; |
IOHIDElementRef tIOHIDElementRef = NULL; |
if (HIDConfigureActionOfType(kActionTypeButton, |
10.0, |
&tIOHIDDeviceRef, |
&tIOHIDElementRef)) |
{ |
#if DEBUG |
HIDDumpElementInfo(tIOHIDElementRef); |
#endif |
actionRecs[kActionFire].fDeviceRef = tIOHIDDeviceRef; |
actionRecs[kActionFire].fElementRef = tIOHIDElementRef; |
NSString *devEleName = Copy_DeviceElementNameString(tIOHIDDeviceRef, |
tIOHIDElementRef); |
if (devEleName) { |
[fireTextField setStringValue:devEleName]; |
} |
// if the calibration parameters haven't been set yet… |
double_t granularity = IOHIDElement_GetCalibrationGranularity(tIOHIDElementRef); |
if (isnan(granularity)) { // … do it now |
IOHIDElement_SetupCalibration(tIOHIDElementRef); |
} |
IOHIDValueRef tIOHIDValueRef; |
if (kIOReturnSuccess == IOHIDDeviceGetValue(IOHIDElementGetDevice(tIOHIDElementRef), |
tIOHIDElementRef, |
&tIOHIDValueRef)) |
{ |
actionRecs[kActionFire].fValue = [self calibrateElementValue:tIOHIDValueRef]; |
} |
playView.fire = actionRecs[kActionFire].fValue; |
} |
} // configureFire |
// -------------------------------------------------------------------------------------------- |
- (IBAction) saveConfiguration:(id)inSender { |
NSLogDebug(@"sender: <%@>", inSender); |
BOOL syncFlag = NO; |
for (int actionIndex = 0; actionIndex < kNumActions; actionIndex++) { |
CFStringRef keyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, |
NULL, |
CFSTR("HID Action #%d"), |
actionIndex); |
if (keyCFStringRef) { |
syncFlag |= HIDSaveElementPref(keyCFStringRef, |
kCFPreferencesCurrentApplication, |
actionRecs[actionIndex].fDeviceRef, |
actionRecs[actionIndex].fElementRef); |
CFRelease(keyCFStringRef); |
} |
} |
if (syncFlag) { |
CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); |
} |
} // saveConfiguration |
// -------------------------------------------------------------------------------------------- |
- (IBAction) restoreConfiguration:(id)inSender { |
NSLogDebug(@"sender: <%@>", inSender); |
for (int actionIndex = 0; actionIndex < kNumActions; actionIndex++) { |
CFStringRef keyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, |
NULL, |
CFSTR("HID Action #%d"), |
actionIndex); |
if (keyCFStringRef) { |
bzero(&actionRecs[actionIndex], |
sizeof(actionRecs[actionIndex])); |
if (HIDRestoreElementPref(keyCFStringRef, |
kCFPreferencesCurrentApplication, |
&actionRecs[actionIndex].fDeviceRef, |
&actionRecs[actionIndex].fElementRef)) |
{ |
// if the calibration parameters haven't been set yet… |
double_t granularity = IOHIDElement_GetCalibrationGranularity(actionRecs[actionIndex].fElementRef); |
if (isnan(granularity)) { // … do it now |
IOHIDElement_SetupCalibration(actionRecs[actionIndex].fElementRef); |
} |
NSString *devEleName = Copy_DeviceElementNameString(actionRecs[actionIndex].fDeviceRef, |
actionRecs[actionIndex].fElementRef); |
if (devEleName) { |
switch (actionIndex) { |
case kActionXAxis: |
{ |
[xAxisTextField setStringValue:devEleName]; |
break; |
} |
case kActionYAxis: |
{ |
[yAxisTextField setStringValue:devEleName]; |
break; |
} |
case kActionThrust: |
{ |
[thrustTextField setStringValue:devEleName]; |
break; |
} |
case kActionFire: |
{ |
[fireTextField setStringValue:devEleName]; |
break; |
} |
default: |
{ |
break; |
} |
} // switch |
} |
} // if HIDRestoreElementPref… |
CFRelease(keyCFStringRef); |
} // if (keyCFStringRef) |
} // for (int actionIndex = 0; actionIndex < kNumActions; actionIndex++) |
} // restoreConfiguration |
// -------------------------------------------------------------------------------------------- |
- (IBAction) rebuild:(id)inSender { |
NSLogDebug(@"sender: <%@>", inSender); |
(void) HIDBuildMultiDeviceList(nil, |
nil, |
0); |
[self restoreConfiguration:nil]; |
} // rebuild |
// -------------------------------------------------------------------------------------------- |
- (IBAction) test:(id)inSender { |
NSLogDebug(@"sender: <%@>", inSender); |
} |
// -------------------------------------------------------------------------------------------- |
- (IBAction) poll:(id)inSender { |
NSLogDebug(@"sender: <%@>", inSender); |
} |
// -------------------------------------------------------------------------------------------- |
- (IBAction) ping:(id)inSender { |
NSLogDebug(@"sender: <%@>", inSender); |
} |
// **************************************************** |
#pragma mark * delegate methods * |
// ---------------------------------------------------- |
// -------------------------------------------------------------------------------------------- |
- (void) applicationDidFinishLaunching:(NSNotification *)aNotification { |
NSLogDebug(@"aNotification: <%@>", aNotification); |
bzero(actionRecs, |
sizeof(actionRecs)); |
playView.minX = playView.minY = 0.0; |
playView.maxX = playView.maxY = 255.0; |
[tabView selectFirstTabViewItem:NULL]; |
(void) [self initHID]; |
} // applicationDidFinishLaunching |
// -------------------------------------------------------------------------------------------- |
- (void) applicationWillTerminate:(NSNotification *)aNotification { |
NSLogDebug(@"aNotification: <%@>", aNotification); |
[self termHID]; |
} // applicationWillTerminate |
// -------------------------------------------------------------------------------------------- |
- (void) tabView:(NSTabView *)inTabView didSelectTabViewItem:(NSTabViewItem *)inTabViewItem { |
NSLogDebug(@"tabView: <%@>, tabViewItem: <%@>", inTabView, inTabViewItem); |
if (gIOHIDManagerRef) { |
if ([[inTabViewItem label] isEqualToString:@"Configure"]) { |
NSLogDebug(@"Configure!"); |
#if USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
if (ioHIDQueueRefsCFArrayRef) { |
CFIndex idx, |
cnt = CFArrayGetCount(ioHIDQueueRefsCFArrayRef); |
for (idx = 0; idx < cnt; idx++) { |
IOHIDQueueRef tIOHIDQueueRef = (IOHIDQueueRef) CFArrayGetValueAtIndex(ioHIDQueueRefsCFArrayRef, |
idx); |
if (!tIOHIDQueueRef) { |
continue; |
} |
IOHIDQueueStop(tIOHIDQueueRef); |
IOHIDQueueRegisterValueAvailableCallback(tIOHIDQueueRef, |
NULL, |
NULL); |
IOHIDQueueUnscheduleFromRunLoop(tIOHIDQueueRef, |
CFRunLoopGetCurrent(), |
kCFRunLoopDefaultMode); |
} |
CFRelease(ioHIDQueueRefsCFArrayRef); |
ioHIDQueueRefsCFArrayRef = NULL; |
} |
#elif USE_INPUT_VALUE_CALLBACK |
for (int actionIndex = 0; actionIndex < kNumActions; actionIndex++) { |
if (actionRecs[actionIndex].fDeviceRef) { |
// unschedule us from runloop |
IOHIDDeviceUnscheduleFromRunLoop(actionRecs[actionIndex].fDeviceRef, |
CFRunLoopGetCurrent(), |
kCFRunLoopDefaultMode); |
// unregister input value callback for this device (dups don't hurt) |
IOHIDDeviceRegisterInputValueCallback(actionRecs[actionIndex].fDeviceRef, |
NULL, |
NULL); |
} |
} |
#else // if USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
if (timer) { |
[timer invalidate]; |
[timer release]; |
timer = nil; |
} |
#endif // if USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
} else if ([[inTabViewItem label] isEqualToString:@"Play"]) { |
NSLogDebug(@"Play!"); |
#if USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
if (ioHIDQueueRefsCFArrayRef) { |
CFRelease(ioHIDQueueRefsCFArrayRef); |
} |
ioHIDQueueRefsCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, |
0, |
&kCFTypeArrayCallBacks); |
for (int actionIndex = 0; actionIndex < kNumActions; actionIndex++) { |
if (actionRecs[actionIndex].fDeviceRef) { |
IOHIDQueueRef tIOHIDQueueRef = NULL; |
// see if we already have a queue for this device |
int idx, |
cnt = CFArrayGetCount(ioHIDQueueRefsCFArrayRef); |
for (idx = 0; idx < cnt; idx++) { |
IOHIDQueueRef tempIOHIDQueueRef = (IOHIDQueueRef) CFArrayGetValueAtIndex(ioHIDQueueRefsCFArrayRef, |
idx); |
if (!tempIOHIDQueueRef) { |
continue; |
} |
if (actionRecs[actionIndex].fDeviceRef == IOHIDQueueGetDevice(tempIOHIDQueueRef)) { |
tIOHIDQueueRef = tempIOHIDQueueRef; // Found one! |
IOHIDQueueStop(tIOHIDQueueRef); // (we'll restart it below) |
break; |
} |
} |
if (!tIOHIDQueueRef) { // nope, create one |
tIOHIDQueueRef = IOHIDQueueCreate(kCFAllocatorDefault, |
actionRecs[actionIndex].fDeviceRef, |
256, |
0); |
if (tIOHIDQueueRef) { // and add it to our array of queues |
CFArrayAppendValue(ioHIDQueueRefsCFArrayRef, |
tIOHIDQueueRef); |
} |
} |
if (tIOHIDQueueRef) { |
IOHIDQueueAddElement(tIOHIDQueueRef, |
actionRecs[actionIndex].fElementRef); |
IOHIDQueueRegisterValueAvailableCallback(tIOHIDQueueRef, |
Handle_ValueAvailableCallback, |
(__bridge void *)(self)); |
IOHIDQueueScheduleWithRunLoop(tIOHIDQueueRef, |
CFRunLoopGetCurrent(), |
kCFRunLoopDefaultMode); |
IOHIDQueueStart(tIOHIDQueueRef); // (re?)start it |
} |
} |
} |
#elif USE_INPUT_VALUE_CALLBACK |
// collect matching dictionarys for up to four devices |
CFMutableArrayRef inputValueMatchingArrays[kNumActions] = {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]); |
} |
} |
#else // if USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
timer = |
[[NSTimer alloc] initWithFireDate:[NSDate date] |
interval:0.1 |
target :self |
selector: |
@selector(idleTimer:) |
userInfo:nil |
repeats :YES]; |
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; |
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSModalPanelRunLoopMode]; |
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode]; |
#endif // USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
} |
} |
} // tabView:didSelectTabViewItem: |
// **************************************************** |
#pragma mark * private class methods * |
// ---------------------------------------------------- |
// -------------------------------------------------------------------------------------------- |
- (OSStatus) initHID { |
OSStatus result = -1; |
// create the manager |
gIOHIDManagerRef = IOHIDManagerCreate(kCFAllocatorDefault, |
kIOHIDOptionsTypeNone); |
if (gIOHIDManagerRef) { |
// open it |
IOReturn tIOReturn = IOHIDManagerOpen(gIOHIDManagerRef, |
kIOHIDOptionsTypeNone); |
if (kIOReturnSuccess == tIOReturn) { |
NSLogDebug(@"IOHIDManager (%p) creaded and opened!\n", |
(void *) gIOHIDManagerRef); |
} else { |
NSLog( @"Couldn’t open IOHIDManager."); |
} |
} else { |
NSLog( @"Couldn’t create a IOHIDManager."); |
} |
if (gIOHIDManagerRef) { |
// schedule with runloop |
IOHIDManagerScheduleWithRunLoop(gIOHIDManagerRef, |
CFRunLoopGetCurrent(), |
kCFRunLoopDefaultMode); |
// register callbacks |
IOHIDManagerRegisterDeviceMatchingCallback(gIOHIDManagerRef, |
Handle_DeviceMatchingCallback, |
(__bridge void *)(self)); |
IOHIDManagerRegisterDeviceRemovalCallback(gIOHIDManagerRef, |
Handle_DeviceRemovalCallback, |
(__bridge void *)(self)); |
} |
if (!HIDBuildMultiDeviceList(nil, |
nil, |
0)) |
{ |
goto Oops; |
} |
#if FALSE // set true to log devices |
{ |
CFIndex idx, |
cnt = CFArrayGetCount(gDeviceCFArrayRef); |
for (idx = 0; idx < cnt; idx++) { |
IOHIDDeviceRef tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, |
idx); |
if (!tIOHIDDeviceRef) { |
continue; |
} |
if (CFGetTypeID(tIOHIDDeviceRef) != IOHIDDeviceGetTypeID()) { |
continue; |
} |
HIDDumpDeviceInfo(tIOHIDDeviceRef); |
} |
fflush(stdout); |
} |
#endif // if TRUE |
[self restoreConfiguration:nil]; |
Oops:; |
return (result); |
} // initHID |
// -------------------------------------------------------------------------------------------- |
- (OSStatus) termHID { |
if (gIOHIDManagerRef) { |
#if USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
if (ioHIDQueueRefsCFArrayRef) { |
CFRelease(ioHIDQueueRefsCFArrayRef); |
ioHIDQueueRefsCFArrayRef = NULL; |
} |
#elif USE_INPUT_VALUE_CALLBACK |
for (int actionIndex = 0; actionIndex < kNumActions; actionIndex++) { |
if (actionRecs[actionIndex].fDeviceRef) { |
IOHIDDeviceUnscheduleFromRunLoop(actionRecs[actionIndex].fDeviceRef, |
CFRunLoopGetCurrent(), |
kCFRunLoopDefaultMode); |
IOHIDDeviceRegisterInputValueCallback(actionRecs[actionIndex].fDeviceRef, |
NULL, |
NULL); |
} |
} |
#else // if USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
[timer release]; |
#endif // USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
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; |
} |
return (noErr); |
} // termHID |
// -------------------------------------------------------------------------------------------- |
- (void) deviceMatchingResult:(IOReturn)inResult sender:(void *)inSender device:(IOHIDDeviceRef)inIOHIDDeviceRef { |
#pragma unused (inResult, inSender, inIOHIDDeviceRef) |
NSLogDebug(@"result: %d, sender: %p, device %p", |
inResult, |
inSender, |
inIOHIDDeviceRef); |
#if DEBUG |
HIDDumpDeviceInfo(inIOHIDDeviceRef); |
#endif // DEBUG |
HIDRebuildDevices(); |
[self restoreConfiguration:nil]; |
} // deviceMatchingResult |
// -------------------------------------------------------------------------------------------- |
- (void) deviceRemovalResult:(IOReturn)inResult sender:(void *)inSender device:(IOHIDDeviceRef)inIOHIDDeviceRef { |
#pragma unused (inResult, inSender, inIOHIDDeviceRef) |
NSLogDebug(@"result: %d, sender: %p, device %p", |
inResult, |
inSender, |
inIOHIDDeviceRef); |
#if DEBUG |
HIDDumpDeviceInfo(inIOHIDDeviceRef); |
#endif // DEBUG |
HIDRebuildDevices(); |
[self restoreConfiguration:nil]; |
} // deviceRemovalResult |
// -------------------------------------------------------------------------------------------- |
#if USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
- (void) valueAvailableResult:(IOReturn)inResult sender:(void *)inSender { |
#pragma unused (inResult, inSender) |
NSLogDebug(@"result: %d, sender: %p", inResult, inSender); |
while (YES) { |
IOHIDValueRef tIOHIDValueRef = IOHIDQueueCopyNextValue((IOHIDQueueRef) inSender); |
if (!tIOHIDValueRef) { |
break; // no more data |
} |
for (int actionIndex = 0; actionIndex < kNumActions; actionIndex++) { |
if (!actionRecs[actionIndex].fDeviceRef || !actionRecs[actionIndex].fElementRef) { |
continue; |
} |
if (actionRecs[actionIndex].fElementRef != IOHIDValueGetElement(tIOHIDValueRef)) { |
continue; |
} |
actionRecs[actionIndex].fValue = [self calibrateElementValue:tIOHIDValueRef]; |
switch (actionIndex) { |
case kActionXAxis: |
{ |
playView.x = actionRecs[kActionXAxis].fValue; |
break; |
} |
case kActionYAxis: |
{ |
playView.y = actionRecs[kActionYAxis].fValue; |
break; |
} |
case kActionThrust: |
{ |
playView.thrust = actionRecs[kActionThrust].fValue; |
break; |
} |
case kActionFire: |
{ |
playView.fire = actionRecs[kActionFire].fValue; |
break; |
} |
default: |
{ |
break; |
} |
} // switch |
NSLogDebug(@"element # %d = { value: %6.2f }.\n", |
actionIndex, |
actionRecs[actionIndex].fValue); |
} |
// fflush( stdout ); |
} |
} // valueAvailableResult:sender |
#elif USE_INPUT_VALUE_CALLBACK |
// -------------------------------------------------------------------------------------------- |
- (void) inputValueResult:(IOReturn)inResult sender:(void *)inSender value:(IOHIDValueRef)inIOHIDValueRef { |
#pragma unused (inResult, inSender, inIOHIDValueRef) |
NSLogDebug(@"result: %d, sender: %p, value %p", |
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: |
{ |
playView.x = actionRecs[kActionXAxis].fValue; |
break; |
} |
case kActionYAxis: |
{ |
playView.y = actionRecs[kActionYAxis].fValue; |
break; |
} |
case kActionThrust: |
{ |
playView.thrust = actionRecs[kActionThrust].fValue; |
break; |
} |
case kActionFire: |
{ |
playView.fire = actionRecs[kActionFire].fValue; |
break; |
} |
default: |
{ |
break; |
} |
} // switch |
NSLogDebug(@"element # %d = { value: %6.2f }.\n", |
actionIndex, |
actionRecs[actionIndex].fValue); |
} |
inResult = kIOReturnSuccess; |
} // inputValueResult |
#else // if USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
// -------------------------------------------------------------------------------------------- |
- (void) idleTimer:(NSTimer *)inTimer { |
NSLogDebug(@"timer: <%@>", inTimer); |
// get input values |
for (int actionIndex = 0; actionIndex < kNumActions; actionIndex++) { |
actionRecs[actionIndex].fValue = 0.0; |
if (actionRecs[actionIndex].fDeviceRef && |
actionRecs[actionIndex].fElementRef) |
{ |
IOHIDValueRef tIOHIDValueRef; |
if (kIOReturnSuccess == IOHIDDeviceGetValue(actionRecs[actionIndex].fDeviceRef, |
actionRecs[actionIndex].fElementRef, |
&tIOHIDValueRef)) { |
actionRecs[actionIndex].fValue = [self calibrateElementValue:tIOHIDValueRef]; |
switch (actionIndex) { |
case kActionXAxis: |
{ |
playView.x = actionRecs[kActionXAxis].fValue; |
break; |
} |
case kActionYAxis: |
{ |
playView.y = actionRecs[kActionYAxis].fValue; |
break; |
} |
case kActionThrust: |
{ |
playView.thrust = actionRecs[kActionThrust].fValue; |
break; |
} |
case kActionFire: |
{ |
playView.fire = actionRecs[kActionFire].fValue; |
break; |
} |
default: |
{ |
break; |
} |
} // switch |
NSLogDebug(@"element # %d = { value: %6.2f }.\n", |
actionIndex, |
actionRecs[actionIndex].fValue); |
} |
} |
} |
} // idleTimer: |
#endif // if USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
// ---------------------------------------------------- |
- (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 (isnan(granularity)) { |
printf("%s, BAD granularity!\n", __PRETTY_FUNCTION__); |
IOHIDElement_SetupCalibration(tIOHIDElementRef); |
granularity = IOHIDElement_GetCalibrationGranularity(tIOHIDElementRef); |
if (isnan(granularity)) { |
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); |
} /* Do_Element_Calibration */ |
// **************************************************** |
#pragma mark * @synthesize properties * |
// ---------------------------------------------------- |
@synthesize window; |
@synthesize tabView; |
@synthesize xAxisTextField; |
@synthesize yAxisTextField; |
@synthesize thrustTextField; |
@synthesize fireTextField; |
@synthesize playView; |
@end |
// **************************************************** |
#pragma mark - * static functions * |
// ---------------------------------------------------- |
// get name of device |
// ---------------------------------------------------- |
static CFStringRef Copy_DeviceName(IOHIDDeviceRef inIOHIDDeviceRef) { |
CFStringRef result = NULL; |
if (inIOHIDDeviceRef) { |
CFStringRef manCFStringRef = IOHIDDevice_GetManufacturer(inIOHIDDeviceRef); |
if (manCFStringRef) { |
// make a copy that we can CFRelease later |
CFMutableStringRef tCFStringRef = CFStringCreateMutableCopy(kCFAllocatorDefault, |
0, |
manCFStringRef); |
// trim off any trailing spaces |
while (CFStringHasSuffix(tCFStringRef, |
CFSTR(" "))) |
{ |
CFIndex cnt = CFStringGetLength(tCFStringRef); |
if (!cnt) { |
break; |
} |
CFStringDelete(tCFStringRef, |
CFRangeMake(cnt - |
1, |
1)); |
} |
manCFStringRef = tCFStringRef; |
} |
if (!manCFStringRef) { |
// use the vendor ID to make a manufacturer string |
long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); |
manCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, |
NULL, |
CFSTR("vendor: %ld"), |
vendorID); |
} |
CFStringRef prodCFStringRef = IOHIDDevice_GetProduct(inIOHIDDeviceRef); |
if (prodCFStringRef) { |
// make a copy that we can CFRelease later |
prodCFStringRef = CFStringCreateCopy(kCFAllocatorDefault, |
prodCFStringRef); |
} else { |
// use the product ID |
long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); |
// to make a product string |
prodCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, |
NULL, |
CFSTR("%@ - product id %ld"), |
manCFStringRef, |
productID); |
} |
assert(prodCFStringRef); |
// if the product name begins with the manufacturer string... |
if (CFStringHasPrefix(prodCFStringRef, |
manCFStringRef)) |
{ |
// then just use the product name |
result = CFStringCreateCopy(kCFAllocatorDefault, |
prodCFStringRef); |
} else { // otherwise append the product name to the manufacturer |
result = CFStringCreateWithFormat(kCFAllocatorDefault, |
NULL, |
CFSTR("%@ - %@"), |
manCFStringRef, |
prodCFStringRef); |
} |
if (manCFStringRef) { |
CFRelease( manCFStringRef); |
} |
if (prodCFStringRef) { |
CFRelease( prodCFStringRef); |
} |
} |
return (result); |
} // Copy_DeviceName |
// ---------------------------------------------------- |
// get name of element for display in window; |
// try names first then default to more generic derived names if device does not provide explicit names |
// ---------------------------------------------------- |
static NSString *Copy_DeviceElementNameString(IOHIDDeviceRef inIOHIDDeviceRef, |
IOHIDElementRef inIOHIDElementRef) { |
NSString *result = NULL; |
if (inIOHIDDeviceRef && inIOHIDElementRef) { |
char cstrDevice[256] = "----", |
cstrElement[256] = "----"; |
// if this is not a valid device |
if (CFGetTypeID(inIOHIDDeviceRef) != IOHIDDeviceGetTypeID()) { |
return (result); |
} |
// if this is not a valid element |
if (CFGetTypeID(inIOHIDElementRef) != IOHIDElementGetTypeID()) { |
return (result); |
} |
CFStringRef devCFStringRef = Copy_DeviceName(inIOHIDDeviceRef); |
if (devCFStringRef) { |
(void) CFStringGetCString(devCFStringRef, |
cstrDevice, |
sizeof(cstrDevice), |
kCFStringEncodingUTF8); |
CFRelease(devCFStringRef); |
} |
CFStringRef eleCFStringRef = IOHIDElementGetName(inIOHIDElementRef); |
if (eleCFStringRef) { |
(void) CFStringGetCString(eleCFStringRef, |
cstrElement, |
sizeof(cstrElement), |
kCFStringEncodingUTF8); |
} else { |
long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); |
long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); |
eleCFStringRef = HIDCopyElementNameFromVendorProductCookie(vendorID, |
productID, |
IOHIDElementGetCookie(inIOHIDElementRef)); |
if (eleCFStringRef) { |
(void) CFStringGetCString(eleCFStringRef, |
cstrElement, |
sizeof(cstrElement), |
kCFStringEncodingUTF8); |
CFRelease(eleCFStringRef); |
} else { |
long usagePage = IOHIDElementGetUsagePage(inIOHIDElementRef); |
long usage = IOHIDElementGetUsage(inIOHIDElementRef); |
eleCFStringRef = HIDCopyElementNameFromVendorProductUsage(vendorID, |
productID, |
usagePage, |
usage); |
if (eleCFStringRef) { |
(void) CFStringGetCString(eleCFStringRef, |
cstrElement, |
sizeof(cstrElement), |
kCFStringEncodingUTF8); |
CFRelease(eleCFStringRef); |
} else { |
eleCFStringRef = HIDCopyUsageName(usagePage, usage); |
if (eleCFStringRef) { |
(void) CFStringGetCString(eleCFStringRef, |
cstrElement, |
sizeof(cstrElement), |
kCFStringEncodingUTF8); |
CFRelease(eleCFStringRef); |
} else { |
sprintf(cstrElement, "ele: %08lX:%08lX", usagePage, usage); |
} |
} // if ( !HIDGetElementNameFromVendorProductUsage(...) ) |
} // if ( !HIDCopyElementNameFromVendorProductCookie(...) ) |
} // if ( eleCFStringRef ) |
result = [NSString stringWithFormat:@"%s, %s", cstrDevice, cstrElement]; |
} |
return (result); |
} // Copy_DeviceElementNameString |
// **************************************************** |
#pragma mark * IOHID Callbacks * |
// ---------------------------------------------------- |
static void Handle_DeviceMatchingCallback(void * inContext, |
IOReturn inResult, |
void * inSender, |
IOHIDDeviceRef inIOHIDDeviceRef) { |
// NSLogDebug(); |
// call the class method |
[(__bridge HID_Config_SaveAppDelegate *) inContext deviceMatchingResult:inResult sender:inSender device:inIOHIDDeviceRef]; |
} // Handle_DeviceMatchingCallback |
static void Handle_DeviceRemovalCallback(void * inContext, |
IOReturn inResult, |
void * inSender, |
IOHIDDeviceRef inIOHIDDeviceRef) { |
// NSLogDebug(); |
// call the class method |
[(__bridge HID_Config_SaveAppDelegate *) inContext deviceRemovalResult:inResult sender:inSender device:inIOHIDDeviceRef]; |
} // Handle_DeviceRemovalCallback |
#if USE_QUEUE_VALUE_AVAILABLE_CALLBACK |
static void Handle_ValueAvailableCallback(void * inContext, |
IOReturn inResult, |
void * inSender) { |
// NSLogDebug(); |
// call the class method |
[(__bridge HID_Config_SaveAppDelegate *) inContext valueAvailableResult:inResult sender:inSender]; |
} // Handle_ValueAvailableCallback |
#elif USE_INPUT_VALUE_CALLBACK |
static void Handle_InputValueCallback(void * inContext, |
IOReturn inResult, |
void * inSender, |
IOHIDValueRef inIOHIDValueRef) { |
// NSLogDebug(); |
// call the class method |
[(__bridge HID_Config_SaveAppDelegate *) inContext inputValueResult:inResult sender:inSender value:inIOHIDValueRef]; |
} // Handle_InputValueCallback |
#endif // USE_INPUT_VALUE_CALLBACK |
Copyright © 2014 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2014-02-28