Analog mouse scrolling.
[enjoyable.git] / JoystickController.m
index 864a917..33201f5 100644 (file)
@@ -7,7 +7,6 @@
 
 #import "JoystickController.h"
 
-#import "ApplicationController.h"
 #import "Config.h"
 #import "ConfigsController.h"
 #import "Joystick.h"
 
 @implementation JoystickController {
     IOHIDManagerRef hidManager;
-    BOOL programmaticallySelecting;
     NSTimer *continuousTimer;
+    NSMutableArray *runningTargets;
+    NSMutableArray *_joysticks;
 }
 
-@synthesize joysticks;
-@synthesize runningTargets;
-@synthesize selectedAction;
-@synthesize frontWindowOnly;
-@synthesize mouseLoc;
-
 - (id)init {
     if ((self = [super init])) {
-        joysticks = [[NSMutableArray alloc] initWithCapacity:16];
+        _joysticks = [[NSMutableArray alloc] initWithCapacity:16];
         runningTargets = [[NSMutableArray alloc] initWithCapacity:32];
     }
     return self;
@@ -48,8 +42,9 @@
 }
 
 - (void)addRunningTarget:(Target *)target {
-    if (![runningTargets containsObject:target])
+    if (![runningTargets containsObject:target]) {
         [runningTargets addObject:target];
+    }
     if (!continuousTimer) {
         continuousTimer = [NSTimer scheduledTimerWithTimeInterval:1.f/60.f
                                                            target:self
     }
 }
 
+- (void)runTargetForDevice:(IOHIDDeviceRef)device value:(IOHIDValueRef)value {
+    Joystick *js = [self findJoystickByRef:device];
+    JSAction *mainAction = [js actionForEvent:value];
+    [mainAction notifyEvent:value];
+    NSArray *children = mainAction.children ? mainAction.children : mainAction ? @[mainAction] : @[];
+    for (JSAction *subaction in children) {
+        Target *target = configsController.currentConfig[subaction];
+        target.magnitude = mainAction.magnitude;
+        target.running = subaction.active;
+        if (target.running && target.isContinuous)
+            [self addRunningTarget:target];
+    }
+}
+
+- (void)showTargetForDevice:(IOHIDDeviceRef)device value:(IOHIDValueRef)value {
+    Joystick *js = [self findJoystickByRef:device];
+    JSAction *handler = [js handlerForEvent:value];
+    if (!handler)
+        return;
+    
+    [self expandRecursive:handler];
+    [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[outlineView rowForItem:handler]] byExtendingSelection: NO];
+    [targetController focusKey];
+}
+
 static void input_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDValueRef value) {
     JoystickController *controller = (__bridge JoystickController *)ctx;
     IOHIDDeviceRef device = IOHIDQueueGetDevice(inSender);
     
-    Joystick *js = [controller findJoystickByRef:device];
-    if (((ApplicationController *)[NSApplication sharedApplication].delegate).active) {
-        JSAction *mainAction = [js actionForEvent:value];
-        [mainAction notifyEvent:value];
-        NSArray *children = mainAction.children ? mainAction.children : mainAction ? @[mainAction] : @[];
-        for (JSAction *subaction in children) {
-            Target *target = controller.currentConfig[subaction];
-            target.magnitude = mainAction.magnitude;
-            target.running = subaction.active;
-            if (target.running && target.isContinuous)
-                [controller addRunningTarget:target];
-        }
-    } else if ([NSApplication sharedApplication].isActive
-               && [NSApplication sharedApplication].mainWindow.isVisible) {
-        // joysticks not active, use it to select stuff
-        JSAction *handler = [js handlerForEvent:value];
-        if (!handler)
-            return;
-        
-        [controller expandRecursive:handler];
-        controller->programmaticallySelecting = YES;
-        [controller->outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[controller->outlineView rowForItem:handler]] byExtendingSelection: NO];
+    if (controller.sendingRealEvents) {
+        [controller runTargetForDevice:device value:value];
+    } else if ([NSApplication sharedApplication].mainWindow.isVisible) {
+        [controller showTargetForDevice:device value:value];
     }
 }
 
@@ -103,17 +105,21 @@ static int findAvailableIndex(NSArray *list, Joystick *js) {
     }
 }
 
+- (void)addJoystickForDevice:(IOHIDDeviceRef)device {
+    IOHIDDeviceRegisterInputValueCallback(device, input_callback, (__bridge void*)self);
+    Joystick *js = [[Joystick alloc] initWithDevice:device];
+    js.index = findAvailableIndex(_joysticks, js);
+    [_joysticks addObject:js];
+    [outlineView reloadData];
+}
+
 static void add_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDDeviceRef device) {
     JoystickController *controller = (__bridge JoystickController *)ctx;
-    IOHIDDeviceRegisterInputValueCallback(device, input_callback, (__bridge void*)controller);
-    Joystick *js = [[Joystick alloc] initWithDevice:device];
-    js.index = findAvailableIndex(controller.joysticks, js);
-    [[controller joysticks] addObject:js];
-    [controller->outlineView reloadData];
+    [controller addJoystickForDevice:device];
 }
 
 - (Joystick *)findJoystickByRef:(IOHIDDeviceRef)device {
-    for (Joystick *js in joysticks)
+    for (Joystick *js in _joysticks)
         if (js.device == device)
             return js;
     return nil;
@@ -121,21 +127,27 @@ static void add_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDDevi
 
 static void remove_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDDeviceRef device) {
     JoystickController *controller = (__bridge JoystickController *)ctx;
-    Joystick *match = [controller findJoystickByRef:device];
+    [controller removeJoystickForDevice:device];
+}
+
+- (void)removeJoystickForDevice:(IOHIDDeviceRef)device {
+    Joystick *match = [self findJoystickByRef:device];
     IOHIDDeviceRegisterInputValueCallback(device, NULL, NULL);
     if (match) {
-        [controller.joysticks removeObject:match];
-        [controller->outlineView reloadData];
+        [_joysticks removeObject:match];
+        [outlineView reloadData];
     }
+    
 }
 
 - (void)updateContinuousActions:(NSTimer *)timer {
     self.mouseLoc = [NSEvent mouseLocation];
-    for (Target *target in [self.runningTargets copy]) {
-        if (![target update:self])
-            [self.runningTargets removeObject:target];
+    for (Target *target in [runningTargets copy]) {
+        if (![target update:self]) {
+            [runningTargets removeObject:target];
+        }
     }
-    if (!self.runningTargets.count) {
+    if (!runningTargets.count) {
         [continuousTimer invalidate];
         continuousTimer = nil;
         NSLog(@"Unscheduled continuous target timer.");
@@ -156,31 +168,37 @@ static void remove_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDD
     IOHIDManagerSetDeviceMatchingMultiple(hidManager, (__bridge CFArrayRef)criteria);
     
     IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-    IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone); // FIXME: If an error happens, report it!
+    IOReturn ret = IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone);
+    if (ret != kIOReturnSuccess) {
+        [[NSAlert alertWithMessageText:@"Input devices are unavailable"
+                         defaultButton:nil
+                       alternateButton:nil
+                           otherButton:nil
+             informativeTextWithFormat:@"Error 0x%08x occured trying to access your devices. "
+                                       @"Input may not be correctly detected or mapped.",
+                                       ret]
+         runModal];
+    }
     
     IOHIDManagerRegisterDeviceMatchingCallback(hidManager, add_callback, (__bridge void *)self);
     IOHIDManagerRegisterDeviceRemovalCallback(hidManager, remove_callback, (__bridge void *)self);
 }
 
-- (Config *)currentConfig {
-    return configsController.currentConfig;
-}
-
 - (JSAction *)selectedAction {
     id item = [outlineView itemAtRow:outlineView.selectedRow];
     return [item children] ? nil : item;
 }
 
-- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
-    return item ? [[item children] count] : [joysticks count];
+- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
+    return item ? [[item children] count] : _joysticks.count;
 }
 
 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
     return item ? [[item children] count] > 0: YES;
 }
 
-- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item {
-    return item ? [item children][index] : joysticks[index];
+- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
+    return item ? [item children][index] : _joysticks[index];
 }
 
 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item  {
@@ -189,12 +207,8 @@ static void remove_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDD
     return [item name];
 }
 
-- (void)outlineViewSelectionDidChange: (NSNotification*) notification {
-    [targetController reset];
-    [targetController load];
-    if (programmaticallySelecting)
-        [targetController focusKey];
-    programmaticallySelecting = NO;
+- (void)outlineViewSelectionDidChange:(NSNotification *)notification {
+    [targetController loadCurrent];
 }
 
 @end