你如何在Swift中使用CGEventTapCreate?

chr*_*isp 8 macos core-graphics cgeventtap swift

有没有人设法让这个功能在Swift中工作?

这是去年的参考SO帖子:使用CGEventTapCreate在Swift中使用参数麻烦

Apple Doc:https://developer.apple.com/library/prerelease/mac/documentation/Carbon/Reference/QuartzEventServicesRef/index.html#//apple_ref/c/func/CGEventTapCreate

以下是CGEventTapCallBack的定义方式:

typealias CGEventTapCallBack = CFunctionPointer<((CGEventTapProxy, CGEventType, CGEvent!, UnsafeMutablePointer<Void>) -> Unmanaged<CGEvent>!)>
Run Code Online (Sandbox Code Playgroud)

以下是我编写块的方法:

let eventTapCallBackBlock : @objc_block
(CGEventTapProxy, CGEventType, CGEventRef, UnsafeMutablePointer<Void>) -> CGEventRef =
{ (eventTapProxy: CGEventTapProxy, eventType: CGEventType, event: CGEventRef, refcon: UnsafeMutablePointer<Void>) in
  return event
}
Run Code Online (Sandbox Code Playgroud)

然后我用回调参数调用了CGEventTapCreate unsafeBitCast(eventTapCallBackBlock, CGEventTapCallBack.self)

我得到了有效的CFMachPortRef回复,但在运行时我在第一个事件上遇到了访问冲突异常.它似乎"似乎"我在当前发布状态下接近迅速解决方案.

使用Xcode版本6.4

Mar*_*n R 23

所述callback的参数CGEventTapCreate()是一个C函数指针,并在夫特1.x中,不可能用夫特函数参数调用它.

但是,在Swift 2(Xcode 7)中,可以使用闭包或全局函数来调用带有函数指针参数的C函数(具有闭包不能捕获其任何本地上下文的限制).

例如,以下是接收,过滤和修改密钥按和发布 到Swift 的完整翻译 :

import Foundation

func myCGEventCallback(proxy : CGEventTapProxy, type : CGEventType, event : CGEvent, refcon : UnsafeMutablePointer<Void>) -> Unmanaged<CGEvent>? {

    if [.KeyDown , .KeyUp].contains(type) {
        var keyCode = CGEventGetIntegerValueField(event, .KeyboardEventKeycode)
        if keyCode == 0 {
            keyCode = 6
        } else if keyCode == 6 {
            keyCode = 0
        }
        CGEventSetIntegerValueField(event, .KeyboardEventKeycode, keyCode)
    }
    return Unmanaged.passRetained(event)
}

let eventMask = (1 << CGEventType.KeyDown.rawValue) | (1 << CGEventType.KeyUp.rawValue)
guard let eventTap = CGEventTapCreate(.CGSessionEventTap,
    .HeadInsertEventTap,
    .Default,
    CGEventMask(eventMask),
    myCGEventCallback,
    nil) else {
        print("failed to create event tap")
        exit(1)
}

let runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0)
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes)
CGEventTapEnable(eventTap, true)
CFRunLoopRun()
Run Code Online (Sandbox Code Playgroud)

Swift 3更新:

import Foundation

func myCGEventCallback(proxy: CGEventTapProxy, type: CGEventType, event: CGEvent, refcon: UnsafeMutableRawPointer?) -> Unmanaged<CGEvent>? {

    if [.keyDown , .keyUp].contains(type) {
        var keyCode = event.getIntegerValueField(.keyboardEventKeycode)
        if keyCode == 0 {
            keyCode = 6
        } else if keyCode == 6 {
            keyCode = 0
        }
        event.setIntegerValueField(.keyboardEventKeycode, value: keyCode)
    }
    return Unmanaged.passRetained(event)
}

let eventMask = (1 << CGEventType.keyDown.rawValue) | (1 << CGEventType.keyUp.rawValue)
guard let eventTap = CGEvent.tapCreate(tap: .cgSessionEventTap,
                                      place: .headInsertEventTap,
                                      options: .defaultTap,
                                      eventsOfInterest: CGEventMask(eventMask),
                                      callback: myCGEventCallback,
                                      userInfo: nil) else {
                                        print("failed to create event tap")
                                        exit(1)
}

let runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0)
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, .commonModes)
CGEvent.tapEnable(tap: eventTap, enable: true)
CFRunLoopRun()
Run Code Online (Sandbox Code Playgroud)

  • 神圣哎呀,这比C/Objective-C版本更难......叹息......至少它是*可能的* (3认同)
  • 可能值得指出(因为我花了几分钟才弄清楚问题所在)函数回调必须是实际的 func,而不是类方法。 (3认同)