1
class func addGlobalMonitorForEvents(matching mask: NSEvent.EventTypeMask, handler block: @escaping (NSEvent) -> Void) -> Any?{
        print("this inside addGlobalMonitorForEvents")
    }
NSEvent.addGlobalMonitorForEvents(matching: .flagsChanged) {
        switch $0.modifierFlags.intersection(.deviceIndependentFlagsMask) {
                case [.shift]:
                    print("shift key is pressed")
                    print("key code is \($0.keyCode))")
                case [.control] where $0.characters == "1":
                    print("control key is pressed")
                    print("characters is \($0.characters)")
                case [.option] where $0.charactersIgnoringModifiers == "1" :
                    print("option key is pressed")
                    print("\($0.charactersIgnoringModifiers) is pressed")
                case [.command] where $0.keyCode == 19:
                    print("Command key is pressed")
                    print("\($0.keyCode) is pressed")
                case [.command, .shift] where $0.characters == "1":
                    print("command-shift keys are pressed")
                    print("\($0.characters) are pressed")
                default:
                    print("no modifier keys are pressed)")
                }
}

Without numeric key, everything is fine. As long as I press shift / command / option with 1. It throwed exception. I tried check numeric by keyCode characters and charactersIgnoringModifiers. All of them either give me an assertion failure Assertion failure in -[NSEvent characters], Assertion failure in -[NSEvent charactersIgnoringModifiers] or not failure but instead print keyCode of modifier key itself 56 or something. I tried chain .contains(.command) and then switch on keyCode. Samething happened. Did I miss something here?

The code is reference from here. Although not sure what class func did, I didn't see the "this inside addGlobalMonitorForEvents" printed out when I press keys. Without this declaration, it simply not works. So I added it anyway.

Any suggestion or explanation would be appreciated. Thanks


Update

1st code snippet the class function declartion is not necessary. Right now able to monitor local event of command+shift +1 but not global one

override func viewDidLoad(){
     super.viewDidLoad()
     NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
            self.keyDown(with: $0)
            return $0
        }
}

override func keyDown(with event: NSEvent) {
        switch event.modifierFlags.intersection(.deviceIndependentFlagsMask) {
        case [.command] where event.characters == "1", [.command, .shift] where event.characters == "1":
            print("command+1 or command+shift+1")
        default:
            break
}

Then I changed to NSEvent.addGlobalMonitorForEvents and check xcodehelper in privacy accessibility(that's the only one I think related, as I run the app in xcode), but unfortunately that doesn't help.

ychz
  • 533
  • 2
  • 7
  • 15
  • 1
    You don’t have to add this method to your code. addGlobalMonitorForEvents Is a class method of NSEvent. – Leo Dabus Oct 15 '19 at 04:40
  • Btw you cannot monitor the keys globally, only the modifiers. ** Key-related events may only be monitored if accessibility is enabled or if your application is trusted for accessibility access (see AXIsProcessTrusted())** – Leo Dabus Oct 15 '19 at 04:42
  • 1
    Please read the documentation of `characters`: "This property is only valid for key-up and key-down events. It raises an NSInternalInconsistencyException if accessed on any other kind of event object." What are you trying to accomplish? – Willeke Oct 15 '19 at 07:38
  • @Willeke trying to trigger a function when command+shift+1 pressed. You are right. I shouldn't read characters inside flagChanges event. Now I am able to monitor local command+shift+1 event, but not global one. – ychz Oct 16 '19 at 00:47
  • @LeoDabus I checked one of your previous [post](https://stackoverflow.com/questions/32446978/swift-capture-keydown-from-nsviewcontroller) and update my question a little bit. Now the question is how enable the accessibility or programmatically set the value related to AXIsProcessTrusted(). I failed to find my app in accessibility as I ran it in xcode. I tried enabling Xcode Helper in accessibility, but that not help. – ychz Oct 16 '19 at 01:23

1 Answers1

3

To enable AXIsProcessTrusted() in your app you need to go to your macOS system preferences, open Security and Privacy, click on Accessibility, click on the plus sign to add your app to the list off apps that can control your computer. You might need to click at the padlock and type your password to unlock your settings.

enter image description here

import Cocoa

class ViewController: NSViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        print("AXIsProcessTrusted:",AXIsProcessTrusted())
        NSEvent.addGlobalMonitorForEvents(matching: .keyDown) {
            self.keyDown(with: $0)
        }
    }
    override func keyDown(with event: NSEvent) {
        switch event.modifierFlags.intersection(.deviceIndependentFlagsMask) {
        case [.command] where event.characters == "1", [.command, .shift] where event.characters == "1":
            print("command+1 or command+shift+1")
        default:
            break
        }
    }
}
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • Thanks Leo. That's really helpful. Though it takes me a while to convince myself to test on actual app when it is not even working in xcode. I struggled to find a way to prove it works as I can't see where it print that message to. I finally proved it by choose "waiting for executable to launch" in scheme. Do you happen to know how can I see the print out easily? The last question is that I actually want monitor cmd+shift+4, which is taking screenshot. But when I press the combination, it doesn't hit the case branch. How should we deal with this scenario? – ychz Oct 16 '19 at 03:59
  • @OscarZhang I think those are reserved. Even if you disable them in keyboard shortcut screenshot preferences. – Leo Dabus Oct 16 '19 at 04:06