-
Notifications
You must be signed in to change notification settings - Fork 0
Fix spotlight backspace character removal #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
e58c783
d0f91f1
0f1bd4d
2508ee1
8038797
21bcb69
94efae9
7c843bc
dc9ae1d
92776d8
91e7727
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,7 @@ | |
| // OpenKey | ||
| // | ||
| // Created by Tuyen on 1/18/19. | ||
| // Copyright © 2019 Tuyen Mai. All rights reserved. | ||
| // Copyright 2019 Tuyen Mai. All rights reserved. | ||
| // | ||
| #import <Cocoa/Cocoa.h> | ||
| #import <Carbon/Carbon.h> | ||
|
|
@@ -51,7 +51,13 @@ | |
| //app which must sent special empty character | ||
| NSArray* _niceSpaceApp = @[@"com.sublimetext.3", | ||
| @"com.sublimetext.2", | ||
| @"com.apple.Spotlight", // Spotlight search | ||
| ]; | ||
|
|
||
| // Array of bundle IDs for apps that should be ignored | ||
| static NSArray* const IGNORED_BUNDLES = @[ | ||
| // Spotlight search - removed to enable Vietnamese input | ||
| ]; | ||
|
|
||
| //app which error with unicode Compound | ||
| NSArray* _unicodeCompoundApp = @[@"com.apple.", | ||
|
|
@@ -85,6 +91,21 @@ | |
|
|
||
| NSString* _frontMostApp = @"UnknownApp"; | ||
|
|
||
| static NSString *lastFocusedAppBundleId = nil; | ||
| static pid_t lastFocusedAppPid = -1; | ||
| static bool _willUpdateFocusedApp = false; | ||
|
|
||
| // Global AX variables | ||
| static AXUIElementRef g_systemWide = NULL; | ||
|
|
||
| // Cleanup function for AX variables | ||
| static void cleanupAXVariables() { | ||
| if (g_systemWide) { | ||
| CFRelease(g_systemWide); | ||
| g_systemWide = NULL; | ||
| } | ||
| } | ||
|
|
||
| void OpenKeyInit() { | ||
| //load saved data | ||
| vFreeMark = 0;//(int)[[NSUserDefaults standardUserDefaults] integerForKey:@"FreeMark"]; | ||
|
|
@@ -143,6 +164,11 @@ void OpenKeyInit() { | |
| } | ||
| } | ||
|
|
||
| void OpenKeyCleanup() { | ||
| cleanupAXVariables(); | ||
| // ... other cleanup code ... | ||
| } | ||
|
|
||
| void RequestNewSession() { | ||
| //send event signal to Engine | ||
| vKeyHandleEvent(vKeyEvent::Mouse, vKeyEventState::MouseDown, 0); | ||
|
|
@@ -174,6 +200,10 @@ BOOL containUnicodeCompoundApp(NSString* topApp) { | |
| return false; | ||
| } | ||
|
|
||
| BOOL isSpotlightApp(NSString* topApp) { | ||
| return topApp != nil && [topApp isEqualToString:@"com.apple.Spotlight"]; | ||
| } | ||
|
|
||
| void saveSmartSwitchKeyData() { | ||
| getSmartSwitchKeySaveData(savedSmartSwitchKeyData); | ||
| NSData* _data = [NSData dataWithBytes:savedSmartSwitchKeyData.data() length:savedSmartSwitchKeyData.size()]; | ||
|
|
@@ -182,6 +212,7 @@ void saveSmartSwitchKeyData() { | |
| } | ||
|
|
||
| void OnActiveAppChanged() { //use for smart switch key; improved on Sep 28th, 2019 | ||
| _willUpdateFocusedApp = true; | ||
| queryFrontMostApp(); | ||
| _languageTemp = getAppInputMethodStatus(string(_frontMostApp.UTF8String), vLanguage | (vCodeTable << 1)); | ||
| if ((_languageTemp & 0x01) != vLanguage) { //for input method | ||
|
|
@@ -356,6 +387,13 @@ void SendBackspace() { | |
| } | ||
| _syncKey.pop_back(); | ||
| } | ||
|
|
||
| // Special handling for Spotlight: ensure backspace removes character, not just completion | ||
| if (isSpotlightApp(FRONT_APP)) { | ||
| // Send an additional backspace to ensure character removal | ||
| CGEventTapPostEvent(_proxy, eventBackSpaceDown); | ||
| CGEventTapPostEvent(_proxy, eventBackSpaceUp); | ||
| } | ||
| } | ||
|
|
||
| void SendShiftAndLeftArrow() { | ||
|
|
@@ -571,10 +609,45 @@ CGKeyCode ConvertEventToKeyboadLayoutCompatKeyCode(CGEventRef keyEvent, CGKeyCod | |
| fallbackKeyCode); | ||
| } | ||
|
|
||
| void updateFocusedAppBundleId() { | ||
| if (!g_systemWide) { | ||
| g_systemWide = AXUIElementCreateSystemWide(); | ||
| } | ||
|
|
||
| AXUIElementRef focusedApp = NULL; | ||
| AXError result = AXUIElementCopyAttributeValue(g_systemWide, kAXFocusedApplicationAttribute, (CFTypeRef*)&focusedApp); | ||
|
|
||
| if (result == kAXErrorSuccess && focusedApp) { | ||
| pid_t pid = 0; | ||
| AXUIElementGetPid(focusedApp, &pid); | ||
|
|
||
| // Check if the focused app has changed | ||
| if (pid != lastFocusedAppPid) { | ||
| NSRunningApplication *app = [NSRunningApplication runningApplicationWithProcessIdentifier:pid]; | ||
| lastFocusedAppBundleId = app.bundleIdentifier; | ||
| lastFocusedAppPid = pid; | ||
| } | ||
|
|
||
| CFRelease(focusedApp); | ||
| return; | ||
| } else { | ||
| // Fallback to NSWorkspace when AX API fails | ||
| NSRunningApplication *frontApp = [[NSWorkspace sharedWorkspace] frontmostApplication]; | ||
| if (frontApp && frontApp.processIdentifier != lastFocusedAppPid) { | ||
| lastFocusedAppBundleId = frontApp.bundleIdentifier; | ||
| lastFocusedAppPid = frontApp.processIdentifier; | ||
| } | ||
| } | ||
|
|
||
| if (focusedApp) { | ||
| CFRelease(focusedApp); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * MAIN HOOK entry, very important function. | ||
| * MAIN Callback. | ||
| */ | ||
| */ | ||
| CGEventRef OpenKeyCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) { | ||
| //dont handle my event | ||
| if (CGEventGetIntegerValueField(event, kCGEventSourceStateID) == CGEventSourceGetSourceStateID(myEventSource)) { | ||
|
|
@@ -663,6 +736,7 @@ CGEventRef OpenKeyCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef e | |
|
|
||
| //handle mouse | ||
| if (type == kCGEventLeftMouseDown || type == kCGEventRightMouseDown || type == kCGEventLeftMouseDragged || type == kCGEventRightMouseDragged) { | ||
| _willUpdateFocusedApp = true; | ||
| RequestNewSession(); | ||
| return event; | ||
| } | ||
|
|
@@ -677,7 +751,7 @@ CGEventRef OpenKeyCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef e | |
| if (CFArrayGetCount(languages) > 0) { | ||
| CFStringRef langRef = (CFStringRef)CFArrayGetValueAtIndex(languages, 0); | ||
| NSString *currentLanguage = (__bridge NSString *)langRef; | ||
| if(![currentLanguage isLike:@"en"]){ | ||
| if(![currentLanguage isLike:@"en"] && ![currentLanguage isLike:@""]){ // empty for "unicode hex input" | ||
| return event; | ||
| } | ||
| CFRelease(langRef); | ||
|
|
@@ -688,6 +762,21 @@ CGEventRef OpenKeyCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef e | |
|
|
||
| //handle keyboard | ||
| if (type == kCGEventKeyDown) { | ||
|
|
||
| //update focused app | ||
| if (_willUpdateFocusedApp) { | ||
| updateFocusedAppBundleId(); | ||
| _willUpdateFocusedApp = false; | ||
| } | ||
| if (OTHER_CONTROL_KEY) { | ||
| _willUpdateFocusedApp = true; | ||
| } | ||
|
|
||
| //ignore some apps | ||
| if (lastFocusedAppBundleId && [IGNORED_BUNDLES containsObject:lastFocusedAppBundleId]) { | ||
| return event; | ||
| } | ||
|
|
||
| //send event signal to Engine | ||
| vKeyHandleEvent(vKeyEvent::Keyboard, | ||
| vKeyEventState::KeyDown, | ||
|
|
@@ -707,7 +796,13 @@ CGEventRef OpenKeyCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef e | |
| } | ||
| _syncKey.pop_back(); | ||
| } | ||
|
|
||
|
|
||
| // Special handling for Spotlight: ensure backspace removes character, not just completion | ||
| if (isSpotlightApp(FRONT_APP)) { | ||
| // Send an additional backspace to ensure character removal | ||
| CGEventTapPostEvent(_proxy, eventBackSpaceDown); | ||
| CGEventTapPostEvent(_proxy, eventBackSpaceUp); | ||
| } | ||
| } else if (pData->extCode == 3) { //normal key | ||
| InsertKeyLength(1); | ||
| } | ||
|
|
@@ -723,6 +818,10 @@ CGEventRef OpenKeyCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef e | |
| if (pData->backspaceCount == 1) | ||
| pData->backspaceCount--; | ||
| } | ||
| } else if (isSpotlightApp(FRONT_APP)) { | ||
| // Special handling for Spotlight: send empty character to clear autocomplete | ||
| SendEmptyCharacter(); | ||
| pData->backspaceCount++; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redundant Spotlight branch duplicates default autocomplete logicLow Severity The |
||
| } else { | ||
| SendEmptyCharacter(); | ||
| pData->backspaceCount++; | ||
|
|
@@ -732,7 +831,7 @@ CGEventRef OpenKeyCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef e | |
|
|
||
| //send backspace | ||
| if (pData->backspaceCount > 0 && pData->backspaceCount < MAX_BUFF) { | ||
| for (_i = 0; _i < pData->backspaceCount; _i++) { | ||
| for (int i = 0; i < pData->backspaceCount; i++) { | ||
| SendBackspace(); | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <VariablesViewState | ||
| version = "1.0"> | ||
| <ContextStates> | ||
| <ContextState | ||
| contextName = "OpenKeyCallback:OpenKey.mm"> | ||
| <PersistentStrings> | ||
| <PersistentString | ||
| value = "OTHER_CONTROL_KEY"> | ||
| </PersistentString> | ||
| </PersistentStrings> | ||
| </ContextState> | ||
| </ContextStates> | ||
| </VariablesViewState> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <Bucket | ||
| uuid = "61B1757B-E811-4F1B-B754-05506CF5FAEB" | ||
| type = "1" | ||
| version = "2.0"> | ||
| <Breakpoints> | ||
| <BreakpointProxy | ||
| BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
| <BreakpointContent | ||
| uuid = "17321050-D561-4BD5-9564-680AA8B7FCBD" | ||
| shouldBeEnabled = "Yes" | ||
| ignoreCount = "0" | ||
| continueAfterRunningActions = "No" | ||
| filePath = "ModernKey/AppDelegate.m" | ||
| startingColumnNumber = "9223372036854775807" | ||
| endingColumnNumber = "9223372036854775807" | ||
| startingLineNumber = "574" | ||
| endingLineNumber = "574"> | ||
| </BreakpointContent> | ||
| </BreakpointProxy> | ||
| <BreakpointProxy | ||
| BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
| <BreakpointContent | ||
| uuid = "CD09A5D5-531F-415F-A2CB-575D4193119E" | ||
| shouldBeEnabled = "Yes" | ||
| ignoreCount = "0" | ||
| continueAfterRunningActions = "No" | ||
| filePath = "ModernKey/AppDelegate.m" | ||
| startingColumnNumber = "9223372036854775807" | ||
| endingColumnNumber = "9223372036854775807" | ||
| startingLineNumber = "558" | ||
| endingLineNumber = "558"> | ||
| </BreakpointContent> | ||
| </BreakpointProxy> | ||
| <BreakpointProxy | ||
| BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
| <BreakpointContent | ||
| uuid = "7EAB6205-9350-449E-BCCC-1D17D1B59860" | ||
| shouldBeEnabled = "No" | ||
| ignoreCount = "0" | ||
| continueAfterRunningActions = "No" | ||
| filePath = "ModernKey/OpenKey.mm" | ||
| startingColumnNumber = "9223372036854775807" | ||
| endingColumnNumber = "9223372036854775807" | ||
| startingLineNumber = "631" | ||
| endingLineNumber = "631" | ||
| landmarkName = "updateFocusedAppBundleId()" | ||
| landmarkType = "9"> | ||
| </BreakpointContent> | ||
| </BreakpointProxy> | ||
| <BreakpointProxy | ||
| BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
| <BreakpointContent | ||
| uuid = "D329BD0D-9CC1-465A-8C8C-147E775037A7" | ||
| shouldBeEnabled = "No" | ||
| ignoreCount = "0" | ||
| continueAfterRunningActions = "No" | ||
| filePath = "ModernKey/OpenKey.mm" | ||
| startingColumnNumber = "9223372036854775807" | ||
| endingColumnNumber = "9223372036854775807" | ||
| startingLineNumber = "630" | ||
| endingLineNumber = "630" | ||
| landmarkName = "updateFocusedAppBundleId()" | ||
| landmarkType = "9"> | ||
| </BreakpointContent> | ||
| </BreakpointProxy> | ||
| <BreakpointProxy | ||
| BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
| <BreakpointContent | ||
| uuid = "98C60ED8-3FF8-4D83-8FD5-D27159B4CC3B" | ||
| shouldBeEnabled = "No" | ||
| ignoreCount = "0" | ||
| continueAfterRunningActions = "No" | ||
| filePath = "ModernKey/OpenKey.mm" | ||
| startingColumnNumber = "9223372036854775807" | ||
| endingColumnNumber = "9223372036854775807" | ||
| startingLineNumber = "632" | ||
| endingLineNumber = "632" | ||
| landmarkName = "updateFocusedAppBundleId()" | ||
| landmarkType = "9"> | ||
| </BreakpointContent> | ||
| </BreakpointProxy> | ||
| <BreakpointProxy | ||
| BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
| <BreakpointContent | ||
| uuid = "78E6070A-CF8B-4165-88C0-A47852247949" | ||
| shouldBeEnabled = "No" | ||
| ignoreCount = "0" | ||
| continueAfterRunningActions = "No" | ||
| filePath = "ModernKey/OpenKey.mm" | ||
| startingColumnNumber = "9223372036854775807" | ||
| endingColumnNumber = "9223372036854775807" | ||
| startingLineNumber = "740" | ||
| endingLineNumber = "740" | ||
| landmarkName = "OpenKeyCallback(proxy, type, event, refcon)" | ||
| landmarkType = "9"> | ||
| </BreakpointContent> | ||
| </BreakpointProxy> | ||
| <BreakpointProxy | ||
| BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
| <BreakpointContent | ||
| uuid = "2C15CF3E-A762-42DA-A5C4-6E9C8319A555" | ||
| shouldBeEnabled = "No" | ||
| ignoreCount = "0" | ||
| continueAfterRunningActions = "No" | ||
| filePath = "ModernKey/OpenKey.mm" | ||
| startingColumnNumber = "9223372036854775807" | ||
| endingColumnNumber = "9223372036854775807" | ||
| startingLineNumber = "742" | ||
| endingLineNumber = "742" | ||
| landmarkName = "OpenKeyCallback(proxy, type, event, refcon)" | ||
| landmarkType = "9"> | ||
| </BreakpointContent> | ||
| </BreakpointProxy> | ||
| <BreakpointProxy | ||
| BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> | ||
| <BreakpointContent | ||
| uuid = "460DC62D-F44F-468B-9B4E-C4162DDDC658" | ||
| shouldBeEnabled = "Yes" | ||
| ignoreCount = "0" | ||
| continueAfterRunningActions = "No" | ||
| filePath = "ModernKey/OpenKey.mm" | ||
| startingColumnNumber = "9223372036854775807" | ||
| endingColumnNumber = "9223372036854775807" | ||
| startingLineNumber = "225" | ||
| endingLineNumber = "225" | ||
| landmarkName = "OnActiveAppChanged()" | ||
| landmarkType = "9"> | ||
| </BreakpointContent> | ||
| </BreakpointProxy> | ||
| </Breakpoints> | ||
| </Bucket> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Committed developer-specific Xcode debug filesLow Severity Developer-specific Xcode debug files were committed, including breakpoints (some with Additional Locations (1) |
||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SendBackspace doubles every backspace causing potential over-deletion
High Severity
SendBackspace()unconditionally sends an extra backspace for Spotlight on every invocation. When called in a loop (e.g., forbackspaceCountiterations during character replacement), this doubles every programmatic backspace. Combined with the autocomplete fix that already callsSendEmptyCharacter()+backspaceCount++to handle autocomplete dismissal, the total backspaces sent become2*(N+1)instead of the neededN+1, likely over-deleting text.