Switch from NSTimer to a CVDisplayLink to handle continuous events. NSTimer was never...
[enjoyable.git] / Classes / NJInputController.m
index cd665de..face5e7 100644 (file)
 #import "NJOutput.h"
 #import "NJEvents.h"
 
+#import <CoreVideo/CoreVideo.h>
+
+@interface NJInputController ()
+
+- (void)updateContinuousOutputs;
+
+@end
+
+static CVReturn displayLink_update_cb(CVDisplayLinkRef displayLink,
+                                          const CVTimeStamp *inNow,
+                                          const CVTimeStamp *inOutputTime,
+                                          CVOptionFlags flagsIn,
+                                          CVOptionFlags *flagsOut,
+                                          void *ctxManager) {
+    NJInputController *manager = (__bridge NJInputController *)ctxManager;
+    [manager performSelectorOnMainThread:@selector(updateContinuousOutputs)
+                              withObject:nil
+                           waitUntilDone:NO];
+    return kCVReturnSuccess;
+}
+
 @implementation NJInputController {
     NJHIDManager *_HIDManager;
-    NSTimer *_continuousOutputsTick;
     NSMutableArray *_continousOutputs;
     NSMutableArray *_devices;
     NSMutableArray *_mappings;
     NJMapping *_manualMapping;
+    CVDisplayLinkRef displayLink;
 
 }
 
     if ((self = [super init])) {
         _devices = [[NSMutableArray alloc] initWithCapacity:16];
         _continousOutputs = [[NSMutableArray alloc] initWithCapacity:32];
+
+        CVReturn error = CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
+        if (error) {
+            [self.delegate inputController:self
+                                  didError:[NSError errorWithDomain:NSCocoaErrorDomain
+                                                               code:error
+                                                           userInfo:nil]];
+            NSLog(@"DisplayLink failed creation with error: %d.", error);
+            displayLink = NULL;
+        }
+        CVDisplayLinkSetOutputCallback(displayLink, displayLink_update_cb, (__bridge void *)self);
         
         _HIDManager = [[NJHIDManager alloc] initWithCriteria:@[
                        @{ NSSTR(kIOHIDDeviceUsagePageKey) : @(kHIDPage_GenericDesktop),
 
 - (void)dealloc {
     [NSNotificationCenter.defaultCenter removeObserver:self];
-    [_continuousOutputsTick invalidate];
+    if (displayLink) {
+        CVDisplayLinkStop(displayLink);
+        CVDisplayLinkRelease(displayLink);
+    }
 }
 
 - (void)addRunningOutput:(NJOutput *)output {
     // 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];
+    if (displayLink && !CVDisplayLinkIsRunning(displayLink)) {
+        CVDisplayLinkStart(displayLink);
     }
 }
 
     }
 }
 
-- (void)updateContinuousOutputs:(NSTimer *)timer {
+- (void)updateContinuousOutputs {
     self.mouseLoc = [NSEvent mouseLocation];
     for (NJOutput *output in [_continousOutputs copy]) {
         if (![output update:self]) {
             [_continousOutputs removeObject:output];
         }
     }
-    if (!_continousOutputs.count) {
-        [_continuousOutputsTick invalidate];
-        _continuousOutputsTick = nil;
+    if (!_continousOutputs.count && displayLink) {
+        CVDisplayLinkStop(displayLink);
     }
 }
 
 - (void)HIDManager:(NJHIDManager *)manager didError:(NSError *)error {
     [self.delegate inputController:self didError:error];
     self.simulatingEvents = NO;
+    if (displayLink)
+        CVDisplayLinkStop(displayLink);
 }
 
 - (void)HIDManagerDidStart:(NJHIDManager *)manager {
 
 - (void)HIDManagerDidStop:(NJHIDManager *)manager {
     [_devices removeAllObjects];
+    if (displayLink)
+        CVDisplayLinkStop(displayLink);
     [self.delegate inputControllerDidStopHID:self];
 }