I'm trying to programmaticaly send a custom key event (function keys, media keys) but it only works in interpreted mode, not in compiled code.
I tried using the following answer: emulate media key press on Mac
The python example works perfectly, the swift example works when called as a script, ie this code:
#!/usr/bin/swift
import Quartz
let NX_KEYTYPE_SOUND_UP: UInt32 = 0
let NX_KEYTYPE_SOUND_DOWN: UInt32 = 1
let NX_KEYTYPE_PLAY: UInt32 = 16
let NX_KEYTYPE_NEXT: UInt32 = 17
let NX_KEYTYPE_PREVIOUS: UInt32 = 18
let NX_KEYTYPE_FAST: UInt32 = 19
let NX_KEYTYPE_REWIND: UInt32 = 20
let supportedKeys: [String: UInt32] = ["playpause": NX_KEYTYPE_PLAY, "next": NX_KEYTYPE_NEXT, "prev": NX_KEYTYPE_PREVIOUS, "volup": NX_KEYTYPE_SOUND_UP, "voldown": NX_KEYTYPE_SOUND_DOWN]
func HIDPostAuxKey(key: UInt32) {
func keyDown(_ down: Bool) {
let flags = NSEvent.ModifierFlags(rawValue: (down ? 0xa00 : 0xb00))
let data1 = Int((key << 16) | (down ? 0xa00 : 0xb00))
let ev = NSEvent.otherEvent(with: NSEvent.EventType.systemDefined,
location: NSPoint(x:0,y:0),
modifierFlags: flags,
timestamp: 0,
windowNumber: 0,
context: nil,
subtype: 8,
data1: data1,
data2: -1)
let cev = ev?.cgEvent
cev?.post(tap: CGEventTapLocation.cghidEventTap)
}
keyDown(true)
keyDown(false)
}
HIDPostAuxKey(key: supportedKeys[CommandLine.arguments[1]]!)
called via terminal (after doing chmod a+x keypress.swift) works perfectly.
./keypress.swift volup
increases the volume, the HID for the volume even appears on screen.
If I try to compile the exact same code with
swiftc -o keypress keypress.swift
And adding the keypress binary to Security & Privacy -> accessibility, it does nothing. No error message, nothing.
First I tried to write a CLI app in Xcode using this code, it doesnt work (no error, but no key pressed). I've tried in Obj-C, in Swift, I checked that sandboxing wasnt enabled, no luck.
I'm stumpled that it works in interpreted mode but not in compiled mode.
Is there some flag to add when compiling to enable posting events? I'm out of ideas.
I'm running MacOS 12.6.2, Xcode 14.2, Swift 5
The difference between interpreted swift and compiled swift is ... speed
So adding
after the cgEvent.post solves the problem. (Note that I tested this on my iMac i5 3.2 GHz, you may need to change this delay)
This is not an perfect solution: the program should manage a queue of the events posted, but better, MacOS should handle this.
EDIT: I was wrong. Sending a dummy event at start solves the problem.
Calling this at start solves the problem. I've read all the documentation I could find, but there's no mention on how or when the event queue is created and why this solves my problem.
I'm still curious why this happens and what's the correct way to solve this.