From 04b38aec873f43de5793a969bcb6e33d9c309d13 Mon Sep 17 00:00:00 2001 From: osy <50960678+osy@users.noreply.github.com> Date: Sat, 19 Sep 2026 23:37:09 -0500 Subject: [PATCH] display: turn off Hot Corners while a VM is in full screen A guest in full screen owns the whole display, and its own corners are commonly used (Start button, Show Desktop, Apple menu). Moving the pointer there also triggered the Hot Corner configured on the host, e.g. Quick Note or Mission Control popping up over the guest. macOS 27 adds NSApplication.PresentationOptions.disableScreenCornerInteractions, which we now request for the full screen space. The camera housing mode forces its own options and has to carry the flag over. On macOS 27.0 the window server keeps the corners disabled for the app after the full screen space is gone, even though AppKit reports no options, until the options change again. Reset them when leaving full screen. Nothing is done for a captured mouse: the pointer is detached from the cursor and parked in the middle of the screen, so it never reaches a corner. Assisted-by: Claude:claude-fable-5-1 --- Platform/macOS/Display/VMDisplayWindow.swift | 19 ++++++++++++++++++- .../Display/VMDisplayWindowController.swift | 7 ++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Platform/macOS/Display/VMDisplayWindow.swift b/Platform/macOS/Display/VMDisplayWindow.swift index d10dde30..381f335a 100644 --- a/Platform/macOS/Display/VMDisplayWindow.swift +++ b/Platform/macOS/Display/VMDisplayWindow.swift @@ -108,6 +108,7 @@ class VMDisplayWindow: NSWindow { center.addObserver(self, selector: #selector(updateMenuBarHidden), name: NSWindow.didResizeNotification, object: self) center.addObserver(self, selector: #selector(willLeaveFullScreen), name: NSWindow.willExitFullScreenNotification, object: self) center.addObserver(self, selector: #selector(willLeaveFullScreen), name: NSWindow.willCloseNotification, object: self) + center.addObserver(self, selector: #selector(didExitFullScreen), name: NSWindow.didExitFullScreenNotification, object: self) center.addObserver(self, selector: #selector(menuBarRevealDidChange), name: MenuBarReveal.didChangeNotification, object: nil) center.addObserver(self, selector: #selector(otherWindowDidChangeOcclusionState), name: NSWindow.didChangeOcclusionStateNotification, object: nil) center.addObserver(self, selector: #selector(updatePresentationOptions), name: NSWindow.didBecomeKeyNotification, object: self) @@ -171,6 +172,18 @@ class VMDisplayWindow: NSWindow { showMenuBar() } + @objc private func didExitFullScreen() { + guard #available(macOS 27, *) else { + return + } + // the window server keeps Hot Corners disabled after the full screen space is gone, until the options change + guard NSApp.presentationOptions.isEmpty else { + return // another full screen window is in front and still wants them disabled + } + NSApp.presentationOptions = [.autoHideDock] + NSApp.presentationOptions = [] + } + /// Hides the menu bar of our full screen space if and only if we are covered by it. @objc private func updateMenuBarHidden() { if isInFullScreenSpace, let screen = screen, frame != screen.frame { @@ -226,7 +239,11 @@ class VMDisplayWindow: NSWindow { // the menu bar is only kept hidden for the space we are in, but what we did must be undone in any case let options: NSApplication.PresentationOptions? if !isMenuBarRevealAllowed && isKeyWindow { - options = [.fullScreen, .hideMenuBar, .hideDock] + var forcedOptions: NSApplication.PresentationOptions = [.fullScreen, .hideMenuBar, .hideDock] + if #available(macOS 27, *) { + forcedOptions.formUnion(revealableOptions.intersection(.disableScreenCornerInteractions)) + } + options = forcedOptions isPresentationOptionsForced = true } else if isPresentationOptionsForced { options = revealableOptions diff --git a/Platform/macOS/Display/VMDisplayWindowController.swift b/Platform/macOS/Display/VMDisplayWindowController.swift index 8d768cdb..e7086c80 100644 --- a/Platform/macOS/Display/VMDisplayWindowController.swift +++ b/Platform/macOS/Display/VMDisplayWindowController.swift @@ -419,7 +419,12 @@ class VMDisplayWindowController: NSWindowController, UTMVirtualMachineDelegate { extension VMDisplayWindowController: NSWindowDelegate { func window(_ window: NSWindow, willUseFullScreenPresentationOptions proposedOptions: NSApplication.PresentationOptions = []) -> NSApplication.PresentationOptions { - return proposedOptions.union([.autoHideToolbar]) + var options = proposedOptions.union([.autoHideToolbar]) + if #available(macOS 27, *) { + // the guest owns the whole screen, so its corners should not trigger Hot Corners on the host + options.insert(.disableScreenCornerInteractions) + } + return options } func windowShouldClose(_ sender: NSWindow) -> Bool {