I am making a macOS app in Swift and the window of my app is always on top of other apps. This is working fine with all apps even if those apps are in full-screen mode. However, when Keynote runs in full-screen mode and my app is on top of it, all the mouse events meant for my app also go to keynote and it exits fullscreen.
I am not sure what's going on but I need to prevent keynote from exiting full-screen. How can I do that?
The window level of my app is NSWindow.Level.popUpMenu
. And, I have tried the following but nothing has worked so far:
window.orderFrontRegardless()
window.makeKeyAndOrderFront(self)
window.order(.above, relativeTo: 0)
TL;DR - The problem here is the application activation.
This doesn't answer your question precisely:
How to prevent other applications from listening to mouse events in macOS?
It's an answer demonstrating how to achieve what you want without preventing other applications from listening to mouse events in macOS.
Check the following screenshots. The first one contains active Xcode window and the other one inactive Xcode window. Your goal is to keep the other application window active even if you click in your overlay. It's irrelevant if the other application is running presentation (like Keynote, in full screen) or not.
Main.storyboard
and remove window & view controller scenesLSUIElement
to YES
(Info.plist
)AppDelegate.swift
code (below)I just tested it with the Keynote 10.0 & macOS Catalina 10.15.4 (19E287) and it works as expected - I can click inside the red overlay without interrupting the running presentation, I can control the presentation with keyboard, ...
NSPanel
instead of NSWindow
styleMask
& .nonactivatingPanel
(can't be used with NSWindow
)
hidesOnDeactivate
to false
becomesKeyOnlyIfNeeded
to true
needsPanelToBecomeKey
if you need keyboard inputcollectionBehavior
to [.canJoinAllSpaces, .fullScreenAuxiliary]
.canJoinAllSpaces
= the window appears in all spaces (like menu bar).fullScreenAuxiliary
= the window with this collection behavior can be shown on the same space as the fullscreen windowimport 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) {
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With