Cocoa: Simulating Command+Tab in Cgevent

Cocoa: Simulating Command+Tab in CGEvent

The Command flag is missing in the keyup event. Add CGEventSetFlags(keyUp, .MaskCommand) before CGEventPost(.CGHIDEventTap, keyUp).

ShortcutRecorder record CMD+Tab

Using setCanCaptureGlobalHotKeys:YES on the SRRecorderControl fixes the issue.

Returning a `CGEvent` of my own creation in `NSEventTap`

Copied from the documentation for CGEventTapCallBack (link in the question):

Discussion

If the event tap is an active filter, your callback function should return one of the following:

  • The (possibly modified) event that is passed in. This event is passed back to the event system.

  • A newly-constructed event. After the new event has been passed back to the event system, the new event will be released along with the original event.

  • NULL if the event passed in is to be deleted.

The new event will be released along with the original event. Don't release the original event and don't release the new event.

Parsing Keyboard Shortcuts from CGEvent

I ended up just converting CGEvent to NSEvent by initializing NSEvent with CGEvent, and get charactersIgnoringModifiers. Non-characters like escape and backspace won't work though. Here's the entire CGEventCallback.

func myCGEventCallback(proxy : CGEventTapProxy, type : CGEventType, event : CGEvent, refcon : UnsafeMutableRawPointer?) -> Unmanaged<CGEvent>? {
if type == .keyDown {
let flags = event.flags
var msg = ""
if flags.contains(.maskAlphaShift) {
msg+="caps+"
}
if flags.contains(.maskShift) {
msg+="shift+"
}
if flags.contains(.maskControl) {
msg+="control+"
}
if flags.contains(.maskAlternate) {
msg+="option+"
}
if flags.contains(.maskCommand) {
msg += "command+"
}
if flags.contains(.maskSecondaryFn) {
msg += "function+"
}
if let other = NSEvent(cgEvent: event), let chars = other.charactersIgnoringModifiers {
msg += chars
debugPrint(msg)
}
}
return Unmanaged.passRetained(event)
}

Simulating global keyboard presses

I had this playground sitting on my Mac, so I figured I'd share it here in case it's helpful:

import Cocoa

let src = CGEventSource(stateID: .hidSystemState)

let down = CGEvent(keyboardEventSource: src, virtualKey: 0x12, keyDown: true)!
let up = CGEvent(keyboardEventSource: src, virtualKey: 0x12, keyDown: false)!

sleep(5)

////down?.post(tap: .cghidEventTap)
////up?.post(tap: .cghidEventTap)
////down?.flags = .maskCommand
//
//let cmd_d = CGEvent(keyboardEventSource: src, virtualKey: 0x37, keyDown: true)!
//let cmd_u = CGEvent(keyboardEventSource: src, virtualKey: 0x37, keyDown: false)!
//let a_d = CGEvent(keyboardEventSource: src, virtualKey: 0x00, keyDown: true)!
//let a_u = CGEvent(keyboardEventSource: src, virtualKey: 0x00, keyDown: false)!
//let delete_d = CGEvent(keyboardEventSource: src, virtualKey: 0x33, keyDown: true)!
//let delete_u = CGEvent(keyboardEventSource: src, virtualKey: 0x33, keyDown: false)!
//
//down.post(tap: .cghidEventTap)
//up.post(tap: .cghidEventTap)
//sleep(1)
////cmd_d.post(tap: .cghidEventTap)
//a_d.flags = .maskCommand
//a_u.flags = .maskCommand
//a_d.post(tap: .cghidEventTap)
//a_u.post(tap: .cghidEventTap)
////cmd_u.post(tap: .cghidEventTap)
//sleep(1)
//delete_d.post(tap: .cghidEventTap)
//delete_u.post(tap: .cghidEventTap)

enum KeyCode: UInt16 {
// https://gist.github.com/swillits/df648e87016772c7f7e5dbed2b345066

// Layout-independent Keys
// eg.These key codes are always the same key on all layouts.
case returnKey = 0x24
// case enter = 0x4C //0x24
case tab = 0x30
case space = 0x31
case delete = 0x33
case escape = 0x35
case command = 0x37
case shift = 0x38
case capsLock = 0x39
case option = 0x3A
case control = 0x3B
case rightShift = 0x3C
case rightOption = 0x3D
case rightControl = 0x3E
case leftArrow = 0x7B
case rightArrow = 0x7C
case downArrow = 0x7D
case upArrow = 0x7E
case volumeUp = 0x48
case volumeDown = 0x49
case mute = 0x4A
case help = 0x72
case home = 0x73
case pageUp = 0x74
case forwardDelete = 0x75
case end = 0x77
case pageDown = 0x79
case function = 0x3F
case f1 = 0x7A
case f2 = 0x78
case f4 = 0x76
case f5 = 0x60
case f6 = 0x61
case f7 = 0x62
case f3 = 0x63
case f8 = 0x64
case f9 = 0x65
case f10 = 0x6D
case f11 = 0x67
case f12 = 0x6F
case f13 = 0x69
case f14 = 0x6B
case f15 = 0x71
case f16 = 0x6A
case f17 = 0x40
case f18 = 0x4F
case f19 = 0x50
case f20 = 0x5A

// US-ANSI Keyboard Positions
// eg. These key codes are for the physical key (in any keyboard layout)
// at the location of the named key in the US-ANSI layout.
case a = 0x00
case b = 0x0B
case c = 0x08
case d = 0x02
case e = 0x0E
case f = 0x03
case g = 0x05
case h = 0x04
case i = 0x22
case j = 0x26
case k = 0x28
case l = 0x25
case m = 0x2E
case n = 0x2D
case o = 0x1F
case p = 0x23
case q = 0x0C
case r = 0x0F
case s = 0x01
case t = 0x11
case u = 0x20
case v = 0x09
case w = 0x0D
case x = 0x07
case y = 0x10
case z = 0x06

case zero = 0x1D
case one = 0x12
case two = 0x13
case three = 0x14
case four = 0x15
case five = 0x17
case six = 0x16
case seven = 0x1A
case eight = 0x1C
case nine = 0x19

case equals = 0x18
case minus = 0x1B
case semicolon = 0x29
case apostrophe = 0x27
case comma = 0x2B
case period = 0x2F
case forwardSlash = 0x2C
case backslash = 0x2A
case grave = 0x32
case leftBracket = 0x21
case rightBracket = 0x1E

case keypadDecimal = 0x41
case keypadMultiply = 0x43
case keypadPlus = 0x45
case keypadClear = 0x47
case keypadDivide = 0x4B
case keypadEnter = 0x4C
case keypadMinus = 0x4E
case keypadEquals = 0x51
case keypad0 = 0x52
case keypad1 = 0x53
case keypad2 = 0x54
case keypad3 = 0x55
case keypad4 = 0x56
case keypad5 = 0x57
case keypad6 = 0x58
case keypad7 = 0x59
case keypad8 = 0x5B
case keypad9 = 0x5C
}
func press(_ key: KeyCode, withModifiers modifiers: CGEventFlags = .init()) {
let down = CGEvent(keyboardEventSource: src, virtualKey: key.rawValue, keyDown: true)!
let up = CGEvent(keyboardEventSource: src, virtualKey: key.rawValue, keyDown: false)!
down.flags = modifiers
up.flags = modifiers
down.post(tap: .cghidEventTap)
up.post(tap: .cghidEventTap)
}
struct KeyPress {
let key: KeyCode
let modifiers: CGEventFlags
}
func press(_ key: KeyPress) {
press(key.key, withModifiers: key.modifiers)
}
/// An enum representing possible typing rates.
///
/// - allAtOnce: All the text should be typed at once.
/// - consistent: The text should be typed at a reasonable speed, but with no variance in delay.
/// - natural: The text should be typed so that it appears natural.
/// - customConsistent: The text should be typed at a consistent speed, specified by the associated value.
/// - customVarying: The text should be typed around a given speed, with 5 possible ranges of variation. Both the base speed and the maximum variance are specified by associated values.
public enum Rate {
/// All the text should be typed at once.
case allAtOnce
/// The text should be typed at a reasonable speed, but with no variance in delay.
case consistent
/// The text should be typed so that it appears natural.
case natural
/// The text should be typed at a specified consistent speed.
/// - µsecondDelay: The delay between each key typed.
case customConsistent(µsecondDelay: UInt32)
/// The text should be typed around a specified given speed, with specified variation. The base delay should be the average delay time, and the max variance is the maximum distance from the average to the fastest/slowest possible delay.
/// - µsecondBaseDelay: The base delay between each key typed.
/// - maxVariance: The delay between each key typed.
case customVarying(µsecondBaseDelay: UInt32, maxVariance: UInt32)
}

func type(_ text: [KeyPress], typing: Rate = .natural) {
for character in text {
press(character)
switch typing {
case .allAtOnce:
usleep(0001000)
case .consistent:
usleep(0100000)
case .natural:
var sleepTime = UInt32.random(in: 0...4)
sleepTime *= 10000
usleep(0080000 + sleepTime)
case .customConsistent(let µsecondDelay):
usleep(µsecondDelay)
case let .customVarying(µsecondBaseDelay, maxVariance):
var sleepTime = UInt32.random(in: 0...4)
let base = µsecondBaseDelay - maxVariance
sleepTime *= (maxVariance / 2)
let µsecondDelay = base + sleepTime
usleep(µsecondDelay)
}
}
}
let lowercaseCharMap: [Character: KeyCode] = [
"a": .a,
"b": .b,
"c": .c,
"d": .d,
"e": .e,
"f": .f,
"g": .g,
"h": .h,
"i": .i,
"j": .j,
"k": .k,
"l": .l,
"m": .m,
"n": .n,
"o": .o,
"p": .p,
"q": .q,
"r": .r,
"s": .s,
"t": .t,
"u": .u,
"v": .v,
"w": .w,
"x": .x,
"y": .y,
"z": .z,
"0": .zero,
"1": .one,
"2": .two,
"3": .three,
"4": .four,
"5": .five,
"6": .six,
"7": .seven,
"8": .eight,
"9": .nine,
"=": .equals,
"-": .minus,
";": .semicolon,
"'": .apostrophe,
",": .comma,
".": .period,
"/": .forwardSlash,
"\\": .backslash,
"`": .grave,
"[": .leftBracket,
"]": .rightBracket,
" ": .space
]
let uppercaseCharMap: [Character: KeyCode] = [
"A": .a,
"B": .b,
"C": .c,
"D": .d,
"E": .e,
"F": .f,
"G": .g,
"H": .h,
"I": .i,
"J": .j,
"K": .k,
"L": .l,
"M": .m,
"N": .n,
"O": .o,
"P": .p,
"Q": .q,
"R": .r,
"S": .s,
"T": .t,
"U": .u,
"V": .v,
"W": .w,
"X": .x,
"Y": .y,
"Z": .z,
")": .zero,
"!": .one,
"@": .two,
"#": .three,
"$": .four,
"%": .five,
"^": .six,
"&": .seven,
"*": .eight,
"(": .nine,
"+": .equals,
"_": .minus,
":": .semicolon,
"\"": .apostrophe,
"<": .comma,
">": .period,
"?": .forwardSlash,
"|": .backslash,
"~": .grave,
"{": .leftBracket,
"}": .rightBracket,
]

func type(_ text: String, typing: Rate = .natural) {
type(str_to_kparr(text), typing: typing)
}

type("Hello, there")

func str_to_kparr(_ str: String) -> [KeyPress] {
str.map { char -> KeyPress in
if let kc = lowercaseCharMap[char] {
return KeyPress(key: kc, modifiers: .init())
}
if let kc = uppercaseCharMap[char] {
return KeyPress(key: kc, modifiers: .maskShift)
}
return KeyPress(key: .three, modifiers: .maskShift)
}
}

func +(lhs: String, rhs: [KeyPress]) -> [KeyPress] {
return str_to_kparr(lhs) + rhs
}
func +(lhs: [KeyPress], rhs: String) -> [KeyPress] {
return lhs + str_to_kparr(rhs)
}

type("! General Kenobi" + [.init(key: .a, modifiers: .maskCommand), .init(key: .delete, modifiers: .init()), .init(key: .space, modifiers: [.maskCommand])])

extension KeyPress {
static let returnKey = KeyPress(key: .returnKey, modifiers: .init())
static let enter = Self.returnKey
static let tab = KeyPress(key: .tab, modifiers: .init())
static let space = KeyPress(key: .space, modifiers: .init())
static let delete = KeyPress(key: .delete, modifiers: .init())
static let escape = KeyPress(key: .escape, modifiers: .init())
static let command = KeyPress(key: .command, modifiers: .init())
static let shift = KeyPress(key: .shift, modifiers: .init())
static let capsLock = KeyPress(key: .capsLock, modifiers: .init())
static let option = KeyPress(key: .option, modifiers: .init())
static let control = KeyPress(key: .control, modifiers: .init())
static let rightShift = KeyPress(key: .rightShift, modifiers: .init())
static let rightOption = KeyPress(key: .rightOption, modifiers: .init())
static let rightControl = KeyPress(key: .rightControl, modifiers: .init())
static let leftArrow = KeyPress(key: .leftArrow, modifiers: .init())
static let rightArrow = KeyPress(key: .rightArrow, modifiers: .init())
static let downArrow = KeyPress(key: .downArrow, modifiers: .init())
static let upArrow = KeyPress(key: .upArrow, modifiers: .init())
static let volumeUp = KeyPress(key: .volumeUp, modifiers: .init())
static let volumeDown = KeyPress(key: .volumeDown, modifiers: .init())
static let mute = KeyPress(key: .mute, modifiers: .init())
static let help = KeyPress(key: .help, modifiers: .init())
static let home = KeyPress(key: .home, modifiers: .init())
static let pageUp = KeyPress(key: .pageUp, modifiers: .init())
static let forwardDelete = KeyPress(key: .forwardDelete, modifiers: .init())
static let end = KeyPress(key: .end, modifiers: .init())
static let pageDown = KeyPress(key: .pageDown, modifiers: .init())
static let function = KeyPress(key: .function, modifiers: .init())
static let f1 = KeyPress(key: .f1, modifiers: .init())
static let f2 = KeyPress(key: .f2, modifiers: .init())
static let f4 = KeyPress(key: .f4, modifiers: .init())
static let f5 = KeyPress(key: .f5, modifiers: .init())
static let f6 = KeyPress(key: .f6, modifiers: .init())
static let f7 = KeyPress(key: .f7, modifiers: .init())
static let f3 = KeyPress(key: .f3, modifiers: .init())
static let f8 = KeyPress(key: .f8, modifiers: .init())
static let f9 = KeyPress(key: .f9, modifiers: .init())
static let f10 = KeyPress(key: .f10, modifiers: .init())
static let f11 = KeyPress(key: .f11, modifiers: .init())
static let f12 = KeyPress(key: .f12, modifiers: .init())
static let f13 = KeyPress(key: .f13, modifiers: .init())
static let f14 = KeyPress(key: .f14, modifiers: .init())
static let f15 = KeyPress(key: .f15, modifiers: .init())
static let f16 = KeyPress(key: .f16, modifiers: .init())
static let f17 = KeyPress(key: .f17, modifiers: .init())
static let f18 = KeyPress(key: .f18, modifiers: .init())
static let f19 = KeyPress(key: .f19, modifiers: .init())
static let f20 = KeyPress(key: .f20, modifiers: .init())

// US-ANSI Keyboard Positions
// eg. These key codes are for the physical key (in any keyboard layout)
// at the location of the named key in the US-ANSI layout.
static let a = KeyPress(key: .a, modifiers: .init())
static let b = KeyPress(key: .b, modifiers: .init())
static let c = KeyPress(key: .c, modifiers: .init())
static let d = KeyPress(key: .d, modifiers: .init())
static let e = KeyPress(key: .e, modifiers: .init())
static let f = KeyPress(key: .f, modifiers: .init())
static let g = KeyPress(key: .g, modifiers: .init())
static let h = KeyPress(key: .h, modifiers: .init())
static let i = KeyPress(key: .i, modifiers: .init())
static let j = KeyPress(key: .j, modifiers: .init())
static let k = KeyPress(key: .k, modifiers: .init())
static let l = KeyPress(key: .l, modifiers: .init())
static let m = KeyPress(key: .m, modifiers: .init())
static let n = KeyPress(key: .n, modifiers: .init())
static let o = KeyPress(key: .o, modifiers: .init())
static let p = KeyPress(key: .p, modifiers: .init())
static let q = KeyPress(key: .q, modifiers: .init())
static let r = KeyPress(key: .r, modifiers: .init())
static let s = KeyPress(key: .s, modifiers: .init())
static let t = KeyPress(key: .t, modifiers: .init())
static let u = KeyPress(key: .u, modifiers: .init())
static let v = KeyPress(key: .v, modifiers: .init())
static let w = KeyPress(key: .w, modifiers: .init())
static let x = KeyPress(key: .x, modifiers: .init())
static let y = KeyPress(key: .y, modifiers: .init())
static let z = KeyPress(key: .z, modifiers: .init())
static let A = KeyPress(key: .a, modifiers: .maskShift)
static let B = KeyPress(key: .b, modifiers: .maskShift)
static let C = KeyPress(key: .c, modifiers: .maskShift)
static let D = KeyPress(key: .d, modifiers: .maskShift)
static let E = KeyPress(key: .e, modifiers: .maskShift)
static let F = KeyPress(key: .f, modifiers: .maskShift)
static let G = KeyPress(key: .g, modifiers: .maskShift)
static let H = KeyPress(key: .h, modifiers: .maskShift)
static let I = KeyPress(key: .i, modifiers: .maskShift)
static let J = KeyPress(key: .j, modifiers: .maskShift)
static let K = KeyPress(key: .k, modifiers: .maskShift)
static let L = KeyPress(key: .l, modifiers: .maskShift)
static let M = KeyPress(key: .m, modifiers: .maskShift)
static let N = KeyPress(key: .n, modifiers: .maskShift)
static let O = KeyPress(key: .o, modifiers: .maskShift)
static let P = KeyPress(key: .p, modifiers: .maskShift)
static let Q = KeyPress(key: .q, modifiers: .maskShift)
static let R = KeyPress(key: .r, modifiers: .maskShift)
static let S = KeyPress(key: .s, modifiers: .maskShift)
static let T = KeyPress(key: .t, modifiers: .maskShift)
static let U = KeyPress(key: .u, modifiers: .maskShift)
static let V = KeyPress(key: .v, modifiers: .maskShift)
static let W = KeyPress(key: .w, modifiers: .maskShift)
static let X = KeyPress(key: .x, modifiers: .maskShift)
static let Y = KeyPress(key: .y, modifiers: .maskShift)
static let Z = KeyPress(key: .z, modifiers: .maskShift)

static let zero = KeyPress(key: .zero, modifiers: .init())
static let one = KeyPress(key: .one, modifiers: .init())
static let two = KeyPress(key: .two, modifiers: .init())
static let three = KeyPress(key: .three, modifiers: .init())
static let four = KeyPress(key: .four, modifiers: .init())
static let five = KeyPress(key: .five, modifiers: .init())
static let six = KeyPress(key: .six, modifiers: .init())
static let seven = KeyPress(key: .seven, modifiers: .init())
static let eight = KeyPress(key: .eight, modifiers: .init())
static let nine = KeyPress(key: .nine, modifiers: .init())
static let leftParenthesis = KeyPress(key: .zero, modifiers: .maskShift)
static let exclamationPoint = KeyPress(key: .one, modifiers: .maskShift)
static let atSign = KeyPress(key: .two, modifiers: .maskShift)
static let numberSign = KeyPress(key: .three, modifiers: .maskShift)
static let dollarSign = KeyPress(key: .four, modifiers: .maskShift)
static let percent = KeyPress(key: .five, modifiers: .maskShift)
static let caret = KeyPress(key: .six, modifiers: .maskShift)
static let ampersand = KeyPress(key: .seven, modifiers: .maskShift)
static let asterisk = KeyPress(key: .eight, modifiers: .maskShift)
static let rightParenthesis = KeyPress(key: .nine, modifiers: .maskShift)

static let equals = KeyPress(key: .equals, modifiers: .init())
static let minus = KeyPress(key: .minus, modifiers: .init())
static let semicolon = KeyPress(key: .semicolon, modifiers: .init())
static let apostrophe = KeyPress(key: .apostrophe, modifiers: .init())
static let comma = KeyPress(key: .comma, modifiers: .init())
static let period = KeyPress(key: .period, modifiers: .init())
static let forwardSlash = KeyPress(key: .forwardSlash, modifiers: .init())
static let backslash = KeyPress(key: .backslash, modifiers: .init())
static let grave = KeyPress(key: .grave, modifiers: .init())
static let leftBracket = KeyPress(key: .leftBracket, modifiers: .init())
static let rightBracket = KeyPress(key: .rightBracket, modifiers: .init())
static let plus = KeyPress(key: .equals, modifiers: .maskShift)
static let underscore = KeyPress(key: .minus, modifiers: .maskShift)
static let colon = KeyPress(key: .semicolon, modifiers: .maskShift)
static let quotationMark = KeyPress(key: .apostrophe, modifiers: .maskShift)
static let lessThan = KeyPress(key: .comma, modifiers: .maskShift)
static let greaterThan = KeyPress(key: .period, modifiers: .maskShift)
static let questionMark = KeyPress(key: .forwardSlash, modifiers: .maskShift)
static let pipe = KeyPress(key: .backslash, modifiers: .maskShift)
static let tilde = KeyPress(key: .grave, modifiers: .maskShift)
static let leftBrace = KeyPress(key: .leftBracket, modifiers: .maskShift)
static let rightBrace = KeyPress(key: .rightBracket, modifiers: .maskShift)

static let keypadDecimal = KeyPress(key: .keypadDecimal, modifiers: .init())
static let keypadMultiply = KeyPress(key: .keypadMultiply, modifiers: .init())
static let keypadPlus = KeyPress(key: .keypadPlus, modifiers: .init())
static let keypadClear = KeyPress(key: .keypadClear, modifiers: .init())
static let keypadDivide = KeyPress(key: .keypadDivide, modifiers: .init())
static let keypadEnter = KeyPress(key: .keypadEnter, modifiers: .init())
static let keypadMinus = KeyPress(key: .keypadMinus, modifiers: .init())
static let keypadEquals = KeyPress(key: .keypadEquals, modifiers: .init())
static let keypad0 = KeyPress(key: .keypad0, modifiers: .init())
static let keypad1 = KeyPress(key: .keypad1, modifiers: .init())
static let keypad2 = KeyPress(key: .keypad2, modifiers: .init())
static let keypad3 = KeyPress(key: .keypad3, modifiers: .init())
static let keypad4 = KeyPress(key: .keypad4, modifiers: .init())
static let keypad5 = KeyPress(key: .keypad5, modifiers: .init())
static let keypad6 = KeyPress(key: .keypad6, modifiers: .init())
static let keypad7 = KeyPress(key: .keypad7, modifiers: .init())
static let keypad8 = KeyPress(key: .keypad8, modifiers: .init())
static let keypad9 = KeyPress(key: .keypad9, modifiers: .init())
}

extension KeyPress {
func withModifiers(_ modifiers: CGEventFlags) -> KeyPress {
return KeyPress(key: self.key, modifiers: modifiers)
}
func appendingModifiers(_ modifiers: CGEventFlags) -> KeyPress {
return self.withModifiers(self.modifiers.union(modifiers))
}
}

sleep(2)
type([KeyPress.space.withModifiers([.maskCommand])] + "My name is Inigo Montoya. You killed my father. Prepare to die" + [KeyPress.space.withModifiers([.maskCommand]), KeyPress.tab.withModifiers(.maskCommand), .command])

func +(lhs: String, rhs: KeyPress) -> [KeyPress] {
return lhs + [rhs]
}
func +(lhs: KeyPress, rhs: String) -> [KeyPress] {
return [lhs] + rhs
}

type([KeyPress.tab.withModifiers(.maskCommand), .command, KeyPress.space.withModifiers([.maskCommand])] + "")

A few notes:

  • I don't know if CGEventSource(stateID: .hidSystemState) is right, or if it should be a different state. Check the documentation.
  • Same for down.post(tap: .cghidEventTap) (docs)
  • Note that you need to post a keyDown: true event and a keydown: false event.
  • Obviously you can't type special characters like , so this code prints # instead
  • Otherwise, you can pass a string to type(_:typing:) and it will type it out.
  • Look through this code before you run it — it's designed to run with Spotlight bound to command-space (otherwise it might type somewhere important) and will type a # at the end in your second open application
  • The typing rate enum is from my Typer library, which uses AppleScript to type (another option for you), but does not support modifiers.

You could also use AppleScript to type, if you're only on a Mac. I believe this would do it:

tell app "System Events" to keystroke "v" using command down
tell app "System Events" to keystroke "v" using {shift down, command down}

Take a look at my Typer library to see how I compile and run AppleScript.

Parsing Keyboard Shortcuts from CGEvent

I ended up just converting CGEvent to NSEvent by initializing NSEvent with CGEvent, and get charactersIgnoringModifiers. Non-characters like escape and backspace won't work though. Here's the entire CGEventCallback.

func myCGEventCallback(proxy : CGEventTapProxy, type : CGEventType, event : CGEvent, refcon : UnsafeMutableRawPointer?) -> Unmanaged<CGEvent>? {
if type == .keyDown {
let flags = event.flags
var msg = ""
if flags.contains(.maskAlphaShift) {
msg+="caps+"
}
if flags.contains(.maskShift) {
msg+="shift+"
}
if flags.contains(.maskControl) {
msg+="control+"
}
if flags.contains(.maskAlternate) {
msg+="option+"
}
if flags.contains(.maskCommand) {
msg += "command+"
}
if flags.contains(.maskSecondaryFn) {
msg += "function+"
}
if let other = NSEvent(cgEvent: event), let chars = other.charactersIgnoringModifiers {
msg += chars
debugPrint(msg)
}
}
return Unmanaged.passRetained(event)
}

CGEventPost - possible bug when simulating keyboard events?

I have found a reliable way to post modified keyboard events - it does not follow the example in Apple's documentation (which doesn't work) but seems to make sense, and most importantly, WORKS.

Rather than sending 'shift key down' and 'shift key up' messages (as instructed in the docs), you need to set a modifier flag on the keypress. Here's how to output an uppercase Z.

CGEventRef event1, event2;
event1 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, true);//'z' keydown event
CGEventSetFlags(event1, kCGEventFlagMaskShift);//set shift key down for above event
CGEventPost(kCGSessionEventTap, event1);//post event

I'm then releasing the 'z' key for completeness (also setting the shift-flag on, though not sure if this is correct).

event2 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, false);
CGEventSetFlags(event2, kCGEventFlagMaskShift);
CGEventPost(kCGSessionEventTap, event2);

Finally (and bizarrely) you DO need to send the 'key up' event for the shift key:

  e5 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)56, false);
CGEventPost(kCGSessionEventTap, e5);

Don't forget to release your events once you're done with them.

I hope this is useful to someone - it took me a long time to get this to work.



Related Topics



Leave a reply



Submit