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.fromOpaque(context).takeUnretainedValue() var id = EventHotKeyID() GetEventParameter( event, EventParamName(kEventParamDirectObject), EventParamType(typeEventHotKeyID), nil, MemoryLayout.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() } }