#import "NJOutput.h"
#import "NJOutputController.h"
#import "NJEvents.h"
+#import "NJDeviceViewController.h"
@implementation NJDeviceController {
NJHIDManager *_hidManager;
NSTimer *_continuousOutputsTick;
NSMutableArray *_continousOutputs;
NSMutableArray *_devices;
- NSMutableArray *_expanded;
}
-#define EXPANDED_MEMORY_MAX_SIZE 100
#define NSSTR(e) ((NSString *)CFSTR(e))
- (id)init {
_devices = [[NSMutableArray alloc] initWithCapacity:16];
_continousOutputs = [[NSMutableArray alloc] initWithCapacity:32];
- NSArray *expanded = [NSUserDefaults.standardUserDefaults objectForKey:@"expanded rows"];
- if (![expanded isKindOfClass:NSArray.class])
- expanded = @[];
- _expanded = [[NSMutableArray alloc] initWithCapacity:MAX(16, _expanded.count)];
- [_expanded addObjectsFromArray:expanded];
-
_hidManager = [[NJHIDManager alloc] initWithCriteria:@[
@{ NSSTR(kIOHIDDeviceUsagePageKey) : @(kHIDPage_GenericDesktop),
NSSTR(kIOHIDDeviceUsageKey) : @(kHIDUsage_GD_Joystick) },
]
delegate:self];
- [NSNotificationCenter.defaultCenter
- addObserver:self
- selector:@selector(startHid)
- name:NSApplicationDidFinishLaunchingNotification
- object:nil];
-
// 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
[_continuousOutputsTick invalidate];
}
-- (void)expandRecursive:(NJInputPathElement *)pathElement {
- if (pathElement) {
- [self expandRecursive:pathElement.parent];
- [outlineView expandItem:pathElement];
- }
-}
-
- (id)elementForUID:(NSString *)uid {
for (NJDevice *dev in _devices) {
id item = [dev elementForUID:uid];
return nil;
}
-- (void)expandRecursiveByUID:(NSString *)uid {
- [self expandRecursive:[self elementForUID:uid]];
-}
-
- (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 (!handler)
return;
- [self expandRecursive:handler];
- [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[outlineView rowForItem:handler]]
- byExtendingSelection: NO];
- if (!self.simulatingEvents)
- [outputController focusKey];
+ [devicesViewController expandAndSelectItem:handler];
+ [outputController focusKey];
}
- (void)hidManager:(NJHIDManager *)manager
}
}
-static int findAvailableIndex(NSArray *list, NJDevice *dev) {
- for (int index = 1; ; index++) {
- BOOL available = YES;
- for (NJDevice *used in list) {
- if ([used.productName isEqualToString:dev.productName]
- && used.index == index) {
+- (void)hidManager:(NJHIDManager *)manager deviceAdded:(IOHIDDeviceRef)device {
+ NJDevice *match = [[NJDevice alloc] initWithDevice:device];
+ match.index = 1;
+ BOOL available;
+ do {
+ available = YES;
+ for (NJDevice *used in _devices) {
+ if ([used isEqual:match]) {
+ match.index += 1;
available = NO;
- break;
}
}
- if (available)
- return index;
- }
-}
+ } while (!available);
-- (void)hidManager:(NJHIDManager *)manager deviceAdded:(IOHIDDeviceRef)device {
- NJDevice *match = [[NJDevice alloc] initWithDevice:device];
- match.index = findAvailableIndex(_devices, match);
[_devices addObject:match];
- [outlineView reloadData];
- [self reexpandAll];
- hidSleepingPrompt.hidden = YES;
- connectDevicePrompt.hidden = !!_devices.count;
+ [devicesViewController addedDevice:match atIndex:_devices.count - 1];
}
- (NJDevice *)findDeviceByRef:(IOHIDDeviceRef)device {
NJDevice *match = [self findDeviceByRef:device];
IOHIDDeviceRegisterInputValueCallback(device, NULL, NULL);
if (match) {
- [_devices removeObject:match];
- [outlineView reloadData];
- connectDevicePrompt.hidden = !!_devices.count;
- hidSleepingPrompt.hidden = YES;
+ NSInteger idx = [_devices indexOfObjectIdenticalTo:match];
+ [_devices removeObjectAtIndex:idx];
+ [devicesViewController removedDevice:match atIndex:idx];
}
- if (_devices.count == 1)
- [outlineView expandItem:_devices[0]];
}
- (void)updateContinuousOutputs:(NSTimer *)timer {
// 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.
- if (!outlineView.window.attachedSheet) {
+ if (!simulatingEventsButton.window.attachedSheet) {
[NSApplication.sharedApplication activateIgnoringOtherApps:YES];
- [outlineView.window makeKeyAndOrderFront:nil];
- [outlineView.window presentError:error
- modalForWindow:outlineView.window
- delegate:nil
- didPresentSelector:nil
- contextInfo:nil];
+ [simulatingEventsButton.window makeKeyAndOrderFront:nil];
+ [simulatingEventsButton.window presentError:error
+ modalForWindow:simulatingEventsButton.window
+ delegate:nil
+ didPresentSelector:nil
+ contextInfo:nil];
}
self.simulatingEvents = NO;
- if (manager.running)
- [self hidManagerDidStart:manager];
- else
- [self hidManagerDidStop:manager];
}
- (void)hidManagerDidStart:(NJHIDManager *)manager {
- hidSleepingPrompt.hidden = YES;
- connectDevicePrompt.hidden = !!_devices.count;
+ [devicesViewController hidStarted];
}
- (void)hidManagerDidStop:(NJHIDManager *)manager {
[_devices removeAllObjects];
- [outlineView reloadData];
- hidSleepingPrompt.hidden = NO;
- connectDevicePrompt.hidden = YES;
+ [devicesViewController hidStopped];
}
- (void)startHid {
}
- (NJInput *)selectedInput {
- NJInputPathElement *item = [outlineView itemAtRow:outlineView.selectedRow];
- return (NJInput *)((!item.children && item.parent) ? item : nil);
-}
-
-- (NSInteger)outlineView:(NSOutlineView *)outlineView
- numberOfChildrenOfItem:(NJInputPathElement *)item {
- return item ? item.children.count : _devices.count;
-}
-
-- (BOOL)outlineView:(NSOutlineView *)outlineView
- isItemExpandable:(NJInputPathElement *)item {
- return item ? [[item children] count] > 0: YES;
-}
-
-- (id)outlineView:(NSOutlineView *)outlineView
- child:(NSInteger)index
- ofItem:(NJInputPathElement *)item {
- return item ? item.children[index] : _devices[index];
-}
-
-- (id)outlineView:(NSOutlineView *)outlineView
-objectValueForTableColumn:(NSTableColumn *)tableColumn
- byItem:(NJInputPathElement *)item {
- return item ? item.name : @"root";
-}
-
-- (void)outlineViewSelectionDidChange:(NSNotification *)notification {
- NJInputPathElement *item = [outlineView itemAtRow:outlineView.selectedRow];
- if (item)
- [NSUserDefaults.standardUserDefaults setObject:item.uid
- forKey:@"selected input"];
- [outputController loadCurrent];
-}
-
-- (BOOL)outlineView:(NSOutlineView *)outlineView
- isGroupItem:(NJInputPathElement *)item {
- return [item isKindOfClass:NJDevice.class];
-}
-
-- (BOOL)outlineView:(NSOutlineView *)outlineView_
- shouldSelectItem:(NJInputPathElement *)item {
- return ![self outlineView:outlineView_ isGroupItem:item];
-}
-
-- (void)outlineViewItemDidExpand:(NSNotification *)notification {
- NJInputPathElement *item = notification.userInfo[@"NSObject"];
- NSString *uid = item.uid;
- if (![_expanded containsObject:uid])
- [_expanded addObject:uid];
- while (_expanded.count > EXPANDED_MEMORY_MAX_SIZE)
- [_expanded removeObjectAtIndex:0];
- [NSUserDefaults.standardUserDefaults setObject:_expanded
- forKey:@"expanded rows"];
-}
-
-- (void)outlineViewItemDidCollapse:(NSNotification *)notification {
- NJInputPathElement *item = notification.userInfo[@"NSObject"];
- [_expanded removeObject:item.uid];
- [NSUserDefaults.standardUserDefaults setObject:_expanded
- forKey:@"expanded rows"];
+ return (NJInput *)devicesViewController.selectedHandler;
}
- (void)setSimulatingEvents:(BOOL)simulatingEvents {
}
}
-- (void)reexpandAll {
- for (NSString *uid in [_expanded copy])
- [self expandRecursiveByUID:uid];
- if (outlineView.selectedRow == -1) {
- NSString *selectedUid = [NSUserDefaults.standardUserDefaults objectForKey:@"selected input"];
- id item = [self elementForUID:selectedUid];
- NSInteger row = [outlineView rowForItem:item];
- if (row >= 0)
- [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
- }
-}
-
- (void)stopHidIfDisabled:(NSNotification *)application {
if (!self.simulatingEvents)
[self stopHid];
self.simulatingEvents = sender.state == NSOnState;
}
+- (void)deviceViewControllerDidSelectNothing:(NJDeviceViewController *)devices {
+ [outputController loadCurrent];
+}
+
+- (void)deviceViewController:(NJDeviceViewController *)devices
+ didSelectBranch:(NJInputPathElement *)handler {
+ [outputController loadCurrent];
+}
+
+- (void)deviceViewController:(NJDeviceViewController *)devices
+ didSelectHandler:(NJInputPathElement *)handler {
+ [outputController loadCurrent];
+}
+
+- (void)deviceViewController:(NJDeviceViewController *)devices
+ didSelectDevice:(NJInputPathElement *)device {
+ [outputController loadCurrent];
+}
+
@end