macOS: Re-enable global keybinds after event tap disable events (#12714)

While testing https://github.com/ghostty-org/ghostty/pull/9857, I
encountered the behavior mentioned below. It's pretty frustrating to
encounter, so I've been actually compiling this fix into my test builds
for last month or so, and the issue has not come back. I exclusively use
the QuickTerminal, so my workflow depends on global keybinds working
reliably.

Issue: https://github.com/ghostty-org/ghostty/issues/12294

The solution includes listening to two events that are fired when a tap
is disabled:
- tapDisabledByTimeout
- tapDisabledByUserInput

When these are fired, we re-enable the tap.

Apple's Docs:
https://developer.apple.com/documentation/coregraphics/cgeventtype?language=swift

Related Discussions:
- https://github.com/ghostty-org/ghostty/discussions/11819
- https://github.com/ghostty-org/ghostty/discussions/12091
This commit is contained in:
Mitchell Hashimoto
2026-05-22 08:56:16 -07:00
committed by GitHub

View File

@@ -16,7 +16,7 @@ class GlobalEventTap {
// The event tap used for global event listening. This is non-nil if it is
// created.
private var eventTap: CFMachPort?
fileprivate var eventTap: CFMachPort?
// This is the timer used to retry enabling the global event tap if we
// don't have permissions.
@@ -125,6 +125,17 @@ private func cgEventFlagsChangedHandler(
) -> Unmanaged<CGEvent>? {
let result = Unmanaged.passUnretained(cgEvent)
// macOS disables the event tap if the callback is too slow or for other
// internal reasons. When that happens it sends this event type. We need
// to re-enable the tap or it stays dead forever.
if type == .tapDisabledByTimeout || type == .tapDisabledByUserInput {
GlobalEventTap.logger.warning("global event tap was disabled by the system, re-enabling")
if let machPort = GlobalEventTap.shared.eventTap {
CGEvent.tapEnable(tap: machPort, enable: true)
}
return result
}
// We only care about keydown events
guard type == .keyDown else { return result }