From 594eda09b1112ae55cd14e1a318cd2d2f7a6cef8 Mon Sep 17 00:00:00 2001 From: Tom Bogle Date: Mon, 5 Feb 2018 21:54:45 -0500 Subject: [PATCH 1/5] [WIP] Beginning of logic to detect low-level events that may signal a context change. --- .../Keyman4MacIM/KMInputMethodAppDelegate.h | 1 + .../Keyman4MacIM/KMInputMethodAppDelegate.m | 57 ++++++++++++++----- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.h b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.h index e2751039bb..9874e32c56 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.h +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.h @@ -56,6 +56,7 @@ extern NSString *const kWebSite; @property (nonatomic, strong) NSMutableArray *activeKeyboards; @property (nonatomic, strong) NSMutableString *contextBuffer; @property (nonatomic, assign) NSEventModifierFlags currentModifierFlags; +@property (nonatomic, assign) BOOL contextChangingEventDetected; @property (nonatomic, strong) OSKWindowController *oskWindow; @property (nonatomic, strong) NSString *keyboardName; @property (nonatomic, strong) NSImage *keyboardIcon; diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m index 4145a45cd3..83144715fa 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m @@ -73,20 +73,31 @@ typedef enum { forEventClass:kInternetEventClass andEventID:kAEGetURL]; - CFMachPortRef eventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, NSFlagsChangedMask, (CGEventTapCallBack)eventTapFunction, nil); + CFMachPortRef flagsChangedEventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, NSFlagsChangedMask, (CGEventTapCallBack)eventTapFunction, nil); - if (!eventTap) + if (!flagsChangedEventTap) NSLog(@"Can't tap into flags changed event!"); else - CFRelease(eventTap); + CFRelease(flagsChangedEventTap); - CFRunLoopSourceRef flagsChangedEventSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0); - if (flagsChangedEventSrc ) { - - CFRunLoopRef runLoop = CFRunLoopGetCurrent(); - if (runLoop) { - CFRunLoopAddSource(runLoop, flagsChangedEventSrc, kCFRunLoopDefaultMode); - } + CFRunLoopSourceRef flagsChangedEventSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, flagsChangedEventTap, 0); + + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); + + if (flagsChangedEventSrc && runLoop) { + CFRunLoopAddSource(runLoop, flagsChangedEventSrc, kCFRunLoopDefaultMode); + } + + CFMachPortRef mouseUpDownEventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, NSLeftMouseDown | NSLeftMouseUp | NSOtherMouseDown | NSOtherMouseUp, (CGEventTapCallBack)eventTapFunction, nil); + + if (!mouseUpDownEventTap) + NSLog(@"Can't tap into mouse up/down events!"); + else + CFRelease(mouseUpDownEventTap); + + CFRunLoopSourceRef mouseUpDownEventSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, mouseUpDownEventTap, 0); + if (mouseUpDownEventSrc && runLoop) { + CFRunLoopAddSource(runLoop, mouseUpDownEventSrc, kCFRunLoopDefaultMode); } } @@ -159,12 +170,28 @@ typedef enum { } CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) { - if (type == kCGEventFlagsChanged) { // This should always be true; it's the only event type we're trying to tap - KMInputMethodAppDelegate *appDelegate = [KMInputMethodAppDelegate AppDelegate]; - if (appDelegate != nil) { - NSEvent* sysEvent = [NSEvent eventWithCGEvent:event]; + KMInputMethodAppDelegate *appDelegate = [KMInputMethodAppDelegate AppDelegate]; + if (appDelegate != nil) { + NSEvent* sysEvent = [NSEvent eventWithCGEvent:event]; + if (appDelegate.debugMode) NSLog(@"System Event: %@", sysEvent); - appDelegate.currentModifierFlags = sysEvent.modifierFlags; + + switch (type) { + case kCGEventFlagsChanged: + appDelegate.currentModifierFlags = sysEvent.modifierFlags; + break; + + case kCGEventLeftMouseUp: + case kCGEventLeftMouseDown: + case kCGEventOtherMouseUp: + case kCGEventOtherMouseDown: + { + // TODO: set context-out-of-date flag + } + break; + + default: + break; } } return event; From f06ca65c31e08c982e50d6c91d38a8e70a608b8a Mon Sep 17 00:00:00 2001 From: Tom Bogle Date: Thu, 1 Mar 2018 11:18:16 -0500 Subject: [PATCH 2/5] [WIP] Use low-level mouse events to detect likely context changes Replaced logic in updateContextBufferIfNeeded to be based on tapping of low-level mouse events instead of relying on list of clients that might have unexpected selection changes. --- .../Keyman4MacIM/KMInputMethodAppDelegate.m | 55 ++++++++++------ .../Keyman4MacIM/KMInputMethodEventHandler.m | 63 +++++++++++-------- 2 files changed, 71 insertions(+), 47 deletions(-) diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m index 83144715fa..b59dbd9bed 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m @@ -73,32 +73,47 @@ typedef enum { forEventClass:kInternetEventClass andEventID:kAEGetURL]; - CFMachPortRef flagsChangedEventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, NSFlagsChangedMask, (CGEventTapCallBack)eventTapFunction, nil); + CFMachPortRef lowLevelEventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, NSFlagsChangedMask | NSLeftMouseDown | NSLeftMouseUp | NSOtherMouseDown | NSOtherMouseUp, (CGEventTapCallBack)eventTapFunction, nil); - if (!flagsChangedEventTap) - NSLog(@"Can't tap into flags changed event!"); + if (!lowLevelEventTap) + NSLog(@"Can't tap into low level events!"); else - CFRelease(flagsChangedEventTap); - - CFRunLoopSourceRef flagsChangedEventSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, flagsChangedEventTap, 0); + CFRelease(lowLevelEventTap); + + CFRunLoopSourceRef runLoopEventSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, lowLevelEventTap, 0); CFRunLoopRef runLoop = CFRunLoopGetCurrent(); - if (flagsChangedEventSrc && runLoop) { - CFRunLoopAddSource(runLoop, flagsChangedEventSrc, kCFRunLoopDefaultMode); + if (runLoopEventSrc && runLoop) { + CFRunLoopAddSource(runLoop, runLoopEventSrc, kCFRunLoopDefaultMode); } - CFMachPortRef mouseUpDownEventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, NSLeftMouseDown | NSLeftMouseUp | NSOtherMouseDown | NSOtherMouseUp, (CGEventTapCallBack)eventTapFunction, nil); - - if (!mouseUpDownEventTap) - NSLog(@"Can't tap into mouse up/down events!"); - else - CFRelease(mouseUpDownEventTap); - - CFRunLoopSourceRef mouseUpDownEventSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, mouseUpDownEventTap, 0); - if (mouseUpDownEventSrc && runLoop) { - CFRunLoopAddSource(runLoop, mouseUpDownEventSrc, kCFRunLoopDefaultMode); - } +// CFMachPortRef flagsChangedEventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, NSFlagsChangedMask, (CGEventTapCallBack)eventTapFunction, nil); +// +// if (!flagsChangedEventTap) +// NSLog(@"Can't tap into flags changed event!"); +// else +// CFRelease(flagsChangedEventTap); +// +// CFRunLoopSourceRef flagsChangedEventSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, flagsChangedEventTap, 0); +// +// CFRunLoopRef runLoop = CFRunLoopGetCurrent(); +// +// if (flagsChangedEventSrc && runLoop) { +// CFRunLoopAddSource(runLoop, flagsChangedEventSrc, kCFRunLoopDefaultMode); +// } +// +// CFMachPortRef mouseUpDownEventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, NSLeftMouseDown | NSLeftMouseUp | NSOtherMouseDown | NSOtherMouseUp, (CGEventTapCallBack)eventTapFunction, nil); +// +// if (!mouseUpDownEventTap) +// NSLog(@"Can't tap into mouse up/down events!"); +// else +// CFRelease(mouseUpDownEventTap); +// +// CFRunLoopSourceRef mouseUpDownEventSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, mouseUpDownEventTap, 0); +// if (mouseUpDownEventSrc && runLoop) { +// CFRunLoopAddSource(runLoop, mouseUpDownEventSrc, kCFRunLoopDefaultMode); +// } } return self; @@ -186,7 +201,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef case kCGEventOtherMouseUp: case kCGEventOtherMouseDown: { - // TODO: set context-out-of-date flag + appDelegate.contextChangingEventDetected = YES; } break; diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m index d59e827c2a..b09c7f4c05 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m @@ -240,35 +240,44 @@ NSRange _previousSelRange; } - (void)updateContextBufferIfNeeded:(id)client { - if ([self.AppDelegate debugMode]) { - NSLog(@"_clientSelectionCanChangeUnexpectedly = %@", _clientSelectionCanChangeUnexpectedly ? @"YES" : @"NO"); - } - // The following is needed because some clients (e.g. Chrome & Terminal) handle mouse down events and we - // never get a crack at them: - // At the start of the loop before asking the Engine to process the event, unless we still have - // pending work to do based on posted deletes or the special kProcessPendingBuffer code, check to see if - // the client still reports a selection that matches the position we would expect based on the length of - // the context buffer. If not, then reset the context buffer. - if (_clientSelectionCanChangeUnexpectedly && _numberOfPostedDeletesToExpect == 0 && - (_pendingBuffer == nil || _pendingBuffer.length == 0)) { - NSRange currentSelRange = [client selectedRange]; - - if (currentSelRange.location == NSNotFound) { - _clientSelectionCanChangeUnexpectedly = NO; - } - else if ((_previousSelRange.location != currentSelRange.location || _cannnotTrustSelectionLength || _previousSelRange.length != currentSelRange.length)) { - if ([self.AppDelegate debugMode]) { - NSLog(@"Client selection may have changed since context was set. Resetting context..."); - NSLog(@" _previousSelRange.location = %lu", _previousSelRange.location); - NSLog(@" _previousSelRange.length = %lu", _previousSelRange.length); - NSLog(@" currentSelRange.location = %lu", currentSelRange.location); - if (_cannnotTrustSelectionLength) - NSLog(@"The following cannot be trusted and will be ignored:"); - NSLog(@" currentSelRange.length = %lu", currentSelRange.length); - } - [self updateContextBuffer:client]; + if (self.AppDelegate.contextChangingEventDetected) + { + if (!_contextOutOfDate && [self.AppDelegate debugMode]) { + NSLog(@"Low-level event requires context to be re-retrieved."); } + _contextOutOfDate = YES; + self.AppDelegate.contextChangingEventDetected = NO; } + +// if ([self.AppDelegate debugMode]) { +// NSLog(@"_clientSelectionCanChangeUnexpectedly = %@", _clientSelectionCanChangeUnexpectedly ? @"YES" : @"NO"); +// } +// // The following is needed because some clients (e.g. Chrome & Terminal) handle mouse down events and we +// // never get a crack at them: +// // At the start of the loop before asking the Engine to process the event, unless we still have +// // pending work to do based on posted deletes or the special kProcessPendingBuffer code, check to see if +// // the client still reports a selection that matches the position we would expect based on the length of +// // the context buffer. If not, then reset the context buffer. +// if (_clientSelectionCanChangeUnexpectedly && _numberOfPostedDeletesToExpect == 0 && +// (_pendingBuffer == nil || _pendingBuffer.length == 0)) { +// NSRange currentSelRange = [client selectedRange]; +// +// if (currentSelRange.location == NSNotFound) { +// _clientSelectionCanChangeUnexpectedly = NO; +// } +// else if ((_previousSelRange.location != currentSelRange.location || _cannnotTrustSelectionLength || _previousSelRange.length != currentSelRange.length)) { +// if ([self.AppDelegate debugMode]) { +// NSLog(@"Client selection may have changed since context was set. Resetting context..."); +// NSLog(@" _previousSelRange.location = %lu", _previousSelRange.location); +// NSLog(@" _previousSelRange.length = %lu", _previousSelRange.length); +// NSLog(@" currentSelRange.location = %lu", currentSelRange.location); +// if (_cannnotTrustSelectionLength) +// NSLog(@"The following cannot be trusted and will be ignored:"); +// NSLog(@" currentSelRange.length = %lu", currentSelRange.length); +// } +// [self updateContextBuffer:client]; +// } +// } if (_contextOutOfDate) [self updateContextBuffer:client]; From 98ad59a86cb5e54d1443a7e81fb3c98ea73bf842 Mon Sep 17 00:00:00 2001 From: Tom Bogle Date: Thu, 1 Mar 2018 16:04:11 -0500 Subject: [PATCH 3/5] Removed commented-out code --- .../Keyman4MacIM/KMInputMethodEventHandler.m | 32 +------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m index b09c7f4c05..d13d743bad 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m @@ -248,37 +248,7 @@ NSRange _previousSelRange; _contextOutOfDate = YES; self.AppDelegate.contextChangingEventDetected = NO; } - -// if ([self.AppDelegate debugMode]) { -// NSLog(@"_clientSelectionCanChangeUnexpectedly = %@", _clientSelectionCanChangeUnexpectedly ? @"YES" : @"NO"); -// } -// // The following is needed because some clients (e.g. Chrome & Terminal) handle mouse down events and we -// // never get a crack at them: -// // At the start of the loop before asking the Engine to process the event, unless we still have -// // pending work to do based on posted deletes or the special kProcessPendingBuffer code, check to see if -// // the client still reports a selection that matches the position we would expect based on the length of -// // the context buffer. If not, then reset the context buffer. -// if (_clientSelectionCanChangeUnexpectedly && _numberOfPostedDeletesToExpect == 0 && -// (_pendingBuffer == nil || _pendingBuffer.length == 0)) { -// NSRange currentSelRange = [client selectedRange]; -// -// if (currentSelRange.location == NSNotFound) { -// _clientSelectionCanChangeUnexpectedly = NO; -// } -// else if ((_previousSelRange.location != currentSelRange.location || _cannnotTrustSelectionLength || _previousSelRange.length != currentSelRange.length)) { -// if ([self.AppDelegate debugMode]) { -// NSLog(@"Client selection may have changed since context was set. Resetting context..."); -// NSLog(@" _previousSelRange.location = %lu", _previousSelRange.location); -// NSLog(@" _previousSelRange.length = %lu", _previousSelRange.length); -// NSLog(@" currentSelRange.location = %lu", currentSelRange.location); -// if (_cannnotTrustSelectionLength) -// NSLog(@"The following cannot be trusted and will be ignored:"); -// NSLog(@" currentSelRange.length = %lu", currentSelRange.length); -// } -// [self updateContextBuffer:client]; -// } -// } - + if (_contextOutOfDate) [self updateContextBuffer:client]; } From fef69ced963b35bd665bc1b41849a22601ef7254 Mon Sep 17 00:00:00 2001 From: Tom Bogle Date: Fri, 2 Mar 2018 01:32:26 -0500 Subject: [PATCH 4/5] Removed commented out code and fixed a typo. --- mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m | 5 ----- .../KMInputMethodBrowserClientEventHandlerTests.m | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m index d13d743bad..3aca836a39 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m @@ -402,11 +402,6 @@ NSRange _previousSelRange; [self.AppDelegate handleKeyEvent:event];*/ if (event.type == NSLeftMouseDown || event.type == NSLeftMouseUp ) { - // if (_clientSelectionCanChangeUnexpectedly) { - // if ([self.AppDelegate debugMode]) - // NSLog(@"WARNING: We are dealing with an app/context where we THINK we shouldn't be getting mouse events, but we just got one!"); - // _clientSelectionCanChangeUnexpectedly = NO; - // } _contextOutOfDate = YES; return NO; } diff --git a/mac/Keyman4MacIM/KeymanTests/KMInputMethodBrowserClientEventHandlerTests.m b/mac/Keyman4MacIM/KeymanTests/KMInputMethodBrowserClientEventHandlerTests.m index 073da00c2b..fa6c76e92e 100644 --- a/mac/Keyman4MacIM/KeymanTests/KMInputMethodBrowserClientEventHandlerTests.m +++ b/mac/Keyman4MacIM/KeymanTests/KMInputMethodBrowserClientEventHandlerTests.m @@ -36,7 +36,7 @@ KMInputMethodBrowserClientEventHandler * _im; // If the expected context is empty, no attempt should be made to check for matching context from // client, so even though the client would also return an empty string, calling checkContextIn // repeatedly has no effect (i.e., it does not set clientSelectionCanChangeUnexpectedly to false). - // Therefore a subsequent key down event *should* reult in a call to the client to inquire about + // Therefore a subsequent key down event *should* result in a call to the client to inquire about // the current selection. id client = OCMStrictProtocolMock(@protocol(IMKTextInput)); OCMStub([client selectedRange]).andReturn(NSMakeRange(0, 0)); From 904447ae18d7dce30d0e71469928ff7ca55b96a5 Mon Sep 17 00:00:00 2001 From: Tom Bogle Date: Mon, 5 Mar 2018 15:14:12 -0500 Subject: [PATCH 5/5] Removed unnecessary event taps for "other" mouse button. Also deleted commented-out code. --- .../Keyman4MacIM/KMInputMethodAppDelegate.m | 29 +------------------ 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m index b59dbd9bed..276f0dd5dd 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m @@ -73,7 +73,7 @@ typedef enum { forEventClass:kInternetEventClass andEventID:kAEGetURL]; - CFMachPortRef lowLevelEventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, NSFlagsChangedMask | NSLeftMouseDown | NSLeftMouseUp | NSOtherMouseDown | NSOtherMouseUp, (CGEventTapCallBack)eventTapFunction, nil); + CFMachPortRef lowLevelEventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, NSFlagsChangedMask | NSLeftMouseDown | NSLeftMouseUp/* | NSOtherMouseDown | NSOtherMouseUp*/, (CGEventTapCallBack)eventTapFunction, nil); if (!lowLevelEventTap) NSLog(@"Can't tap into low level events!"); @@ -87,33 +87,6 @@ typedef enum { if (runLoopEventSrc && runLoop) { CFRunLoopAddSource(runLoop, runLoopEventSrc, kCFRunLoopDefaultMode); } - -// CFMachPortRef flagsChangedEventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, NSFlagsChangedMask, (CGEventTapCallBack)eventTapFunction, nil); -// -// if (!flagsChangedEventTap) -// NSLog(@"Can't tap into flags changed event!"); -// else -// CFRelease(flagsChangedEventTap); -// -// CFRunLoopSourceRef flagsChangedEventSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, flagsChangedEventTap, 0); -// -// CFRunLoopRef runLoop = CFRunLoopGetCurrent(); -// -// if (flagsChangedEventSrc && runLoop) { -// CFRunLoopAddSource(runLoop, flagsChangedEventSrc, kCFRunLoopDefaultMode); -// } -// -// CFMachPortRef mouseUpDownEventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, NSLeftMouseDown | NSLeftMouseUp | NSOtherMouseDown | NSOtherMouseUp, (CGEventTapCallBack)eventTapFunction, nil); -// -// if (!mouseUpDownEventTap) -// NSLog(@"Can't tap into mouse up/down events!"); -// else -// CFRelease(mouseUpDownEventTap); -// -// CFRunLoopSourceRef mouseUpDownEventSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, mouseUpDownEventTap, 0); -// if (mouseUpDownEventSrc && runLoop) { -// CFRunLoopAddSource(runLoop, mouseUpDownEventSrc, kCFRunLoopDefaultMode); -// } } return self;