Как вы используете CGEventTapCreate в Swift?

Кому-нибудь удалось заставить эту функцию работать в Swift?

Вот ссылка на сообщение SO за прошлый год: Использование CGEventTapCreate Проблемы с параметрами в Swift

Документ Apple: https://developer.apple.com/library/prerelease/mac/documentation/Carbon/Reference/QuartzEventServicesRef/index.html

Вот как определяется 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
}

Затем я вызвал CGEventTapCreate с параметром обратного вызова, как unsafeBitCast(eventTapCallBackBlock, CGEventTapCallBack.self)

Я получаю действительный CFMachPortRef назад, но во время выполнения я получаю исключение нарушения доступа при первом событии. Казалось бы, я близок к быстрому решению в его текущем состоянии выпуска.

Используя версию XCode 6.4

1 ответ

Решение

callback параметр CGEventTapCreate() является указателем на функцию C, и в Swift 1.x его невозможно вызвать с аргументом функции Swift.

Однако в 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()

Обновление для 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()
Другие вопросы по тегам