Quinn’s sample works perfectly for capturing key up/down events. Thanks for that!
I need to get the characters associated with key-up or key-down events. Using the NSEvent’s characters property, I wrote this in the didReceiveEvent callback:
let nsEvent = NSEvent(cgEvent: event)
print("Characters: \(event.characters)")
However, this crashes in a SwiftUI or AppKit app with a failing assertion in isIGetInputSourceRef while processing the raw key code.
Running event.characters on the main thread seems to be a workaround. There’s no documentation suggesting this, and the CGEvent comes in on a background thread through the tap. Other CGEvent and NSEvent properties work fine from any thread.
Is it expected that event.characters must be called from the main thread? Is it safe to dispatch it to the main queue, or could it fail if the main queue is handling other events? What’s the recommended approach?
Additionally, in my command-line executable without a UI, accessing characters from a background thread works fine. Why does this fail in a UI app? Is accessing it on the main thread a requirement?
It would be very valuable if someone could shed some light on this.
Post
Replies
Boosts
Views
Activity
I've also seen the createPCMBuffer implementation return nil in certain cases, breaking our microphone audiowave code and ScreenCaptureKit audio recording implementation.
Seems like copying the unsafe pointer and using it outside the block you pass to withAudioBufferList isn't a good idea. We've adjusted our implementation to this, and that fixes the problems:
func createPCMBuffer(for sampleBuffer: CMSampleBuffer) -> AVAudioPCMBuffer? {
try? sampleBuffer.withAudioBufferList { audioBufferList, _ -> AVAudioPCMBuffer? in
guard let absd = sampleBuffer.formatDescription?.audioStreamBasicDescription else { return nil }
guard let format = AVAudioFormat(standardFormatWithSampleRate: absd.mSampleRate, channels: absd.mChannelsPerFrame) else { return nil }
return AVAudioPCMBuffer(pcmFormat: format, bufferListNoCopy: audioBufferList.unsafePointer)
}
}
Wanting to monitor NSEvents globally in a command line app (written in Swift) I stumbled upon this thread. I also expected RunLoop.main.run() to keep the app running and help the observer receive events, but it didn't work. No errors, but also no events received in the handler.
With the pointers from Quinn here and some more reading about how an AppKit app launched I realised there also is the NSApplication.shared.run() method to start the runloop for a NSApplication. That worked out!
So the following code is a minimal sample for a working command line app that is monitoring events:
import AppKit
@main
public struct CLIapp {
public static func main() {
NSEvent.addGlobalMonitorForEvents(matching: .any) { event in print("Event received") }
NSApplication.shared.run() // This keeps the app running & makes sure events are received
}
}