2

How can I listen to NSEvents while my app has the focus, but independently of which window is active?

I'm not looking to listen to system-wide events, I only want to listen to single and/or modified keystrokes while my app has the focus, but all examples I could find, listen NSEvents from NSWindow or NSView.

My app has multiple windows and I want to catch all keystrokes while any of those windows are focused.

gcstr
  • 1,466
  • 1
  • 21
  • 45

2 Answers2

3

This will work, but it's overkill for this problem. See Alexander's answer for the correct approach.


Override NSApplication.sendEvent. This is the central dispatching method for all events in the application. You can determine which events you care about, and either consume them, or send them along using super. This is a documented override point in NSApplication. It is not a hack.

Note that this requires subclassing NSApplication itself. It's not handled by the application delegate. For instructions on subclassing NSApplication in Swift, see Subclass NSApplication in Swift. (Read all the comments; the answer is a little messy. The key is setting NSPrincipalClass.)

For more technical details, see the Cocoa Event Handling Guide, which covers the whole architecture.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Hey Rob, do you know how this compares with the `NSApplication.addLocalMonitorForEvents(matching:handler:)` method? If I had to guest, the default implementation of `NSApplication.sendEvent` is what notifies those event handlers. (This would be easy enough to verify: override it and _don't_ call `super`, see if the `NSEvent` handlers stop working, but I don't have time to check that right now) – Alexander Feb 04 '23 at 20:40
  • That's probably better. The last time I actually did this was on code originally built for 10.2, and I'm clearly way behind the times. :D Thanks. – Rob Napier Feb 04 '23 at 21:45
  • That NSEevent API was introduced in 10.6, so that makes sense! – Alexander Feb 04 '23 at 21:49
2

It looks like the NSEvent.addLocalMonitorForEvents(matching:handler:) API is exactly what you're looking for.

Alexander
  • 59,041
  • 12
  • 98
  • 151