From c7ab9954ff04c5491ab22dc1d48af5a556bed3ca Mon Sep 17 00:00:00 2001 From: Bryan Lee <38807139+liby@users.noreply.github.com> Date: Mon, 14 Apr 2025 22:49:15 +0800 Subject: [PATCH] Fix macOS quick terminal fullscreen in front of fullscreen window When a main Ghostty window is in fullscreen mode, quick terminal could get stuck after the first fullscreen toggle due to presentation options conflicts. This fix improves handling of menu bar visibility when toggling fullscreen on spaces that already have fullscreen windows. --- .../QuickTerminalController.swift | 14 ++------ macos/Sources/Helpers/Fullscreen.swift | 33 ++++++++++++++++--- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift b/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift index 690c7f132..fac3a2fbb 100644 --- a/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift +++ b/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift @@ -33,9 +33,6 @@ class QuickTerminalController: BaseTerminalController { /// The configuration derived from the Ghostty config so we don't need to rely on references. private var derivedConfig: DerivedConfig - /// Storage for the window frame before entering fullscreen mode - private var savedWindowFrame: NSRect? = nil - init(_ ghostty: Ghostty.App, position: QuickTerminalPosition = .top, baseConfig base: Ghostty.SurfaceConfiguration? = nil, @@ -488,16 +485,9 @@ class QuickTerminalController: BaseTerminalController { @objc private func onToggleFullscreen(notification: SwiftUI.Notification) { guard let target = notification.object as? Ghostty.SurfaceView else { return } guard target == self.focusedSurface else { return } - guard let window = self.window else { return } - guard let screen = window.screen ?? NSScreen.main else { return } - if let originalFrame = savedWindowFrame { - window.setFrame(originalFrame, display: true) - savedWindowFrame = nil - } else { - savedWindowFrame = window.frame - window.setFrame(screen.frame, display: true) - } + // We ignore the requested mode and always use non-native for the quick terminal + toggleFullscreen(mode: .nonNative) } @objc private func ghosttyConfigDidChange(_ notification: Notification) { diff --git a/macos/Sources/Helpers/Fullscreen.swift b/macos/Sources/Helpers/Fullscreen.swift index 59865fc9e..850485830 100644 --- a/macos/Sources/Helpers/Fullscreen.swift +++ b/macos/Sources/Helpers/Fullscreen.swift @@ -167,6 +167,9 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle { // screen the window is currently on. guard let screen = window.screen else { return } + // Check if we're on a space that has a fullscreen window already + let alreadyHasFullscreenWindow = hasScreenWithFullscreenWindow(screen) + // Save the state that we need to exit again guard let savedState = SavedState(window) else { return } self.savedState = savedState @@ -179,8 +182,9 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle { hideDock() } - // Hide the menu if requested - if (properties.hideMenu) { + // Hide the menu if requested and if we don't already have a fullscreen window + // on this space (which would cause conflicts with presentation options) + if (properties.hideMenu && !alreadyHasFullscreenWindow) { hideMenu() } @@ -325,11 +329,32 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle { // MARK: Menu func hideMenu() { - NSApp.acquirePresentationOption(.autoHideMenuBar) + // Check if we're on a space that already has a fullscreen window + // which would cause macOS to already have the menu bar hidden + let currentOptions = NSApp.presentationOptions + if !currentOptions.contains(.autoHideMenuBar) && !currentOptions.contains(.hideMenuBar) { + // Only set if not already managed by the system + NSApp.acquirePresentationOption(.autoHideMenuBar) + } } func unhideMenu() { - NSApp.releasePresentationOption(.autoHideMenuBar) + let currentOptions = NSApp.presentationOptions + if currentOptions.contains(.autoHideMenuBar) { + // Only release what we've acquired + NSApp.releasePresentationOption(.autoHideMenuBar) + } + } + + /// Determines if a screen already has a fullscreen window, which would + /// affect how we handle presentation options + private func hasScreenWithFullscreenWindow(_ screen: NSScreen) -> Bool { + // Check if we're on a space with a fullscreen window already + let currentOptions = NSApp.presentationOptions + + // If any of these are set, macOS is probably already managing the menu bar + return currentOptions.contains(.hideMenuBar) || + currentOptions.contains(.autoHideMenuBar) } /// The state that must be saved for non-native fullscreen to exit fullscreen.