Problem
KeyFoley currently monitors ordinary keyboard input with a session-level CGEventTap using .headInsertEventTap and .listenOnly.
This makes playback depend on the ordering of event taps installed by other applications. A reproducible example is HyperSwitch, which registers global shortcuts such as Cmd+Tab and `Cmd+``.
Reproduction
- Start KeyFoley.
- Start HyperSwitch afterwards.
- Hold
Cmd and press Tab or `.
- HyperSwitch handles the shortcut, but KeyFoley does not play the corresponding key sound.
- Restart KeyFoley while HyperSwitch remains running.
- The same shortcuts now produce sound normally.
Expected behavior
KeyFoley should play sounds for physical keyboard presses regardless of whether another application later consumes the corresponding macOS keyboard event.
Current behavior
The behavior depends on startup / event-tap registration order. A later application can insert its tap ahead of KeyFoley and consume an event before KeyFoley receives it.
Why this happens
The ordinary keyboard path currently uses:
CGEvent.tapCreate(
tap: .cgSessionEventTap,
place: .headInsertEventTap,
options: .listenOnly,
...
)
.listenOnly prevents KeyFoley itself from modifying the event stream, but does not guarantee that KeyFoley receives events discarded by an earlier active event tap.
As a result, applications that intercept global shortcuts can suppress KeyFoley playback depending on tap ordering.
Workaround
Restart KeyFoley after the conflicting application has started. This recreates KeyFoley's head-insert tap and places it ahead of existing taps again.
This is only a temporary workaround: if the other application restarts or recreates its tap later, the issue can return.
Possible direction
Evaluate making IOHIDManager the primary source for ordinary physical keyboard events, while keeping the existing MonitoredKeyEvent boundary so the upper layers (AppModel, sound playback, pet handling, diagnostics) do not need a broad rewrite.
KeyFoley already uses IOHID for Caps Lock, so that implementation can serve as a starting point for a generalized keyboard HID monitor.
A possible target architecture:
IOHIDManager
└─ ordinary keyboard / modifier events
CGEventTap
└─ system-defined / auxiliary events or fallback cases
↓
MonitoredKeyEvent
↓
existing playback / pet / diagnostics pipeline
Important migration work would include:
- HID usage → existing virtual-key-code / internal event mapping
- modifier press/release semantics
- auto-repeat behavior
- multiple keyboards and device hot-plugging
- avoiding duplicate events during migration
- retaining CGEventTap only where macOS-level event semantics are actually needed
Input Monitoring permission would still be required; this change is intended to make physical key detection independent of downstream CGEventTap consumers and startup order, not to bypass macOS privacy controls.
Problem
KeyFoley currently monitors ordinary keyboard input with a session-level
CGEventTapusing.headInsertEventTapand.listenOnly.This makes playback depend on the ordering of event taps installed by other applications. A reproducible example is HyperSwitch, which registers global shortcuts such as
Cmd+Taband `Cmd+``.Reproduction
Cmdand pressTabor`.Expected behavior
KeyFoley should play sounds for physical keyboard presses regardless of whether another application later consumes the corresponding macOS keyboard event.
Current behavior
The behavior depends on startup / event-tap registration order. A later application can insert its tap ahead of KeyFoley and consume an event before KeyFoley receives it.
Why this happens
The ordinary keyboard path currently uses:
.listenOnlyprevents KeyFoley itself from modifying the event stream, but does not guarantee that KeyFoley receives events discarded by an earlier active event tap.As a result, applications that intercept global shortcuts can suppress KeyFoley playback depending on tap ordering.
Workaround
Restart KeyFoley after the conflicting application has started. This recreates KeyFoley's head-insert tap and places it ahead of existing taps again.
This is only a temporary workaround: if the other application restarts or recreates its tap later, the issue can return.
Possible direction
Evaluate making
IOHIDManagerthe primary source for ordinary physical keyboard events, while keeping the existingMonitoredKeyEventboundary so the upper layers (AppModel, sound playback, pet handling, diagnostics) do not need a broad rewrite.KeyFoley already uses IOHID for Caps Lock, so that implementation can serve as a starting point for a generalized keyboard HID monitor.
A possible target architecture:
Important migration work would include:
Input Monitoring permission would still be required; this change is intended to make physical key detection independent of downstream CGEventTap consumers and startup order, not to bypass macOS privacy controls.