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..276f0dd5dd 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m @@ -73,20 +73,19 @@ typedef enum { forEventClass:kInternetEventClass andEventID:kAEGetURL]; - CFMachPortRef eventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, NSFlagsChangedMask, (CGEventTapCallBack)eventTapFunction, nil); + CFMachPortRef lowLevelEventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, NSFlagsChangedMask | NSLeftMouseDown | NSLeftMouseUp/* | NSOtherMouseDown | NSOtherMouseUp*/, (CGEventTapCallBack)eventTapFunction, nil); - if (!eventTap) - NSLog(@"Can't tap into flags changed event!"); + if (!lowLevelEventTap) + NSLog(@"Can't tap into low level events!"); else - CFRelease(eventTap); - - CFRunLoopSourceRef flagsChangedEventSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0); - if (flagsChangedEventSrc ) { - - CFRunLoopRef runLoop = CFRunLoopGetCurrent(); - if (runLoop) { - CFRunLoopAddSource(runLoop, flagsChangedEventSrc, kCFRunLoopDefaultMode); - } + CFRelease(lowLevelEventTap); + + CFRunLoopSourceRef runLoopEventSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, lowLevelEventTap, 0); + + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); + + if (runLoopEventSrc && runLoop) { + CFRunLoopAddSource(runLoop, runLoopEventSrc, kCFRunLoopDefaultMode); } } @@ -159,12 +158,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: + { + appDelegate.contextChangingEventDetected = YES; + } + break; + + default: + break; } } return event; diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m index d59e827c2a..3aca836a39 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodEventHandler.m @@ -240,36 +240,15 @@ 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 (_contextOutOfDate) [self updateContextBuffer:client]; } @@ -423,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));