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 documentation 是 leftMouseUp
事件。
在 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
.
时使用的
我正在尝试使用 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 documentation 是 leftMouseUp
事件。
在 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
.