TL;DR - 这里的问题是应用程序激活。
这并不能准确回答您的问题:
如何防止其他应用程序在 macOS 中监听鼠标事件?
这是一个演示如何在不阻止其他应用程序侦听 macOS 中的鼠标事件的情况下实现您想要的答案的答案。
活动与非活动窗口
检查以下屏幕截图。第一个包含活动的 Xcode 窗口和另一个非活动的 Xcode 窗口。您的目标是保持其他应用程序窗口处于活动状态,即使您单击叠加层也是如此。其他应用程序是否正在运行演示文稿(如 Keynote,全屏)无关紧要。
示例项目设置
- 创建一个新项目(Xcode - macOS 应用程序 - Swift & Storyboard)
- 打开
Main.storyboard
并移除窗口和视图控制器场景
- 设置
LSUIElement
为YES
( Info.plist
)
- 添加 HotKey Swift 包 ( https://github.com/soffes/HotKey )
- 复制并粘贴
AppDelegate.swift
代码(如下)
- 运行
- 使用 Cmd + Opt + O 切换红色叠加层
我刚刚使用 Keynote 10.0 和 macOS Catalina 10.15.4 (19E287) 对其进行了测试,它按预期工作 - 我可以在红色覆盖层内单击而不会中断正在运行的演示文稿,我可以使用键盘控制演示文稿,...
重要部件
- 使用
NSPanel
代替NSWindow
- 使用
styleMask
& .nonactivatingPanel
(不能与 一起使用NSWindow
)
- 设置
hidesOnDeactivate
为false
- 启动应用程序时不要隐藏,被激活然后你激活任何其他应用程序
- 设置
becomesKeyOnlyIfNeeded
为true
- 避免成为鼠标点击的关键窗口
- 搜索
needsPanelToBecomeKey
是否需要键盘输入
- 设置
collectionBehavior
为[.canJoinAllSpaces, .fullScreenAuxiliary]
.canJoinAllSpaces
= 窗口出现在所有空间中(如菜单栏)
.fullScreenAuxiliary
= 具有此收集行为的窗口可以显示在与全屏窗口相同的空间上
AppDelegate.swift
import Cocoa
import HotKey
final class OverlayView: NSView {
private var path: NSBezierPath?
override func keyDown(with event: NSEvent) {
print("keyDown - \(event.keyCode)")
}
override func keyUp(with event: NSEvent) {
print("keyUp - \(event.keyCode)")
}
override func mouseDown(with event: NSEvent) {
let point = self.convert(event.locationInWindow, from: nil)
path = NSBezierPath()
path?.move(to: point)
needsDisplay = true
}
override func mouseUp(with event: NSEvent) {
path = nil
needsDisplay = true
}
override func mouseDragged(with event: NSEvent) {
let point = self.convert(event.locationInWindow, from: nil)
path?.line(to: point)
needsDisplay = true
}
override func draw(_ dirtyRect: NSRect) {
guard let ctx = NSGraphicsContext.current?.cgContext else {
return
}
defer {
ctx.restoreGState()
}
ctx.saveGState()
NSColor.green.set()
ctx.stroke(bounds, width: 8.0)
guard let path = path else {
return
}
path.lineWidth = 5.0
NSColor.green.set()
path.stroke()
}
override var acceptsFirstResponder: Bool {
true
}
override var needsPanelToBecomeKey: Bool {
true
}
}
final class OverlayWindow: NSPanel {
convenience init() {
self.init(
contentRect: NSScreen.main!.frame,
styleMask: [.borderless, .fullSizeContentView, .nonactivatingPanel],
backing: .buffered,
defer: false
)
canHide = false
hidesOnDeactivate = false
contentView = OverlayView()
isFloatingPanel = true
becomesKeyOnlyIfNeeded = true
acceptsMouseMovedEvents = true
isOpaque = false
hasShadow = false
titleVisibility = .hidden
level = .popUpMenu
backgroundColor = NSColor.black.withAlphaComponent(0.001)
collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
}
override var canBecomeKey: Bool {
true
}
}
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
private var hotKey: HotKey!
private var overlayWindowController: NSWindowController?
func applicationDidFinishLaunching(_ aNotification: Notification) {
hotKey = HotKey(key: .o, modifiers: [.command, .option])
hotKey.keyDownHandler = toggleOverlay
}
private func toggleOverlay() {
if overlayWindowController != nil {
overlayWindowController?.close()
overlayWindowController = nil
} else {
overlayWindowController = NSWindowController(window: OverlayWindow())
overlayWindowController?.showWindow(self)
overlayWindowController?.window?.makeKey()
}
}
func applicationWillTerminate(_ aNotification: Notification) {
}
}