Clean up JoystickController. Modernize more Objective-C syntax. Remove direct public...
authorJoe Wreschnig <joe.wreschnig@gmail.com>
Wed, 27 Feb 2013 16:46:43 +0000 (17:46 +0100)
committerJoe Wreschnig <joe.wreschnig@gmail.com>
Wed, 27 Feb 2013 16:46:43 +0000 (17:46 +0100)
JoystickController.h
JoystickController.m
Target.h
Target.m
TargetMouseMove.m

index c698c17679c5a0208bd3d5f6dc80ca2bef266951..9cebde246714a4d1a29904e9d6e05ff27b2597c1 100644 (file)
 @class TargetController;
 
 @interface JoystickController : NSObject {
-       NSMutableArray *joysticks;
-    NSMutableArray *runningTargets;
-       IOHIDManagerRef hidManager;
-       IBOutlet NSOutlineView* outlineView;
-       IBOutlet TargetController* targetController;
-       IBOutlet ConfigsController* configsController;
-       id selectedAction;
-       BOOL programmaticallySelecting;
-    BOOL frontWindowOnly;
-    
-    @public
-    NSPoint mouseLoc;
+       IBOutlet NSOutlineView *outlineView;
+       IBOutlet TargetController *targetController;
+       IBOutlet ConfigsController *configsController;
 }
 
--(void) setup;
--(Joystick*) findJoystickByRef: (IOHIDDeviceRef) device;
+- (void)setup;
+- (Joystick *)findJoystickByRef:(IOHIDDeviceRef)device;
 
 @property (readonly) JSAction *selectedAction;
 @property (readonly) NSMutableArray *joysticks;
 @property (readonly) NSMutableArray *runningTargets;
-@property (readwrite) BOOL frontWindowOnly;
+@property (assign) NSPoint mouseLoc;
+@property (assign) BOOL frontWindowOnly;
 
 @end
index 6d73a2a7d609b1ad6c248d03005780bbee155de7..c5ddcdc0cbe217b6f8d3b6655ceebc3e032d4e1a 100644 (file)
 
 #import "JoystickController.h"
 
-@implementation JoystickController
-
-@synthesize joysticks, runningTargets, selectedAction, frontWindowOnly;
-
--(id) init {
-       if(self=[super init]) {
-               joysticks = [[NSMutableArray alloc]init];
-        runningTargets = [[NSMutableArray alloc]init];
-               programmaticallySelecting = NO;
-        mouseLoc.x = mouseLoc.y = 0;
-       }
-       return self;
-}
-
--(void) dealloc {
-       for(int i=0; i<[joysticks count]; i++) {
-               [joysticks[i] invalidate];
-       }
-       IOHIDManagerClose(hidManager, kIOHIDOptionsTypeNone);
-       CFRelease(hidManager);
-}
-
-static NSMutableDictionary* create_criterion( UInt32 inUsagePage, UInt32 inUsage )
-{
-       NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
-       dict[(NSString*)CFSTR(kIOHIDDeviceUsagePageKey)] = @(inUsagePage);
-       dict[(NSString*)CFSTR(kIOHIDDeviceUsageKey)] = @(inUsage);
-       return dict;
-} 
-
--(void) expandRecursive: (id) handler {
-       if([handler base])
-               [self expandRecursive: [handler base]];
-       [outlineView expandItem: handler];
-}
-
-static void timer_callback(CFRunLoopTimerRef timer, void *ctx) {
-    JoystickController *jc = (__bridge JoystickController *)ctx;
-    jc->mouseLoc = [NSEvent mouseLocation];
-    for (Target *target in [jc runningTargets]) {
-        [target update: jc];
+@implementation JoystickController {
+    IOHIDManagerRef hidManager;
+    BOOL programmaticallySelecting;
+    NSTimer *continuousTimer;
+}
+
+@synthesize joysticks;
+@synthesize runningTargets;
+@synthesize selectedAction;
+@synthesize frontWindowOnly;
+@synthesize mouseLoc;
+
+- (id)init {
+    if ((self = [super init])) {
+        joysticks = [[NSMutableArray alloc] initWithCapacity:16];
+        runningTargets = [[NSMutableArray alloc] initWithCapacity:32];
+    }
+    return self;
+}
+
+- (void)dealloc {
+    [continuousTimer invalidate];
+    IOHIDManagerClose(hidManager, kIOHIDOptionsTypeNone);
+    CFRelease(hidManager);
+}
+
+- (void)expandRecursive:(id)handler {
+    if ([handler base])
+        [self expandRecursive:[handler base]];
+    [outlineView expandItem:handler];
+}
+
+- (void)addRunningTarget:(Target *)target {
+    if (![runningTargets containsObject:target])
+        [runningTargets addObject:target];
+    if (!continuousTimer) {
+        continuousTimer = [NSTimer scheduledTimerWithTimeInterval:1.f/60.f
+                                                           target:self
+                                                         selector:@selector(updateContinuousActions:)
+                                                         userInfo:nil
+                                                          repeats:YES];
+        NSLog(@"Scheduled continuous target timer.");
     }
 }
 
 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];
-               if(!mainAction)
-                       return;
-               
-               [mainAction notifyEvent: value];
-               NSArray* children = [mainAction children];
-               if(!children)
-                       children = @[mainAction];
-               for(id subaction in children) {
-                       Target* target = [[controller->configsController currentConfig] getTargetForAction:subaction];
-                       if(!target)
-                               continue;
-                       /* target application? doesn't seem to be any need since we are only active when it's in front */
-                       /* might be required for some strange actions */
-            if ([target running] != [subaction active]) {
-                if ([subaction active]) {
-                    [target trigger: controller];
-                }
-                else {
-                    [target untrigger: controller];
-                }
-                [target setRunning: [subaction active]];
+    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];
+        if (!mainAction)
+            return;
+        
+        [mainAction notifyEvent:value];
+        NSArray *children = mainAction.children ? mainAction.children : @[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;
             }
             
-            if ([mainAction isKindOfClass: [JSActionAnalog class]]) {
-                double realValue = [(JSActionAnalog*)mainAction getRealValue: IOHIDValueGetIntegerValue(value)];
-                [target setInputValue: realValue];
-            
-                // Add to list of running targets
-                if ([target isContinuous] && [target running]) {
-                    if (![controller.runningTargets containsObject:target]) {
-                        [[controller runningTargets] addObject: target];
-                    }
-                }
+            // 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];
+            }
+        }
+    } 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];
+    }
+}
+
+static int findAvailableIndex(NSArray *list, Joystick *js) {
+    for (int index = 0; ; index++) {
+        BOOL available = YES;
+        for (Joystick *used in list) {
+            if ([used.productName isEqualToString:js.productName] && used.index == index) {
+                available = NO;
+                break;
             }
-               }
-       } else if([[NSApplication sharedApplication] isActive] && [[[NSApplication sharedApplication]mainWindow]isVisible]) {
-               // joysticks not active, use it to select stuff
-               id handler = [js handlerForEvent: value];
-               if(!handler)
-                       return;
-       
-               [controller expandRecursive: handler];
-               controller->programmaticallySelecting = YES;
-               [controller->outlineView selectRowIndexes: [NSIndexSet indexSetWithIndex: [controller->outlineView rowForItem: handler]] byExtendingSelection: NO];
-       }
-}
-
-static int findAvailableIndex(id list, Joystick* js) {
-       BOOL available;
-       Joystick* js2;
-       for(int index=0;;index++) {
-               available = YES;
-               for(int i=0; i<[list count]; i++) {
-                       js2 = list[i];
-                       if([js2 vendorId] == [js vendorId] && [js2 productId] == [js productId] && [js index] == index) {
-                               available = NO;
-                               break;
-                       }
-               }
-               if(available)
-                       return index;
-       }
+        }
+        if (available)
+            return index;
+    }
 }
 
 static void add_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDDeviceRef device) {
-       JoystickController *controller = (__bridge JoystickController *)ctx;
+    JoystickController *controller = (__bridge JoystickController *)ctx;
     IOHIDDeviceRegisterInputValueCallback(device, input_callback, (__bridge void*)controller);
-       Joystick *js = [[Joystick alloc] initWithDevice:device];
+    Joystick *js = [[Joystick alloc] initWithDevice:device];
     js.index = findAvailableIndex(controller.joysticks, js);
-       [[controller joysticks] addObject:js];
-       [controller->outlineView reloadData];
+    [[controller joysticks] addObject:js];
+    [controller->outlineView reloadData];
 }
-       
+
 - (Joystick *)findJoystickByRef:(IOHIDDeviceRef)device {
     for (Joystick *js in joysticks)
         if (js.device == device)
             return js;
-       return nil;
-}      
+    return nil;
+}
 
 static void remove_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDDeviceRef device) {
-       JoystickController *controller = (__bridge JoystickController *)ctx;
-       Joystick *match = [controller findJoystickByRef:device];
+    JoystickController *controller = (__bridge JoystickController *)ctx;
+    Joystick *match = [controller findJoystickByRef:device];
     IOHIDDeviceRegisterInputValueCallback(device, NULL, NULL);
-       if (match) {
+    if (match) {
         [controller.joysticks removeObject:match];
         [controller->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];
+    }
+    if (!self.runningTargets.count) {
+        [continuousTimer invalidate];
+        continuousTimer = nil;
+        NSLog(@"Unscheduled continuous target timer.");
+    }
+}
+
+#define NSSTR(e) ((NSString *)CFSTR(e))
+
 - (void)setup {
-    hidManager = IOHIDManagerCreate( kCFAllocatorDefault, kIOHIDOptionsTypeNone);
-       NSArray *criteria = @[
-        create_criterion(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick),
-        create_criterion(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad),
-        create_criterion(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController)];
-       
-       IOHIDManagerSetDeviceMatchingMultiple(hidManager, (CFArrayRef)CFBridgingRetain(criteria));
+    hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+    NSArray *criteria = @[ @{ NSSTR(kIOHIDDeviceUsagePageKey) : @(kHIDPage_GenericDesktop),
+                              NSSTR(kIOHIDDeviceUsageKey) : @(kHIDUsage_GD_Joystick) },
+                           @{ NSSTR(kIOHIDDeviceUsagePageKey) : @(kHIDPage_GenericDesktop),
+                              NSSTR(kIOHIDDeviceUsageKey) : @(kHIDUsage_GD_GamePad) },
+                           @{ NSSTR(kIOHIDDeviceUsagePageKey) : @(kHIDPage_GenericDesktop),
+                              NSSTR(kIOHIDDeviceUsageKey) : @(kHIDUsage_GD_MultiAxisController) }
+                           ];
+    IOHIDManagerSetDeviceMatchingMultiple(hidManager, (__bridge CFArrayRef)criteria);
+    
+    IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone); // FIXME: If an error happens, report it!
     
-       IOHIDManagerScheduleWithRunLoop( hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
-       IOReturn tIOReturn = IOHIDManagerOpen( hidManager, kIOHIDOptionsTypeNone );
-       (void)tIOReturn;
-       
-       IOHIDManagerRegisterDeviceMatchingCallback(hidManager, add_callback, (__bridge void *)self );
-       IOHIDManagerRegisterDeviceRemovalCallback(hidManager, remove_callback, (__bridge void *)self);
-
-    // Setup timer for continuous targets
-    CFRunLoopTimerContext ctx = {
-        0, (__bridge void*)self, NULL, NULL, NULL
-    };
-    CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault,
-                                                   CFAbsoluteTimeGetCurrent(), 1.0/80.0,
-                                                   0, 0, timer_callback, &ctx);
-    CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
+    IOHIDManagerRegisterDeviceMatchingCallback(hidManager, add_callback, (__bridge void *)self);
+    IOHIDManagerRegisterDeviceRemovalCallback(hidManager, remove_callback, (__bridge void *)self);
 }
 
 - (JSAction *)selectedAction {
@@ -192,17 +188,17 @@ static void remove_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDD
 }
 
 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item  {
-       if(item == nil)
-               return @"root";
-       return [item name];
+    if(item == nil)
+        return @"root";
+    return [item name];
 }
 
 - (void)outlineViewSelectionDidChange: (NSNotification*) notification {
-       [targetController reset];
-       [targetController load];
-       if (programmaticallySelecting)
-               [targetController focusKey];
-       programmaticallySelecting = NO;
+    [targetController reset];
+    [targetController load];
+    if (programmaticallySelecting)
+        [targetController focusKey];
+    programmaticallySelecting = NO;
 }
-       
+
 @end
index 43b7d0c10fa3a345c657afadecf70f1a45d08f6e..51470bfcd7f089f18945725f0c6cc2b7315e041b 100644 (file)
--- a/Target.h
+++ b/Target.h
@@ -19,7 +19,7 @@
 @property(readwrite) double inputValue;
 -(void) trigger: (JoystickController *)jc;
 -(void) untrigger: (JoystickController *)jc;
--(void) update: (JoystickController *)jc;
+- (BOOL)update:(JoystickController *)jc;
 -(NSString*) stringify;
 +(Target*) unstringify: (NSString*) str withConfigList: (NSArray*) configs;
 
index 052f01a852e407d132cbfd59963ed27064846d4c..e7ea673885798d5461d4c1acffc4ae13e15678d2 100644 (file)
--- a/Target.m
+++ b/Target.m
@@ -41,8 +41,8 @@
        // no-op by default
 }
 
--(void) update: (JoystickController *) jc {
-    [self doesNotRecognizeSelector:_cmd];
+- (BOOL)update:(JoystickController *)jc {
+    return NO;
 }
 
 -(BOOL) isContinuous {
index 86dd7367749b562936018f232bfb253b8da3caa4..78d9c93198d75d684a13845f3264ce3b9ddc7a2c 100644 (file)
     return;
 }
 
--(void) update: (JoystickController *)jc {
+- (BOOL)update:(JoystickController *)jc {
     //printf("Dir %d inputValue %f\n", [self dir], [self inputValue]);
     if (fabs([self inputValue]) < 0.01)
-        return; // dead zone
+        return NO; // dead zone
     
     NSRect screenRect = [[NSScreen mainScreen] frame];
     NSInteger height = screenRect.size.height;
         dx = [self inputValue] * speed;
     else
         dy = [self inputValue] * speed;
-    NSPoint *mouseLoc = &jc->mouseLoc;
-    mouseLoc->x += dx;
-    mouseLoc->y -= dy;
+    NSPoint mouseLoc = jc.mouseLoc;
+    mouseLoc.x += dx;
+    mouseLoc.y -= dy;
+    jc.mouseLoc = mouseLoc;
     
     CGEventRef move = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved,
-                                              CGPointMake(mouseLoc->x, height - mouseLoc->y),
+                                              CGPointMake(mouseLoc.x, height - mouseLoc.y),
                                               0);
     CGEventSetType(move, kCGEventMouseMoved);
     CGEventSetIntegerValueField(move, kCGMouseEventDeltaX, dx);
@@ -73,6 +74,7 @@
     }
     
     CFRelease(move);
+    return dx || dy;
 }
 
 @end