MacOS Quartz Event Tap 监听错误事件

MacOS Quartz Event Tap listening to wrong events

我正在尝试使用 CGEvent.tapCreate(tap:place:options:eventsOfInterest:callback:userInfo:) 方法拦截鼠标移动事件,如下所示:

let cfMachPort = CGEvent.tapCreate(tap: CGEventTapLocation.cghidEventTap, 
                                   place: CGEventTapPlacement.headInsertEventTap, 
                                   options: CGEventTapOptions.defaultTap, 
                                   eventsOfInterest:CGEventMask(CGEventType.mouseMoved.rawValue), 
                                   callback: {(eventTapProxy, eventType, event, mutablePointer) -> Unmanaged<CGEvent>? in event
    print(event.type.rawValue)   //Breakpoint
    return nil
}, userInfo: nil)

let runloopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, cfMachPort!, 0)

let runLoop = RunLoop.current
let cfRunLoop = runLoop.getCFRunLoop()
CFRunLoopAddSource(cfRunLoop, runloopSource, CFRunLoopMode.defaultMode)

我作为事件类型传递 eventsOfInterest mouseMoved 原始值为 5 的事件 as seen in the documentation. But for some reason my print() is not executed unless I click with the mouse. Inspecting the send mouse event in the debugger gives me a raw value of 2, which according to the documentationleftMouseUp 事件。

CGEvent.tapCreate(tap:place:options:eventsOfInterest:callback:userInfo:)documentation 中说:

事件点击接收按键弹起和按键按下事件[...]

所以似乎该方法通常会忽略 mouseMoved 事件?!但是我应该如何收听 mouseMoved 事件呢?我试图防止我的光标(自定义光标)被替换(例如,当我将鼠标悬停在屏幕底部的应用程序停靠栏上时)。

这是一种监听 mouseMove 全局事件的方法(使用 Xcode 11.2+、macOS 10.15 进行测试)

// ... say in AppDelegate

var globalObserver: Any!
var localObserver: Any!

func applicationDidFinishLaunching(_ aNotification: Notification) {

    globalObserver = NSEvent.addGlobalMonitorForEvents(matching: .mouseMoved) { event in
        let location = event.locationInWindow
        print("in background: {\(location.x), \(location.y)}")
    }

    localObserver = NSEvent.addLocalMonitorForEvents(matching: .mouseMoved) { event in
        let location = event.locationInWindow
        print("active: {\(location.x), \(location.y)}")
        return event
    }
    ...

您需要对用于创建 CGEventMask 参数的 CGEventType 值进行位移。在Objective-C中,有一个宏可以做到这一点:CGEventMaskBit

来自 CGEventMask 文档:

to form the bit mask, use the CGEventMaskBit macro to convert each constant into an event mask and then OR the individual masks together

我不知道 swift 中的等效机制;但宏本身看起来像这样:

*/ #define CGEventMaskBit(eventType) ((CGEventMask)1 << (eventType))

在你的例子中,手动移动参数就足够了;例如

eventsOfInterest:CGEventMask(1 << CGEventType.mouseMoved.rawValue),

我要指出问题中给出的代码示例有点危险;因为它创建一个默认事件点击,然后删除事件而不是允许它们被处理。这搞乱了鼠标点击处理,并且使用鼠标实际终止应用程序很棘手。任何人 运行 该示例都可以将事件点击类型设置为 CGEventTapOptions.listenOnly 以防止出现这种情况。

您的代码中还有另一处不正确的地方,尽管您可能很幸运并且它通常不会导致问题。

CFRunLoopAddSource 的模式参数所述:“使用常量 kCFRunLoopCommonModes 将源添加到由所有常见模式监视的对象集中。”

第三个参数应该改为 CFRunLoopMode.commonModes。 你所拥有的,CFRunLoopMode.defaultMode aka kCFRunLoopDefaultMode,是在调用 CFRunLoopRun.

时使用的