【问题标题】:MacOS Quartz Event Tap listening to wrong eventsMacOS Quartz Event Tap 监听错误事件
【发布时间】:2020-02-05 16:07:03
【问题描述】:

我正在尝试使用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。但由于某种原因,除非我用鼠标单击,否则我的 print() 不会执行。在调试器中检查发送鼠标事件给我一个原始值 2,根据the documentationleftMouseUp 事件。

documentation for CGEvent.tapCreate(tap:place:options:eventsOfInterest:callback:userInfo:) 中写着:

事件点击接收按键向上和按键向下事件 [...]

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

【问题讨论】:

  • 不,很遗憾没有。除了上述 API 描述了一个遗留 API 之外,我还需要捕获应用程序范围之外的事件。所以我需要一个更底层的方法来捕捉鼠标事件。
  • 您在哪个版本的 macOS 上测试?你的应用是沙盒的吗?无论哪种方式,您的应用都可能需要被授予用户控制 UI 的权限。
  • @KenThomases 我在 10.15.2 上进行测试,并且该应用程序没有被沙盒化。我给予了可访问性和屏幕录制津贴。
  • @KenThomases 给出的代码示例实际上只会在未授予访问权限时崩溃;因为cfMachPort 将为零
  • @TheNextman 我在 Swift 上找不到任何名为 CGEventMaskBit 的东西。我正在使用CGEventMask(CGEventType.mouseMoved.rawValue) 来获取 UInt64。根据文档,这是正确的。我也找不到像kCGEventMaskForAllEvents 这样的kCGEventMask*developer.apple.com/documentation/coregraphics/cgeventmask

标签: swift macos core-graphics


【解决方案1】:

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

来自CGEventMask 文档:

要形成位掩码,请使用 CGEventMaskBit 宏将每个常量转换为事件掩码,然后将各个掩码 OR 在一起

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

*/ #define CGEventMaskBit(eventType) ((CGEventMask)1 &lt;&lt; (eventType))

在您的示例中,只需手动移动参数就足够了;例如

eventsOfInterest:CGEventMask(1 &lt;&lt; CGEventType.mouseMoved.rawValue),

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

【讨论】:

  • 就像我上面提到的那样,当我将鼠标悬停在扩展坞上时,我试图防止光标发生变化。我现在尝试为我的码头的 pid 创建一个事件点击。但是当我使用CGEventTapOptions.defaultTap 并返回 nil 时,我的事件似乎仍然被转发。我已经尝试了 2 天来防止光标在扩展坞上更新,但我不知道该怎么做。你有什么想法吗?
  • 我不确定,但我认为阻止事件不是解决方案(我也看到了你的另一个问题,但不确定它是否真的是重复的)。如果是我,我会尝试使用 CoreGraphics 私有 API 来完全隐藏系统光标并自己绘制:github.com/jamesarosen/iTerm2/blob/master/CGSInternal/…
  • 我不知道该怎么做?如果我隐藏系统光标,我怎么能画出我的?只是使用视图并将其放置在同一位置?
  • 这个标题有一个隐藏光标的功能,我不确定它是否有效。 “我怎么画我的?只是使用一个视图并将其放置在同一位置?” - 差不多。我以前从未尝试过这样的事情,只是告诉你如果我是你我会尝试什么。祝你好运!
  • 但是如果我使用例如一个 NSWindow 来作为我的内容,我需要停用 x- 按钮来获得一个窗口。据我所知,这违反了苹果用户界面指南。关于如何遵守的任何想法?我正在尝试开发类似于this 的东西。
【解决方案2】:

这是一种监听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
    }
    ...

【讨论】:

  • 遗憾的是 addGlobalMonitorForEvents(matching:handler:) 不允许更改事件。此外,当我将鼠标悬停在可用于阻止光标更新的停靠栏上时,我没有收到任何有趣的事件。所以这个解决方案不适用于我的情况。
【解决方案3】:

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

正如CFRunLoopAddSource 的模式参数所记录的那样:“使用常量 kCFRunLoopCommonModes 将源添加到由所有常见模式监视的对象集。”

第三个参数应该是CFRunLoopMode.commonModes。 你所拥有的 CFRunLoopMode.defaultMode 又名 kCFRunLoopDefaultMode 是在调用 CFRunLoopRun 时使用的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-11
    • 1970-01-01
    • 2022-12-05
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    相关资源
    最近更新 更多