macOS: non-native fullscreen should not hide menu on fullscreen space
Fixes #7075 We have to use private APIs for this, I couldn't find a reliable way otherwise.pull/7091/head
parent
9d9d781a0b
commit
453e6590e8
|
|
@ -55,6 +55,8 @@
|
|||
A571AB1D2A206FCF00248498 /* GhosttyKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5D495A1299BEC7E00DD1313 /* GhosttyKit.xcframework */; };
|
||||
A57D79272C9C879B001D522E /* SecureInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57D79262C9C8798001D522E /* SecureInput.swift */; };
|
||||
A586167C2B7703CC009BDB1D /* fish in Resources */ = {isa = PBXBuildFile; fileRef = A586167B2B7703CC009BDB1D /* fish */; };
|
||||
A5874D992DAD751B00E83852 /* CGS.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5874D982DAD751A00E83852 /* CGS.swift */; };
|
||||
A5874D9D2DAD786100E83852 /* NSWindow+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5874D9C2DAD785F00E83852 /* NSWindow+Extension.swift */; };
|
||||
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59444F629A2ED5200725BBA /* SettingsView.swift */; };
|
||||
A59630972AEE163600D64628 /* HostingWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59630962AEE163600D64628 /* HostingWindow.swift */; };
|
||||
A596309A2AEE1C6400D64628 /* Terminal.xib in Resources */ = {isa = PBXBuildFile; fileRef = A59630992AEE1C6400D64628 /* Terminal.xib */; };
|
||||
|
|
@ -154,6 +156,8 @@
|
|||
A571AB1C2A206FC600248498 /* Ghostty-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Ghostty-Info.plist"; sourceTree = "<group>"; };
|
||||
A57D79262C9C8798001D522E /* SecureInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureInput.swift; sourceTree = "<group>"; };
|
||||
A586167B2B7703CC009BDB1D /* fish */ = {isa = PBXFileReference; lastKnownFileType = folder; name = fish; path = "../zig-out/share/fish"; sourceTree = "<group>"; };
|
||||
A5874D982DAD751A00E83852 /* CGS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGS.swift; sourceTree = "<group>"; };
|
||||
A5874D9C2DAD785F00E83852 /* NSWindow+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSWindow+Extension.swift"; sourceTree = "<group>"; };
|
||||
A59444F629A2ED5200725BBA /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||
A59630962AEE163600D64628 /* HostingWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostingWindow.swift; sourceTree = "<group>"; };
|
||||
A59630992AEE1C6400D64628 /* Terminal.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Terminal.xib; sourceTree = "<group>"; };
|
||||
|
|
@ -274,13 +278,13 @@
|
|||
A534263D2A7DCBB000EBB7A2 /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A5874D9B2DAD781100E83852 /* Private */,
|
||||
A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */,
|
||||
A5A6F7292CC41B8700B232A5 /* Xcode.swift */,
|
||||
A5CEAFFE29C2410700646FDA /* Backport.swift */,
|
||||
A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */,
|
||||
A5CBD0572C9F30860017A1AE /* Cursor.swift */,
|
||||
A5D0AF3C2B37804400D21823 /* CodableBridge.swift */,
|
||||
A5A2A3C92D4445E20033CF96 /* Dock.swift */,
|
||||
A52FFF582CAA4FF1000C6A5B /* Fullscreen.swift */,
|
||||
A59630962AEE163600D64628 /* HostingWindow.swift */,
|
||||
A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */,
|
||||
|
|
@ -293,6 +297,7 @@
|
|||
A52FFF5C2CAB4D05000C6A5B /* NSScreen+Extension.swift */,
|
||||
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
||||
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
||||
A5874D9C2DAD785F00E83852 /* NSWindow+Extension.swift */,
|
||||
A5985CD62C320C4500C57AD3 /* String+Extension.swift */,
|
||||
A5CC36142C9CDA03004D6760 /* View+Extension.swift */,
|
||||
A5CA378D2D31D6C100931030 /* Weak.swift */,
|
||||
|
|
@ -403,6 +408,15 @@
|
|||
path = "Secure Input";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A5874D9B2DAD781100E83852 /* Private */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A5874D982DAD751A00E83852 /* CGS.swift */,
|
||||
A5A2A3C92D4445E20033CF96 /* Dock.swift */,
|
||||
);
|
||||
path = Private;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A59630982AEE1C4400D64628 /* Terminal */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -634,6 +648,7 @@
|
|||
A59630A42AF059BB00D64628 /* Ghostty.SplitNode.swift in Sources */,
|
||||
A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */,
|
||||
A54B0CEB2D0CFB4C00CBEFF8 /* NSImage+Extension.swift in Sources */,
|
||||
A5874D9D2DAD786100E83852 /* NSWindow+Extension.swift in Sources */,
|
||||
A54D786C2CA7978E001B19B1 /* BaseTerminalController.swift in Sources */,
|
||||
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */,
|
||||
CFBB5FEA2D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift in Sources */,
|
||||
|
|
@ -669,6 +684,7 @@
|
|||
A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */,
|
||||
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */,
|
||||
A5333E1C2B5A1CE3008AEFF7 /* CrossKit.swift in Sources */,
|
||||
A5874D992DAD751B00E83852 /* CGS.swift in Sources */,
|
||||
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */,
|
||||
A56D58862ACDDB4100508D2C /* Ghostty.Shell.swift in Sources */,
|
||||
A5985CD72C320C4500C57AD3 /* String+Extension.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -3,12 +3,6 @@ import Cocoa
|
|||
import SwiftUI
|
||||
import GhosttyKit
|
||||
|
||||
// This is a Apple's private function that we need to call to get the active space.
|
||||
@_silgen_name("CGSGetActiveSpace")
|
||||
func CGSGetActiveSpace(_ cid: Int) -> size_t
|
||||
@_silgen_name("CGSMainConnectionID")
|
||||
func CGSMainConnectionID() -> Int
|
||||
|
||||
/// Controller for the "quick" terminal.
|
||||
class QuickTerminalController: BaseTerminalController {
|
||||
override var windowNibName: NSNib.Name? { "QuickTerminal" }
|
||||
|
|
@ -25,7 +19,7 @@ class QuickTerminalController: BaseTerminalController {
|
|||
private var previousApp: NSRunningApplication? = nil
|
||||
|
||||
// The active space when the quick terminal was last shown.
|
||||
private var previousActiveSpace: size_t = 0
|
||||
private var previousActiveSpace: CGSSpace? = nil
|
||||
|
||||
/// Non-nil if we have hidden dock state.
|
||||
private var hiddenDock: HiddenDock? = nil
|
||||
|
|
@ -154,7 +148,7 @@ class QuickTerminalController: BaseTerminalController {
|
|||
animateOut()
|
||||
|
||||
case .move:
|
||||
let currentActiveSpace = CGSGetActiveSpace(CGSMainConnectionID())
|
||||
let currentActiveSpace = CGSSpace.active()
|
||||
if previousActiveSpace == currentActiveSpace {
|
||||
// We haven't moved spaces. We lost focus to another app on the
|
||||
// current space. Animate out.
|
||||
|
|
@ -224,7 +218,7 @@ class QuickTerminalController: BaseTerminalController {
|
|||
}
|
||||
|
||||
// Set previous active space
|
||||
self.previousActiveSpace = CGSGetActiveSpace(CGSMainConnectionID())
|
||||
self.previousActiveSpace = CGSSpace.active()
|
||||
|
||||
// Animate the window in
|
||||
animateWindowIn(window: window, from: position)
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
|||
}
|
||||
|
||||
// Hide the menu if requested
|
||||
if (properties.hideMenu) {
|
||||
if (properties.hideMenu && savedState.menu) {
|
||||
hideMenu()
|
||||
}
|
||||
|
||||
|
|
@ -224,7 +224,9 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
|||
if savedState.dock {
|
||||
unhideDock()
|
||||
}
|
||||
if (properties.hideMenu && savedState.menu) {
|
||||
unhideMenu()
|
||||
}
|
||||
|
||||
// Restore our saved state
|
||||
window.styleMask = savedState.styleMask
|
||||
|
|
@ -340,6 +342,7 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
|||
let contentFrame: NSRect
|
||||
let styleMask: NSWindow.StyleMask
|
||||
let dock: Bool
|
||||
let menu: Bool
|
||||
|
||||
init?(_ window: NSWindow) {
|
||||
guard let contentView = window.contentView else { return nil }
|
||||
|
|
@ -350,6 +353,12 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
|||
self.contentFrame = window.convertToScreen(contentView.frame)
|
||||
self.styleMask = window.styleMask
|
||||
self.dock = window.screen?.hasDock ?? false
|
||||
|
||||
// We hide the menu only if this window is not on any fullscreen
|
||||
// spaces. We do this because fullscreen spaces already hide the
|
||||
// menu and if we insert/remove this presentation option we get
|
||||
// issues (see #7075)
|
||||
self.menu = CGSSpace.list(for: window.cgWindowId).allSatisfy { $0.type != .fullscreen }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
import AppKit
|
||||
|
||||
extension NSWindow {
|
||||
/// Get the CGWindowID type for the window (used for low level CoreGraphics APIs).
|
||||
var cgWindowId: CGWindowID {
|
||||
CGWindowID(windowNumber)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
import AppKit
|
||||
|
||||
// MARK: - CGS Private API Declarations
|
||||
|
||||
typealias CGSConnectionID = Int32
|
||||
typealias CGSSpaceID = size_t
|
||||
|
||||
@_silgen_name("CGSMainConnectionID")
|
||||
private func CGSMainConnectionID() -> CGSConnectionID
|
||||
|
||||
@_silgen_name("CGSGetActiveSpace")
|
||||
private func CGSGetActiveSpace(_ cid: CGSConnectionID) -> CGSSpaceID
|
||||
|
||||
@_silgen_name("CGSSpaceGetType")
|
||||
private func CGSSpaceGetType(_ cid: CGSConnectionID, _ spaceID: CGSSpaceID) -> CGSSpaceType
|
||||
|
||||
@_silgen_name("CGSCopySpacesForWindows")
|
||||
func CGSCopySpacesForWindows(
|
||||
_ cid: CGSConnectionID,
|
||||
_ mask: CGSSpaceMask,
|
||||
_ windowIDs: CFArray
|
||||
) -> Unmanaged<CFArray>?
|
||||
|
||||
// MARK: - CGS Space
|
||||
|
||||
/// https://github.com/NUIKit/CGSInternal/blob/c4f6f559d624dc1cfc2bf24c8c19dbf653317fcf/CGSSpace.h#L40
|
||||
/// converted to Swift
|
||||
struct CGSSpaceMask: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
static let includesCurrent = CGSSpaceMask(rawValue: 1 << 0)
|
||||
static let includesOthers = CGSSpaceMask(rawValue: 1 << 1)
|
||||
static let includesUser = CGSSpaceMask(rawValue: 1 << 2)
|
||||
|
||||
static let includesVisible = CGSSpaceMask(rawValue: 1 << 16)
|
||||
|
||||
static let currentSpace: CGSSpaceMask = [.includesUser, .includesCurrent]
|
||||
static let otherSpaces: CGSSpaceMask = [.includesOthers, .includesCurrent]
|
||||
static let allSpaces: CGSSpaceMask = [.includesUser, .includesOthers, .includesCurrent]
|
||||
static let allVisibleSpaces: CGSSpaceMask = [.includesVisible, .allSpaces]
|
||||
}
|
||||
|
||||
/// Represents a unique identifier for a macOS Space (Desktop, Fullscreen, etc).
|
||||
struct CGSSpace: Hashable, CustomStringConvertible {
|
||||
let rawValue: CGSSpaceID
|
||||
|
||||
var description: String {
|
||||
"SpaceID(\(rawValue))"
|
||||
}
|
||||
|
||||
/// Returns the currently active space.
|
||||
static func active() -> CGSSpace {
|
||||
let space = CGSGetActiveSpace(CGSMainConnectionID())
|
||||
return .init(rawValue: space)
|
||||
}
|
||||
|
||||
/// List the sapces for the given window.
|
||||
static func list(for windowID: CGWindowID, mask: CGSSpaceMask = .allSpaces) -> [CGSSpace] {
|
||||
guard let spaces = CGSCopySpacesForWindows(
|
||||
CGSMainConnectionID(),
|
||||
mask,
|
||||
[windowID] as CFArray
|
||||
) else { return [] }
|
||||
guard let spaceIDs = spaces.takeRetainedValue() as? [CGSSpaceID] else { return [] }
|
||||
return spaceIDs.map(CGSSpace.init)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CGS Space Types
|
||||
|
||||
enum CGSSpaceType: UInt32 {
|
||||
case user = 0
|
||||
case system = 2
|
||||
case fullscreen = 4
|
||||
}
|
||||
|
||||
extension CGSSpace {
|
||||
var type: CGSSpaceType {
|
||||
CGSSpaceGetType(CGSMainConnectionID(), rawValue)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue