/* * Copyright (c) 2007 Dave Dribin * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #import "DDHidLib.h" #include @interface DDHidJoystick (DDHidJoystickDelegate) - (void) ddhidJoystick: (DDHidJoystick *) joystick stick: (unsigned) stick xChanged: (int) value; - (void) ddhidJoystick: (DDHidJoystick *) joystick stick: (unsigned) stick yChanged: (int) value; - (void) ddhidJoystick: (DDHidJoystick *) joystick stick: (unsigned) stick otherAxis: (unsigned) otherAxis valueChanged: (int) value; - (void) ddhidJoystick: (DDHidJoystick *) joystick stick: (unsigned) stick povNumber: (unsigned) povNumber valueChanged: (int) value; - (void) ddhidJoystick: (DDHidJoystick *) joystick buttonDown: (unsigned) buttonNumber; - (void) ddhidJoystick: (DDHidJoystick *) joystick buttonUp: (unsigned) buttonNumber; @end @interface DDHidJoystick (Private) - (void) initLogicalDeviceElements; - (void) initJoystickElements: (NSArray *) elements; - (void) addStick: (NSArray *) stickElements; - (void) ddhidQueueHasEvents: (DDHidQueue *) hidQueue; - (int) normalizeValue: (int) value forElement: (DDHidElement *) element; - (int) povValue: (int) value forElement: (DDHidElement *) element; - (BOOL) findStick: (unsigned *) stick element: (DDHidElement **) elementOut withXAxisCookie: (IOHIDElementCookie) cookie; - (BOOL) findStick: (unsigned *) stick element: (DDHidElement **) elementOut withYAxisCookie: (IOHIDElementCookie) cookie; - (BOOL) findStick: (unsigned *) stickOut otherAxis: (unsigned *) axisOut element: (DDHidElement **) elementOut withCookie: (IOHIDElementCookie) cookie; - (BOOL) findStick: (unsigned *) stickOut povNumber: (unsigned *) povNumber element: (DDHidElement **) elementOut withCookie: (IOHIDElementCookie) cookie; @end @implementation DDHidJoystick + (NSArray *) allJoysticks; { NSArray * joysticks = [DDHidDevice allDevicesMatchingUsagePage: kHIDPage_GenericDesktop usageId: kHIDUsage_GD_Joystick withClass: self skipZeroLocations: YES]; NSArray * gamepads = [DDHidDevice allDevicesMatchingUsagePage: kHIDPage_GenericDesktop usageId: kHIDUsage_GD_GamePad withClass: self skipZeroLocations: YES]; NSMutableArray * allJoysticks = [NSMutableArray arrayWithArray: joysticks]; [allJoysticks addObjectsFromArray: gamepads]; [allJoysticks sortUsingSelector: @selector(compareByLocationId:)]; return allJoysticks; } - (id) initLogicalWithDevice: (io_object_t) device logicalDeviceNumber: (int) logicalDeviceNumber error: (NSError **) error; { self = [super initLogicalWithDevice: device logicalDeviceNumber: logicalDeviceNumber error: error]; if (self == nil) return nil; mButtonElements = [[NSMutableArray alloc] init]; mSticks = [[NSMutableArray alloc] init]; mLogicalDeviceElements = [[NSMutableArray alloc] init]; [self initLogicalDeviceElements]; int logicalDeviceCount = [mLogicalDeviceElements count]; if (logicalDeviceCount == 0) { [self release]; return nil; } mLogicalDeviceNumber = logicalDeviceNumber; if (mLogicalDeviceNumber >= logicalDeviceCount) mLogicalDeviceNumber = logicalDeviceCount - 1; [self initJoystickElements: [mLogicalDeviceElements objectAtIndex: mLogicalDeviceNumber]]; [mButtonElements sortUsingSelector: @selector(compareByUsage:)]; mDelegate = nil; return self; } //=========================================================== // dealloc //=========================================================== - (void) dealloc { [mLogicalDeviceElements release]; [mSticks release]; [mButtonElements release]; mLogicalDeviceElements = nil; mSticks = nil; mButtonElements = nil; [super dealloc]; } - (int) logicalDeviceCount; { return [mLogicalDeviceElements count]; } #pragma mark - #pragma mark Joystick Elements //=========================================================== // - buttonElements //=========================================================== - (NSArray *) buttonElements; { return mButtonElements; } - (unsigned) numberOfButtons; { return [mButtonElements count]; } #pragma mark - #pragma mark Sticks - indexed accessors - (unsigned int) countOfSticks { return [mSticks count]; } - (DDHidJoystickStick *) objectInSticksAtIndex: (unsigned int)index { return [mSticks objectAtIndex: index]; } - (void) addElementsToQueue: (DDHidQueue *) queue; { NSEnumerator * e = [mSticks objectEnumerator]; DDHidJoystickStick * stick; while (stick = [e nextObject]) { [queue addElements: [stick allElements]]; } [queue addElements: mButtonElements]; } #pragma mark - #pragma mark Asynchronous Notification - (void) setDelegate: (id) delegate; { mDelegate = delegate; } - (void) addElementsToDefaultQueue; { [self addElementsToQueue: mDefaultQueue]; } @end @implementation DDHidJoystick (Private) - (void) initLogicalDeviceElements; { NSArray * topLevelElements = [self elements]; if ([topLevelElements count] == 0) { [mLogicalDeviceElements addObject: topLevelElements]; return; } NSEnumerator * e = [topLevelElements objectEnumerator]; DDHidElement * element; while (element = [e nextObject]) { unsigned usagePage = [[element usage] usagePage]; unsigned usageId = [[element usage] usageId]; if (usagePage == kHIDPage_GenericDesktop && (usageId == kHIDUsage_GD_Joystick || usageId == kHIDUsage_GD_GamePad)) { [mLogicalDeviceElements addObject: [NSArray arrayWithObject: element]]; } } } - (void) initJoystickElements: (NSArray *) elements; { NSEnumerator * e = [elements objectEnumerator]; DDHidElement * element; DDHidJoystickStick * currentStick = [[[DDHidJoystickStick alloc] init] autorelease]; BOOL stickHasElements = NO; while (element = [e nextObject]) { unsigned usagePage = [[element usage] usagePage]; unsigned usageId = [[element usage] usageId]; NSArray * subElements = [element elements]; if ([subElements count] > 0) { [self initJoystickElements: subElements]; } else if ((usagePage == kHIDPage_GenericDesktop) && (usageId == kHIDUsage_GD_Pointer)) { [self addStick: subElements]; } else if ([currentStick addElement: element]) { stickHasElements = YES; } else if ((usagePage == kHIDPage_Button) && (usageId > 0)) { [mButtonElements addObject: element]; } } if (stickHasElements) { [mSticks addObject: currentStick]; } } - (void) addStick: (NSArray *) elements; { NSEnumerator * e = [elements objectEnumerator]; DDHidElement * element; while (element = [e nextObject]) { NSLog(@"Stick element: %@", [[element usage] usageName]); } } - (void) ddhidQueueHasEvents: (DDHidQueue *) hidQueue; { DDHidEvent * event; while ((event = [hidQueue nextEvent])) { IOHIDElementCookie cookie = [event elementCookie]; SInt32 value = [event value]; DDHidElement * element; unsigned stick; unsigned otherAxis; unsigned povNumber; if ([self findStick: &stick element: &element withXAxisCookie: cookie]) { int normalizedValue = [self normalizeValue: value forElement: element]; [self ddhidJoystick: self stick: stick xChanged: normalizedValue]; } else if ([self findStick: &stick element: &element withYAxisCookie: cookie]) { int normalizedValue = [self normalizeValue: value forElement: element]; [self ddhidJoystick: self stick: stick yChanged: normalizedValue]; } else if ([self findStick: &stick otherAxis: &otherAxis element: &element withCookie: cookie]) { int normalizedValue = [self normalizeValue: value forElement: element]; [self ddhidJoystick: self stick: stick otherAxis: otherAxis valueChanged: normalizedValue]; } else if ([self findStick: &stick povNumber: &povNumber element: &element withCookie: cookie]) { int povValue = [self povValue: value forElement: element]; [self ddhidJoystick: self stick: stick povNumber: povNumber valueChanged: povValue]; } else { unsigned i = 0; for (i = 0; i < [[self buttonElements] count]; i++) { if (cookie == [[[self buttonElements] objectAtIndex: i] cookie]) break; } if (value == 1) { [self ddhidJoystick: self buttonDown: i]; } else if (value == 0) { [self ddhidJoystick: self buttonUp: i]; } else { DDHidElement * element = [self elementForCookie: [event elementCookie]]; NSLog(@"Element: %@, value: %d", [[element usage] usageName], (int)[event value]); } } } } - (int) normalizeValue: (int) value forElement: (DDHidElement *) element; { int normalizedUnits = DDHID_JOYSTICK_VALUE_MAX - DDHID_JOYSTICK_VALUE_MIN; int elementUnits = [element maxValue] - [element minValue]; int normalizedValue = (((int64_t)(value - [element minValue]) * normalizedUnits) / elementUnits) + DDHID_JOYSTICK_VALUE_MIN; return normalizedValue; } - (int) povValue: (int) value forElement: (DDHidElement *) element; { long max = [element maxValue]; long min = [element minValue]; // If the value is outside the min/max range, it's probably in a // centered/NULL state. if ((value < min) || (value > max)) { return -1; } // Do like DirectInput and express the hatswitch value in hundredths of a // degree, clockwise from north. return 36000 / (max - min + 1) * (value - min); } - (BOOL) findStick: (unsigned *) stick element: (DDHidElement **) elementOut withXAxisCookie: (IOHIDElementCookie) cookie; { unsigned i; for (i = 0; i < [mSticks count]; i++) { DDHidElement * element = [[mSticks objectAtIndex: i] xAxisElement]; if ((element != nil) && ([element cookie] == cookie)) { *stick = i; *elementOut = element; return YES; } } return NO; } - (BOOL) findStick: (unsigned *) stick element: (DDHidElement **) elementOut withYAxisCookie: (IOHIDElementCookie) cookie; { unsigned i; for (i = 0; i < [mSticks count]; i++) { DDHidElement * element = [[mSticks objectAtIndex: i] yAxisElement]; if ((element != nil) && ([element cookie] == cookie)) { *stick = i; *elementOut = element; return YES; } } return NO; } - (BOOL) findStick: (unsigned *) stickOut otherAxis: (unsigned *) axisOut element: (DDHidElement **) elementOut withCookie: (IOHIDElementCookie) cookie; { unsigned i; for (i = 0; i < [mSticks count]; i++) { DDHidJoystickStick * stick = [mSticks objectAtIndex: i]; unsigned j; for (j = 0; j < [stick countOfStickElements]; j++) { DDHidElement * element = [stick objectInStickElementsAtIndex: j]; if ((element != nil) && ([element cookie] == cookie)) { *stickOut = i; *axisOut = j; *elementOut = element; return YES; } } } return NO; } - (BOOL) findStick: (unsigned *) stickOut povNumber: (unsigned *) povNumber element: (DDHidElement **) elementOut withCookie: (IOHIDElementCookie) cookie; { unsigned i; for (i = 0; i < [mSticks count]; i++) { DDHidJoystickStick * stick = [mSticks objectAtIndex: i]; unsigned j; for (j = 0; j < [stick countOfPovElements]; j++) { DDHidElement * element = [stick objectInPovElementsAtIndex: j]; if ((element != nil) && ([element cookie] == cookie)) { *stickOut = i; *povNumber = j; *elementOut = element; return YES; } } } return NO; } @end @implementation DDHidJoystick (DDHidJoystickDelegate) - (void) ddhidJoystick: (DDHidJoystick *) joystick stick: (unsigned) stick xChanged: (int) value; { if ([mDelegate respondsToSelector: _cmd]) [mDelegate ddhidJoystick: joystick stick: stick xChanged: value]; } - (void) ddhidJoystick: (DDHidJoystick *) joystick stick: (unsigned) stick yChanged: (int) value; { if ([mDelegate respondsToSelector: _cmd]) [mDelegate ddhidJoystick: joystick stick: stick yChanged: value]; } - (void) ddhidJoystick: (DDHidJoystick *) joystick stick: (unsigned) stick otherAxis: (unsigned) otherAxis valueChanged: (int) value; { if ([mDelegate respondsToSelector: _cmd]) [mDelegate ddhidJoystick: joystick stick: stick otherAxis: otherAxis valueChanged: value]; } - (void) ddhidJoystick: (DDHidJoystick *) joystick stick: (unsigned) stick povNumber: (unsigned) povNumber valueChanged: (int) value; { if ([mDelegate respondsToSelector: _cmd]) [mDelegate ddhidJoystick: joystick stick: stick povNumber: povNumber valueChanged: value]; } - (void) ddhidJoystick: (DDHidJoystick *) joystick buttonDown: (unsigned) buttonNumber; { if ([mDelegate respondsToSelector: _cmd]) [mDelegate ddhidJoystick: joystick buttonDown: buttonNumber]; } - (void) ddhidJoystick: (DDHidJoystick *) joystick buttonUp: (unsigned) buttonNumber; { if ([mDelegate respondsToSelector: _cmd]) [mDelegate ddhidJoystick: joystick buttonUp: buttonNumber]; } @end @implementation DDHidJoystickStick - (id) init { self = [super init]; if (self == nil) return nil; mXAxisElement = nil; mYAxisElement = nil; mStickElements = [[NSMutableArray alloc] init]; mPovElements = [[NSMutableArray alloc] init]; return self; } //=========================================================== // dealloc //=========================================================== - (void) dealloc { [mXAxisElement release]; [mYAxisElement release]; [mStickElements release]; [mPovElements release]; mXAxisElement = nil; mYAxisElement = nil; mStickElements = nil; mPovElements = nil; [super dealloc]; } - (BOOL) addElement: (DDHidElement *) element; { DDHidUsage * usage = [element usage]; if ([usage usagePage] != kHIDPage_GenericDesktop) return NO; BOOL elementAdded = YES; switch ([usage usageId]) { case kHIDUsage_GD_X: if (mXAxisElement == nil) mXAxisElement = [element retain]; else [mStickElements addObject: element]; break; case kHIDUsage_GD_Y: if (mYAxisElement == nil) mYAxisElement = [element retain]; else [mStickElements addObject: element]; break; case kHIDUsage_GD_Z: case kHIDUsage_GD_Rx: case kHIDUsage_GD_Ry: case kHIDUsage_GD_Rz: [mStickElements addObject: element]; break; case kHIDUsage_GD_Hatswitch: [mPovElements addObject: element]; break; default: elementAdded = NO; } return elementAdded; } - (NSArray *) allElements; { NSMutableArray * elements = [NSMutableArray array]; if (mXAxisElement != nil) [elements addObject: mXAxisElement]; if (mYAxisElement != nil) [elements addObject: mYAxisElement]; [elements addObjectsFromArray: mStickElements]; [elements addObjectsFromArray: mPovElements]; return elements; } - (DDHidElement *) xAxisElement; { return mXAxisElement; } - (DDHidElement *) yAxisElement; { return mYAxisElement; } #pragma mark - #pragma mark mStickElements - indexed accessors - (unsigned int) countOfStickElements { return [mStickElements count]; } - (DDHidElement *) objectInStickElementsAtIndex: (unsigned int)index { return [mStickElements objectAtIndex: index]; } #pragma mark - #pragma mark PovElements - indexed accessors - (unsigned int) countOfPovElements; { return [mPovElements count]; } - (DDHidElement *) objectInPovElementsAtIndex: (unsigned int)index; { return [mPovElements objectAtIndex: index]; } - (NSString *) description; { return [mStickElements description]; } @end