Better constructor for Config.
[enjoyable.git] / JoystickController.m
index c5ddcdc..13db938 100644 (file)
@@ -7,17 +7,24 @@
 
 #import "JoystickController.h"
 
+#import "Config.h"
+#import "ConfigsController.h"
+#import "Joystick.h"
+#import "JSAction.h"
+#import "Target.h"
+#import "TargetController.h"
+
 @implementation JoystickController {
     IOHIDManagerRef hidManager;
-    BOOL programmaticallySelecting;
     NSTimer *continuousTimer;
+    NSMutableArray *runningTargets;
 }
 
 @synthesize joysticks;
-@synthesize runningTargets;
 @synthesize selectedAction;
 @synthesize frontWindowOnly;
 @synthesize mouseLoc;
+@synthesize sendingRealEvents;
 
 - (id)init {
     if ((self = [super init])) {
@@ -40,8 +47,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
@@ -57,48 +65,31 @@ static void input_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDVa
     IOHIDDeviceRef device = IOHIDQueueGetDevice(inSender);
     
     Joystick *js = [controller findJoystickByRef:device];
-    if (((ApplicationController *)[NSApplication sharedApplication].delegate).active) {
+    if (controller.sendingRealEvents) {
         JSAction *mainAction = [js actionForEvent:value];
-        if (!mainAction)
-            return;
-        
         [mainAction notifyEvent:value];
-        NSArray *children = mainAction.children ? mainAction.children : @[mainAction];
+        NSArray *children = mainAction.children ? mainAction.children : mainAction ? @[mainAction] : @[];
         for (JSAction *subaction in children) {
-            Target *target = [[controller->configsController currentConfig] getTargetForAction:subaction];
-            if (!target)
-                continue;
-            // TODO: Can we just trigger based on setRunning:?
-            if (target.running != subaction.active) {
-                if (subaction.active)
-                    [target trigger:controller];
-                else
-                    [target untrigger:controller];
-                target.running = subaction.active;
-            }
-            
-            // FIXME: Hack, should just expose analog info properly in continuous target.
-            if ([mainAction isKindOfClass:[JSActionAnalog class]]) {
-                double realValue = [(JSActionAnalog *)mainAction getRealValue:IOHIDValueGetIntegerValue(value)];
-                [target setInputValue:realValue];
-                if (target.isContinuous && target.running)
-                    [controller addRunningTarget:target];
-            }
+            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
+    } else if ([NSApplication sharedApplication].isActive
+               && [NSApplication sharedApplication].mainWindow.isVisible) {
         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];
+        [controller->targetController focusKey];
     }
 }
 
 static int findAvailableIndex(NSArray *list, Joystick *js) {
-    for (int index = 0; ; index++) {
+    for (int index = 1; ; index++) {
         BOOL available = YES;
         for (Joystick *used in list) {
             if ([used.productName isEqualToString:js.productName] && used.index == index) {
@@ -139,11 +130,13 @@ static void remove_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDD
 
 - (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];
+            NSLog(@"Removing action, now running %lu.", runningTargets.count);
+        }
     }
-    if (!self.runningTargets.count) {
+    if (!runningTargets.count) {
         [continuousTimer invalidate];
         continuousTimer = nil;
         NSLog(@"Unscheduled continuous target timer.");
@@ -164,12 +157,26 @@ 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;
@@ -196,9 +203,6 @@ static void remove_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDD
 - (void)outlineViewSelectionDidChange: (NSNotification*) notification {
     [targetController reset];
     [targetController load];
-    if (programmaticallySelecting)
-        [targetController focusKey];
-    programmaticallySelecting = NO;
 }
 
 @end