fix: handle fn key while consuming shortcut keys

This commit is contained in:
nchopra 2025-12-22 16:27:12 +05:30
parent a4aa058957
commit 765991ff72
2 changed files with 41 additions and 1 deletions

View file

@ -17,6 +17,23 @@ class ShortcutManager {
private var pushToTalkKeys: [String] = []
private var toggleRecordingKeys: [String] = []
// ============================================================================
// IMPORTANT: Fn Key State Tracking
// ============================================================================
// We track the Fn key state ourselves via flagsChanged events instead of
// trusting event.flags.contains(.maskSecondaryFn) on keyDown/keyUp events.
//
// WHY: macOS reports UNRELIABLE Fn flag on keyDown events, especially on
// MacBooks with the Globe/Fn key. The flag can be true even when Fn is NOT
// pressed, causing arrow keys and other keys to be incorrectly consumed.
//
// FIX: We update fnKeyDown only when we receive flagsChanged events (which
// are reliable for modifier state), and use this tracked state for shortcut
// matching in shouldConsumeKey().
// ============================================================================
private var fnKeyDown: Bool = false
private let lock = NSLock()
private let dateFormatter: DateFormatter
@ -41,6 +58,15 @@ class ShortcutManager {
logToStderr("[ShortcutManager] Shortcuts updated - PTT: \(pushToTalk), Toggle: \(toggleRecording)")
}
/// Update the tracked Fn key state
/// Called from event tap callback when flagsChanged event is received
/// We track Fn separately because macOS can report unreliable Fn flag on keyDown events
func setFnKeyState(_ isDown: Bool) {
lock.lock()
defer { lock.unlock() }
fnKeyDown = isDown
}
/// Check if this key event should be consumed (prevent default behavior)
/// Called from event tap callback for keyDown/keyUp events only
func shouldConsumeKey(keyCode: Int, modifiers: ModifierState) -> Bool {
@ -53,8 +79,10 @@ class ShortcutManager {
}
// Build set of currently active keys (modifiers + this regular key)
// Note: We use tracked fnKeyDown instead of modifiers.fn because macOS
// can report unreliable Fn flag on keyDown events (especially on MacBooks)
var activeKeys = Set<String>()
if modifiers.fn { activeKeys.insert("Fn") }
if fnKeyDown { activeKeys.insert("Fn") }
if modifiers.cmd { activeKeys.insert("Cmd") }
if modifiers.ctrl { activeKeys.insert("Ctrl") }
if modifiers.alt { activeKeys.insert("Alt") }

View file

@ -81,6 +81,18 @@ func eventTapCallback(
// Modifier-only events always pass through - they don't cause unwanted behavior on their own
let keyCode = event.getIntegerValueField(.keyboardEventKeycode)
// ====================================================================
// IMPORTANT: Track Fn key state via flagsChanged (NOT keyDown flags)
// ====================================================================
// macOS reports UNRELIABLE .maskSecondaryFn on keyDown events,
// especially on MacBooks. The flag can be TRUE even when Fn is NOT
// pressed! flagsChanged events are reliable, so we track Fn state
// here and use it in ShortcutManager.shouldConsumeKey().
// See ShortcutManager.swift for more details.
// ====================================================================
let fnPressed = event.flags.contains(.maskSecondaryFn)
ShortcutManager.shared.setFnKeyState(fnPressed)
let payload = KeyEventPayload(
key: nil,
code: nil,