Formal protocol for the interface shared between Joysticks and JSActions, use new...
[enjoyable.git] / Joystick.m
index 8fa6791..4753caf 100644 (file)
 //  Created by Sam McCall on 4/05/09.
 //
 
-@implementation Joystick
+#import "Joystick.h"
 
+#import "JSAction.h"
+#import "JSActionAnalog.h"
+#import "JSActionButton.h"
+#import "JSActionHat.h"
 
-@synthesize    vendorId, productId, productName, name, index, device, children;
+static NSArray *ActionsForElement(IOHIDDeviceRef device, id base) {
+    CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
+    NSMutableArray *children = [NSMutableArray arrayWithCapacity:CFArrayGetCount(elements)];
+    
+    int buttons = 0;
+    int axes = 0;
+    int hats = 0;
+    
+    for (int i = 0; i < CFArrayGetCount(elements); i++) {
+        IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
+        int type = IOHIDElementGetType(element);
+        unsigned usage = IOHIDElementGetUsage(element);
+        unsigned usagePage = IOHIDElementGetUsagePage(element);
+        long max = IOHIDElementGetPhysicalMax(element);
+        long min = IOHIDElementGetPhysicalMin(element);
+        CFStringRef elName = IOHIDElementGetName(element);
+        
+        JSAction *action = nil;
+        
+        if (!(type == kIOHIDElementTypeInput_Misc
+              || type == kIOHIDElementTypeInput_Axis
+              || type == kIOHIDElementTypeInput_Button))
+             continue;
+        
+        if (max - min == 1 || usagePage == kHIDPage_Button || type == kIOHIDElementTypeInput_Button) {
+            action = [[JSActionButton alloc] initWithName:(__bridge NSString *)elName
+                                                      idx:++buttons
+                                                      max:max];
+        } else if (usage == kHIDUsage_GD_Hatswitch) {
+            action = [[JSActionHat alloc] initWithIndex:++hats];
+        } else if (usage >= kHIDUsage_GD_X && usage <= kHIDUsage_GD_Rz) {
+            action = [[JSActionAnalog alloc] initWithIndex:++axes
+                                                    rawMin:min
+                                                    rawMax:max];
+        } else {
+            continue;
+        }
+        
+        // TODO(jfw): Should be moved into better constructors.
+        action.base = base;
+        action.cookie = IOHIDElementGetCookie(element);
+        [children addObject:action];
+    }
 
--(id)initWithDevice: (IOHIDDeviceRef) newDevice {
-       if(self=[super init]) {
-               children = [[NSMutableArray alloc]init];
-               
-               device = newDevice;
-               productName = (NSString*)IOHIDDeviceGetProperty( device, CFSTR(kIOHIDProductKey) );
-               vendorId = [(NSNumber*)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)) intValue];
-               productId = [(NSNumber*)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)) intValue];
-               
-               name = productName;
-       }
-       return self;
+    CFRelease(elements);
+    return children;
 }
 
--(void) setIndex: (int) newIndex {
-       index = newIndex;
-       name = [[NSString alloc] initWithFormat: @"%@ #%d", productName, (index+1)];
-}
--(int) index {
-       return index;
+@implementation Joystick {
+    int vendorId;
+    int productId;
 }
 
--(void) invalidate {
-       IOHIDDeviceClose(device, kIOHIDOptionsTypeNone);
-       NSLog(@"Removed a device: %@", [self name]);
+- (id)initWithDevice:(IOHIDDeviceRef)dev {
+    if ((self = [super init])) {
+        self.device = dev;
+        self.productName = (__bridge NSString *)IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDProductKey));
+        vendorId = [(__bridge NSNumber *)IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDVendorIDKey)) intValue];
+        productId = [(__bridge NSNumber *)IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDProductIDKey)) intValue];
+        self.children = ActionsForElement(dev, self);
+    }
+    return self;
 }
 
--(id) base {
-       return NULL;
+- (NSString *)name {
+    return [NSString stringWithFormat:@"%@ #%d", _productName, _index];
 }
 
--(void) populateActions {
-       NSArray* elements = (NSArray*)IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
-       
-       int buttons = 0;
-       int axes = 0;
-       
-       for(int i=0; i<[elements count]; i++) {
-               IOHIDElementRef element = (IOHIDElementRef)[elements objectAtIndex: 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: (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];
-       }
+- (id)base {
+    return nil;
 }
 
--(NSString*) stringify {
-       return [[NSString alloc] initWithFormat: @"%d~%d~%d", vendorId, productId, index];
+- (NSString *)uid {
+    return [NSString stringWithFormat: @"%d:%d:%d", vendorId, productId, _index];
 }
 
-- (JSAction*) findActionByCookie: (void*) cookie {
-       for(int i=0; i<[children count]; i++)
-               if([[children objectAtIndex:i]cookie] == cookie)
-                       return (JSAction*)[children objectAtIndex:i];
-       return NULL;
+- (JSAction *)findActionByCookie:(IOHIDElementCookie)cookie {
+    for (JSAction *child in _children)
+        if (child.cookie == cookie)
+            return child;
+    return nil;
 }
 
--(id) handlerForEvent: (IOHIDValueRef) value {
-       JSAction* mainAction = [self actionForEvent: value];
-       if(!mainAction)
-               return NULL;
-       return [mainAction findSubActionForValue: value];
+- (JSAction *)handlerForEvent:(IOHIDValueRef)value {
+    JSAction *mainAction = [self actionForEvent:value];
+    return [mainAction findSubActionForValue:value];
 }
--(JSAction*) actionForEvent: (IOHIDValueRef) value {
-       IOHIDElementRef elt = IOHIDValueGetElement(value);
-       void* cookie = IOHIDElementGetCookie(elt);
-       return [self findActionByCookie: cookie];
+
+- (JSAction *)actionForEvent:(IOHIDValueRef)value {
+    IOHIDElementRef elt = IOHIDValueGetElement(value);
+    IOHIDElementCookie cookie = IOHIDElementGetCookie(elt);
+    return [self findActionByCookie:cookie];
 }
 
 @end