1

I am using event monitor to detect the key press event in swift. However, event monitor doesn't seem to detect command key or any other modifier key (shift, tab, opt ...) press. Is there a different way to detect modifier key press?. Note that I am not looking for a way to detect key combinations (ex: cmd+r) which can be done by using event.modifierFlags, but a way to know when command key alone is pressed.

override func viewDidLoad() {
        super.viewDidLoad()
        NSEvent.addLocalMonitorForEvents(matching: .keyDown, handler: commandKey(evt:))
}

    func commandKey(evt: NSEvent) -> NSEvent{
        if evt.keyCode == 55 { //key code for command is 55
          print("commanded")
        }
        return evt
    }

unknown
  • 788
  • 9
  • 24
  • 1
    Does this answer your question? [Global modifier key press detection in Swift](https://stackoverflow.com/questions/41927843/global-modifier-key-press-detection-in-swift) – Willeke Mar 03 '21 at 20:00

2 Answers2

3

Found a solution. Seems .flagsChanged event is used to detect modifier key press.

override func viewDidLoad() {
        super.viewDidLoad()
        NSEvent.addLocalMonitorForEvents(matching: .flagsChanged, handler: commandKey(evt:))
}

func commandKey(evt: NSEvent) -> NSEvent{
        if evt.modifierFlags.contains(.command){
            print("commanded")
        }
        return evt
}
unknown
  • 788
  • 9
  • 24
  • 2
    The modifier keys (command, shift, option, control) are not considered keys in their own right. they change the interpretation of the other keys on the keyboard. Monitoring flag changes is the right way to do this. – Duncan C Mar 03 '21 at 17:57
0
// monitor will work iside your app and outside as global monitor
let monitor = NSEvent.localAndGlobalMonitor(matching: .flagsChanged)
        
monitor
    .filter { $0.modifierFlags.check(equals: [.command]) }
    .onUpdate { _ in print("CMD key pressed. And only CMD.") }

func for checking that:

  • this exact set of modifiers was pressed
  • all other modifiers - not
import SwiftUI

extension NSEvent.ModifierFlags {
    func check(equals: [NSEvent.ModifierFlags] ) -> Bool {
        var notEquals: [NSEvent.ModifierFlags] = [.shift, .command, .control, .option]
        
        equals.forEach{ val in notEquals.removeFirst(where: { $0 == val }) }
        
        var result = true
        
        equals.forEach{ val in
            if result {
                result = self.contains(val)
            }
        }
        
        notEquals.forEach{ val in
            if result {
                result = !self.contains(val)
            }
        }
        
        return result
    }

    func check(oneOf flags: [NSEvent.ModifierFlags] ) -> Bool {
        for flag in flags {
            if check(equals: [flag]) {
                return true
            }
        }
        
        return false
    }
}

public extension Array {
    mutating func removeFirst(where predicate: (Element)->(Bool)) {
        if let idx = self.firstIndex(where: predicate) {
            remove(at: idx)
        }
    }
}

Warning: this code wasn't tested with CapsLock

Andrew_STOP_RU_WAR_IN_UA
  • 9,318
  • 5
  • 65
  • 101