Merge device and mapping controllers into NJInputController.
authorJoe Wreschnig <joe.wreschnig@gmail.com>
Wed, 20 Mar 2013 19:51:30 +0000 (20:51 +0100)
committerJoe Wreschnig <joe.wreschnig@gmail.com>
Wed, 20 Mar 2013 19:51:30 +0000 (20:51 +0100)
18 files changed:
Classes/EnjoyableApplicationDelegate.h
Classes/EnjoyableApplicationDelegate.m
Classes/NJDeviceController.h [deleted file]
Classes/NJDeviceController.m [deleted file]
Classes/NJInputController.h [new file with mode: 0644]
Classes/NJInputController.m [new file with mode: 0644]
Classes/NJMappingsController.h [deleted file]
Classes/NJMappingsController.m [deleted file]
Classes/NJOutput.h
Classes/NJOutput.m
Classes/NJOutputController.h
Classes/NJOutputController.m
Classes/NJOutputMapping.m
Classes/NJOutputMouseMove.m
Classes/NJOutputMouseScroll.m
Enjoyable.xcodeproj/project.pbxproj
Info.plist
Resources/English.lproj/MainMenu.xib

index 3b1f2f4..ddc6acf 100644 (file)
 #import "NJMappingsViewController.h"
 #import "NJDeviceViewController.h"
 #import "NJOutputController.h"
-#import "NJDeviceController.h"
+#import "NJInputController.h"
 
 @interface EnjoyableApplicationDelegate : NSObject <NSApplicationDelegate,
-                                                    NJDeviceControllerDelegate,
+                                                    NJInputControllerDelegate,
                                                     NJDeviceViewControllerDelegate,
                                                     NJMappingsViewControllerDelegate,
                                                     NJMappingMenuDelegate,
                                                     NSWindowDelegate>
 
-@property (nonatomic, strong) IBOutlet NJMappingsController *mappingsController;
-@property (nonatomic, strong) IBOutlet NJDeviceController *deviceController;
+@property (nonatomic, strong) IBOutlet NJInputController *inputController;
 @property (nonatomic, strong) IBOutlet NJOutputController *outputController;
 @property (nonatomic, strong) IBOutlet NJMappingsViewController *mvc;
 @property (nonatomic, strong) IBOutlet NJDeviceViewController *dvc;
index 7646363..96aa8dd 100644 (file)
@@ -10,7 +10,7 @@
 #import "EnjoyableApplicationDelegate.h"
 
 #import "NJMapping.h"
-#import "NJMappingsController.h"
+#import "NJInput.h"
 #import "NJEvents.h"
 
 @implementation EnjoyableApplicationDelegate {
@@ -20,7 +20,7 @@
 - (void)didSwitchApplication:(NSNotification *)note {
     NSRunningApplication *activeApp = note.userInfo[NSWorkspaceApplicationKey];
     if (activeApp)
-        [self.mappingsController activateMappingForProcess:activeApp];
+        [self.inputController activateMappingForProcess:activeApp];
 }
 
 - (void)applicationWillFinishLaunching:(NSNotification *)notification {
         name:NJEventSimulationStopped
         object:nil];
 
-    [self.mappingsController load];
+    [self.inputController load];
     [self.mvc.mappingList reloadData];
     [self.mvc changedActiveMappingToIndex:
-     [self.mappingsController indexOfMapping:
-      self.mappingsController.currentMapping]];
+     [self.inputController indexOfMapping:
+      self.inputController.currentMapping]];
 
     statusItem = [NSStatusBar.systemStatusBar statusItemWithLength:36];
     statusItem.image = [NSImage imageNamed:@"Status Menu Icon Disabled"];
     NSURL *URL = [NSURL fileURLWithPath:filename];
     NJMapping *mapping = [NJMapping mappingWithContentsOfURL:URL
                                                        error:&error];
-    if ([[self.mappingsController mappingForKey:mapping.name] hasConflictWith:mapping]) {
-        [self promptForMapping:mapping atIndex:self.mappingsController.mappings.count];
-    } else if ([self.mappingsController  mappingForKey:mapping.name]) {
-        [[self.mappingsController mappingForKey:mapping.name] mergeEntriesFrom:mapping];
+    if ([[self.inputController mappingForKey:mapping.name] hasConflictWith:mapping]) {
+        [self promptForMapping:mapping atIndex:self.inputController.mappings.count];
+    } else if ([self.inputController  mappingForKey:mapping.name]) {
+        [[self.inputController mappingForKey:mapping.name] mergeEntriesFrom:mapping];
     } else if (mapping) {
         [self.mvc beginUpdates];
-        [self.mappingsController addMapping:mapping];
-        [self.mvc addedMappingAtIndex:self.mappingsController.mappings.count - 1 startEditing:NO];
+        [self.inputController addMapping:mapping];
+        [self.mvc addedMappingAtIndex:self.inputController.mappings.count - 1 startEditing:NO];
         [self.mvc endUpdates];
-        [self.mappingsController activateMapping:mapping];
+        [self.inputController activateMapping:mapping];
     } else {
         [self.window presentError:error
                    modalForWindow:self.window
 }
 
 - (void)mappingWasChosen:(NJMapping *)mapping {
-    [self.mappingsController activateMapping:mapping];
+    [self.inputController activateMapping:mapping];
 }
 
 - (void)mappingListShouldOpen {
                       NSError *error;
                       NJMapping *mapping = [NJMapping mappingWithContentsOfURL:panel.URL
                                                                          error:&error];
-                      if ([[self.mappingsController mappingForKey:mapping.name] hasConflictWith:mapping]) {
-                          [self promptForMapping:mapping atIndex:self.mappingsController.mappings.count];
-                      } else if ([self.mappingsController mappingForKey:mapping.name]) {
-                          [[self.mappingsController mappingForKey:mapping.name] mergeEntriesFrom:mapping];
+                      if ([[self.inputController mappingForKey:mapping.name] hasConflictWith:mapping]) {
+                          [self promptForMapping:mapping atIndex:self.inputController.mappings.count];
+                      } else if ([self.inputController mappingForKey:mapping.name]) {
+                          [[self.inputController mappingForKey:mapping.name] mergeEntriesFrom:mapping];
                       } else if (mapping) {
-                          [self.mappingsController addMapping:mapping];
+                          [self.inputController addMapping:mapping];
                       } else {
                           [self.window presentError:error
                                      modalForWindow:self.window
 - (void)exportMappingClicked:(id)sender {
     NSSavePanel *panel = [NSSavePanel savePanel];
     panel.allowedFileTypes = @[ @"enjoyable" ];
-    NJMapping *mapping = self.mappingsController.currentMapping;
+    NJMapping *mapping = self.inputController.currentMapping;
     panel.nameFieldStringValue = [mapping.name stringByFixingPathComponent];
     [panel beginSheetModalForWindow:self.window
                   completionHandler:^(NSInteger result) {
     [alert.window orderOut:nil];
     switch (returnCode) {
         case NSAlertFirstButtonReturn: // Merge
-            [self.mappingsController mergeMapping:newMapping intoMapping:oldMapping];
-            [self.mappingsController activateMapping:oldMapping];
+            [self.inputController mergeMapping:newMapping intoMapping:oldMapping];
+            [self.inputController activateMapping:oldMapping];
             break;
         case NSAlertThirdButtonReturn: // New Mapping
             [self.mvc beginUpdates];
-            [self.mappingsController addMapping:newMapping];
+            [self.inputController addMapping:newMapping];
             [self.mvc addedMappingAtIndex:idx startEditing:YES];
             [self.mvc endUpdates];
-            [self.mappingsController activateMapping:newMapping];
+            [self.inputController activateMapping:newMapping];
             break;
         default: // Cancel, other.
             break;
 }
 
 - (void)promptForMapping:(NJMapping *)mapping atIndex:(NSInteger)idx {
-    NJMapping *mergeInto = [self.mappingsController mappingForKey:mapping.name];
+    NJMapping *mergeInto = [self.inputController mappingForKey:mapping.name];
     NSAlert *conflictAlert = [[NSAlert alloc] init];
     conflictAlert.messageText = NSLocalizedString(@"import conflict prompt", @"Title of import conflict alert");
     conflictAlert.informativeText =
 }
 
 - (NSInteger)numberOfMappings:(NJMappingsViewController *)mvc {
-    return self.mappingsController.mappings.count;
+    return self.inputController.mappings.count;
 }
 
 - (NJMapping *)mappingsViewController:(NJMappingsViewController *)mvc
                       mappingForIndex:(NSUInteger)idx {
-    return self.mappingsController.mappings[idx];
+    return self.inputController.mappings[idx];
 }
 
 - (void)mappingsViewController:(NJMappingsViewController *)mvc
           renameMappingAtIndex:(NSInteger)index
                         toName:(NSString *)name {
-    [self.mappingsController renameMapping:self.mappingsController.mappings[index]
+    [self.inputController renameMapping:self.inputController.mappings[index]
                                         to:name];
 }
 
        canMoveMappingFromIndex:(NSInteger)fromIdx
                        toIndex:(NSInteger)toIdx {
     return fromIdx != toIdx && fromIdx != 0 && toIdx != 0
-            && toIdx < (NSInteger)self.mappingsController.mappings.count;
+            && toIdx < (NSInteger)self.inputController.mappings.count;
 }
 
 - (void)mappingsViewController:(NJMappingsViewController *)mvc
                        toIndex:(NSInteger)toIdx {
     [mvc beginUpdates];
     [mvc.mappingList moveRowAtIndex:fromIdx toIndex:toIdx];
-    [self.mappingsController moveMoveMappingFromIndex:fromIdx toIndex:toIdx];
+    [self.inputController moveMoveMappingFromIndex:fromIdx toIndex:toIdx];
     [mvc endUpdates];
 }
 
           removeMappingAtIndex:(NSInteger)idx {
     [mvc beginUpdates];
     [mvc removedMappingAtIndex:idx];
-    [self.mappingsController removeMappingAtIndex:idx];
+    [self.inputController removeMappingAtIndex:idx];
     [mvc endUpdates];
 }
 
                          error:(NSError **)error {
     NJMapping *mapping = [NJMapping mappingWithContentsOfURL:url
                                                        error:error];
-    if ([[self.mappingsController mappingForKey:mapping.name] hasConflictWith:mapping]) {
+    if ([[self.inputController mappingForKey:mapping.name] hasConflictWith:mapping]) {
         [self promptForMapping:mapping atIndex:index];
-    } else if ([self.mappingsController mappingForKey:mapping.name]) {
-        [[self.mappingsController mappingForKey:mapping.name] mergeEntriesFrom:mapping];
+    } else if ([self.inputController mappingForKey:mapping.name]) {
+        [[self.inputController mappingForKey:mapping.name] mergeEntriesFrom:mapping];
     } else if (mapping) {
         [self.mvc beginUpdates];
         [self.mvc addedMappingAtIndex:index startEditing:NO];
-        [self.mappingsController insertMapping:mapping atIndex:index];
+        [self.inputController insertMapping:mapping atIndex:index];
         [self.mvc endUpdates];
     }
     return !!mapping;
 - (void)mappingsViewController:(NJMappingsViewController *)mvc
                     addMapping:(NJMapping *)mapping {
     [mvc beginUpdates];
-    [mvc addedMappingAtIndex:self.mappingsController.mappings.count startEditing:YES];
-    [self.mappingsController addMapping:mapping];
+    [mvc addedMappingAtIndex:self.inputController.mappings.count startEditing:YES];
+    [self.inputController addMapping:mapping];
     [mvc endUpdates];
-    [self.mappingsController activateMapping:mapping];
+    [self.inputController activateMapping:mapping];
 }
 
 - (void)mappingsViewController:(NJMappingsViewController *)mvc
            choseMappingAtIndex:(NSInteger)idx {
-    [self.mappingsController activateMapping:self.mappingsController.mappings[idx]];
+    [self.inputController activateMapping:self.inputController.mappings[idx]];
 }
 
 - (id)deviceViewController:(NJDeviceViewController *)dvc
              elementForUID:(NSString *)uid {
-    return [self.deviceController elementForUID:uid];
+    return [self.inputController elementForUID:uid];
 }
 
 - (void)deviceViewControllerDidSelectNothing:(NJDeviceViewController *)dvc {
     [self.outputController loadInput:dvc.selectedHandler];
 }
 
-- (void)deviceController:(NJDeviceController *)dc
+- (void)deviceController:(NJInputController *)dc
             didAddDevice:(NJDevice *)device {
     [self.dvc addedDevice:device atIndex:dc.devices.count - 1];
 }
 
-- (void)deviceController:(NJDeviceController *)dc
+- (void)deviceController:(NJInputController *)dc
   didRemoveDeviceAtIndex:(NSInteger)idx {
     [self.dvc removedDeviceAtIndex:idx];
 }
 
-- (void)deviceControllerDidStartHID:(NJDeviceController *)dc {
+- (void)deviceControllerDidStartHID:(NJInputController *)dc {
     [self.dvc hidStarted];
 }
 
-- (void)deviceControllerDidStopHID:(NJDeviceController *)dc {
+- (void)deviceControllerDidStopHID:(NJInputController *)dc {
     [self.dvc hidStopped];
 }
 
-- (void)deviceController:(NJDeviceController *)dc didInput:(NJInput *)input {
+- (void)deviceController:(NJInputController *)dc didInput:(NJInput *)input {
+    [self.dvc expandAndSelectItem:input];
     [self.outputController loadInput:input];
     [self.outputController focusKey];
 }
 
-- (void)deviceController:(NJDeviceController *)dc didError:(NSError *)error {
+- (void)deviceController:(NJInputController *)dc didError:(NSError *)error {
     // Since the error shows the window, it can trigger another attempt
     // to re-open the HID manager, which will also probably fail and error,
     // so don't bother repeating ourselves.
 }
 
 - (NSInteger)numberOfDevicesInDeviceList:(NJDeviceViewController *)dvc {
-    return self.deviceController.devices.count;
+    return self.inputController.devices.count;
 }
 
 - (NJDevice *)deviceViewController:(NJDeviceViewController *)dvc
                     deviceForIndex:(NSUInteger)idx {
-    return self.deviceController.devices[idx];
+    return self.inputController.devices[idx];
 }
 
 - (IBAction)simulatingEventsChanged:(NSButton *)sender {
-    self.deviceController.simulatingEvents = sender.state == NSOnState;
+    self.inputController.simulatingEvents = sender.state == NSOnState;
 }
 
 @end
diff --git a/Classes/NJDeviceController.h b/Classes/NJDeviceController.h
deleted file mode 100644 (file)
index 41017db..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-//  NJDeviceController.h
-//  Enjoy
-//
-//  Created by Sam McCall on 4/05/09.
-//  Copyright 2009 University of Otago. All rights reserved.
-//
-
-#import "NJHIDManager.h"
-
-@class NJDevice;
-@class NJInput;
-@class NJInputPathElement;
-@class NJMappingsController;
-
-@protocol NJDeviceControllerDelegate;
-
-@interface NJDeviceController : NSObject <NJHIDManagerDelegate> {
-    IBOutlet NJMappingsController *mappingsController;
-}
-
-@property (nonatomic, weak) IBOutlet id <NJDeviceControllerDelegate> delegate;
-
-@property (nonatomic, assign) NSPoint mouseLoc;
-@property (nonatomic, assign) BOOL simulatingEvents;
-@property (nonatomic, readonly) NSArray *devices;
-
-- (NJInputPathElement *)elementForUID:(NSString *)uid;
-
-@end
-
-@protocol NJDeviceControllerDelegate
-
-- (void)deviceController:(NJDeviceController *)dc didAddDevice:(NJDevice *)device;
-- (void)deviceController:(NJDeviceController *)dc didRemoveDeviceAtIndex:(NSInteger)idx;
-- (void)deviceController:(NJDeviceController *)dc didInput:(NJInput *)input;
-- (void)deviceControllerDidStartHID:(NJDeviceController *)dc;
-- (void)deviceControllerDidStopHID:(NJDeviceController *)dc;
-- (void)deviceController:(NJDeviceController *)dc didError:(NSError *)error;
-
-@end
diff --git a/Classes/NJDeviceController.m b/Classes/NJDeviceController.m
deleted file mode 100644 (file)
index 5df317b..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-//
-//  NJDeviceController.m
-//  Enjoy
-//
-//  Created by Sam McCall on 4/05/09.
-//
-
-#import "NJDeviceController.h"
-
-#import "NJMapping.h"
-#import "NJMappingsController.h"
-#import "NJDevice.h"
-#import "NJInput.h"
-#import "NJOutput.h"
-#import "NJEvents.h"
-
-@implementation NJDeviceController {
-    NJHIDManager *_hidManager;
-    NSTimer *_continuousOutputsTick;
-    NSMutableArray *_continousOutputs;
-    NSMutableArray *_devices;
-}
-
-#define NSSTR(e) ((NSString *)CFSTR(e))
-
-- (id)init {
-    if ((self = [super init])) {
-        _devices = [[NSMutableArray alloc] initWithCapacity:16];
-        _continousOutputs = [[NSMutableArray alloc] initWithCapacity:32];
-        
-        _hidManager = [[NJHIDManager alloc] initWithCriteria:@[
-                       @{ 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) }
-                       ]
-                                                    delegate:self];
-
-        // The HID manager uses 5-10ms per second doing basically
-        // nothing if a noisy device is plugged in (the % of that
-        // spent in input_callback is negligible, so it's not
-        // something we can make faster). I don't really think that's
-        // acceptable, CPU/power wise. So if simulation is disabled
-        // and the window is closed, just switch off the HID manager
-        // entirely. This probably also has some marginal benefits for
-        // compatibility with other applications that want exclusive
-        // grabs.
-        [NSNotificationCenter.defaultCenter
-            addObserver:self
-            selector:@selector(stopHidIfDisabled:)
-            name:NSApplicationDidResignActiveNotification
-            object:nil];
-        [NSNotificationCenter.defaultCenter
-            addObserver:self
-            selector:@selector(startHid)
-            name:NSApplicationDidBecomeActiveNotification
-            object:nil];
-    }
-    return self;
-}
-
-- (void)dealloc {
-    [NSNotificationCenter.defaultCenter removeObserver:self];
-    [_continuousOutputsTick invalidate];
-}
-
-- (void)addRunningOutput:(NJOutput *)output {
-    // Axis events will trigger every small movement, don't keep
-    // re-adding them or they trigger multiple times each time.
-    if (![_continousOutputs containsObject:output])
-        [_continousOutputs addObject:output];
-    if (!_continuousOutputsTick) {
-        _continuousOutputsTick = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0
-                                                           target:self
-                                                         selector:@selector(updateContinuousOutputs:)
-                                                         userInfo:nil
-                                                          repeats:YES];
-    }
-}
-
-- (void)runOutputForDevice:(IOHIDDeviceRef)device value:(IOHIDValueRef)value {
-    NJDevice *dev = [self findDeviceByRef:device];
-    NJInput *mainInput = [dev inputForEvent:value];
-    [mainInput notifyEvent:value];
-    NSArray *children = mainInput.children ? mainInput.children : mainInput ? @[mainInput] : @[];
-    for (NJInput *subInput in children) {
-        NJOutput *output = mappingsController.currentMapping[subInput];
-        output.magnitude = subInput.magnitude;
-        output.running = subInput.active;
-        if ((output.running || output.magnitude) && output.isContinuous)
-            [self addRunningOutput:output];
-    }
-}
-
-- (void)showOutputForDevice:(IOHIDDeviceRef)device value:(IOHIDValueRef)value {
-    NJDevice *dev = [self findDeviceByRef:device];
-    NJInput *handler = [dev handlerForEvent:value];
-    if (!handler)
-        return;
-    
-    [self.delegate deviceController:self didInput:handler];
-}
-
-- (void)hidManager:(NJHIDManager *)manager
-      valueChanged:(IOHIDValueRef)value
-        fromDevice:(IOHIDDeviceRef)device {
-    if (self.simulatingEvents
-        && !NSApplication.sharedApplication.isActive) {
-        [self runOutputForDevice:device value:value];
-    } else {
-        [self showOutputForDevice:device value:value];
-    }
-}
-
-- (void)addDevice:(NJDevice *)device {
-    BOOL available;
-    do {
-        available = YES;
-        for (NJDevice *used in _devices) {
-            if ([used isEqual:device]) {
-                device.index += 1;
-                available = NO;
-            }
-        }
-    } while (!available);
-    
-    [_devices addObject:device];
-}
-
-- (void)hidManager:(NJHIDManager *)manager deviceAdded:(IOHIDDeviceRef)device {
-    NJDevice *match = [[NJDevice alloc] initWithDevice:device];
-    [self addDevice:match];
-    [self.delegate deviceController:self didAddDevice:match];
-}
-
-- (NJDevice *)findDeviceByRef:(IOHIDDeviceRef)device {
-    for (NJDevice *dev in _devices)
-        if (dev.device == device)
-            return dev;
-    return nil;
-}
-
-- (void)hidManager:(NJHIDManager *)manager deviceRemoved:(IOHIDDeviceRef)device {
-    NJDevice *match = [self findDeviceByRef:device];
-    if (match) {
-        NSInteger idx = [_devices indexOfObjectIdenticalTo:match];
-        [_devices removeObjectAtIndex:idx];
-        [self.delegate deviceController:self didRemoveDeviceAtIndex:idx];
-    }
-}
-
-- (void)updateContinuousOutputs:(NSTimer *)timer {
-    self.mouseLoc = [NSEvent mouseLocation];
-    for (NJOutput *output in [_continousOutputs copy]) {
-        if (![output update:self]) {
-            [_continousOutputs removeObject:output];
-        }
-    }
-    if (!_continousOutputs.count) {
-        [_continuousOutputsTick invalidate];
-        _continuousOutputsTick = nil;
-    }
-}
-
-- (void)hidManager:(NJHIDManager *)manager didError:(NSError *)error {
-    [self.delegate deviceController:self didError:error];
-    self.simulatingEvents = NO;
-}
-
-- (void)hidManagerDidStart:(NJHIDManager *)manager {
-    [self.delegate deviceControllerDidStartHID:self];
-}
-
-- (void)hidManagerDidStop:(NJHIDManager *)manager {
-    [_devices removeAllObjects];
-    [self.delegate deviceControllerDidStopHID:self];
-}
-
-- (void)startHid {
-    [_hidManager start];
-}
-
-- (void)stopHid {
-    [_hidManager stop];
-}
-
-- (void)setSimulatingEvents:(BOOL)simulatingEvents {
-    if (simulatingEvents != _simulatingEvents) {
-        _simulatingEvents = simulatingEvents;
-        NSString *name = simulatingEvents
-            ? NJEventSimulationStarted
-            : NJEventSimulationStopped;
-        [NSNotificationCenter.defaultCenter postNotificationName:name
-                                                          object:self];
-
-        if (!simulatingEvents && !NSApplication.sharedApplication.isActive)
-            [self stopHid];
-        else
-            [self startHid];
-    }
-}
-
-- (void)stopHidIfDisabled:(NSNotification *)application {
-    if (!self.simulatingEvents && !NSProcessInfo.processInfo.isBeingDebugged)
-        [self stopHid];
-}
-
-- (NJInputPathElement *)elementForUID:(NSString *)uid {
-    for (NJDevice *dev in _devices) {
-        id item = [dev elementForUID:uid];
-        if (item)
-            return item;
-    }
-    return nil;
-}
-
-@end
diff --git a/Classes/NJInputController.h b/Classes/NJInputController.h
new file mode 100644 (file)
index 0000000..06852f2
--- /dev/null
@@ -0,0 +1,58 @@
+//
+//  NJDeviceController.h
+//  Enjoy
+//
+//  Created by Sam McCall on 4/05/09.
+//  Copyright 2009 University of Otago. All rights reserved.
+//
+
+#import "NJHIDManager.h"
+
+@class NJDevice;
+@class NJInput;
+@class NJInputPathElement;
+@class NJMapping;
+
+@protocol NJInputControllerDelegate;
+
+@interface NJInputController : NSObject <NJHIDManagerDelegate>
+
+@property (nonatomic, weak) IBOutlet id <NJInputControllerDelegate> delegate;
+
+@property (nonatomic, assign) NSPoint mouseLoc;
+@property (nonatomic, assign) BOOL simulatingEvents;
+@property (nonatomic, readonly) NSArray *devices;
+
+@property (nonatomic, readonly) NJMapping *currentMapping;
+@property (nonatomic, readonly) NSArray *mappings;
+
+- (NJMapping *)mappingForKey:(NSString *)name;
+- (NSInteger)indexOfMapping:(NJMapping *)mapping;
+
+- (void)addMapping:(NJMapping *)mapping;
+- (void)insertMapping:(NJMapping *)mapping atIndex:(NSInteger)idx;
+- (void)removeMappingAtIndex:(NSInteger)idx;
+- (void)mergeMapping:(NJMapping *)mapping intoMapping:(NJMapping *)existing;
+- (void)moveMoveMappingFromIndex:(NSInteger)fromIdx toIndex:(NSInteger)toIdx;
+- (void)renameMapping:(NJMapping *)mapping to:(NSString *)name;
+
+- (void)activateMapping:(NJMapping *)mapping;
+- (void)activateMappingForProcess:(NSRunningApplication *)app;
+
+- (void)save;
+- (void)load;
+
+- (NJInputPathElement *)elementForUID:(NSString *)uid;
+
+@end
+
+@protocol NJInputControllerDelegate
+
+- (void)deviceController:(NJInputController *)dc didAddDevice:(NJDevice *)device;
+- (void)deviceController:(NJInputController *)dc didRemoveDeviceAtIndex:(NSInteger)idx;
+- (void)deviceController:(NJInputController *)dc didInput:(NJInput *)input;
+- (void)deviceControllerDidStartHID:(NJInputController *)dc;
+- (void)deviceControllerDidStopHID:(NJInputController *)dc;
+- (void)deviceController:(NJInputController *)dc didError:(NSError *)error;
+
+@end
diff --git a/Classes/NJInputController.m b/Classes/NJInputController.m
new file mode 100644 (file)
index 0000000..4ca33e1
--- /dev/null
@@ -0,0 +1,363 @@
+//
+//  NJDeviceController.m
+//  Enjoy
+//
+//  Created by Sam McCall on 4/05/09.
+//
+
+#import "NJInputController.h"
+
+#import "NJMapping.h"
+#import "NJDevice.h"
+#import "NJInput.h"
+#import "NJOutput.h"
+#import "NJEvents.h"
+
+@implementation NJInputController {
+    NJHIDManager *_hidManager;
+    NSTimer *_continuousOutputsTick;
+    NSMutableArray *_continousOutputs;
+    NSMutableArray *_devices;
+    NSMutableArray *_mappings;
+    NJMapping *_manualMapping;
+
+}
+
+#define NSSTR(e) ((NSString *)CFSTR(e))
+
+- (id)init {
+    if ((self = [super init])) {
+        _devices = [[NSMutableArray alloc] initWithCapacity:16];
+        _continousOutputs = [[NSMutableArray alloc] initWithCapacity:32];
+        
+        _hidManager = [[NJHIDManager alloc] initWithCriteria:@[
+                       @{ 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) }
+                       ]
+                                                    delegate:self];
+
+        _mappings = [[NSMutableArray alloc] init];
+        _currentMapping = [[NJMapping alloc] initWithName:
+                           NSLocalizedString(@"(default)", @"default name for first the mapping")];
+        _manualMapping = _currentMapping;
+        [_mappings addObject:_currentMapping];
+
+        // The HID manager uses 5-10ms per second doing basically
+        // nothing if a noisy device is plugged in (the % of that
+        // spent in input_callback is negligible, so it's not
+        // something we can make faster). I don't really think that's
+        // acceptable, CPU/power wise. So if simulation is disabled
+        // and the window is closed, just switch off the HID manager
+        // entirely. This probably also has some marginal benefits for
+        // compatibility with other applications that want exclusive
+        // grabs.
+        [NSNotificationCenter.defaultCenter
+            addObserver:self
+            selector:@selector(stopHidIfDisabled:)
+            name:NSApplicationDidResignActiveNotification
+            object:nil];
+        [NSNotificationCenter.defaultCenter
+            addObserver:self
+            selector:@selector(startHid)
+            name:NSApplicationDidBecomeActiveNotification
+            object:nil];
+    }
+    return self;
+}
+
+- (void)dealloc {
+    [NSNotificationCenter.defaultCenter removeObserver:self];
+    [_continuousOutputsTick invalidate];
+}
+
+- (void)addRunningOutput:(NJOutput *)output {
+    // Axis events will trigger every small movement, don't keep
+    // re-adding them or they trigger multiple times each time.
+    if (![_continousOutputs containsObject:output])
+        [_continousOutputs addObject:output];
+    if (!_continuousOutputsTick) {
+        _continuousOutputsTick = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0
+                                                           target:self
+                                                         selector:@selector(updateContinuousOutputs:)
+                                                         userInfo:nil
+                                                          repeats:YES];
+    }
+}
+
+- (void)runOutputForDevice:(IOHIDDeviceRef)device value:(IOHIDValueRef)value {
+    NJDevice *dev = [self findDeviceByRef:device];
+    NJInput *mainInput = [dev inputForEvent:value];
+    [mainInput notifyEvent:value];
+    NSArray *children = mainInput.children ? mainInput.children : mainInput ? @[mainInput] : @[];
+    for (NJInput *subInput in children) {
+        NJOutput *output = self.currentMapping[subInput];
+        output.magnitude = subInput.magnitude;
+        output.running = subInput.active;
+        if ((output.running || output.magnitude) && output.isContinuous)
+            [self addRunningOutput:output];
+    }
+}
+
+- (void)showOutputForDevice:(IOHIDDeviceRef)device value:(IOHIDValueRef)value {
+    NJDevice *dev = [self findDeviceByRef:device];
+    NJInput *handler = [dev handlerForEvent:value];
+    if (!handler)
+        return;
+    
+    [self.delegate deviceController:self didInput:handler];
+}
+
+- (void)hidManager:(NJHIDManager *)manager
+      valueChanged:(IOHIDValueRef)value
+        fromDevice:(IOHIDDeviceRef)device {
+    if (self.simulatingEvents
+        && !NSApplication.sharedApplication.isActive) {
+        [self runOutputForDevice:device value:value];
+    } else {
+        [self showOutputForDevice:device value:value];
+    }
+}
+
+- (void)addDevice:(NJDevice *)device {
+    BOOL available;
+    do {
+        available = YES;
+        for (NJDevice *used in _devices) {
+            if ([used isEqual:device]) {
+                device.index += 1;
+                available = NO;
+            }
+        }
+    } while (!available);
+    
+    [_devices addObject:device];
+}
+
+- (void)hidManager:(NJHIDManager *)manager deviceAdded:(IOHIDDeviceRef)device {
+    NJDevice *match = [[NJDevice alloc] initWithDevice:device];
+    [self addDevice:match];
+    [self.delegate deviceController:self didAddDevice:match];
+}
+
+- (NJDevice *)findDeviceByRef:(IOHIDDeviceRef)device {
+    for (NJDevice *dev in _devices)
+        if (dev.device == device)
+            return dev;
+    return nil;
+}
+
+- (void)hidManager:(NJHIDManager *)manager deviceRemoved:(IOHIDDeviceRef)device {
+    NJDevice *match = [self findDeviceByRef:device];
+    if (match) {
+        NSInteger idx = [_devices indexOfObjectIdenticalTo:match];
+        [_devices removeObjectAtIndex:idx];
+        [self.delegate deviceController:self didRemoveDeviceAtIndex:idx];
+    }
+}
+
+- (void)updateContinuousOutputs:(NSTimer *)timer {
+    self.mouseLoc = [NSEvent mouseLocation];
+    for (NJOutput *output in [_continousOutputs copy]) {
+        if (![output update:self]) {
+            [_continousOutputs removeObject:output];
+        }
+    }
+    if (!_continousOutputs.count) {
+        [_continuousOutputsTick invalidate];
+        _continuousOutputsTick = nil;
+    }
+}
+
+- (void)hidManager:(NJHIDManager *)manager didError:(NSError *)error {
+    [self.delegate deviceController:self didError:error];
+    self.simulatingEvents = NO;
+}
+
+- (void)hidManagerDidStart:(NJHIDManager *)manager {
+    [self.delegate deviceControllerDidStartHID:self];
+}
+
+- (void)hidManagerDidStop:(NJHIDManager *)manager {
+    [_devices removeAllObjects];
+    [self.delegate deviceControllerDidStopHID:self];
+}
+
+- (void)startHid {
+    [_hidManager start];
+}
+
+- (void)stopHid {
+    [_hidManager stop];
+}
+
+- (void)setSimulatingEvents:(BOOL)simulatingEvents {
+    if (simulatingEvents != _simulatingEvents) {
+        _simulatingEvents = simulatingEvents;
+        NSString *name = simulatingEvents
+            ? NJEventSimulationStarted
+            : NJEventSimulationStopped;
+        [NSNotificationCenter.defaultCenter postNotificationName:name
+                                                          object:self];
+
+        if (!simulatingEvents && !NSApplication.sharedApplication.isActive)
+            [self stopHid];
+        else
+            [self startHid];
+    }
+}
+
+- (void)stopHidIfDisabled:(NSNotification *)application {
+    if (!self.simulatingEvents && !NSProcessInfo.processInfo.isBeingDebugged)
+        [self stopHid];
+}
+
+- (NJInputPathElement *)elementForUID:(NSString *)uid {
+    for (NJDevice *dev in _devices) {
+        id item = [dev elementForUID:uid];
+        if (item)
+            return item;
+    }
+    return nil;
+}
+
+- (NJMapping *)mappingForKey:(NSString *)name {
+    for (NJMapping *mapping in _mappings)
+        if ([name isEqualToString:mapping.name])
+            return mapping;
+    return nil;
+}
+
+- (void)mappingsSet {
+    [self postLoadProcess];
+    [NSNotificationCenter.defaultCenter
+     postNotificationName:NJEventMappingListChanged
+     object:self
+     userInfo:@{ NJMappingListKey: _mappings,
+     NJMappingKey: _currentMapping }];
+}
+
+- (void)mappingsChanged {
+    [self save];
+    [self mappingsSet];
+}
+
+- (void)activateMappingForProcess:(NSRunningApplication *)app {
+    NJMapping *oldMapping = _manualMapping;
+    NSArray *names = app.possibleMappingNames;
+    BOOL found = NO;
+    for (NSString *name in names) {
+        NJMapping *mapping = [self mappingForKey:name];
+        if (mapping) {
+            [self activateMapping:mapping];
+            found = YES;
+            break;
+        }
+    }
+    
+    if (!found) {
+        [self activateMapping:oldMapping];
+        if ([oldMapping.name.lowercaseString isEqualToString:@"@application"]
+            || [oldMapping.name.lowercaseString isEqualToString:
+                NSLocalizedString(@"@Application", nil).lowercaseString]) {
+                oldMapping.name = app.bestMappingName;
+                [self mappingsChanged];
+            }
+    }
+    _manualMapping = oldMapping;
+}
+
+- (void)activateMappingForcibly:(NJMapping *)mapping {
+    NSLog(@"Switching to mapping %@.", mapping.name);
+    _currentMapping = mapping;
+    NSUInteger idx = [self indexOfMapping:_currentMapping];
+    [NSNotificationCenter.defaultCenter
+     postNotificationName:NJEventMappingChanged
+     object:self
+     userInfo:@{ NJMappingKey : _currentMapping,
+     NJMappingIndexKey: @(idx) }];
+}
+
+- (void)activateMapping:(NJMapping *)mapping {
+    if (!mapping)
+        mapping = _manualMapping;
+    if (mapping == _currentMapping)
+        return;
+    _manualMapping = mapping;
+    [self activateMappingForcibly:mapping];
+}
+
+- (void)save {
+    NSLog(@"Saving mappings to defaults.");
+    NSMutableArray *ary = [[NSMutableArray alloc] initWithCapacity:_mappings.count];
+    for (NJMapping *mapping in _mappings)
+        [ary addObject:[mapping serialize]];
+    [NSUserDefaults.standardUserDefaults setObject:ary forKey:@"mappings"];
+}
+
+- (void)postLoadProcess {
+    for (NJMapping *mapping in self.mappings)
+        [mapping postLoadProcess:self.mappings];
+}
+
+- (void)load {
+    NSUInteger selected = [NSUserDefaults.standardUserDefaults integerForKey:@"selected"];
+    NSArray *storedMappings = [NSUserDefaults.standardUserDefaults arrayForKey:@"mappings"];
+    NSMutableArray* newMappings = [[NSMutableArray alloc] initWithCapacity:storedMappings.count];
+    
+    for (NSDictionary *serialization in storedMappings)
+        [newMappings addObject:
+         [[NJMapping alloc] initWithSerialization:serialization]];
+    
+    if (newMappings.count) {
+        _mappings = newMappings;
+        if (selected >= newMappings.count)
+            selected = 0;
+        [self activateMapping:_mappings[selected]];
+        [self mappingsSet];
+    }
+}
+
+- (NSInteger)indexOfMapping:(NJMapping *)mapping {
+    return [_mappings indexOfObjectIdenticalTo:mapping];
+}
+
+- (void)mergeMapping:(NJMapping *)mapping intoMapping:(NJMapping *)existing {
+    [existing mergeEntriesFrom:mapping];
+    [self mappingsChanged];
+    if (existing == _currentMapping)
+        [self activateMappingForcibly:mapping];
+}
+
+- (void)renameMapping:(NJMapping *)mapping to:(NSString *)name {
+    mapping.name = name;
+    [self mappingsChanged];
+    if (mapping == _currentMapping)
+        [self activateMappingForcibly:mapping];
+}
+
+- (void)addMapping:(NJMapping *)mapping {
+    [self insertMapping:mapping atIndex:_mappings.count];
+}
+
+- (void)insertMapping:(NJMapping *)mapping atIndex:(NSInteger)idx {
+    [_mappings insertObject:mapping atIndex:idx];
+    [self mappingsChanged];
+}
+
+- (void)removeMappingAtIndex:(NSInteger)idx {
+    NSInteger currentIdx = [self indexOfMapping:_currentMapping];
+    [_mappings removeObjectAtIndex:idx];
+    [self activateMapping:self.mappings[MIN(currentIdx, _mappings.count - 1)]];
+    [self mappingsChanged];
+}
+
+- (void)moveMoveMappingFromIndex:(NSInteger)fromIdx toIndex:(NSInteger)toIdx {
+    [_mappings moveObjectAtIndex:fromIdx toIndex:toIdx];
+    [self mappingsChanged];
+}
+
+@end
diff --git a/Classes/NJMappingsController.h b/Classes/NJMappingsController.h
deleted file mode 100644 (file)
index b9d3e67..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-//
-//  NJMappingsController.h
-//  Enjoy
-//
-//  Created by Sam McCall on 4/05/09.
-//  Copyright 2009 University of Otago. All rights reserved.
-//
-
-@class NJMapping;
-
-@interface NJMappingsController : NSObject
-
-@property (nonatomic, readonly) NJMapping *currentMapping;
-@property (nonatomic, readonly) NSArray *mappings;
-
-- (NJMapping *)mappingForKey:(NSString *)name;
-- (NSInteger)indexOfMapping:(NJMapping *)mapping;
-
-- (void)addMapping:(NJMapping *)mapping;
-- (void)insertMapping:(NJMapping *)mapping atIndex:(NSInteger)idx;
-- (void)removeMappingAtIndex:(NSInteger)idx;
-- (void)mergeMapping:(NJMapping *)mapping intoMapping:(NJMapping *)existing;
-- (void)moveMoveMappingFromIndex:(NSInteger)fromIdx toIndex:(NSInteger)toIdx;
-- (void)renameMapping:(NJMapping *)mapping to:(NSString *)name;
-
-- (void)activateMapping:(NJMapping *)mapping;
-- (void)activateMappingForProcess:(NSRunningApplication *)app;
-
-- (void)save;
-- (void)load;
-
-@end
diff --git a/Classes/NJMappingsController.m b/Classes/NJMappingsController.m
deleted file mode 100644 (file)
index 6902504..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-//
-//  NJMappingsController.m
-//  Enjoy
-//
-//  Created by Sam McCall on 4/05/09.
-//
-
-#import "NJMappingsController.h"
-
-#import "NJMapping.h"
-#import "NJOutput.h"
-#import "NJEvents.h"
-
-@implementation NJMappingsController {
-    NSMutableArray *_mappings;
-    NJMapping *_manualMapping;
-}
-
-- (id)init {
-    if ((self = [super init])) {
-        _mappings = [[NSMutableArray alloc] init];
-        _currentMapping = [[NJMapping alloc] initWithName:
-                           NSLocalizedString(@"(default)", @"default name for first the mapping")];
-        _manualMapping = _currentMapping;
-        [_mappings addObject:_currentMapping];
-    }
-    return self;
-}
-
-- (NJMapping *)mappingForKey:(NSString *)name {
-    for (NJMapping *mapping in _mappings)
-        if ([name isEqualToString:mapping.name])
-            return mapping;
-    return nil;
-}
-
-- (void)mappingsSet {
-    [self postLoadProcess];
-    [NSNotificationCenter.defaultCenter
-        postNotificationName:NJEventMappingListChanged
-                      object:self
-                    userInfo:@{ NJMappingListKey: _mappings,
-                                NJMappingKey: _currentMapping }];
-}
-
-- (void)mappingsChanged {
-    [self save];
-    [self mappingsSet];
-}
-
-- (void)activateMappingForProcess:(NSRunningApplication *)app {
-    NJMapping *oldMapping = _manualMapping;
-    NSArray *names = app.possibleMappingNames;
-    BOOL found = NO;
-    for (NSString *name in names) {
-        NJMapping *mapping = [self mappingForKey:name];
-        if (mapping) {
-            [self activateMapping:mapping];
-            found = YES;
-            break;
-        }
-    }
-
-    if (!found) {
-        [self activateMapping:oldMapping];
-        if ([oldMapping.name.lowercaseString isEqualToString:@"@application"]
-            || [oldMapping.name.lowercaseString isEqualToString:
-                NSLocalizedString(@"@Application", nil).lowercaseString]) {
-            oldMapping.name = app.bestMappingName;
-            [self mappingsChanged];
-        }
-    }
-    _manualMapping = oldMapping;
-}
-
-- (void)activateMappingForcibly:(NJMapping *)mapping {
-    NSLog(@"Switching to mapping %@.", mapping.name);
-    _currentMapping = mapping;
-    NSUInteger idx = [self indexOfMapping:_currentMapping];
-    [NSNotificationCenter.defaultCenter
-     postNotificationName:NJEventMappingChanged
-                  object:self
-                userInfo:@{ NJMappingKey : _currentMapping,
-                            NJMappingIndexKey: @(idx) }];
-}
-
-- (void)activateMapping:(NJMapping *)mapping {
-    if (!mapping)
-        mapping = _manualMapping;
-    if (mapping == _currentMapping)
-        return;
-    _manualMapping = mapping;
-    [self activateMappingForcibly:mapping];
-}
-
-- (void)save {
-    NSLog(@"Saving mappings to defaults.");
-    NSMutableArray *ary = [[NSMutableArray alloc] initWithCapacity:_mappings.count];
-    for (NJMapping *mapping in _mappings)
-        [ary addObject:[mapping serialize]];
-    [NSUserDefaults.standardUserDefaults setObject:ary forKey:@"mappings"];
-}
-
-- (void)postLoadProcess {
-    for (NJMapping *mapping in self.mappings)
-        [mapping postLoadProcess:self.mappings];
-}
-
-- (void)load {
-    NSUInteger selected = [NSUserDefaults.standardUserDefaults integerForKey:@"selected"];
-    NSArray *storedMappings = [NSUserDefaults.standardUserDefaults arrayForKey:@"mappings"];
-    NSMutableArray* newMappings = [[NSMutableArray alloc] initWithCapacity:storedMappings.count];
-
-    for (NSDictionary *serialization in storedMappings)
-        [newMappings addObject:
-         [[NJMapping alloc] initWithSerialization:serialization]];
-    
-    if (newMappings.count) {
-        _mappings = newMappings;
-        if (selected >= newMappings.count)
-            selected = 0;
-        [self activateMapping:_mappings[selected]];
-        [self mappingsSet];
-    }
-}
-
-- (NSInteger)indexOfMapping:(NJMapping *)mapping {
-    return [_mappings indexOfObjectIdenticalTo:mapping];
-}
-
-- (void)mergeMapping:(NJMapping *)mapping intoMapping:(NJMapping *)existing {
-    [existing mergeEntriesFrom:mapping];
-    [self mappingsChanged];
-    if (existing == _currentMapping)
-        [self activateMappingForcibly:mapping];
-}
-
-- (void)renameMapping:(NJMapping *)mapping to:(NSString *)name {
-    mapping.name = name;
-    [self mappingsChanged];
-    if (mapping == _currentMapping)
-        [self activateMappingForcibly:mapping];
-}
-
-- (void)addMapping:(NJMapping *)mapping {
-    [self insertMapping:mapping atIndex:_mappings.count];
-}
-
-- (void)insertMapping:(NJMapping *)mapping atIndex:(NSInteger)idx {
-    [_mappings insertObject:mapping atIndex:idx];
-    [self mappingsChanged];    
-}
-
-- (void)removeMappingAtIndex:(NSInteger)idx {
-    NSInteger currentIdx = [self indexOfMapping:_currentMapping];
-    [_mappings removeObjectAtIndex:idx];
-    [self activateMapping:self.mappings[MIN(currentIdx, _mappings.count - 1)]];
-    [self mappingsChanged];
-}
-
-- (void)moveMoveMappingFromIndex:(NSInteger)fromIdx toIndex:(NSInteger)toIdx {
-    [_mappings moveObjectAtIndex:fromIdx toIndex:toIdx];
-    [self mappingsChanged];
-}
-
-@end
index aeb8e46..52c0320 100644 (file)
@@ -6,7 +6,7 @@
 //  Copyright 2009 University of Otago. All rights reserved.
 //
 
-@class NJDeviceController;
+@class NJInputController;
 
 @interface NJOutput : NSObject
 
@@ -16,7 +16,7 @@
 
 - (void)trigger;
 - (void)untrigger;
-- (BOOL)update:(NJDeviceController *)jc;
+- (BOOL)update:(NJInputController *)jc;
 
 - (NSDictionary *)serialize;
 + (NJOutput *)outputDeserialize:(NSDictionary *)serialization;
index 54809ce..8b21164 100644 (file)
@@ -60,7 +60,7 @@
 - (void)untrigger {
 }
 
-- (BOOL)update:(NJDeviceController *)jc {
+- (BOOL)update:(NJInputController *)jc {
     return NO;
 }
 
index c18275f..b22afdb 100644 (file)
@@ -8,8 +8,7 @@
 
 #import "NJKeyInputField.h"
 
-@class NJMappingsController;
-@class NJDeviceController;
+@class NJInputController;
 @class NJOutput;
 @class NJInput;
 
@@ -23,7 +22,7 @@
     IBOutlet NSSlider *scrollSpeedSlider;
     IBOutlet NSTextField *title;
     IBOutlet NSPopUpButton *mappingPopup;
-    IBOutlet NJMappingsController *mappingsController;
+    IBOutlet NJInputController *inputController;
     IBOutlet NSButton *smoothCheck;
     IBOutlet NSButton *unknownMapping;
 }
index 22c387f..f8ed2ea 100644 (file)
@@ -7,11 +7,10 @@
 
 #import "NJOutputController.h"
 
-#import "NJMappingsController.h"
 #import "NJMapping.h"
 #import "NJInput.h"
 #import "NJEvents.h"
-#import "NJDeviceController.h"
+#import "NJInputController.h"
 #import "NJKeyInputField.h"
 #import "NJOutputMapping.h"
 #import "NJOutputController.h"
 }
 
 - (NJOutput *)currentOutput {
-    return mappingsController.currentMapping[_input];
+    return inputController.currentMapping[_input];
 }
 
 - (NJOutput *)makeOutput {
             break;
         case 2: {
             NJOutputMapping *c = [[NJOutputMapping alloc] init];
-            c.mapping = mappingsController.mappings[mappingPopup.indexOfSelectedItem];
+            c.mapping = inputController.mappings[mappingPopup.indexOfSelectedItem];
             return c;
         }
         case 3: {
 
 - (void)commit {
     [self cleanUpInterface];
-    mappingsController.currentMapping[_input] = [self makeOutput];
-    [mappingsController save];
+    inputController.currentMapping[_input] = [self makeOutput];
+    [inputController save];
 }
 
 - (BOOL)enabled {
index efb7c00..cfcc34f 100644 (file)
@@ -9,7 +9,6 @@
 
 #import "EnjoyableApplicationDelegate.h"
 #import "NJMapping.h"
-#import "NJMappingsController.h"
 
 @implementation NJOutputMapping
 
@@ -34,7 +33,7 @@
 - (void)trigger {
     EnjoyableApplicationDelegate *ctrl = (EnjoyableApplicationDelegate *)NSApplication.sharedApplication.delegate;
     if (_mapping) {
-        [ctrl.mappingsController activateMapping:_mapping];
+        [ctrl.inputController activateMapping:_mapping];
         self.mappingName = _mapping.name;
     } else {
         // TODO: Show an error message? Unobtrusively since something
index 84c0ea3..f943546 100644 (file)
@@ -7,7 +7,7 @@
 
 #import "NJOutputMouseMove.h"
 
-#import "NJDeviceController.h"
+#import "NJInputController.h"
 
 @implementation NJOutputMouseMove
 
@@ -37,7 +37,7 @@
 
 #define CLAMP(a, l, h) MIN(h, MAX(a, l))
 
-- (BOOL)update:(NJDeviceController *)jc {
+- (BOOL)update:(NJInputController *)jc {
     if (self.magnitude < 0.05)
         return NO; // dead zone
     
index 06913d6..b2775a8 100644 (file)
@@ -52,7 +52,7 @@
     }
 }
 
-- (BOOL)update:(NJDeviceController *)jc {
+- (BOOL)update:(NJInputController *)jc {
     if (self.magnitude < 0.05f)
         return NO; // dead zone
     
index 74db726..c19007b 100644 (file)
                EEF17D3816E8E2E100D7DC4D /* NSView+FirstResponder.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF17D3216E8E2E100D7DC4D /* NSView+FirstResponder.m */; };
                EEF17D5C16E8E2EF00D7DC4D /* EnjoyableApplicationDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF17D3A16E8E2EF00D7DC4D /* EnjoyableApplicationDelegate.m */; };
                EEF17D5D16E8E2EF00D7DC4D /* NJDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF17D3C16E8E2EF00D7DC4D /* NJDevice.m */; };
-               EEF17D5E16E8E2EF00D7DC4D /* NJDeviceController.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF17D3E16E8E2EF00D7DC4D /* NJDeviceController.m */; };
+               EEF17D5E16E8E2EF00D7DC4D /* NJInputController.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF17D3E16E8E2EF00D7DC4D /* NJInputController.m */; };
                EEF17D5F16E8E2EF00D7DC4D /* NJInput.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF17D4016E8E2EF00D7DC4D /* NJInput.m */; };
                EEF17D6016E8E2EF00D7DC4D /* NJInputAnalog.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF17D4216E8E2EF00D7DC4D /* NJInputAnalog.m */; };
                EEF17D6116E8E2EF00D7DC4D /* NJInputButton.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF17D4416E8E2EF00D7DC4D /* NJInputButton.m */; };
                EEF17D6216E8E2EF00D7DC4D /* NJInputHat.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF17D4616E8E2EF00D7DC4D /* NJInputHat.m */; };
                EEF17D6316E8E2EF00D7DC4D /* NJKeyInputField.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF17D4916E8E2EF00D7DC4D /* NJKeyInputField.m */; };
                EEF17D6416E8E2EF00D7DC4D /* NJMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF17D4B16E8E2EF00D7DC4D /* NJMapping.m */; };
-               EEF17D6516E8E2EF00D7DC4D /* NJMappingsController.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF17D4D16E8E2EF00D7DC4D /* NJMappingsController.m */; };
                EEF17D6616E8E2EF00D7DC4D /* NJOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF17D4F16E8E2EF00D7DC4D /* NJOutput.m */; };
                EEF17D6716E8E2EF00D7DC4D /* NJOutputController.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF17D5116E8E2EF00D7DC4D /* NJOutputController.m */; };
                EEF17D6816E8E2EF00D7DC4D /* NJOutputKeyPress.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF17D5316E8E2EF00D7DC4D /* NJOutputKeyPress.m */; };
                EEF17D3A16E8E2EF00D7DC4D /* EnjoyableApplicationDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = EnjoyableApplicationDelegate.m; path = Classes/EnjoyableApplicationDelegate.m; sourceTree = "<group>"; };
                EEF17D3B16E8E2EF00D7DC4D /* NJDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NJDevice.h; path = Classes/NJDevice.h; sourceTree = "<group>"; };
                EEF17D3C16E8E2EF00D7DC4D /* NJDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NJDevice.m; path = Classes/NJDevice.m; sourceTree = "<group>"; };
-               EEF17D3D16E8E2EF00D7DC4D /* NJDeviceController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NJDeviceController.h; path = Classes/NJDeviceController.h; sourceTree = "<group>"; };
-               EEF17D3E16E8E2EF00D7DC4D /* NJDeviceController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NJDeviceController.m; path = Classes/NJDeviceController.m; sourceTree = "<group>"; };
+               EEF17D3D16E8E2EF00D7DC4D /* NJInputController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NJInputController.h; path = Classes/NJInputController.h; sourceTree = "<group>"; };
+               EEF17D3E16E8E2EF00D7DC4D /* NJInputController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NJInputController.m; path = Classes/NJInputController.m; sourceTree = "<group>"; };
                EEF17D3F16E8E2EF00D7DC4D /* NJInput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NJInput.h; path = Classes/NJInput.h; sourceTree = "<group>"; };
                EEF17D4016E8E2EF00D7DC4D /* NJInput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NJInput.m; path = Classes/NJInput.m; sourceTree = "<group>"; };
                EEF17D4116E8E2EF00D7DC4D /* NJInputAnalog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NJInputAnalog.h; path = Classes/NJInputAnalog.h; sourceTree = "<group>"; };
                EEF17D4916E8E2EF00D7DC4D /* NJKeyInputField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NJKeyInputField.m; path = Classes/NJKeyInputField.m; sourceTree = "<group>"; };
                EEF17D4A16E8E2EF00D7DC4D /* NJMapping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NJMapping.h; path = Classes/NJMapping.h; sourceTree = "<group>"; };
                EEF17D4B16E8E2EF00D7DC4D /* NJMapping.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NJMapping.m; path = Classes/NJMapping.m; sourceTree = "<group>"; };
-               EEF17D4C16E8E2EF00D7DC4D /* NJMappingsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NJMappingsController.h; path = Classes/NJMappingsController.h; sourceTree = "<group>"; };
-               EEF17D4D16E8E2EF00D7DC4D /* NJMappingsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NJMappingsController.m; path = Classes/NJMappingsController.m; sourceTree = "<group>"; };
                EEF17D4E16E8E2EF00D7DC4D /* NJOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NJOutput.h; path = Classes/NJOutput.h; sourceTree = "<group>"; };
                EEF17D4F16E8E2EF00D7DC4D /* NJOutput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NJOutput.m; path = Classes/NJOutput.m; sourceTree = "<group>"; };
                EEF17D5016E8E2EF00D7DC4D /* NJOutputController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NJOutputController.h; path = Classes/NJOutputController.h; sourceTree = "<group>"; };
                        children = (
                                EEF17D3916E8E2EF00D7DC4D /* EnjoyableApplicationDelegate.h */,
                                EEF17D3A16E8E2EF00D7DC4D /* EnjoyableApplicationDelegate.m */,
-                               EEF17D3D16E8E2EF00D7DC4D /* NJDeviceController.h */,
-                               EEF17D3E16E8E2EF00D7DC4D /* NJDeviceController.m */,
-                               EEF17D4C16E8E2EF00D7DC4D /* NJMappingsController.h */,
-                               EEF17D4D16E8E2EF00D7DC4D /* NJMappingsController.m */,
+                               EEF17D3D16E8E2EF00D7DC4D /* NJInputController.h */,
+                               EEF17D3E16E8E2EF00D7DC4D /* NJInputController.m */,
                                EEF17D5016E8E2EF00D7DC4D /* NJOutputController.h */,
                                EEF17D5116E8E2EF00D7DC4D /* NJOutputController.m */,
                                EEF17D4816E8E2EF00D7DC4D /* NJKeyInputField.h */,
                                EEF17D3816E8E2E100D7DC4D /* NSView+FirstResponder.m in Sources */,
                                EEF17D5C16E8E2EF00D7DC4D /* EnjoyableApplicationDelegate.m in Sources */,
                                EEF17D5D16E8E2EF00D7DC4D /* NJDevice.m in Sources */,
-                               EEF17D5E16E8E2EF00D7DC4D /* NJDeviceController.m in Sources */,
+                               EEF17D5E16E8E2EF00D7DC4D /* NJInputController.m in Sources */,
                                EEF17D5F16E8E2EF00D7DC4D /* NJInput.m in Sources */,
                                EEF17D6016E8E2EF00D7DC4D /* NJInputAnalog.m in Sources */,
                                EEF17D6116E8E2EF00D7DC4D /* NJInputButton.m in Sources */,
                                EEF17D6216E8E2EF00D7DC4D /* NJInputHat.m in Sources */,
                                EEF17D6316E8E2EF00D7DC4D /* NJKeyInputField.m in Sources */,
                                EEF17D6416E8E2EF00D7DC4D /* NJMapping.m in Sources */,
-                               EEF17D6516E8E2EF00D7DC4D /* NJMappingsController.m in Sources */,
                                EEF17D6616E8E2EF00D7DC4D /* NJOutput.m in Sources */,
                                EEF17D6716E8E2EF00D7DC4D /* NJOutputController.m in Sources */,
                                EEF17D6816E8E2EF00D7DC4D /* NJOutputKeyPress.m in Sources */,
index 78a6a1a..3114a8b 100644 (file)
@@ -46,7 +46,7 @@
        <key>CFBundleSignature</key>
        <string>????</string>
        <key>CFBundleVersion</key>
-       <string>548</string>
+       <string>559</string>
        <key>LSApplicationCategoryType</key>
        <string>public.app-category.utilities</string>
        <key>NSHumanReadableCopyright</key>
index b3d81ba..0c068fc 100644 (file)
                                                                <int key="NSvFlags">268</int>
                                                                <string key="NSFrame">{{7, 14}, {36, 25}}</string>
                                                                <reference key="NSSuperview"/>
-                                                               <reference key="NSWindow"/>
                                                                <string key="NSReuseIdentifierKey">_NS:9</string>
                                                                <bool key="NSEnabled">YES</bool>
                                                                <object class="NSButtonCell" key="NSCell" id="422366518">
                                                                <int key="NSvFlags">268</int>
                                                                <string key="NSFrame">{{0, 14}, {140, 25}}</string>
                                                                <reference key="NSSuperview"/>
-                                                               <reference key="NSWindow"/>
                                                                <string key="NSReuseIdentifierKey">_NS:9</string>
                                                                <bool key="NSEnabled">YES</bool>
                                                                <object class="NSButtonCell" key="NSCell" id="850080795">
@@ -570,7 +568,7 @@ aW5nLg</string>
                                                                                                        <string key="NSFrameSize">{232, 321}</string>
                                                                                                        <reference key="NSSuperview" ref="698362889"/>
                                                                                                        <reference key="NSWindow"/>
-                                                                                                       <reference key="NSNextKeyView" ref="1036252745"/>
+                                                                                                       <reference key="NSNextKeyView" ref="892486973"/>
                                                                                                        <bool key="NSEnabled">YES</bool>
                                                                                                        <bool key="NSAllowsLogicalLayoutDirection">NO</bool>
                                                                                                        <bool key="NSControlAllowsExpansionToolTips">YES</bool>
@@ -700,7 +698,7 @@ aW5nLg</string>
                                                                        <string key="NSFrameSize">{234, 323}</string>
                                                                        <reference key="NSSuperview" ref="734312853"/>
                                                                        <reference key="NSWindow"/>
-                                                                       <reference key="NSNextKeyView" ref="892486973"/>
+                                                                       <reference key="NSNextKeyView" ref="698362889"/>
                                                                        <int key="NSsFlags">150034</int>
                                                                        <reference key="NSVScroller" ref="1036252745"/>
                                                                        <reference key="NSHScroller" ref="892486973"/>
@@ -1218,6 +1216,101 @@ aW5nLg</string>
                                <string key="NSFrameAutosaveName">Enjoyable</string>
                                <bool key="NSWindowIsRestorable">YES</bool>
                        </object>
+                       <object class="NSMenu" id="388664617">
+                               <string key="NSTitle"/>
+                               <array class="NSMutableArray" key="NSMenuItems">
+                                       <object class="NSMenuItem" id="679462300">
+                                               <reference key="NSMenu" ref="388664617"/>
+                                               <string key="NSTitle">Enable</string>
+                                               <string key="NSKeyEquiv"/>
+                                               <int key="NSMnemonicLoc">2147483647</int>
+                                               <reference key="NSOnImage" ref="35465992"/>
+                                               <reference key="NSMixedImage" ref="502551668"/>
+                                       </object>
+                                       <object class="NSMenuItem" id="462014786">
+                                               <reference key="NSMenu" ref="388664617"/>
+                                               <bool key="NSIsDisabled">YES</bool>
+                                               <bool key="NSIsSeparator">YES</bool>
+                                               <string key="NSTitle"/>
+                                               <string key="NSKeyEquiv"/>
+                                               <int key="NSMnemonicLoc">2147483647</int>
+                                               <reference key="NSOnImage" ref="35465992"/>
+                                               <reference key="NSMixedImage" ref="502551668"/>
+                                       </object>
+                                       <object class="NSMenuItem" id="937740972">
+                                               <reference key="NSMenu" ref="388664617"/>
+                                               <bool key="NSIsDisabled">YES</bool>
+                                               <bool key="NSIsSeparator">YES</bool>
+                                               <string key="NSTitle"/>
+                                               <string key="NSKeyEquiv"/>
+                                               <int key="NSMnemonicLoc">2147483647</int>
+                                               <reference key="NSOnImage" ref="35465992"/>
+                                               <reference key="NSMixedImage" ref="502551668"/>
+                                       </object>
+                                       <object class="NSMenuItem" id="548842355">
+                                               <reference key="NSMenu" ref="388664617"/>
+                                               <string key="NSTitle">Show Enjoyable</string>
+                                               <string key="NSKeyEquiv"/>
+                                               <int key="NSMnemonicLoc">2147483647</int>
+                                               <reference key="NSOnImage" ref="35465992"/>
+                                               <reference key="NSMixedImage" ref="502551668"/>
+                                       </object>
+                                       <object class="NSMenuItem" id="567028745">
+                                               <reference key="NSMenu" ref="388664617"/>
+                                               <string key="NSTitle">Quit Enjoyable</string>
+                                               <string key="NSKeyEquiv"/>
+                                               <int key="NSMnemonicLoc">2147483647</int>
+                                               <reference key="NSOnImage" ref="35465992"/>
+                                               <reference key="NSMixedImage" ref="502551668"/>
+                                       </object>
+                               </array>
+                       </object>
+                       <object class="NSMenu" id="330397624">
+                               <string key="NSTitle"/>
+                               <array class="NSMutableArray" key="NSMenuItems">
+                                       <object class="NSMenuItem" id="803553950">
+                                               <reference key="NSMenu" ref="330397624"/>
+                                               <string key="NSTitle">Enable</string>
+                                               <string key="NSKeyEquiv"/>
+                                               <int key="NSMnemonicLoc">2147483647</int>
+                                               <reference key="NSOnImage" ref="35465992"/>
+                                               <reference key="NSMixedImage" ref="502551668"/>
+                                       </object>
+                                       <object class="NSMenuItem" id="598206649">
+                                               <reference key="NSMenu" ref="330397624"/>
+                                               <bool key="NSIsDisabled">YES</bool>
+                                               <bool key="NSIsSeparator">YES</bool>
+                                               <string key="NSTitle"/>
+                                               <string key="NSKeyEquiv"/>
+                                               <int key="NSMnemonicLoc">2147483647</int>
+                                               <reference key="NSOnImage" ref="35465992"/>
+                                               <reference key="NSMixedImage" ref="502551668"/>
+                                       </object>
+                               </array>
+                       </object>
+                       <object class="NSCustomObject" id="207406104">
+                               <string key="NSClassName">EnjoyableApplicationDelegate</string>
+                       </object>
+                       <object class="NSCustomObject" id="1007832501">
+                               <string key="NSClassName">NJInputController</string>
+                       </object>
+                       <object class="NSCustomObject" id="801536542">
+                               <string key="NSClassName">NJOutputController</string>
+                       </object>
+                       <object class="NSCustomObject" id="647344717">
+                               <string key="NSClassName">NJDeviceViewController</string>
+                       </object>
+                       <object class="NSCustomObject" id="70919963">
+                               <string key="NSClassName">NJMappingsViewController</string>
+                       </object>
+                       <object class="NSPopover" id="586993839">
+                               <nil key="NSNextResponder"/>
+                               <int key="NSAppearance">0</int>
+                               <int key="NSBehavior">1</int>
+                               <double key="NSContentWidth">0.0</double>
+                               <double key="NSContentHeight">0.0</double>
+                               <bool key="NSAnimates">YES</bool>
+                       </object>
                        <object class="NSCustomView" id="671181514">
                                <reference key="NSNextResponder"/>
                                <int key="NSvFlags">256</int>
@@ -1236,7 +1329,7 @@ aW5nLg</string>
                                                                                <string key="NSFrameSize">{198, 198}</string>
                                                                                <reference key="NSSuperview" ref="947403733"/>
                                                                                <reference key="NSWindow"/>
-                                                                               <reference key="NSNextKeyView" ref="968378655"/>
+                                                                               <reference key="NSNextKeyView" ref="553414014"/>
                                                                                <bool key="NSEnabled">YES</bool>
                                                                                <bool key="NSAllowsLogicalLayoutDirection">NO</bool>
                                                                                <bool key="NSControlAllowsExpansionToolTips">YES</bool>
@@ -1328,7 +1421,7 @@ aW5nLg</string>
                                                <string key="NSFrame">{{0, 20}, {200, 200}}</string>
                                                <reference key="NSSuperview" ref="671181514"/>
                                                <reference key="NSWindow"/>
-                                               <reference key="NSNextKeyView" ref="553414014"/>
+                                               <reference key="NSNextKeyView" ref="947403733"/>
                                                <int key="NSsFlags">150034</int>
                                                <reference key="NSVScroller" ref="968378655"/>
                                                <reference key="NSHScroller" ref="553414014"/>
@@ -1473,104 +1566,6 @@ aW5nLg</string>
                                <reference key="NSNextKeyView" ref="443618264"/>
                                <string key="NSClassName">NSView</string>
                        </object>
-                       <object class="NSMenu" id="388664617">
-                               <string key="NSTitle"/>
-                               <array class="NSMutableArray" key="NSMenuItems">
-                                       <object class="NSMenuItem" id="679462300">
-                                               <reference key="NSMenu" ref="388664617"/>
-                                               <string key="NSTitle">Enable</string>
-                                               <string key="NSKeyEquiv"/>
-                                               <int key="NSMnemonicLoc">2147483647</int>
-                                               <reference key="NSOnImage" ref="35465992"/>
-                                               <reference key="NSMixedImage" ref="502551668"/>
-                                       </object>
-                                       <object class="NSMenuItem" id="462014786">
-                                               <reference key="NSMenu" ref="388664617"/>
-                                               <bool key="NSIsDisabled">YES</bool>
-                                               <bool key="NSIsSeparator">YES</bool>
-                                               <string key="NSTitle"/>
-                                               <string key="NSKeyEquiv"/>
-                                               <int key="NSMnemonicLoc">2147483647</int>
-                                               <reference key="NSOnImage" ref="35465992"/>
-                                               <reference key="NSMixedImage" ref="502551668"/>
-                                       </object>
-                                       <object class="NSMenuItem" id="937740972">
-                                               <reference key="NSMenu" ref="388664617"/>
-                                               <bool key="NSIsDisabled">YES</bool>
-                                               <bool key="NSIsSeparator">YES</bool>
-                                               <string key="NSTitle"/>
-                                               <string key="NSKeyEquiv"/>
-                                               <int key="NSMnemonicLoc">2147483647</int>
-                                               <reference key="NSOnImage" ref="35465992"/>
-                                               <reference key="NSMixedImage" ref="502551668"/>
-                                       </object>
-                                       <object class="NSMenuItem" id="548842355">
-                                               <reference key="NSMenu" ref="388664617"/>
-                                               <string key="NSTitle">Show Enjoyable</string>
-                                               <string key="NSKeyEquiv"/>
-                                               <int key="NSMnemonicLoc">2147483647</int>
-                                               <reference key="NSOnImage" ref="35465992"/>
-                                               <reference key="NSMixedImage" ref="502551668"/>
-                                       </object>
-                                       <object class="NSMenuItem" id="567028745">
-                                               <reference key="NSMenu" ref="388664617"/>
-                                               <string key="NSTitle">Quit Enjoyable</string>
-                                               <string key="NSKeyEquiv"/>
-                                               <int key="NSMnemonicLoc">2147483647</int>
-                                               <reference key="NSOnImage" ref="35465992"/>
-                                               <reference key="NSMixedImage" ref="502551668"/>
-                                       </object>
-                               </array>
-                       </object>
-                       <object class="NSMenu" id="330397624">
-                               <string key="NSTitle"/>
-                               <array class="NSMutableArray" key="NSMenuItems">
-                                       <object class="NSMenuItem" id="803553950">
-                                               <reference key="NSMenu" ref="330397624"/>
-                                               <string key="NSTitle">Enable</string>
-                                               <string key="NSKeyEquiv"/>
-                                               <int key="NSMnemonicLoc">2147483647</int>
-                                               <reference key="NSOnImage" ref="35465992"/>
-                                               <reference key="NSMixedImage" ref="502551668"/>
-                                       </object>
-                                       <object class="NSMenuItem" id="598206649">
-                                               <reference key="NSMenu" ref="330397624"/>
-                                               <bool key="NSIsDisabled">YES</bool>
-                                               <bool key="NSIsSeparator">YES</bool>
-                                               <string key="NSTitle"/>
-                                               <string key="NSKeyEquiv"/>
-                                               <int key="NSMnemonicLoc">2147483647</int>
-                                               <reference key="NSOnImage" ref="35465992"/>
-                                               <reference key="NSMixedImage" ref="502551668"/>
-                                       </object>
-                               </array>
-                       </object>
-                       <object class="NSCustomObject" id="207406104">
-                               <string key="NSClassName">EnjoyableApplicationDelegate</string>
-                       </object>
-                       <object class="NSPopover" id="586993839">
-                               <nil key="NSNextResponder"/>
-                               <int key="NSAppearance">0</int>
-                               <int key="NSBehavior">1</int>
-                               <double key="NSContentWidth">0.0</double>
-                               <double key="NSContentHeight">0.0</double>
-                               <bool key="NSAnimates">YES</bool>
-                       </object>
-                       <object class="NSCustomObject" id="468285243">
-                               <string key="NSClassName">NJMappingsController</string>
-                       </object>
-                       <object class="NSCustomObject" id="1007832501">
-                               <string key="NSClassName">NJDeviceController</string>
-                       </object>
-                       <object class="NSCustomObject" id="801536542">
-                               <string key="NSClassName">NJOutputController</string>
-                       </object>
-                       <object class="NSCustomObject" id="647344717">
-                               <string key="NSClassName">NJDeviceViewController</string>
-                       </object>
-                       <object class="NSCustomObject" id="70919963">
-                               <string key="NSClassName">NJMappingsViewController</string>
-                       </object>
                        <object class="NSCustomObject" id="9563326">
                                <string key="NSClassName">NJMappingMenuController</string>
                        </object>
@@ -1698,14 +1693,6 @@ aW5nLg</string>
                                        </object>
                                        <int key="connectionID">1002</int>
                                </object>
-                               <object class="IBConnectionRecord">
-                                       <object class="IBOutletConnection" key="connection">
-                                               <string key="label">mappingsController</string>
-                                               <reference key="source" ref="1007832501"/>
-                                               <reference key="destination" ref="468285243"/>
-                                       </object>
-                                       <int key="connectionID">822</int>
-                               </object>
                                <object class="IBConnectionRecord">
                                        <object class="IBOutletConnection" key="connection">
                                                <string key="label">delegate</string>
@@ -1714,14 +1701,6 @@ aW5nLg</string>
                                        </object>
                                        <int key="connectionID">1029</int>
                                </object>
-                               <object class="IBConnectionRecord">
-                                       <object class="IBOutletConnection" key="connection">
-                                               <string key="label">mappingsController</string>
-                                               <reference key="source" ref="207406104"/>
-                                               <reference key="destination" ref="468285243"/>
-                                       </object>
-                                       <int key="connectionID">820</int>
-                               </object>
                                <object class="IBConnectionRecord">
                                        <object class="IBOutletConnection" key="connection">
                                                <string key="label">window</string>
@@ -1786,14 +1765,6 @@ aW5nLg</string>
                                        </object>
                                        <int key="connectionID">1025</int>
                                </object>
-                               <object class="IBConnectionRecord">
-                                       <object class="IBOutletConnection" key="connection">
-                                               <string key="label">deviceController</string>
-                                               <reference key="source" ref="207406104"/>
-                                               <reference key="destination" ref="1007832501"/>
-                                       </object>
-                                       <int key="connectionID">1026</int>
-                               </object>
                                <object class="IBConnectionRecord">
                                        <object class="IBOutletConnection" key="connection">
                                                <string key="label">outputController</string>
@@ -1818,6 +1789,14 @@ aW5nLg</string>
                                        </object>
                                        <int key="connectionID">1031</int>
                                </object>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBOutletConnection" key="connection">
+                                               <string key="label">inputController</string>
+                                               <reference key="source" ref="207406104"/>
+                                               <reference key="destination" ref="1007832501"/>
+                                       </object>
+                                       <int key="connectionID">1032</int>
+                               </object>
                                <object class="IBConnectionRecord">
                                        <object class="IBActionConnection" key="connection">
                                                <string key="label">performClick:</string>
@@ -1842,14 +1821,6 @@ aW5nLg</string>
                                        </object>
                                        <int key="connectionID">991</int>
                                </object>
-                               <object class="IBConnectionRecord">
-                                       <object class="IBOutletConnection" key="connection">
-                                               <string key="label">mappingsController</string>
-                                               <reference key="source" ref="801536542"/>
-                                               <reference key="destination" ref="468285243"/>
-                                       </object>
-                                       <int key="connectionID">821</int>
-                               </object>
                                <object class="IBConnectionRecord">
                                        <object class="IBOutletConnection" key="connection">
                                                <string key="label">mouseDirSelect</string>
@@ -1994,6 +1965,14 @@ aW5nLg</string>
                                        </object>
                                        <int key="connectionID">1022</int>
                                </object>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBOutletConnection" key="connection">
+                                               <string key="label">inputController</string>
+                                               <reference key="source" ref="801536542"/>
+                                               <reference key="destination" ref="1007832501"/>
+                                       </object>
+                                       <int key="connectionID">1034</int>
+                               </object>
                                <object class="IBConnectionRecord">
                                        <object class="IBOutletConnection" key="connection">
                                                <string key="label">delegate</string>
@@ -2537,11 +2516,6 @@ aW5nLg</string>
                                                <reference key="object" ref="1008023024"/>
                                                <reference key="parent" ref="456935010"/>
                                        </object>
-                                       <object class="IBObjectRecord">
-                                               <int key="objectID">514</int>
-                                               <reference key="object" ref="468285243"/>
-                                               <reference key="parent" ref="0"/>
-                                       </object>
                                        <object class="IBObjectRecord">
                                                <int key="objectID">479</int>
                                                <reference key="object" ref="1007832501"/>
@@ -3171,7 +3145,6 @@ aW5nLg</string>
                                </object>
                                <string key="511.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
                                <string key="512.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
-                               <string key="514.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
                                <string key="56.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
                                <string key="57.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
                                <string key="58.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -3373,7 +3346,7 @@ aW5nLg</string>
                        <nil key="activeLocalization"/>
                        <dictionary class="NSMutableDictionary" key="localizations"/>
                        <nil key="sourceID"/>
-                       <int key="maxID">1031</int>
+                       <int key="maxID">1034</int>
                </object>
                <object class="IBClassDescriber" key="IBDocument.Classes">
                        <array class="NSMutableArray" key="referencedPartialClassDescriptions">
@@ -3405,10 +3378,9 @@ aW5nLg</string>
                                                </object>
                                        </dictionary>
                                        <dictionary class="NSMutableDictionary" key="outlets">
-                                               <string key="deviceController">NJDeviceController</string>
                                                <string key="dockMenu">NSMenu</string>
                                                <string key="dvc">NJDeviceViewController</string>
-                                               <string key="mappingsController">NJMappingsController</string>
+                                               <string key="inputController">NJInputController</string>
                                                <string key="mvc">NJMappingsViewController</string>
                                                <string key="outputController">NJOutputController</string>
                                                <string key="simulatingEventsButton">NSButton</string>
@@ -3416,10 +3388,6 @@ aW5nLg</string>
                                                <string key="window">NSWindow</string>
                                        </dictionary>
                                        <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
-                                               <object class="IBToOneOutletInfo" key="deviceController">
-                                                       <string key="name">deviceController</string>
-                                                       <string key="candidateClassName">NJDeviceController</string>
-                                               </object>
                                                <object class="IBToOneOutletInfo" key="dockMenu">
                                                        <string key="name">dockMenu</string>
                                                        <string key="candidateClassName">NSMenu</string>
@@ -3428,9 +3396,9 @@ aW5nLg</string>
                                                        <string key="name">dvc</string>
                                                        <string key="candidateClassName">NJDeviceViewController</string>
                                                </object>
-                                               <object class="IBToOneOutletInfo" key="mappingsController">
-                                                       <string key="name">mappingsController</string>
-                                                       <string key="candidateClassName">NJMappingsController</string>
+                                               <object class="IBToOneOutletInfo" key="inputController">
+                                                       <string key="name">inputController</string>
+                                                       <string key="candidateClassName">NJInputController</string>
                                                </object>
                                                <object class="IBToOneOutletInfo" key="mvc">
                                                        <string key="name">mvc</string>
@@ -3458,28 +3426,6 @@ aW5nLg</string>
                                                <string key="minorKey">./Classes/EnjoyableApplicationDelegate.h</string>
                                        </object>
                                </object>
-                               <object class="IBPartialClassDescription">
-                                       <string key="className">NJDeviceController</string>
-                                       <string key="superclassName">NSObject</string>
-                                       <dictionary class="NSMutableDictionary" key="outlets">
-                                               <string key="delegate">id</string>
-                                               <string key="mappingsController">NJMappingsController</string>
-                                       </dictionary>
-                                       <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
-                                               <object class="IBToOneOutletInfo" key="delegate">
-                                                       <string key="name">delegate</string>
-                                                       <string key="candidateClassName">id</string>
-                                               </object>
-                                               <object class="IBToOneOutletInfo" key="mappingsController">
-                                                       <string key="name">mappingsController</string>
-                                                       <string key="candidateClassName">NJMappingsController</string>
-                                               </object>
-                                       </dictionary>
-                                       <object class="IBClassDescriptionSource" key="sourceIdentifier">
-                                               <string key="majorKey">IBProjectSource</string>
-                                               <string key="minorKey">./Classes/NJDeviceController.h</string>
-                                       </object>
-                               </object>
                                <object class="IBPartialClassDescription">
                                        <string key="className">NJDeviceViewController</string>
                                        <string key="superclassName">NSObject</string>
@@ -3512,6 +3458,25 @@ aW5nLg</string>
                                                <string key="minorKey">./Classes/NJDeviceViewController.h</string>
                                        </object>
                                </object>
+                               <object class="IBPartialClassDescription">
+                                       <string key="className">NJInputController</string>
+                                       <string key="superclassName">NSObject</string>
+                                       <object class="NSMutableDictionary" key="outlets">
+                                               <string key="NS.key.0">delegate</string>
+                                               <string key="NS.object.0">id</string>
+                                       </object>
+                                       <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+                                               <string key="NS.key.0">delegate</string>
+                                               <object class="IBToOneOutletInfo" key="NS.object.0">
+                                                       <string key="name">delegate</string>
+                                                       <string key="candidateClassName">id</string>
+                                               </object>
+                                       </object>
+                                       <object class="IBClassDescriptionSource" key="sourceIdentifier">
+                                               <string key="majorKey">IBProjectSource</string>
+                                               <string key="minorKey">./Classes/NJInputController.h</string>
+                                       </object>
+                               </object>
                                <object class="IBPartialClassDescription">
                                        <string key="className">NJKeyInputField</string>
                                        <string key="superclassName">NSControl</string>
@@ -3558,14 +3523,6 @@ aW5nLg</string>
                                                <string key="minorKey">./Classes/NJMappingMenuController.h</string>
                                        </object>
                                </object>
-                               <object class="IBPartialClassDescription">
-                                       <string key="className">NJMappingsController</string>
-                                       <string key="superclassName">NSObject</string>
-                                       <object class="IBClassDescriptionSource" key="sourceIdentifier">
-                                               <string key="majorKey">IBProjectSource</string>
-                                               <string key="minorKey">./Classes/NJMappingsController.h</string>
-                                       </object>
-                               </object>
                                <object class="IBPartialClassDescription">
                                        <string key="className">NJMappingsViewController</string>
                                        <string key="superclassName">NSViewController</string>
@@ -3685,9 +3642,9 @@ aW5nLg</string>
                                                </object>
                                        </dictionary>
                                        <dictionary class="NSMutableDictionary" key="outlets">
+                                               <string key="inputController">NJInputController</string>
                                                <string key="keyInput">NJKeyInputField</string>
                                                <string key="mappingPopup">NSPopUpButton</string>
-                                               <string key="mappingsController">NJMappingsController</string>
                                                <string key="mouseBtnSelect">NSSegmentedControl</string>
                                                <string key="mouseDirSelect">NSSegmentedControl</string>
                                                <string key="mouseSpeedSlider">NSSlider</string>
@@ -3699,6 +3656,10 @@ aW5nLg</string>
                                                <string key="unknownMapping">NSButton</string>
                                        </dictionary>
                                        <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+                                               <object class="IBToOneOutletInfo" key="inputController">
+                                                       <string key="name">inputController</string>
+                                                       <string key="candidateClassName">NJInputController</string>
+                                               </object>
                                                <object class="IBToOneOutletInfo" key="keyInput">
                                                        <string key="name">keyInput</string>
                                                        <string key="candidateClassName">NJKeyInputField</string>
@@ -3707,10 +3668,6 @@ aW5nLg</string>
                                                        <string key="name">mappingPopup</string>
                                                        <string key="candidateClassName">NSPopUpButton</string>
                                                </object>
-                                               <object class="IBToOneOutletInfo" key="mappingsController">
-                                                       <string key="name">mappingsController</string>
-                                                       <string key="candidateClassName">NJMappingsController</string>
-                                               </object>
                                                <object class="IBToOneOutletInfo" key="mouseBtnSelect">
                                                        <string key="name">mouseBtnSelect</string>
                                                        <string key="candidateClassName">NSSegmentedControl</string>