【问题标题】:How do you use CGEventTapCreate in Swift?你如何在 Swift 中使用 CGEventTapCreate?
【发布时间】:2015-10-31 16:21:58
【问题描述】:

有没有人设法让这个函数在 Swift 中工作?

这是去年的参考 SO 帖子:Using CGEventTapCreate Trouble with parameters in Swift

苹果文档: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>!)>

这是我编写代码块的方式:

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

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

我得到了一个有效的CFMachPortRef,但是在运行时我在第一个事件上得到了访问冲突异常。在当前的发布状态下,“似乎”我已经接近了一个快速的解决方案。

使用 Xcode 6.4 版

【问题讨论】:

    标签: swift macos core-graphics cgeventtap


    【解决方案1】:

    CGEventTapCreate()callback参数是一个C函数指针, 在 Swift 1.x 中,不能使用 Swift 函数参数调用它。

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

    作为一个例子,这里是一个完整的翻译 Receiving, Filtering, and Modifying Key Presses and Releases 斯威夫特:

    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()
    

    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()
    

    【讨论】:

    • 天哪,这比 C/Objective-C 版本更丑……叹息……至少它是可能的
    • 感谢您为 Swift 2 更新它!
    • 关于如何为 refcon 参数传递值的任何想法?你的例子是传递 nil,我没有任何运气试图找到一种方法来传递一个 Swift 类型的值,然后我可以在回调函数中解包并调用一个方法(例如委托对象) .
    • 可能值得指出(因为我花了几分钟才弄清楚问题出在哪里)函数回调必须是实际的 func,而不是类方法。
    • @JamesWaldrop:是的,回调不能是实例或类方法。我 can 是一个闭包(不捕获上下文)。我稍后会更新答案以使其更清楚。感谢您的反馈!
    猜你喜欢
    • 1970-01-01
    • 2020-09-18
    • 2014-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-04
    相关资源
    最近更新 更多