有一个swift
名为的小脚本skey.swift
,即使它们不处于活动状态(例如它们在后台),也会通过它们的PID
(进程 ID)将按键发送到进程中。
import Foundation
if CommandLine.argc < 2 {
print("Error", CommandLine.arguments[0], " No arguments are passed.")
exit(1)
}
let src = CGEventSource(stateID: CGEventSourceStateID.hidSystemState)
let key_d = CGEvent(keyboardEventSource: src, virtualKey: 0x12, keyDown: true) // key "1" press
let key_u = CGEvent(keyboardEventSource: src, virtualKey: 0x12, keyDown: false) // key "1" release
for i in 1 ..< Int(CommandLine.argc) {
if let pid = pid_t(CommandLine.arguments[i]) {
print("arg:", pid)
key_d?.postToPid( pid )
key_u?.postToPid( pid )
}
}
测试用例:
- 让我们说运行
TextEdit.app
和Notes.app
- 从
ps axu | grep -E 'MacOS/(TextEdit|Notes)'
- 示例中记下他们的 PID
me 87756 3,3 0,3 4716112 97228 ?? S 11:54 0:28.01 /Applications/Notes.app/Contents/MacOS/Notes
me 83077 0,0 0,1 4609916 49312 ?? S 8:00 0:04.58 /Applications/TextEdit.app/Contents/MacOS/TextEdit
- 记住PID:
87756 83077
- 从终端运行
swift skey.swift 87756 83077
- 在两个应用程序窗口(TextEdit 和 Notes)中出现“1”,即使它们在后台(终端在活动应用程序中)。
- 所以,脚本按预期工作!
问题
- 编译脚本
swiftc skey.swift
- 使用相同的参数运行编译后的二进制文件,例如
./skey 87756 83077
- 只有第一个 PID 获得“1”(在这种情况下,仅在注释中)
为什么运行脚本之间有这样的区别:
swift skey.swift 87756 83077
- 或者
./skey 87756 83077
编辑
刚刚 在这个源代码中找到了 一个评论
// Maybe this is a dumb way to solve this, but let's sleep for just a moment so they events don’t arrive out of order.
所以,开始尝试usleep
自己。50µs 无济于事,但使用2x1000µs HELPED,最后编译和解释版本的工作方式完全相同。
所以,这行得通。
for i in 1 ..< Int(CommandLine.argc) {
if let pid = pid_t(CommandLine.arguments[i]) {
print("arg:", pid)
key_d?.postToPid( pid )
usleep(1000)
key_u?.postToPid( pid )
usleep(1000)
}
}
但是,作为谈论“愚蠢方式”的评论 -很高兴知道正确的方式,因为代码应该在任何 macOS 中工作......
编辑 2
根据@willeke的评论,我尝试了以下
for i in 1 ..< Int(CommandLine.argc) {
if let pid = pid_t(CommandLine.arguments[i]) {
print("arg:", pid)
key_d?.postToPid( pid )
key_u?.postToPid( pid )
}
}
sleep(1) // give enough time to send events before exit
它按预期工作。看来事件发送过程一定存在,否则他的所有事件都被丢弃。