From 1781a167df3d4d8727ede252f6bfd0a72701e210 Mon Sep 17 00:00:00 2001 From: neo Date: Thu, 21 May 2026 12:40:22 +0700 Subject: [PATCH 1/2] Fix UI not show on Tahoe 26.4.1 --- Sources/OpenKey/macOS/ModernKey/AppDelegate.m | 69 +++++++++++++++---- 1 file changed, 54 insertions(+), 15 deletions(-) diff --git a/Sources/OpenKey/macOS/ModernKey/AppDelegate.m b/Sources/OpenKey/macOS/ModernKey/AppDelegate.m index 011708e2..c783b0c0 100644 --- a/Sources/OpenKey/macOS/ModernKey/AppDelegate.m +++ b/Sources/OpenKey/macOS/ModernKey/AppDelegate.m @@ -62,6 +62,13 @@ @interface AppDelegate () @end +static void okdbg(NSString *fmt, ...) { + va_list ap; va_start(ap, fmt); + NSString *msg = [[NSString alloc] initWithFormat:fmt arguments:ap]; + va_end(ap); + FILE *f = fopen("/tmp/openkey-debug.log", "a"); + if (f) { fprintf(f, "%s\n", msg.UTF8String); fclose(f); } +} @implementation AppDelegate { NSWindowController *_mainWC; @@ -110,25 +117,31 @@ -(void)askPermission { } - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + okdbg(@"applicationDidFinishLaunching START"); appDelegate = self; - + [self registerSupportedNotification]; - + //set quick tooltip [[NSUserDefaults standardUserDefaults] setObject: [NSNumber numberWithInt: 50] forKey: @"NSInitialToolTipDelay"]; - + //check whether this app has been launched before that or not NSArray* runningApp = [[NSWorkspace sharedWorkspace] runningApplications]; if ([runningApp containsObject:OPENKEY_BUNDLE]) { //if already running -> exit + okdbg(@"already running, terminating"); [NSApp terminate:nil]; return; } - + okdbg(@"past dup check"); + // check if user granted Accessabilty permission - if (!MJAccessibilityIsEnabled()) { - [self askPermission]; - return; + BOOL axOk = MJAccessibilityIsEnabled(); + okdbg(@"accessibility=%d", axOk); + if (!axOk) { + // Don't early-return — let the UI still open so the user can interact. + // The event tap (typing) will be disabled until accessibility is granted. + MJAccessibilityOpenPanel(); } vShowIconOnDock = (int)[[NSUserDefaults standardUserDefaults] integerForKey:@"vShowIconOnDock"]; @@ -142,10 +155,12 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { //init dispatch_async(dispatch_get_main_queue(), ^{ - if (![OpenKeyManager initEventTap]) { + BOOL tapOk = [OpenKeyManager initEventTap]; + NSInteger showui = [[NSUserDefaults standardUserDefaults] integerForKey:@"ShowUIOnStartup"]; + okdbg(@"didFinishLaunching dispatch: tapOk=%d showui=%ld", tapOk, (long)showui); + if (!tapOk) { [self onControlPanelSelected]; } else { - NSInteger showui = [[NSUserDefaults standardUserDefaults] integerForKey:@"ShowUIOnStartup"]; if (showui == 1) { [self onControlPanelSelected]; } @@ -472,15 +487,35 @@ -(void)onQuickConvert { } -(void) onControlPanelSelected { + okdbg(@"onControlPanelSelected called, _mainWC=%@", _mainWC); if (_mainWC == nil) { _mainWC = [[NSStoryboard storyboardWithName:@"Main" bundle:nil] instantiateControllerWithIdentifier:@"OpenKey"]; + okdbg(@"instantiated _mainWC=%@ window=%@", _mainWC, _mainWC.window); } - //[OpenKeyManager showDockIcon:YES]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; if ([_mainWC.window isVisible]) { + [NSApp activateIgnoringOtherApps:YES]; + okdbg(@"already visible, frame=%@", NSStringFromRect(_mainWC.window.frame)); return; } + [NSApp activateIgnoringOtherApps:YES]; + [_mainWC showWindow:nil]; [_mainWC.window makeKeyAndOrderFront:nil]; - [_mainWC.window setLevel:NSFloatingWindowLevel]; + [_mainWC.window setLevel:NSNormalWindowLevel]; + + // Center on the screen with the menu bar (primary built-in display). + // Saved frames + multi-display setups can place the window off-screen + // or on a display the user isn't currently looking at. + [_mainWC.window setFrameAutosaveName:@""]; + NSScreen *primary = [NSScreen screens].firstObject ?: [NSScreen mainScreen]; + NSRect visible = primary.visibleFrame; + NSRect frame = _mainWC.window.frame; + NSPoint origin = NSMakePoint( + visible.origin.x + (visible.size.width - frame.size.width) / 2, + visible.origin.y + (visible.size.height - frame.size.height) / 2); + [_mainWC.window setFrameOrigin:origin]; + okdbg(@"primary visibleFrame=%@ moved to=%@", NSStringFromRect(visible), NSStringFromRect(_mainWC.window.frame)); + okdbg(@"after show: visible=%d frame=%@ screen=%@", [_mainWC.window isVisible], NSStringFromRect(_mainWC.window.frame), _mainWC.window.screen); } -(void) onMacroSelected { @@ -488,9 +523,11 @@ -(void) onMacroSelected { _macroWC = [[NSStoryboard storyboardWithName:@"Main" bundle:nil] instantiateControllerWithIdentifier:@"MacroWindow"]; } //[OpenKeyManager showDockIcon:YES]; - if ([_macroWC.window isVisible]) + if ([_macroWC.window isVisible]) { + [NSApp activateIgnoringOtherApps:YES]; return; - + } + [NSApp activateIgnoringOtherApps:YES]; [_macroWC.window makeKeyAndOrderFront:nil]; [_macroWC.window setLevel:NSFloatingWindowLevel]; } @@ -500,9 +537,11 @@ -(void) onAboutSelected { _aboutWC = [[NSStoryboard storyboardWithName:@"Main" bundle:nil] instantiateControllerWithIdentifier:@"AboutWindow"]; } //[OpenKeyManager showDockIcon:YES]; - if ([_aboutWC.window isVisible]) + if ([_aboutWC.window isVisible]) { + [NSApp activateIgnoringOtherApps:YES]; return; - + } + [NSApp activateIgnoringOtherApps:YES]; [_aboutWC.window makeKeyAndOrderFront:nil]; [_aboutWC.window setLevel:NSFloatingWindowLevel]; } From 8d763b5ff8439c20eb919b6ce35c248f90dfee92 Mon Sep 17 00:00:00 2001 From: neo Date: Thu, 21 May 2026 13:23:22 +0700 Subject: [PATCH 2/2] Support switch E <-> V using Fn (Globe) key on new Mac keyboard --- Sources/OpenKey/engine/Engine.h | 2 + Sources/OpenKey/macOS/ModernKey/AppDelegate.m | 45 +++++------------- .../ModernKey/Base.lproj/Main.storyboard | 47 ++++++++++++++----- Sources/OpenKey/macOS/ModernKey/OpenKey.mm | 2 + .../OpenKey/macOS/ModernKey/OpenKeyManager.m | 9 ++-- .../OpenKey/macOS/ModernKey/ViewController.m | 9 ++++ 6 files changed, 62 insertions(+), 52 deletions(-) diff --git a/Sources/OpenKey/engine/Engine.h b/Sources/OpenKey/engine/Engine.h index f288f8c5..f1e54905 100644 --- a/Sources/OpenKey/engine/Engine.h +++ b/Sources/OpenKey/engine/Engine.h @@ -32,12 +32,14 @@ #define HAS_OPTION(data) ((data & 0x200) ? 1 : 0) #define HAS_COMMAND(data) ((data & 0x400) ? 1 : 0) #define HAS_SHIFT(data) ((data & 0x800) ? 1 : 0) +#define HAS_FN(data) ((data & 0x1000) ? 1 : 0) #define GET_BOOL(data) (data ? 1 : 0) #define HAS_BEEP(data) (data & 0x8000) #define SET_SWITCH_KEY(data, key) data = (data & 0xFF) | key #define SET_CONTROL_KEY(data, val) data|=val<<8; #define SET_OPTION_KEY(data, val) data|=val<<9; #define SET_COMMAND_KEY(data, val) data|=val<<10; +#define SET_FN_KEY(data, val) data|=val<<12; //define these variable in your application //API diff --git a/Sources/OpenKey/macOS/ModernKey/AppDelegate.m b/Sources/OpenKey/macOS/ModernKey/AppDelegate.m index c783b0c0..3216104d 100644 --- a/Sources/OpenKey/macOS/ModernKey/AppDelegate.m +++ b/Sources/OpenKey/macOS/ModernKey/AppDelegate.m @@ -62,13 +62,6 @@ @interface AppDelegate () @end -static void okdbg(NSString *fmt, ...) { - va_list ap; va_start(ap, fmt); - NSString *msg = [[NSString alloc] initWithFormat:fmt arguments:ap]; - va_end(ap); - FILE *f = fopen("/tmp/openkey-debug.log", "a"); - if (f) { fprintf(f, "%s\n", msg.UTF8String); fclose(f); } -} @implementation AppDelegate { NSWindowController *_mainWC; @@ -117,7 +110,6 @@ -(void)askPermission { } - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - okdbg(@"applicationDidFinishLaunching START"); appDelegate = self; [self registerSupportedNotification]; @@ -129,38 +121,31 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { //check whether this app has been launched before that or not NSArray* runningApp = [[NSWorkspace sharedWorkspace] runningApplications]; if ([runningApp containsObject:OPENKEY_BUNDLE]) { //if already running -> exit - okdbg(@"already running, terminating"); [NSApp terminate:nil]; return; } - okdbg(@"past dup check"); // check if user granted Accessabilty permission - BOOL axOk = MJAccessibilityIsEnabled(); - okdbg(@"accessibility=%d", axOk); - if (!axOk) { - // Don't early-return — let the UI still open so the user can interact. - // The event tap (typing) will be disabled until accessibility is granted. - MJAccessibilityOpenPanel(); + if (!MJAccessibilityIsEnabled()) { + [self askPermission]; + return; } - + vShowIconOnDock = (int)[[NSUserDefaults standardUserDefaults] integerForKey:@"vShowIconOnDock"]; if (vShowIconOnDock) [NSApp setActivationPolicy: NSApplicationActivationPolicyRegular]; - + if (vSwitchKeyStatus & 0x8000) NSBeep(); [self createStatusBarMenu]; - + //init dispatch_async(dispatch_get_main_queue(), ^{ - BOOL tapOk = [OpenKeyManager initEventTap]; - NSInteger showui = [[NSUserDefaults standardUserDefaults] integerForKey:@"ShowUIOnStartup"]; - okdbg(@"didFinishLaunching dispatch: tapOk=%d showui=%ld", tapOk, (long)showui); - if (!tapOk) { + if (![OpenKeyManager initEventTap]) { [self onControlPanelSelected]; } else { + NSInteger showui = [[NSUserDefaults standardUserDefaults] integerForKey:@"ShowUIOnStartup"]; if (showui == 1) { [self onControlPanelSelected]; } @@ -487,25 +472,19 @@ -(void)onQuickConvert { } -(void) onControlPanelSelected { - okdbg(@"onControlPanelSelected called, _mainWC=%@", _mainWC); if (_mainWC == nil) { _mainWC = [[NSStoryboard storyboardWithName:@"Main" bundle:nil] instantiateControllerWithIdentifier:@"OpenKey"]; - okdbg(@"instantiated _mainWC=%@ window=%@", _mainWC, _mainWC.window); } - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; if ([_mainWC.window isVisible]) { [NSApp activateIgnoringOtherApps:YES]; - okdbg(@"already visible, frame=%@", NSStringFromRect(_mainWC.window.frame)); return; } [NSApp activateIgnoringOtherApps:YES]; - [_mainWC showWindow:nil]; [_mainWC.window makeKeyAndOrderFront:nil]; - [_mainWC.window setLevel:NSNormalWindowLevel]; + [_mainWC.window setLevel:NSFloatingWindowLevel]; - // Center on the screen with the menu bar (primary built-in display). - // Saved frames + multi-display setups can place the window off-screen - // or on a display the user isn't currently looking at. + // Center on the primary screen — saved frames from prior multi-display + // setups can land the window off-screen. [_mainWC.window setFrameAutosaveName:@""]; NSScreen *primary = [NSScreen screens].firstObject ?: [NSScreen mainScreen]; NSRect visible = primary.visibleFrame; @@ -514,8 +493,6 @@ -(void) onControlPanelSelected { visible.origin.x + (visible.size.width - frame.size.width) / 2, visible.origin.y + (visible.size.height - frame.size.height) / 2); [_mainWC.window setFrameOrigin:origin]; - okdbg(@"primary visibleFrame=%@ moved to=%@", NSStringFromRect(visible), NSStringFromRect(_mainWC.window.frame)); - okdbg(@"after show: visible=%d frame=%@ screen=%@", [_mainWC.window isVisible], NSStringFromRect(_mainWC.window.frame), _mainWC.window.screen); } -(void) onMacroSelected { diff --git a/Sources/OpenKey/macOS/ModernKey/Base.lproj/Main.storyboard b/Sources/OpenKey/macOS/ModernKey/Base.lproj/Main.storyboard index 1ad0aedf..848672a3 100644 --- a/Sources/OpenKey/macOS/ModernKey/Base.lproj/Main.storyboard +++ b/Sources/OpenKey/macOS/ModernKey/Base.lproj/Main.storyboard @@ -889,10 +889,10 @@ - + - + @@ -900,7 +900,7 @@ + + + + + + + + + + - + @@ -967,7 +987,7 @@ - + @@ -976,7 +996,7 @@ - + @@ -985,7 +1005,7 @@ - + @@ -994,7 +1014,7 @@ - + @@ -1012,7 +1032,7 @@ - + @@ -1692,6 +1712,7 @@ + diff --git a/Sources/OpenKey/macOS/ModernKey/OpenKey.mm b/Sources/OpenKey/macOS/ModernKey/OpenKey.mm index 4292db2d..18e75a1d 100644 --- a/Sources/OpenKey/macOS/ModernKey/OpenKey.mm +++ b/Sources/OpenKey/macOS/ModernKey/OpenKey.mm @@ -500,6 +500,8 @@ bool checkHotKey(int hotKeyData, bool checkKeyCode=true) { return false; if (HAS_SHIFT(hotKeyData) ^ GET_BOOL(_lastFlag & kCGEventFlagMaskShift)) return false; + if (HAS_FN(hotKeyData) ^ GET_BOOL(_lastFlag & kCGEventFlagMaskSecondaryFn)) + return false; if (checkKeyCode) { if (GET_SWITCH_KEY(hotKeyData) != _keycode) return false; diff --git a/Sources/OpenKey/macOS/ModernKey/OpenKeyManager.m b/Sources/OpenKey/macOS/ModernKey/OpenKeyManager.m index 2d1b665b..fbb24b04 100644 --- a/Sources/OpenKey/macOS/ModernKey/OpenKeyManager.m +++ b/Sources/OpenKey/macOS/ModernKey/OpenKeyManager.m @@ -71,12 +71,11 @@ +(BOOL)initEventTap { // Add to the current run loop. CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes); - // Enable the event tap. + // Enable the event tap. The source is on the main run loop, which + // NSApplication is already running — calling CFRunLoopRun() here would + // start a nested loop that blocks AppKit from processing UI events. CGEventTapEnable(eventTap, true); - - // Set it all running. - CFRunLoopRun(); - + return YES; } diff --git a/Sources/OpenKey/macOS/ModernKey/ViewController.m b/Sources/OpenKey/macOS/ModernKey/ViewController.m index d4c5b73c..0f2a00d5 100644 --- a/Sources/OpenKey/macOS/ModernKey/ViewController.m +++ b/Sources/OpenKey/macOS/ModernKey/ViewController.m @@ -44,6 +44,7 @@ @implementation ViewController { __weak IBOutlet NSButton *CustomSwitchOption; __weak IBOutlet NSButton *CustomSwitchControl; __weak IBOutlet NSButton *CustomSwitchShift; + __weak IBOutlet NSButton *CustomSwitchFn; __weak IBOutlet MyTextField *CustomSwitchKey; __weak IBOutlet NSButton *CustomBeepSound; NSArray* tabviews, *tabbuttons; @@ -250,6 +251,13 @@ - (IBAction)onShiftSwitchKey:(NSButton *)sender { [[NSUserDefaults standardUserDefaults] setInteger:vSwitchKeyStatus forKey:@"SwitchKeyStatus"]; } +- (IBAction)onFnSwitchKey:(NSButton *)sender { + NSInteger val = [self setCustomValue:sender keyToSet:nil]; + vSwitchKeyStatus &= (~0x1000); + vSwitchKeyStatus |= val << 12; + [[NSUserDefaults standardUserDefaults] setInteger:vSwitchKeyStatus forKey:@"SwitchKeyStatus"]; +} + -(void)onMyTextFieldKeyChange:(unsigned short)keyCode character:(unsigned short)character { vSwitchKeyStatus &= 0xFFFFFF00; vSwitchKeyStatus |= keyCode; @@ -467,6 +475,7 @@ -(void)fillData { CustomSwitchOption.state = (vSwitchKeyStatus & 0x200) ? NSControlStateValueOn : NSControlStateValueOff; CustomSwitchCommand.state = (vSwitchKeyStatus & 0x400) ? NSControlStateValueOn : NSControlStateValueOff; CustomSwitchShift.state = (vSwitchKeyStatus & 0x800) ? NSControlStateValueOn : NSControlStateValueOff; + CustomSwitchFn.state = (vSwitchKeyStatus & 0x1000) ? NSControlStateValueOn : NSControlStateValueOff; CustomBeepSound.state = (vSwitchKeyStatus & 0x8000) ? NSControlStateValueOn : NSControlStateValueOff; [CustomSwitchKey setTextByChar:((vSwitchKeyStatus>>24) & 0xFF)];