2 // JoystickController.m
5 // Created by Sam McCall on 4/05/09.
8 @implementation JoystickController
10 @synthesize joysticks, selectedAction;
13 if(self=[super init]) {
14 joysticks = [[NSMutableArray alloc]init];
15 programmaticallySelecting = NO;
21 for(int i=0; i<[joysticks count]; i++) {
22 [[joysticks objectAtIndex:i] invalidate];
24 IOHIDManagerClose(hidManager, kIOHIDOptionsTypeNone);
25 CFRelease(hidManager);
29 static NSMutableDictionary* create_criterion( UInt32 inUsagePage, UInt32 inUsage )
31 NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
32 [dict setObject: [NSNumber numberWithInt: inUsagePage] forKey: (NSString*)CFSTR(kIOHIDDeviceUsagePageKey)];
33 [dict setObject: [NSNumber numberWithInt: inUsage] forKey: (NSString*)CFSTR(kIOHIDDeviceUsageKey)];
37 -(void) expandRecursive: (id) handler {
39 [self expandRecursive: [handler base]];
40 [outlineView expandItem: handler];
43 void input_callback(void* inContext, IOReturn inResult, void* inSender, IOHIDValueRef value) {
44 JoystickController* self = (JoystickController*)inContext;
45 IOHIDDeviceRef device = IOHIDQueueGetDevice((IOHIDQueueRef) inSender);
47 Joystick* js = [self findJoystickByRef: device];
48 if([[[NSApplication sharedApplication] delegate] active]) {
50 JSAction* mainAction = [js actionForEvent: value];
54 [mainAction notifyEvent: value];
55 NSArray* subactions = [mainAction subActions];
57 subactions = [NSArray arrayWithObject:mainAction];
58 for(id subaction in subactions) {
59 Target* target = [[self->configsController currentConfig] getTargetForAction:subaction];
62 /* target application? doesn't seem to be any need since we are only active when it's in front */
63 /* might be required for some strange actions */
64 [target setRunning: [subaction active]];
65 [target setInputValue: IOHIDValueGetIntegerValue(value)];
67 } else if([[NSApplication sharedApplication] isActive] && [[[NSApplication sharedApplication]mainWindow]isVisible]) {
68 // joysticks not active, use it to select stuff
69 id handler = [js handlerForEvent: value];
73 [self expandRecursive: handler];
74 self->programmaticallySelecting = YES;
75 [self->outlineView selectRowIndexes: [NSIndexSet indexSetWithIndex: [self->outlineView rowForItem: handler]] byExtendingSelection: NO];
79 int findAvailableIndex(id list, Joystick* js) {
82 for(int index=0;;index++) {
84 for(int i=0; i<[list count]; i++) {
85 js2 = [list objectAtIndex: i];
86 if([js2 vendorId] == [js vendorId] && [js2 productId] == [js productId] && [js index] == index) {
96 void add_callback(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef device) {
97 JoystickController* self = (JoystickController*)inContext;
99 IOHIDDeviceOpen(device, kIOHIDOptionsTypeNone);
100 IOHIDDeviceRegisterInputValueCallback(device, input_callback, (void*) self);
102 Joystick *js = [[Joystick alloc] initWithDevice: device];
103 [js setIndex: findAvailableIndex([self joysticks], js)];
105 [js populateActions];
107 [[self joysticks] addObject: js];
108 [self->outlineView reloadData];
111 -(Joystick*) findJoystickByRef: (IOHIDDeviceRef) device {
112 for(int i=0; i<[joysticks count]; i++)
113 if([[joysticks objectAtIndex:i] device] == device)
114 return [joysticks objectAtIndex:i];
118 void remove_callback(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef device) {
119 JoystickController* self = (JoystickController*)inContext;
121 Joystick* match = [self findJoystickByRef: device];
125 [[self joysticks] removeObject: match];
128 [self->outlineView reloadData];
132 hidManager = IOHIDManagerCreate( kCFAllocatorDefault, kIOHIDOptionsTypeNone);
133 NSArray *criteria = [NSArray arrayWithObjects:
134 create_criterion(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick),
135 create_criterion(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad),
136 create_criterion(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController),
137 create_criterion(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard),
140 IOHIDManagerSetDeviceMatchingMultiple(hidManager, (CFArrayRef)criteria);
142 IOHIDManagerScheduleWithRunLoop( hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
143 IOReturn tIOReturn = IOHIDManagerOpen( hidManager, kIOHIDOptionsTypeNone );
146 IOHIDManagerRegisterDeviceMatchingCallback( hidManager, add_callback, (void*)self );
147 IOHIDManagerRegisterDeviceRemovalCallback(hidManager, remove_callback, (void*) self);
148 // IOHIDManagerRegisterInputValueCallback(hidManager, input_callback, (void*)self);
149 // register individually so we can find the device more easily
152 -(id) determineSelectedAction {
153 id item = [outlineView itemAtRow: [outlineView selectedRow]];
156 if([item isKindOfClass: [JSAction class]] && [item subActions] != NULL)
158 if([item isKindOfClass: [Joystick class]])
165 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
167 return [joysticks count];
168 if([item isKindOfClass: [Joystick class]])
169 return [[item children] count];
170 if([item isKindOfClass: [JSAction class]] && [item subActions] != NULL)
171 return [[item subActions] count];
175 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
178 if([item isKindOfClass: [Joystick class]])
180 if([item isKindOfClass: [JSAction class]])
181 return [item subActions]==NULL ? NO : YES;
185 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item {
187 return [joysticks objectAtIndex: index];
189 if([item isKindOfClass: [Joystick class]])
190 return [[item children] objectAtIndex: index];
192 if([item isKindOfClass: [JSAction class]])
193 return [[item subActions] objectAtIndex:index];
197 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
203 - (void)outlineViewSelectionDidChange: (NSNotification*) notification {
204 [targetController reset];
205 selectedAction = [self determineSelectedAction];
206 [targetController load];
207 if(programmaticallySelecting)
208 [targetController focusKey];
209 programmaticallySelecting = NO;