Skip to content

fix(macOS): re-enable event tap after macOS disables it - #332

Open
duyhnynh wants to merge 1 commit into
tuyenvm:masterfrom
duyhnynh:fix/reenable-event-tap-after-macos-disables-it
Open

fix(macOS): re-enable event tap after macOS disables it#332
duyhnynh wants to merge 1 commit into
tuyenvm:masterfrom
duyhnynh:fix/reenable-event-tap-after-macos-disables-it

Conversation

@duyhnynh

Copy link
Copy Markdown

Problem

On macOS, OpenKey sometimes stops converting Vietnamese in every app at once. The menu bar icon still shows the current mode and the switch key still toggles it, but no keystroke is converted. Quitting and relaunching OpenKey is the only way back.

This matches #258, which describes the same thing ("Tất cả các ứng dụng đều không gõ được VI ... khắc phục: reboot lại OS và bật lại OpenKey").

Root cause

initEventTap creates an active event tap:

eventTap = CGEventTapCreate(kCGSessionEventTap,
                            kCGHeadInsertEventTap,
                            0,                    // active tap
                            eventMask,
                            OpenKeyCallback,
                            NULL);

macOS puts a deadline on an active tap's callback. If OpenKeyCallback does not return in time — which happens when the machine is under sustained load — the system switches the tap off and reports it back through the same callback as the pseudo event type kCGEventTapDisabledByTimeout. kCGEventTapDisabledByUserInput is the other case.

Two things then go wrong on master:

  1. OpenKeyCallback never checks type against those two values, so the notification falls through the normal key-handling path and is ignored.
  2. CGEventTapEnable is called exactly once, at the end of initEventTap.
$ grep -rn "kCGEventTapDisabled" .
(no matches)

So once macOS turns the tap off, nothing ever turns it back on. Everything else in the app keeps working — which is why the menu bar and the switch key still respond, and why it looks like OpenKey is running fine.

Fix

Handle both notifications at the top of the callback and re-enable the tap. eventTap is file-static in OpenKeyManager.m, so re-enabling goes through a small C function there, in the same style as the existing cross-file extern declarations.

The normal event path is untouched — the guard returns before any existing logic runs.

Testing

  • xcodebuild -project Sources/OpenKey/macOS/OpenKey.xcodeproj -scheme OpenKey -configuration ReleaseBUILD SUCCEEDED (Xcode 26.1.1, macOS 26.3)
  • nm on the built binary confirms the new path is present and CGEventTapEnable now has two call sites instead of one.
  • No new warnings.

Note: MACOSX_DEPLOYMENT_TARGET in the project is 10.14, which no longer builds on current Xcode (SDK does not contain 'libarclite'). I built with an override; that is a pre-existing issue unrelated to this change and I have not touched the project file.

What I did not verify

I have not captured the moment the tap flips to disabled. The failure needs the callback to be mid-flight during a CPU stall, so it only happens while you are actually typing, and I could not force it on demand. The evidence here is the code path plus the symptom match with #258, not a recorded transition.

For anyone wanting to check their own machine, CGGetEventTapList() reports an enabled flag per tap and will show OpenKey's tap as disabled while the process is still alive.

Refs #258

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 tuyenvm#258

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant