Files

62 lines
2.4 KiB
Swift

import Carbon
final class HotKeyManager: @unchecked Sendable {
var onLocal: (@Sendable () -> Void)?
var onCloud: (@Sendable () -> Void)?
private var localRef: EventHotKeyRef?
private var cloudRef: EventHotKeyRef?
private var handlerRef: EventHandlerRef?
private let signature: OSType = 0x574B4559 // WKEY
func register() throws {
unregister()
var eventType = EventTypeSpec(eventClass: OSType(kEventClassKeyboard), eventKind: UInt32(kEventHotKeyPressed))
let status = InstallEventHandler(
GetApplicationEventTarget(),
{ _, event, context in
guard let context, let event else { return noErr }
let manager = Unmanaged<HotKeyManager>.fromOpaque(context).takeUnretainedValue()
var id = EventHotKeyID()
GetEventParameter(
event,
EventParamName(kEventParamDirectObject),
EventParamType(typeEventHotKeyID),
nil,
MemoryLayout<EventHotKeyID>.size,
nil,
&id
)
if id.id == 1 { manager.onLocal?() }
if id.id == 2 { manager.onCloud?() }
return noErr
},
1,
&eventType,
Unmanaged.passUnretained(self).toOpaque(),
&handlerRef
)
guard status == noErr else { throw WhisperKeyError.processFailed("Carbon handler: \(status)") }
let localID = EventHotKeyID(signature: signature, id: 1)
let cloudID = EventHotKeyID(signature: signature, id: 2)
guard RegisterEventHotKey(UInt32(kVK_F5), 0, localID, GetApplicationEventTarget(), 0, &localRef) == noErr else {
throw WhisperKeyError.processFailed("F5 уже занята. Отключите сочетание системной диктовки.")
}
guard RegisterEventHotKey(UInt32(kVK_F5), UInt32(optionKey), cloudID, GetApplicationEventTarget(), 0, &cloudRef) == noErr else {
throw WhisperKeyError.processFailed("Option+F5 уже занята.")
}
}
func unregister() {
if let localRef { UnregisterEventHotKey(localRef) }
if let cloudRef { UnregisterEventHotKey(cloudRef) }
if let handlerRef { RemoveEventHandler(handlerRef) }
localRef = nil
cloudRef = nil
handlerRef = nil
}
deinit { unregister() }
}