【问题标题】:Simulate keypress using Swift使用 Swift 模拟按键
【发布时间】:2014-12-15 12:40:02
【问题描述】:

我正在寻找一种在 OSX 中模拟击键的方法。我使用 Objective-C 找到了另一个解决方案 (Simulate keypress for system wide hotkeys),但我需要使用 Swift 来完成。如何适应 CGEventCreateKeyboardEvent?

【问题讨论】:

标签: cocoa swift keypress keystroke


【解决方案1】:

使用 Swift 3

let src = CGEventSource(stateID: CGEventSourceStateID.hidSystemState)

let cmdd = CGEvent(keyboardEventSource: src, virtualKey: 0x38, keyDown: true)
let cmdu = CGEvent(keyboardEventSource: src, virtualKey: 0x38, keyDown: false)
let spcd = CGEvent(keyboardEventSource: src, virtualKey: 0x31, keyDown: true)
let spcu = CGEvent(keyboardEventSource: src, virtualKey: 0x31, keyDown: false)

spcd?.flags = CGEventFlags.maskCommand;

let loc = CGEventTapLocation.cghidEventTap

cmdd?.post(tap: loc)
spcd?.post(tap: loc)
spcu?.post(tap: loc)
cmdu?.post(tap: loc)

【讨论】:

  • 就我而言(Swift 5),我不得不使用:let src = CGEventSource(stateID: .privateState)
【解决方案2】:

链接答案上的代码很容易转换为 Swift 代码,但是在此过程中您需要注意一些问题:

CGEventSourceCreate 采用CGEventSourceStateID,这是UInt32 的类型别名,但诸如kCGEventSourceStateHIDSystemState 之类的常量被定义为Int,因此您必须将它们转换为CGEventSourceStateID(kCGEventSourceStateHIDSystemState)CGEventFlags 也是如此。

CGEventSourceCreateCGEventCreateKeyboardEvent 返回一个 Unmanaged<CGEventSource>(或 Unmanaged<CGEvent>)。 Core Graphics 的自动生成的 Swift API 不知道返回的对象是否需要由您释放,因此您需要检查这些调用的 API 文档,然后在返回时相应地使用 takeRetainedValue()takeUnretainedValue()值,将它们转换为您要使用的基础类型。

最后,它们返回隐式解包的可选项,因此您需要决定是要检查 nil 还是仅仅忍受运行时爆炸的兴奋(如果它们返回一个)。

鉴于在演示按 Cmd-Space 到 Swift 的答案中将 Objective-C 转换为非常简单,我只是尝试将其粘贴到一个草稿应用程序中并且效果很好:

(虽然我还没有检查 API 文档来确定保留是否正确)

let src = CGEventSourceCreate(CGEventSourceStateID(kCGEventSourceStateHIDSystemState)).takeRetainedValue()

let cmdd = CGEventCreateKeyboardEvent(src, 0x38, true).takeRetainedValue()
let cmdu = CGEventCreateKeyboardEvent(src, 0x38, false).takeRetainedValue()
let spcd = CGEventCreateKeyboardEvent(src, 0x31, true).takeRetainedValue()
let spcu = CGEventCreateKeyboardEvent(src, 0x31, false).takeRetainedValue()

CGEventSetFlags(spcd, CGEventFlags(kCGEventFlagMaskCommand));
CGEventSetFlags(spcd, CGEventFlags(kCGEventFlagMaskCommand));

let loc = CGEventTapLocation(kCGHIDEventTap)

CGEventPost(loc, cmdd)
CGEventPost(loc, spcd)
CGEventPost(loc, spcu)
CGEventPost(loc, cmdu)

【讨论】:

  • @Airspeed:很好的答案。虽然我们可以将“十六进制键值”变成更易读的东西,比如 KVK_ANSI_A
  • 我已翻译,我将其发布在答案中
  • 嗨,如果你能帮我解决这个问题stackoverflow.com/questions/68902254/…
【解决方案3】:

斯威夫特 3

对我来说,像 0x124 这样的十六进制键值不起作用,但简单的 UInt 124 就可以了!

一个不错的键码集合可以是found here! 这个复制粘贴代码 sn-p 模拟右箭头键。更改您要模拟的任何内容的密钥编号:

    // Simulate Right Arrow keypress
    let rightArrowKeyCode: UInt16 = 124

    let keyDownEvent = CGEvent(keyboardEventSource: nil, virtualKey: rightArrowKeyCode, keyDown: true)
    keyDownEvent?.flags = CGEventFlags.maskCommand
    keyDownEvent?.post(tap: CGEventTapLocation.cghidEventTap)

    let keyUpEvent = CGEvent(keyboardEventSource: nil, virtualKey: rightArrowKeyCode, keyDown: false)
    keyUpEvent?.flags = CGEventFlags.maskCommand
    keyUpEvent?.post(tap: CGEventTapLocation.cghidEventTap)

更新: 对于 macOS Mojave 及更高版本,您应该允许您的应用在系统偏好设置 > 安全和隐私 > 辅助功能中控制您的计算机

【讨论】:

  • Carbon.HIToolbox中有一个键符号列表,格式为kVK_...例如kVK_ANSI_MM键。
  • 我们必须如何与发布此类信息的其他开发人员建立联系,这真是令人惊讶。苹果应该解雇管理文档部门的撒旦,并雇佣一些有能力的人。他们的文档很臭。
  • 它确实可以在 Mojave 下工作,但您需要允许您的应用在系统偏好设置 > 安全和隐私 > 可访问性中控制您的计算机
  • 这似乎不适用于 macOS Mojave。任何人都可以确认吗?之前它工作得非常好..
【解决方案4】:

我为 Swift 4 项目制作了这个,不要忘记应用沙盒不允许应用发送这样的击键,因此需要将其关闭。这意味着您的应用将被 AppStore 禁止。

import Foundation

class FakeKey {
    static func send(_ keyCode: CGKeyCode, useCommandFlag: Bool) {
        let sourceRef = CGEventSource(stateID: .combinedSessionState)

        if sourceRef == nil {
            NSLog("FakeKey: No event source")
            return
        }

        let keyDownEvent = CGEvent(keyboardEventSource: sourceRef,
                                   virtualKey: keyCode,
                                   keyDown: true)
        if useCommandFlag {
            keyDownEvent?.flags = .maskCommand
        }

        let keyUpEvent = CGEvent(keyboardEventSource: sourceRef,
                                 virtualKey: keyCode,
                                 keyDown: false)

        keyDownEvent?.post(tap: .cghidEventTap)
        keyUpEvent?.post(tap: .cghidEventTap)
    }
}

【讨论】:

  • 很遗憾我没有安装 Mojave,请将您找到的修复程序发布为新答案。您可能想向 Apple 提交错误。 (此代码基本上使用与其他答案相同的 API。)
  • 这个问题似乎与“可访问性”API 有关。您需要授予它权限才能使用它。在我的情况下,它“默默地”失败了,因为我拒绝了一次权限提示。
  • 这是“好”消息,没问题,这只是 Apple 设计的方式。
  • 我为 cutbox/CutBox gist.github.com/jasonm23/3d2c4040a5f879a945264d1bd0a630ad987654321@不断更新的版本
【解决方案5】:

刺激快捷键/热键的答案组合。斯威夫特 5.1

let source = CGEventSource(stateID: CGEventSourceStateID.hidSystemState)

let cmdKey: UInt16 = 0x38
let numberThreeKey: UInt16 = 0x14

let cmdDown = CGEvent(keyboardEventSource: source, virtualKey: cmdKey, keyDown: true)
let cmdUp = CGEvent(keyboardEventSource: source, virtualKey: cmdKey, keyDown: false)
let keyThreeDown = CGEvent(keyboardEventSource: source, virtualKey: numberThreeKey, keyDown: true)
let keyThreeUp = CGEvent(keyboardEventSource: source, virtualKey: numberThreeKey, keyDown: false)


fileprivate func testShortcut() {

let loc = CGEventTapLocation.cghidEventTap

cmdDown?.flags = CGEventFlags.maskCommand
cmdUp?.flags = CGEventFlags.maskCommand
keyThreeDown?.flags = CGEventFlags.maskCommand
keyThreeUp?.flags = CGEVentFlags.maskCommand

cmdDown?.post(tap: loc)
keyThreeDown?.post(tap: loc)
cmdUp?.post(tap: loc)
keyThreeUp?.post(tap: loc)

}

手动编写可能包含错误。

【讨论】:

  • 嗨 Sam,这与我的 Swift 4 版本非常接近,只需将它们粘贴在一起合并差异就很有意义。很高兴为你做这件事,但我想我会给你机会先做编辑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-18
  • 2015-11-26
  • 2010-10-16
  • 1970-01-01
  • 2010-11-30
  • 2023-03-22
相关资源
最近更新 更多