861ab8d8bc1c44459d22f6515c077cb22aa21b51
[enjoyable.git] / Joystick.m
1 //
2 // Joystick.m
3 // Enjoy
4 //
5 // Created by Sam McCall on 4/05/09.
6 //
7
8 #import "Joystick.h"
9
10 static NSArray *ActionsForElement(IOHIDDeviceRef device, id base) {
11 CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
12 NSMutableArray *children = [NSMutableArray arrayWithCapacity:CFArrayGetCount(elements)];
13
14 int buttons = 0;
15 int axes = 0;
16
17 for (int i = 0; i < CFArrayGetCount(elements); i++) {
18 IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
19 int type = IOHIDElementGetType(element);
20 int usage = IOHIDElementGetUsage(element);
21 int usagePage = IOHIDElementGetUsagePage(element);
22 int max = IOHIDElementGetPhysicalMax(element);
23 int min = IOHIDElementGetPhysicalMin(element);
24 CFStringRef elName = IOHIDElementGetName(element);
25
26 JSAction *action = nil;
27
28 if (!(type == kIOHIDElementTypeInput_Misc
29 || type == kIOHIDElementTypeInput_Axis
30 || type == kIOHIDElementTypeInput_Button))
31 continue;
32
33 if (max - min == 1 || usagePage == kHIDPage_Button || type == kIOHIDElementTypeInput_Button) {
34 action = [[JSActionButton alloc] initWithName:(__bridge NSString *)elName
35 idx:++buttons
36 max:max];
37 } else if (usage == kHIDUsage_GD_Hatswitch) {
38 action = [[JSActionHat alloc] init];
39 } else if (usage >= kHIDUsage_GD_X && usage <= kHIDUsage_GD_Rz) {
40 // TODO(jfw): Scaling equation doesn't seem right if min != 0.
41 action = [[JSActionAnalog alloc] initWithIndex:++axes
42 offset:-1.f
43 scale:2.f / (max - min)];
44 } else {
45 continue;
46 }
47
48 // TODO(jfw): Should be moved into better constructors.
49 action.base = base;
50 action.cookie = IOHIDElementGetCookie(element);
51 [children addObject:action];
52 }
53 return children;
54 }
55
56 @implementation Joystick
57
58 @synthesize vendorId;
59 @synthesize productId;
60 @synthesize productName;
61 @synthesize index;
62 @synthesize device;
63 @synthesize children;
64
65 - (id)initWithDevice:(IOHIDDeviceRef)dev {
66 if ((self = [super init])) {
67 self.device = dev;
68 self.productName = (__bridge NSString *)IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDProductKey));
69 self.vendorId = [(__bridge NSNumber *)IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDVendorIDKey)) intValue];
70 self.productId = [(__bridge NSNumber *)IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDProductIDKey)) intValue];
71 self.children = ActionsForElement(dev, self);
72 }
73 return self;
74 }
75
76 - (NSString *)name {
77 return [NSString stringWithFormat:@"%@ #%d", productName, index];
78 }
79
80 - (id)base {
81 // FIXME(jfw): This is a hack because actions get joysticks as their base.
82 return nil;
83 }
84
85 - (NSString *)stringify {
86 return [[NSString alloc] initWithFormat: @"%d~%d~%d", vendorId, productId, index];
87 }
88
89 - (JSAction *)findActionByCookie:(void *)cookie {
90 for (JSAction *child in children)
91 if (child.cookie == cookie)
92 return child;
93 return nil;
94 }
95
96 - (id)handlerForEvent:(IOHIDValueRef) value {
97 JSAction *mainAction = [self actionForEvent:value];
98 return [mainAction findSubActionForValue:value];
99 }
100
101 - (JSAction *)actionForEvent:(IOHIDValueRef)value {
102 IOHIDElementRef elt = IOHIDValueGetElement(value);
103 void *cookie = IOHIDElementGetCookie(elt);
104 return [self findActionByCookie:cookie];
105 }
106
107 @end