How to listen to global hotkeys with Swift in an OS X app?

前端 未结 6 1756
北荒
北荒 2020-12-12 17:08

I\'m trying to have a handler in my Mac OS X app written in Swift for a global (system-wide) hotkey combo but I just cannot find proper documentation for it. I\'ve read that

6条回答
  •  既然无缘
    2020-12-12 17:55

    The following code works for me for Swift 5.0.1. This solution is the combination of the solution from the accepted answer by Charlie Monroe and the recommendation by Rob Napier to use DDHotKey.

    DDHotKey seems to work out of the box but it had one limitation that I had to change: the eventKind is hardcoded to kEventHotKeyReleased while I needed both kEventHotKeyPressed and kEventHotKeyReleased event types.

    eventSpec.eventKind = kEventHotKeyReleased;
    

    If you want to handle both Pressed and Released events, just add a second InstallEventHandler call which registers the other event kind.

    This the complete example of the code that registers the "Command + R" key for the kEventHotKeyReleased type.

    import Carbon
    
    extension String {
      /// This converts string to UInt as a fourCharCode
      public var fourCharCodeValue: Int {
        var result: Int = 0
        if let data = self.data(using: String.Encoding.macOSRoman) {
          data.withUnsafeBytes({ (rawBytes) in
            let bytes = rawBytes.bindMemory(to: UInt8.self)
            for i in 0 ..< data.count {
              result = result << 8 + Int(bytes[i])
            }
          })
        }
        return result
      }
    }
    
    class HotkeySolution {
      static
      func getCarbonFlagsFromCocoaFlags(cocoaFlags: NSEvent.ModifierFlags) -> UInt32 {
        let flags = cocoaFlags.rawValue
        var newFlags: Int = 0
    
        if ((flags & NSEvent.ModifierFlags.control.rawValue) > 0) {
          newFlags |= controlKey
        }
    
        if ((flags & NSEvent.ModifierFlags.command.rawValue) > 0) {
          newFlags |= cmdKey
        }
    
        if ((flags & NSEvent.ModifierFlags.shift.rawValue) > 0) {
          newFlags |= shiftKey;
        }
    
        if ((flags & NSEvent.ModifierFlags.option.rawValue) > 0) {
          newFlags |= optionKey
        }
    
        if ((flags & NSEvent.ModifierFlags.capsLock.rawValue) > 0) {
          newFlags |= alphaLock
        }
    
        return UInt32(newFlags);
      }
    
      static func register() {
        var hotKeyRef: EventHotKeyRef?
        let modifierFlags: UInt32 =
          getCarbonFlagsFromCocoaFlags(cocoaFlags: NSEvent.ModifierFlags.command)
    
        let keyCode = kVK_ANSI_R
        var gMyHotKeyID = EventHotKeyID()
    
        gMyHotKeyID.id = UInt32(keyCode)
    
        // Not sure what "swat" vs "htk1" do.
        gMyHotKeyID.signature = OSType("swat".fourCharCodeValue)
        // gMyHotKeyID.signature = OSType("htk1".fourCharCodeValue)
    
        var eventType = EventTypeSpec()
        eventType.eventClass = OSType(kEventClassKeyboard)
        eventType.eventKind = OSType(kEventHotKeyReleased)
    
        // Install handler.
        InstallEventHandler(GetApplicationEventTarget(), {
          (nextHanlder, theEvent, userData) -> OSStatus in
          // var hkCom = EventHotKeyID()
    
          // GetEventParameter(theEvent,
          //                   EventParamName(kEventParamDirectObject),
          //                   EventParamType(typeEventHotKeyID),
          //                   nil,
          //                   MemoryLayout.size,
          //                   nil,
          //                   &hkCom)
    
          NSLog("Command + R Released!")
    
          return noErr
          /// Check that hkCom in indeed your hotkey ID and handle it.
        }, 1, &eventType, nil, nil)
    
        // Register hotkey.
        let status = RegisterEventHotKey(UInt32(keyCode),
                                         modifierFlags,
                                         gMyHotKeyID,
                                         GetApplicationEventTarget(),
                                         0,
                                         &hotKeyRef)
        assert(status == noErr)
      }
    }
    

提交回复
热议问题