Capture Global Keydown Events (not simply observe) - swift

I've created some global hotkeys. However using addGlobalMonitorForEventsMatchingMask I can only observe keydown events. What if I want to capture the event and prevent other applications from receiving it?
Here's what I have for observing keydown events:
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(aNotification: NSNotification) {
NSEvent.addGlobalMonitorForEventsMatchingMask(.KeyDownMask, handler: keyDown)
NSEvent.addLocalMonitorForEventsMatchingMask(.KeyDownMask) { (event) -> NSEvent! in
self.keyDown(event)
return event
}
}
func keyDown(event : NSEvent) {
if event.modifierFlags.contains(.ControlKeyMask) && event.modifierFlags.contains(.AlternateKeyMask) && event.modifierFlags.contains(.CommandKeyMask) && event.keyCode == 126 {
print("⌃⌥⌘↑ pressed")
}
}
}
I'm not sure where to start to capture keydown events. Any help would be great. Thanks.

DDHotKey allows you to capture keydown events and use them to trigger function in you app. I discovered it recently in this post. If you don't already know how to integrate Objective-C code into a Swift project, you can find the Apple documentation here.

Related

Key down event handler running multiple times in Swift

I am trying to do something when a given key is pressed in a macOS App. First, I ran into a problem where the keyDown event was detected multiple times on each press, therefore executing the handler multiple times. As per a suggestion, I added code to check whether the event is a repeat and it seemed to work at the time. However, this solution seems to work only some of the time, other times the event is getting detected multiple times. Also, I can't seem find a pattern in situations when it works and when it doesn't.What might be the problem and how could I fix it.
Code:
override func viewDidLoad() {
super.viewDidLoad()
NSEvent.addLocalMonitorForEvents(matching: .keyDown, handler: checkKeyDown(event:))
}
func checkKeyDown(event: NSEvent) -> NSEvent{
if event.isARepeat == false{
if event.keyCode == 36{
print("Hello World!")
}
}
return event
}
Removing the event monitor when the window closes seems to have fixed this issue.
var numKeyDown : Any?
override func viewDidLoad() {
super.viewDidLoad()
numKeyDown = NSEvent.addLocalMonitorForEvents(matching: .keyDown, handler: checkKeyDown(event:))
}
override func viewWillDisappear(){
if let numMonitor = self.numKeyDown {
NSEvent.removeMonitor(numMonitor)
}
}
func checkKeyDown(event: NSEvent) -> NSEvent{
if event.isARepeat == false{
if event.keyCode == 36{
print("Hello World!")
}
}
return event
}

How can I respond to a keyUp event?

I am writing a simple menu bar application for MacOS using SwiftUI. I would like the application to respond to the escape key. Using what I have pieced together so far, I have something like this:
extension NSWindow {
open override func keyDown(with event: NSEvent) {
print("keyDown: \(event.keyCode)")
}
open override func keyUp(with event: NSEvent) {
print("keyUp: \(event.keyCode)")
}
}
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// etc
}
}
The AppDelegate creates and launches a NSPopover.
When I run the application, I do get the keyUp messages, but, not the keyDown for some reason.
The question is how can I respond to the key from within the AppDelegate? Ultimately, I want to close the popover using the escape key, but I would also like to explore other possibilities.

listening for multitouch events in OS X using the touchesBegan and touchesMoved event handlers

I am trying to record touches and finger movements that users perform on a 2018 MacBook Trackpad.
When I try to test the API and print to console when the user touches the Trackpad, I see no output.
How do I listen for touch events and get data from them?
I referred to the handling Trackpad events documentation. The handling multitouch events section of the page says:
A view by default does not accept touch events. To handle touch events, your custom view must first call the NSView method setAcceptsTouchEvents: with an argument of YES.
However, the setAcceptsTouchEvents documentation says the method is deprecated.
When I try just printing a log on detecting a touch to test if the API is working, I do not see any console output. here is my code:
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
override func touchesBegan(with event: NSEvent) {
print("touched!");
}
}
how do I get the console statement to print something?
I was playing around with this and here is what I figured out.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the //view.
//view.acceptsTouchEvents = true; //deprecated but works.deprecated
view.allowedTouchTypes = [NSTouch.TouchTypeMask.direct, NSTouch.TouchTypeMask.indirect];
}
and then, you can override the functions when touches are in different phases as explained in the question.
Note that to do the "allowedTouchTypes" massage in a subclass it is like this:
import Cocoa
class UnView: NSView{
override var allowedTouchTypes: NSTouch.TouchTypeMask {
get { return [] } .. as you wish
set { }
}
}

OSX swift 3 - can't disable keyDown. error sound

Im am trying to disable error sound when I push down space bar and arrow keys. I tryed handling events with super.keyDown(with: event) no luck. Cant find any other working solutions apart from using global key frameworks. Are there any other options I have?
NSEvent.addLocalMonitorForEvents(matching: .keyDown) { (aEvent) -> NSEvent? in
self.keyDown(with: aEvent)
return aEvent
}
}
override func keyDown(with event: NSEvent) {
super.keyDown(with: event)
}
Update: I've found out that the root cause of the problem was, that a view was the first responder that shouldn't be actually. After setting the responder to nil self.view.window?.makeFirstResponder(nil) I was able to fix this. I've also used performKeyEquivalent as this answer suggested.
I know my answer is very late, but maybe it will help you or someone else in the future. I'm not sure if that's the best way to do it but it does work. Simply return nil instead of the event.
NSEvent.addLocalMonitorForEvents(matching: .keyDown) { (aEvent) -> NSEvent? in
self.keyDown(with: aEvent)
return nil
}
override func keyDown(with event: NSEvent) {
super.keyDown(with: event)
}
Apple's documentation of the method says the following for the block parameter:
The event handler block object. It is passed the event to monitor. You can return the event unmodified, create and return a new NSEvent object, or return nil to stop the dispatching of the event.
The only downside is, that there will be no sound at all. Even if the key event is not handled by you.

How to handle escape key press event? [duplicate]

This question already has answers here:
Swift - Capture keydown from NSViewController
(5 answers)
the "funk" sound when hitting escape key in app
(1 answer)
Closed 5 years ago.
In Swift, is there a function in Cocoa framework to handle the press so that you can register which keyboard key has been hit?
I'd like to get the escape key
UPDATED:
i just found that the noise was casued by
super.keyDown(with: event)
wich is not needed for the thing to work
why did u add that line?
Create a subclass of NSWindow and implement keyDown event:
import Cocoa
import Carbon.HIToolbox
class CustomWindow: NSWindow {
override func keyDown(with event: NSEvent) {
switch Int(event.keyCode) {
case kVK_Escape:
print("Esc pressed")
default:
break
}
super.keyDown(with: event)
}
}
This line:
import Carbon.HIToolbox
Lets you use handy constants for keys, such as kVK_Escape.
Set this class as your main window class in the Interface Builder and you're all set:
P.S. To do the same form NSViewController, in viewDidLoad do:
NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
self.keyDown(with: $0)
return $0
}
P.P.S. To mute "bang" sound, don't call super upon Escape key press - move super call to default:
default:
super.keyDown(with: event)
EDIT:
If you don't want any sound on Escape key press, then the following approach should be used:
Make an NSView subclass and set it to main view of the view controller:
import Cocoa
import Carbon.HIToolbox
class CustomView: NSView {
override func performKeyEquivalent(with event: NSEvent) -> Bool {
switch Int(event.keyCode) {
case kVK_Escape:
print("Esc pressed")
return true
default:
return super.performKeyEquivalent(with: event)
}
}
}