import Carbon import AppKit /// Manages global hotkey registration using Carbon RegisterEventHotKey. final class HotKeyManager { typealias Handler = () -> Void private var hotKeyRef: EventHotKeyRef? private var eventHandler: EventHandlerRef? private let handler: Handler /// The signature used to identify our hotkey events. private static let hotKeySignature: FourCharCode = { let chars: [UInt8] = [0x4D, 0x43, 0x54, 0x48] // "MCTH" return FourCharCode(chars[0]) << 24 | FourCharCode(chars[1]) << 16 | FourCharCode(chars[2]) << 8 | FourCharCode(chars[3]) }() private static let hotKeyID = EventHotKeyID(signature: hotKeySignature, id: 1) init(handler: @escaping Handler) { self.handler = handler } deinit { unregister() } /// Register the global hotkey. Default: Ctrl+Up Arrow. func register(keyCode: UInt32 = UInt32(kVK_UpArrow), modifiers: UInt32 = UInt32(controlKey)) { unregister() // Install Carbon event handler var eventType = EventTypeSpec(eventClass: OSType(kEventClassKeyboard), eventKind: UInt32(kEventHotKeyPressed)) let selfPtr = Unmanaged.passUnretained(self).toOpaque() InstallEventHandler( GetApplicationEventTarget(), { (_, event, userData) -> OSStatus in guard let userData = userData else { return OSStatus(eventNotHandledErr) } let manager = Unmanaged.fromOpaque(userData).takeUnretainedValue() var hotKeyID = EventHotKeyID() GetEventParameter( event, EventParamName(kEventParamDirectObject), EventParamType(typeEventHotKeyID), nil, MemoryLayout.size, nil, &hotKeyID ) if hotKeyID.signature == HotKeyManager.hotKeySignature { DispatchQueue.main.async { manager.handler() } } return noErr }, 1, &eventType, selfPtr, &eventHandler ) // Register the hotkey let hotKeyIDVar = Self.hotKeyID RegisterEventHotKey( keyCode, modifiers, hotKeyIDVar, GetApplicationEventTarget(), 0, &hotKeyRef ) } func unregister() { if let ref = hotKeyRef { UnregisterEventHotKey(ref) hotKeyRef = nil } if let handler = eventHandler { RemoveEventHandler(handler) eventHandler = nil } } }