22

I just experimented with the addLocalMonitorForEventsMatchingMask:handler: method in NSEvent and came across the following question: How do I find out if only certain modifiers were pressed?

A short example to set this question into context: I wanted to listen for the shortcut "⌘+W". Therefore I wrote the following code:

[NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler:^(NSEvent *theEvent) {
    if ([theEvent modifierFlags] & NSCommandKeyMask && [theEvent keyCode] == 13) {
        [self.window performClose:self];
    }
    return theEvent;
}];

This works well, however the shortcut will be triggered, even if more modifier keys are pressed, e.g. "⌃+⌘+W" or "⇧+⌃+⌥+⌘+W". Is there a way to circumvent this?

A simple solution would be to check for all other modifier keys and ensure they are not pressed. This seems tedious and error prone - besides it's ugly enough as it is now with the unary "&". In addition you may get into trouble if (for some reason) another modifier key is added to keyboard layouts.

As always I'm thankful for any recommendations.

Florian Pilz
  • 8,002
  • 4
  • 22
  • 30

4 Answers4

34

I think this'll do it:

// Mask out everything but the key flags
NSUInteger flags = [theEvent modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
if( flags == NSCommandKeyMask ){
    // Got it!
}

Hat tip to SpaceDog for pointing out the deprecation of the original mask name, NSDeviceIndependentModifierFlagsMask.

jscs
  • 63,694
  • 13
  • 151
  • 195
  • 5
    Wow, that's neat! Now I have to use the unary "&" just once and finally understood what the `NSDeviceIndependentModifierFlagsMask` is for: The modifierFlags normally are set to 256 by default and other flags are added to it, this way I clean up so only the pressed keys are represented by the `modifierFlags` value. This way it's possible to write combinations of modifier keys like in Carbon, e.g. `flags == NSCommandKeyMask + NSControlKeyMas` if ⌘ **and** ⌃ should be pressed rather writing `flags & NSCommandKeyMask && flags & NSControlKeyMask`. Thanks a lot! – Florian Pilz May 22 '11 at 05:41
  • Pedantic mode dictates that masks should only be (bitwise) OR'd ('|'). Otherwise carry bits will come back to bite you. – geowar Feb 04 '16 at 16:50
  • `[theEvent modifierFlags] & (NSControlKeyMask|NSCommandKeyMask)` can be used for multiples – Yunus Nedim Mehel Jun 09 '16 at 13:28
  • 1
    unfortunately, thanks to Apple, NSDeviceIndependentModifierFlagsMask is deprecated. Back to the drawing board. – Duck Sep 29 '17 at 05:24
14

Swift 5 version

if event.modifierFlags.intersection(.deviceIndependentFlagsMask) == .command {
    // Got it!
}
vicegax
  • 4,709
  • 28
  • 37
6

@JoshCaswell answer has been outdated thanks to Apple, because NSDeviceIndependentModifierFlagsMask has been deprecated since 10.12.

His answer updated to the new syntax is

// Mask out everything but the key flags
NSUInteger flags = [theEvent modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
if( flags == NSCommandKeyMask ){
  // Got it!
}

NSDeviceIndependentModifierFlagsMask has been replaced with NSEventModifierFlagDeviceIndependentFlagsMask because it makes a world of difference...

Duck
  • 34,902
  • 47
  • 248
  • 470
2

Maybe even better...

if event.modifierFlags.contains(.shift){
    // do it
}
Marc T.
  • 5,090
  • 1
  • 23
  • 40