From 5a5e5fd4155ef9ef9990dc2c168aff832b83e2d9 Mon Sep 17 00:00:00 2001 From: Duy Huynh Date: Sat, 29 Aug 2026 00:12:18 +0700 Subject: [PATCH] fix(macOS): re-enable event tap after macOS disables it initEventTap creates an active event tap, so macOS puts a deadline on OpenKeyCallback. When the callback misses that deadline (sustained CPU load) or on kCGEventTapDisabledByUserInput, the system switches the tap off and reports it back through the callback as a pseudo event type. OpenKeyCallback never checked for those two types, and CGEventTapEnable was only ever called once during setup. Once macOS turned the tap off, nothing turned it back on: the menu bar still showed the current mode and the switch key still worked, but no keystroke was converted in any app until OpenKey was relaunched. Handle both notifications in the callback and re-enable the tap. The normal event path is unchanged. Refs #258 Co-Authored-By: Claude Opus 5 (1M context) --- Sources/OpenKey/macOS/ModernKey/OpenKey.mm | 11 +++++++++++ Sources/OpenKey/macOS/ModernKey/OpenKeyManager.m | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/Sources/OpenKey/macOS/ModernKey/OpenKey.mm b/Sources/OpenKey/macOS/ModernKey/OpenKey.mm index 1e6b6c14..16cc9ea1 100644 --- a/Sources/OpenKey/macOS/ModernKey/OpenKey.mm +++ b/Sources/OpenKey/macOS/ModernKey/OpenKey.mm @@ -68,6 +68,9 @@ CGKeyCode _keycode; CGEventFlags _flag, _lastFlag = 0, _privateFlag; CGEventTapProxy _proxy; + + //defined in OpenKeyManager.m + extern void OpenKeyReEnableEventTap(void); Uint16 _newCharString[MAX_UNICODE_STRING]; Uint16 _newCharSize; @@ -599,6 +602,14 @@ CGKeyCode ConvertEventToKeyboadLayoutCompatKeyCode(CGEventRef keyEvent, CGKeyCod * MAIN Callback. */ CGEventRef OpenKeyCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) { + //macOS turns the tap off if this callback misses its deadline (heavy system load) + //or after certain user input, and tells us through these two pseudo event types. + //Without re-enabling here, OpenKey silently stops converting until it is relaunched. + if (type == kCGEventTapDisabledByTimeout || type == kCGEventTapDisabledByUserInput) { + OpenKeyReEnableEventTap(); + return event; + } + //dont handle my event if (CGEventGetIntegerValueField(event, kCGEventSourceStateID) == CGEventSourceGetSourceStateID(myEventSource)) { return event; diff --git a/Sources/OpenKey/macOS/ModernKey/OpenKeyManager.m b/Sources/OpenKey/macOS/ModernKey/OpenKeyManager.m index 97883f1a..4ac82862 100644 --- a/Sources/OpenKey/macOS/ModernKey/OpenKeyManager.m +++ b/Sources/OpenKey/macOS/ModernKey/OpenKeyManager.m @@ -77,6 +77,14 @@ +(BOOL)initEventTap { return YES; } +// Called from OpenKeyCallback when macOS reports that it turned our tap off. +// Only this file owns `eventTap`, so re-enabling has to happen here. +void OpenKeyReEnableEventTap(void) { + if (eventTap) { + CGEventTapEnable(eventTap, true); + } +} + +(BOOL)stopEventTap { if (_isInited) { //release all object CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);