diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index 453a27e..7b68a6b 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -323,6 +323,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate { guard let self else { return } self.isTranscribing = false guard !text.isEmpty else { return } + // KeyboardInjector posts CGEvents and does not touch AppKit, so it can run off-main. self.keyboardInjector.type(text) } } diff --git a/Sources/HotkeyManager.swift b/Sources/HotkeyManager.swift index 6558934..a0f7f3c 100644 --- a/Sources/HotkeyManager.swift +++ b/Sources/HotkeyManager.swift @@ -6,8 +6,10 @@ class HotkeyManager { private let onRecordStop: () -> Void private var isHeld = false private var globalKeyDown: Any? + private var globalKeyUp: Any? private var globalFlags: Any? private var localKeyDown: Any? + private var localKeyUp: Any? private var localFlags: Any? static let keyCodeKey = "hotkeyKeyCode" @@ -16,20 +18,28 @@ class HotkeyManager { // Read live from UserDefaults so updates take effect without re-registering private var triggerKeyCode: UInt16 { - let v = UserDefaults.standard.integer(forKey: HotkeyManager.keyCodeKey) - return v != 0 ? UInt16(v) : UInt16(kVK_ANSI_D) + guard UserDefaults.standard.object(forKey: HotkeyManager.keyCodeKey) != nil else { + return UInt16(kVK_ANSI_D) + } + return UInt16(UserDefaults.standard.integer(forKey: HotkeyManager.keyCodeKey)) } private var requiredModifiers: NSEvent.ModifierFlags { - let v = UserDefaults.standard.integer(forKey: HotkeyManager.modifiersKey) - return v != 0 ? NSEvent.ModifierFlags(rawValue: UInt(v)) : [.control, .option] + guard UserDefaults.standard.object(forKey: HotkeyManager.modifiersKey) != nil else { + return [.control, .option] + } + let mods = NSEvent.ModifierFlags(rawValue: UInt(UserDefaults.standard.integer(forKey: HotkeyManager.modifiersKey))) + return mods.isEmpty ? [.control, .option] : mods } static func displayString() -> String { - let storedMods = UserDefaults.standard.integer(forKey: modifiersKey) - let mods = storedMods != 0 - ? NSEvent.ModifierFlags(rawValue: UInt(storedMods)) - : NSEvent.ModifierFlags([.control, .option]) + let mods: NSEvent.ModifierFlags + if UserDefaults.standard.object(forKey: modifiersKey) != nil { + let storedMods = NSEvent.ModifierFlags(rawValue: UInt(UserDefaults.standard.integer(forKey: modifiersKey))) + mods = storedMods.isEmpty ? [.control, .option] : storedMods + } else { + mods = [.control, .option] + } let char = UserDefaults.standard.string(forKey: charKey) ?? "D" var s = "" if mods.contains(.control) { s += "⌃" } @@ -54,6 +64,14 @@ class HotkeyManager { } } + let handleKeyUp: (NSEvent) -> Void = { [weak self] event in + guard let self, self.isHeld else { return } + if event.keyCode == self.triggerKeyCode { + self.isHeld = false + self.onRecordStop() + } + } + let handleFlags: (NSEvent) -> Void = { [weak self] event in guard let self, self.isHeld else { return } let mods = event.modifierFlags.intersection(.deviceIndependentFlagsMask) @@ -64,11 +82,15 @@ class HotkeyManager { } globalKeyDown = NSEvent.addGlobalMonitorForEvents(matching: .keyDown, handler: handleKeyDown) + globalKeyUp = NSEvent.addGlobalMonitorForEvents(matching: .keyUp, handler: handleKeyUp) globalFlags = NSEvent.addGlobalMonitorForEvents(matching: .flagsChanged, handler: handleFlags) localKeyDown = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { event in handleKeyDown(event); return event } + localKeyUp = NSEvent.addLocalMonitorForEvents(matching: .keyUp) { event in + handleKeyUp(event); return event + } localFlags = NSEvent.addLocalMonitorForEvents(matching: .flagsChanged) { event in handleFlags(event); return event } @@ -78,7 +100,7 @@ class HotkeyManager { } deinit { - for m in [globalKeyDown, globalFlags, localKeyDown, localFlags] { + for m in [globalKeyDown, globalKeyUp, globalFlags, localKeyDown, localKeyUp, localFlags] { if let m { NSEvent.removeMonitor(m) } } }