Post real modifier key presses around typed keys in hands
This commit is contained in:
@@ -12,10 +12,20 @@ let keyCodes: [String: CGKeyCode] = [
|
||||
"forwarddelete": 117, "end": 119, "pagedown": 121, "left": 123, "right": 124, "down": 125, "up": 126,
|
||||
]
|
||||
|
||||
let modifierFlags: [String: CGEventFlags] = [
|
||||
"cmd": .maskCommand, "command": .maskCommand, "shift": .maskShift, "alt": .maskAlternate,
|
||||
"opt": .maskAlternate, "option": .maskAlternate, "ctrl": .maskControl, "control": .maskControl,
|
||||
"fn": .maskSecondaryFn,
|
||||
struct Modifier: Equatable {
|
||||
let code: CGKeyCode
|
||||
let flag: CGEventFlags
|
||||
}
|
||||
|
||||
// Each left-hand key's flag also carries its device-dependent bit (NX_DEVICEL*KEYMASK), as a real keyboard's does.
|
||||
let shiftKey = Modifier(code: 56, flag: CGEventFlags(rawValue: CGEventFlags.maskShift.rawValue | 0x2))
|
||||
let commandKey = Modifier(code: 55, flag: CGEventFlags(rawValue: CGEventFlags.maskCommand.rawValue | 0x8))
|
||||
let optionKey = Modifier(code: 58, flag: CGEventFlags(rawValue: CGEventFlags.maskAlternate.rawValue | 0x20))
|
||||
let controlKey = Modifier(code: 59, flag: CGEventFlags(rawValue: CGEventFlags.maskControl.rawValue | 0x1))
|
||||
|
||||
let modifiers: [String: Modifier] = [
|
||||
"cmd": commandKey, "command": commandKey, "shift": shiftKey, "alt": optionKey, "opt": optionKey,
|
||||
"option": optionKey, "ctrl": controlKey, "control": controlKey, "fn": Modifier(code: 63, flag: .maskSecondaryFn),
|
||||
]
|
||||
|
||||
func pause(_ ms: Int) async throws {
|
||||
@@ -29,15 +39,15 @@ let shifted: [Character: String] = [
|
||||
|
||||
let named: [Character: String] = [" ": "space", "\n": "return", "\t": "tab"]
|
||||
|
||||
func keystroke(_ ch: Character) -> (CGKeyCode, CGEventFlags)? {
|
||||
func keystroke(_ ch: Character) -> (CGKeyCode, [Modifier])? {
|
||||
if let name = named[ch], let c = keyCodes[name] {
|
||||
return (c, [])
|
||||
}
|
||||
if let base = shifted[ch], let c = keyCodes[base] {
|
||||
return (c, .maskShift)
|
||||
return (c, [shiftKey])
|
||||
}
|
||||
if ch.isUppercase, let c = keyCodes[ch.lowercased()] {
|
||||
return (c, .maskShift)
|
||||
return (c, [shiftKey])
|
||||
}
|
||||
if let c = keyCodes[String(ch)] {
|
||||
return (c, [])
|
||||
@@ -45,6 +55,30 @@ func keystroke(_ ch: Character) -> (CGKeyCode, CGEventFlags)? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func postModifier(_ m: Modifier, down: Bool, _ flags: CGEventFlags) {
|
||||
let e = CGEvent(keyboardEventSource: nil, virtualKey: m.code, keyDown: down)
|
||||
e?.type = .flagsChanged
|
||||
e?.flags = flags
|
||||
post(e)
|
||||
}
|
||||
|
||||
func press(_ code: CGKeyCode, _ mods: [Modifier]) {
|
||||
var flags: CGEventFlags = []
|
||||
for m in mods {
|
||||
flags.insert(m.flag)
|
||||
postModifier(m, down: true, flags)
|
||||
}
|
||||
for down in [true, false] {
|
||||
let e = CGEvent(keyboardEventSource: nil, virtualKey: code, keyDown: down)
|
||||
e?.flags = flags
|
||||
post(e)
|
||||
}
|
||||
for m in mods.reversed() {
|
||||
flags.remove(m.flag)
|
||||
postModifier(m, down: false, flags)
|
||||
}
|
||||
}
|
||||
|
||||
func lockInput() async throws -> Int32 {
|
||||
let fd = open(NSTemporaryDirectory() + "byre-hands-input.lock", O_CREAT | O_RDWR, 0o600)
|
||||
if fd < 0 {
|
||||
@@ -132,13 +166,12 @@ func scroll(_ p: CGPoint, dx: Int32, dy: Int32) {
|
||||
|
||||
func type(_ s: String) async throws {
|
||||
for ch in s {
|
||||
if let (code, mods) = keystroke(ch) {
|
||||
press(code, mods)
|
||||
try await pause(15)
|
||||
continue
|
||||
}
|
||||
for down in [true, false] {
|
||||
if let (code, flags) = keystroke(ch) {
|
||||
let e = CGEvent(keyboardEventSource: nil, virtualKey: code, keyDown: down)
|
||||
e?.flags = flags
|
||||
post(e)
|
||||
continue
|
||||
}
|
||||
let units = Array(String(ch).utf16)
|
||||
let e = CGEvent(keyboardEventSource: nil, virtualKey: 0, keyDown: down)
|
||||
e?.keyboardSetUnicodeString(stringLength: units.count, unicodeString: units)
|
||||
@@ -149,11 +182,13 @@ func type(_ s: String) async throws {
|
||||
}
|
||||
|
||||
func key(_ combo: String) throws {
|
||||
var flags: CGEventFlags = []
|
||||
var mods: [Modifier] = []
|
||||
var code: CGKeyCode?
|
||||
for part in combo.lowercased().split(separator: "+").map(String.init) {
|
||||
if let f = modifierFlags[part] {
|
||||
flags.insert(f)
|
||||
if let m = modifiers[part] {
|
||||
if !mods.contains(m) {
|
||||
mods.append(m)
|
||||
}
|
||||
continue
|
||||
}
|
||||
guard let c = keyCodes[part] else {
|
||||
@@ -164,9 +199,5 @@ func key(_ combo: String) throws {
|
||||
guard let code else {
|
||||
throw ToolError("no key in \(combo.debugDescription)")
|
||||
}
|
||||
for down in [true, false] {
|
||||
let e = CGEvent(keyboardEventSource: nil, virtualKey: code, keyDown: down)
|
||||
e?.flags = flags
|
||||
post(e)
|
||||
}
|
||||
press(code, mods)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user