6

I'm trying to set up a hotkey (option-tab) using NSEvent.addGlobalMonitorForEvents

let keycode = 48 // tab
let keymask: NSEvent.ModifierFlags = .option

func handler(event: NSEvent!) {

    if event.keyCode == keycode && 
        event.modifierFlags == event.modifierFlags & keymask     {

        // do whatever

    }
}

NSEvent.addGlobalMonitorForEvents(matching: [.keyDown], handler: handler)

But I get this error in the bitwise AND part:

Cannot convert value of type 'NSEvent.ModifierFlags' to expected argument type 'UInt8'

Replacing event.ModifierFlags with UInt8(event.modifierFlags.rawValue) etc also fails.

How do I fix this?

Edit:

Directly comparing event.modifierFlags == keymask fails (Even when I press only option-tab (and no other modifier key)).

On printing event.modifierFlags to console, a different value gets printed depending on whether I press the left or right option keys.

print (event.modifierFlags) // prints: ModifierFlags(rawValue: 524576) using LEFT OPTION 
// and  ModifierFlags(rawValue: 524608) when using RIGHT OPTION.

print(keymask) // prints: ModifierFlags(rawValue: 524288)

At this point my vague hunch is that I need to ignore the lower order bits and compare only the higher order ones. I just don't know how to go about doing this.

Anshuman Sharma
  • 417
  • 2
  • 11

1 Answers1

15

NSEvent.ModifierFlags is an OptionSet (which is a SetAlgebra). See the documentation for those as needed.

Given your current code, you basically want to see if the only flags set are those in the mask. But you do need to mask out for just the device independent flags.

Update your if as:

if event.keyCode == keyCode && event.modifierFlags.intersection(.deviceIndependentFlagsMask) == keymask {
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • This is what I tried first, but the equality comparison returns false. – Anshuman Sharma Nov 13 '17 at 00:14
  • Is your goal to ensure that the only flags set in `modifierFlags` is the `.option` flag? If so, then simply using `==` will work. If it isn't, then there are other flags set. If your goal is something else, then please explain. – rmaddy Nov 13 '17 at 00:16
  • [This link] (https://developer.apple.com/documentation/appkit/nsevent/1534405-modifierflags) says that the .modifierFields property is "An integer bit field indicating the event’s modifier keys. .... You can examine individual flag settings using the C bitwise AND operator with the predefined key masks described in Constants. The lower 16 bits of the modifier flags are reserved for device-dependent bits." – Anshuman Sharma Nov 13 '17 at 00:17
  • 1
    Yes, my goal is to ensure that only flag set is `.option` but the equality fails. – Anshuman Sharma Nov 13 '17 at 00:18
  • 1
    The info in that link doesn't really apply to the Swift API. That seems to be a left-over from the Objective-C API. Run your code. Examine the actual value of `modifierFlags`. What is it? It must contain more than just `.option`. – rmaddy Nov 13 '17 at 00:19
  • Have updated my Question with more info. Please see now. – Anshuman Sharma Nov 13 '17 at 00:25
  • Also, I get different values using left option or right option. See further edits above. – Anshuman Sharma Nov 13 '17 at 00:40
  • See my update. That should ignore the lower order, device dependent bits that may be set. – rmaddy Nov 13 '17 at 00:45
  • Works now. Wow, this should be better documented! – Anshuman Sharma Nov 13 '17 at 00:50
  • I know its not the same question, but how would I do the same for a `CGEvent` ? The same syntax doesn't work there and again, there's no documentation on the page. – Anshuman Sharma Nov 13 '17 at 01:00
  • `CGEvent.CGEventFlags` is also an `OptionSet`. But there doesn't seem to the need to worry about masking out device dependent bits. So something like `someCGEventFlags == someMask` will ensure that the only flags set are those in your mask. Do a search on `CGEventFlags`. You'll probably find lots of examples. Just make sure the example is using Swift 3 or later for the proper syntax. – rmaddy Nov 13 '17 at 01:06
  • I am actually getting the exact same values for left and right option printed using CGEventTaps. – Anshuman Sharma Nov 13 '17 at 01:08
  • ^Clarification - what I meant was: I am actually getting *different* values for left and right option printed using CGEventTaps, which are exactly the same as the values I was getting using NSEvents. – Anshuman Sharma Nov 13 '17 at 01:15
  • I solve this by using and comparing the `rawValue`s if they are not 0. For example `if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.option.rawValue != 0) { ...` for the option-key where `event` is the the `NSEvent` instance of course – Andreas Utzinger Feb 24 '19 at 21:16