diff options
Diffstat (limited to 'lib/DDHidJoystick.m')
| -rw-r--r-- | lib/DDHidJoystick.m | 654 |
1 files changed, 654 insertions, 0 deletions
diff --git a/lib/DDHidJoystick.m b/lib/DDHidJoystick.m new file mode 100644 index 0000000..601f9b3 --- /dev/null +++ b/lib/DDHidJoystick.m @@ -0,0 +1,654 @@ +/* + * 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 <IOKit/hid/IOHIDUsageTables.h> + +@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], [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 |
