From 1ddf0ccce8bbc789483ae087390c2e93abdbd7ea Mon Sep 17 00:00:00 2001 From: Joe Wreschnig Date: Mon, 11 Mar 2013 17:55:14 +0100 Subject: [PATCH] Disable HID manager entirely when the window is closed and translation is disabled. --- Classes/NJDeviceController.m | 81 +++++++++++++++++++++++++++++------- Info.plist | 2 +- 2 files changed, 68 insertions(+), 15 deletions(-) diff --git a/Classes/NJDeviceController.m b/Classes/NJDeviceController.m index cb7b917..2849838 100644 --- a/Classes/NJDeviceController.m +++ b/Classes/NJDeviceController.m @@ -26,19 +26,39 @@ if ((self = [super init])) { _devices = [[NSMutableArray alloc] initWithCapacity:16]; _continousOutputs = [[NSMutableArray alloc] initWithCapacity:32]; + [NSNotificationCenter.defaultCenter + addObserver:self + selector:@selector(applicationDidFinishLaunching:) + 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 + // something we can make faster). I don't really think that's + // acceptable, CPU/power wise. So if translation 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(setup) - name:NSApplicationDidFinishLaunchingNotification + selector:@selector(closeHidIfDisabled:) + name:NSApplicationDidResignActiveNotification + object:nil]; + [NSNotificationCenter.defaultCenter + addObserver:self + selector:@selector(openHid) + name:NSApplicationDidBecomeActiveNotification object:nil]; } return self; } - (void)dealloc { + [NSNotificationCenter.defaultCenter removeObserver:self]; [_continuousOutputsTick invalidate]; - IOHIDManagerClose(_hidManager, kIOHIDOptionsTypeNone); - CFRelease(_hidManager); + [self closeHid]; } - (void)expandRecursive:(id )pathElement { @@ -166,8 +186,12 @@ static void remove_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDD #define NSSTR(e) ((NSString *)CFSTR(e)) -- (void)setup { +- (void)openHid { + if (_hidManager) + return; + NSLog(@"Opening HID manager."); _hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + IOHIDManagerScheduleWithRunLoop(_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); NSArray *criteria = @[ @{ NSSTR(kIOHIDDeviceUsagePageKey) : @(kHIDPage_GenericDesktop), NSSTR(kIOHIDDeviceUsageKey) : @(kHIDUsage_GD_Joystick) }, @{ NSSTR(kIOHIDDeviceUsagePageKey) : @(kHIDPage_GenericDesktop), @@ -176,8 +200,6 @@ static void remove_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDD NSSTR(kIOHIDDeviceUsageKey) : @(kHIDUsage_GD_MultiAxisController) } ]; IOHIDManagerSetDeviceMatchingMultiple(_hidManager, (__bridge CFArrayRef)criteria); - - IOHIDManagerScheduleWithRunLoop(_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); IOReturn ret = IOHIDManagerOpen(_hidManager, kIOHIDOptionsTypeNone); if (ret != kIOReturnSuccess) { [[NSAlert alertWithMessageText:@"Input devices are unavailable" @@ -187,14 +209,28 @@ static void remove_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDD informativeTextWithFormat:@"Error 0x%08x occured trying to access your devices. " @"Input may not be correctly detected or mapped.", ret] - beginSheetModalForWindow:outlineView.window - modalDelegate:nil - didEndSelector:nil - contextInfo:nil]; + beginSheetModalForWindow:outlineView.window + modalDelegate:nil + didEndSelector:nil + contextInfo:nil]; + [self closeHid]; + } else { + IOHIDManagerRegisterDeviceMatchingCallback(_hidManager, add_callback, (__bridge void *)self); + IOHIDManagerRegisterDeviceRemovalCallback(_hidManager, remove_callback, (__bridge void *)self); } - - IOHIDManagerRegisterDeviceMatchingCallback(_hidManager, add_callback, (__bridge void *)self); - IOHIDManagerRegisterDeviceRemovalCallback(_hidManager, remove_callback, (__bridge void *)self); +} + +- (void)closeHid { + if (_hidManager) { + NSLog(@"Closing HID manager."); + IOHIDManagerUnscheduleFromRunLoop(_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOHIDManagerClose(_hidManager, kIOHIDOptionsTypeNone); + CFRelease(_hidManager); + _hidManager = NULL; + } + [_devices removeAllObjects]; + [outlineView reloadData]; + connectDevicePrompt.hidden = !!_devices.count; } - (NJInput *)selectedInput { @@ -248,9 +284,26 @@ objectValueForTableColumn:(NSTableColumn *)tableColumn : NJEventTranslationDeactivated; [NSNotificationCenter.defaultCenter postNotificationName:name object:self]; + + if (!translatingEvents && !NSApplication.sharedApplication.isActive) + [self closeHid]; + else if (translatingEvents || NSApplication.sharedApplication.isActive) + [self openHid]; } } +- (void)closeHidIfDisabled:(NSNotification *)application { + if (!self.translatingEvents) + [self closeHid]; +} + +- (void)applicationDidFinishLaunching:(NSNotification *)application { + // NSApplicationWillBecomeActiveNotification occurs just slightly + // too late - there's one tick where the UI is showing "No + // devices" even with a device plugged in. + [self openHid]; +} + - (IBAction)translatingEventsChanged:(NSButton *)sender { self.translatingEvents = sender.state == NSOnState; } diff --git a/Info.plist b/Info.plist index 8401695..1ce7758 100644 --- a/Info.plist +++ b/Info.plist @@ -46,7 +46,7 @@ CFBundleSignature ???? CFBundleVersion - 146 + 175 LSApplicationCategoryType public.app-category.utilities NSHumanReadableCopyright -- 2.30.2