// // Joystick.m // Enjoy // // Created by Sam McCall on 4/05/09. // @implementation Joystick @synthesize vendorId, productId, productName, name, index, device, children; -(id)initWithDevice: (IOHIDDeviceRef) newDevice { if(self=[super init]) { children = [[NSMutableArray alloc]init]; device = newDevice; productName = (__bridge NSString*)IOHIDDeviceGetProperty( device, CFSTR(kIOHIDProductKey) ); vendorId = [(__bridge NSNumber*)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)) intValue]; productId = [(__bridge NSNumber*)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)) intValue]; name = productName; } return self; } -(void) setIndex: (int) newIndex { index = newIndex; name = [[NSString alloc] initWithFormat: @"%@ #%d", productName, (index+1)]; } -(int) index { return index; } -(void) invalidate { IOHIDDeviceClose(device, kIOHIDOptionsTypeNone); NSLog(@"Removed a device: %@", [self name]); } -(id) base { return NULL; } -(void) populateActions { NSArray* elements = (NSArray*)CFBridgingRelease(IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone)); int buttons = 0; int axes = 0; for(int i=0; i<[elements count]; i++) { IOHIDElementRef element = (__bridge IOHIDElementRef)elements[i]; int type = IOHIDElementGetType(element); int usage = IOHIDElementGetUsage(element); int usagePage = IOHIDElementGetUsagePage(element); int max = IOHIDElementGetPhysicalMax(element); int min = IOHIDElementGetPhysicalMin(element); CFStringRef elName = IOHIDElementGetName(element); // if(usagePage != 1 || usagePage == 9) { // NSLog(@"Skipping usage page %x usage %x", usagePage, usage); // continue; // } JSAction* action = NULL; if(!(type == kIOHIDElementTypeInput_Misc || type == kIOHIDElementTypeInput_Axis || type == kIOHIDElementTypeInput_Button)) { continue; } if((max - min == 1) || usagePage == kHIDPage_Button || type == kIOHIDElementTypeInput_Button) { action = [[JSActionButton alloc] initWithIndex: buttons++ andName: (__bridge NSString *)elName]; [(JSActionButton*)action setMax: max]; } else if(usage == 0x39) action = [[JSActionHat alloc] init]; else { if(usage >= 0x30 && usage < 0x36) { action = [[JSActionAnalog alloc] initWithIndex: axes++]; [(JSActionAnalog*)action setOffset: (double)-1.0]; [(JSActionAnalog*)action setScale: (double)2.0/(max - min)]; } else continue; } [action setBase: self]; [action setUsage: usage]; [action setCookie: IOHIDElementGetCookie(element)]; [children addObject:action]; } } -(NSString*) stringify { return [[NSString alloc] initWithFormat: @"%d~%d~%d", vendorId, productId, index]; } - (JSAction*) findActionByCookie: (void*) cookie { for(int i=0; i<[children count]; i++) if([children[i]cookie] == cookie) return (JSAction*)children[i]; return NULL; } -(id) handlerForEvent: (IOHIDValueRef) value { JSAction* mainAction = [self actionForEvent: value]; if(!mainAction) return NULL; return [mainAction findSubActionForValue: value]; } -(JSAction*) actionForEvent: (IOHIDValueRef) value { IOHIDElementRef elt = IOHIDValueGetElement(value); void* cookie = IOHIDElementGetCookie(elt); return [self findActionByCookie: cookie]; } @end