Merge pull request #640 from keymanapp/mac-detect-context-changes-caused-by-low-level-events

[Mac] Detect context changes caused by low level events
This commit is contained in:
Tom Bogle 2018-03-05 15:19:19 -05:00 committed by GitHub
commit 080397dad7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 41 additions and 51 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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));