mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-10 10:55:33 +00:00
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:
commit
080397dad7
4 changed files with 41 additions and 51 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue