mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 09:16:11 +03:00
macos: clamp window size to screen size on screen parameter changes
Fixes #2462 This sets up a listener for screen parameter changes. This only triggers when a screen is added, removed, or a parameter such as its resolution changes. This doesn't trigger when a window is simply moved from one screen to another. On parameter change, we ensure that the window is within the bounds of the screen. As an exception, if the window was previously already outside the bounds of the screen, we don't move it back in.
This commit is contained in:
@ -61,6 +61,7 @@
|
||||
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */; };
|
||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5D02AE0DEA7009128F3 /* MetalView.swift */; };
|
||||
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; };
|
||||
A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A6F7292CC41B8700B232A5 /* Xcode.swift */; };
|
||||
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
||||
A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */; };
|
||||
A5CBD0582C9F30960017A1AE /* Cursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0572C9F30860017A1AE /* Cursor.swift */; };
|
||||
@ -139,6 +140,7 @@
|
||||
A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorView.swift; sourceTree = "<group>"; };
|
||||
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalView.swift; sourceTree = "<group>"; };
|
||||
A5A1F8842A489D6800D1E8BC /* terminfo */ = {isa = PBXFileReference; lastKnownFileType = folder; name = terminfo; path = "../zig-out/share/terminfo"; sourceTree = "<group>"; };
|
||||
A5A6F7292CC41B8700B232A5 /* Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xcode.swift; sourceTree = "<group>"; };
|
||||
A5B30531299BEAAA0047F10C /* Ghostty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ghostty.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A5B30538299BEAAB0047F10C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
A5B3053D299BEAAB0047F10C /* Ghostty.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ghostty.entitlements; sourceTree = "<group>"; };
|
||||
@ -233,6 +235,7 @@
|
||||
A534263D2A7DCBB000EBB7A2 /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A5A6F7292CC41B8700B232A5 /* Xcode.swift */,
|
||||
A5CEAFFE29C2410700646FDA /* Backport.swift */,
|
||||
A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */,
|
||||
A5CBD0572C9F30860017A1AE /* Cursor.swift */,
|
||||
@ -582,6 +585,7 @@
|
||||
A52FFF5D2CAB4D08000C6A5B /* NSScreen+Extension.swift in Sources */,
|
||||
A53426352A7DA53D00EBB7A2 /* AppDelegate.swift in Sources */,
|
||||
A5CBD0582C9F30960017A1AE /* Cursor.swift in Sources */,
|
||||
A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */,
|
||||
A52FFF5B2CAA54B1000C6A5B /* FullscreenMode+Extension.swift in Sources */,
|
||||
A5333E222B5A2128008AEFF7 /* SurfaceView_AppKit.swift in Sources */,
|
||||
A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */,
|
||||
|
@ -57,6 +57,14 @@ class BaseTerminalController: NSWindowController,
|
||||
/// Event monitor (see individual events for why)
|
||||
private var eventMonitor: Any? = nil
|
||||
|
||||
/// The previous frame information from the window
|
||||
private var savedFrame: SavedFrame? = nil
|
||||
|
||||
struct SavedFrame {
|
||||
let window: NSRect
|
||||
let screen: NSRect
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) is not supported for this view")
|
||||
}
|
||||
@ -80,6 +88,11 @@ class BaseTerminalController: NSWindowController,
|
||||
selector: #selector(onConfirmClipboardRequest),
|
||||
name: Ghostty.Notification.confirmClipboard,
|
||||
object: nil)
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(didChangeScreenParametersNotification),
|
||||
name: NSApplication.didChangeScreenParametersNotification,
|
||||
object: nil)
|
||||
|
||||
// Listen for local events that we need to know of outside of
|
||||
// single surface handlers.
|
||||
@ -89,6 +102,8 @@ class BaseTerminalController: NSWindowController,
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
|
||||
if let eventMonitor {
|
||||
NSEvent.removeMonitor(eventMonitor)
|
||||
}
|
||||
@ -121,6 +136,57 @@ class BaseTerminalController: NSWindowController,
|
||||
}
|
||||
}
|
||||
|
||||
// Call this whenever the frame changes
|
||||
private func windowFrameDidChange() {
|
||||
// We need to update our saved frame information in case of monitor
|
||||
// changes (see didChangeScreenParameters notification).
|
||||
savedFrame = nil
|
||||
guard let window, let screen = window.screen else { return }
|
||||
savedFrame = .init(window: window.frame, screen: screen.visibleFrame)
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
|
||||
@objc private func didChangeScreenParametersNotification(_ notification: Notification) {
|
||||
// If we have a window that is visible and it is outside the bounds of the
|
||||
// screen then we clamp it back to within the screen.
|
||||
guard let window else { return }
|
||||
guard window.isVisible else { return }
|
||||
guard let screen = window.screen else { return }
|
||||
|
||||
let visibleFrame = screen.visibleFrame
|
||||
var newFrame = window.frame
|
||||
|
||||
// Clamp width/height
|
||||
if newFrame.size.width > visibleFrame.size.width {
|
||||
newFrame.size.width = visibleFrame.size.width
|
||||
}
|
||||
if newFrame.size.height > visibleFrame.size.height {
|
||||
newFrame.size.height = visibleFrame.size.height
|
||||
}
|
||||
|
||||
// Ensure the window is on-screen. We only do this if the previous frame
|
||||
// was also on screen. If a user explicitly wanted their window off screen
|
||||
// then we let it stay that way.
|
||||
x: if newFrame.origin.x < visibleFrame.origin.x {
|
||||
if let savedFrame, savedFrame.window.origin.x < savedFrame.screen.origin.x {
|
||||
break x;
|
||||
}
|
||||
|
||||
newFrame.origin.x = visibleFrame.origin.x
|
||||
}
|
||||
y: if newFrame.origin.y < visibleFrame.origin.y {
|
||||
if let savedFrame, savedFrame.window.origin.y < savedFrame.screen.origin.y {
|
||||
break y;
|
||||
}
|
||||
|
||||
newFrame.origin.y = visibleFrame.origin.y
|
||||
}
|
||||
|
||||
// Apply the new window frame
|
||||
window.setFrame(newFrame, display: true)
|
||||
}
|
||||
|
||||
// MARK: Local Events
|
||||
|
||||
private func localEventHandler(_ event: NSEvent) -> NSEvent? {
|
||||
@ -371,6 +437,14 @@ class BaseTerminalController: NSWindowController,
|
||||
}
|
||||
}
|
||||
|
||||
func windowDidResize(_ notification: Notification) {
|
||||
windowFrameDidChange()
|
||||
}
|
||||
|
||||
func windowDidMove(_ notification: Notification) {
|
||||
windowFrameDidChange()
|
||||
}
|
||||
|
||||
// MARK: First Responder
|
||||
|
||||
@IBAction func close(_ sender: Any) {
|
||||
|
@ -358,7 +358,8 @@ class TerminalController: BaseTerminalController {
|
||||
self.fixTabBar()
|
||||
}
|
||||
|
||||
func windowDidMove(_ notification: Notification) {
|
||||
override func windowDidMove(_ notification: Notification) {
|
||||
super.windowDidMove(notification)
|
||||
self.fixTabBar()
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,13 @@ extension Ghostty {
|
||||
// same filesystem concept.
|
||||
#if os(macOS)
|
||||
ghostty_config_load_default_files(cfg);
|
||||
ghostty_config_load_cli_args(cfg);
|
||||
|
||||
// We only load CLI args when not running in Xcode because in Xcode we
|
||||
// pass some special parameters to control the debugger.
|
||||
if !isRunningInXcode() {
|
||||
ghostty_config_load_cli_args(cfg);
|
||||
}
|
||||
|
||||
ghostty_config_load_recursive_files(cfg);
|
||||
#endif
|
||||
|
||||
|
10
macos/Sources/Helpers/Xcode.swift
Normal file
10
macos/Sources/Helpers/Xcode.swift
Normal file
@ -0,0 +1,10 @@
|
||||
import Foundation
|
||||
|
||||
/// True if we appear to be running in Xcode.
|
||||
func isRunningInXcode() -> Bool {
|
||||
if let _ = ProcessInfo.processInfo.environment["__XCODE_BUILT_PRODUCTS_DIR_PATHS"] {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
Reference in New Issue
Block a user