diff --git a/macos/Sources/Features/Terminal/TerminalController.swift b/macos/Sources/Features/Terminal/TerminalController.swift index 779c13d9c..2e6021f56 100644 --- a/macos/Sources/Features/Terminal/TerminalController.swift +++ b/macos/Sources/Features/Terminal/TerminalController.swift @@ -527,7 +527,7 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr frame.origin.x = max(screen.frame.origin.x, min(frame.origin.x, screen.frame.maxX - newWidth)) frame.origin.y = max(screen.frame.origin.y, min(frame.origin.y, screen.frame.maxY - newHeight)) - return frame + return adjustForWindowPosition(frame: frame, on: screen) } guard let initialFrame else { return nil } @@ -545,7 +545,30 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr frame.origin.x = max(screen.frame.origin.x, min(frame.origin.x, screen.frame.maxX - newWidth)) frame.origin.y = max(screen.frame.origin.y, min(frame.origin.y, screen.frame.maxY - newHeight)) - return frame + return adjustForWindowPosition(frame: frame, on: screen) + } + + /// Adjusts the given frame for the configured window position. + func adjustForWindowPosition(frame: NSRect, on screen: NSScreen) -> NSRect { + guard let x = derivedConfig.windowPositionX else { return frame } + guard let y = derivedConfig.windowPositionY else { return frame } + + // Convert top-left coordinates to bottom-left origin using our utility extension + let origin = screen.origin( + fromTopLeftOffsetX: CGFloat(x), + offsetY: CGFloat(y), + windowSize: frame.size) + + // Clamp the origin to ensure the window stays fully visible on screen + var safeOrigin = origin + let vf = screen.visibleFrame + safeOrigin.x = min(max(safeOrigin.x, vf.minX), vf.maxX - frame.width) + safeOrigin.y = min(max(safeOrigin.y, vf.minY), vf.maxY - frame.height) + + // Return our new origin + var result = frame + result.origin = safeOrigin + return result } /// This is called anytime a node in the surface tree is being removed. @@ -1358,12 +1381,16 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr let macosWindowButtons: Ghostty.MacOSWindowButtons let macosTitlebarStyle: String let maximize: Bool + let windowPositionX: Int16? + let windowPositionY: Int16? init() { self.backgroundColor = Color(NSColor.windowBackgroundColor) self.macosWindowButtons = .visible self.macosTitlebarStyle = "system" self.maximize = false + self.windowPositionX = nil + self.windowPositionY = nil } init(_ config: Ghostty.Config) { @@ -1371,6 +1398,8 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr self.macosWindowButtons = config.macosWindowButtons self.macosTitlebarStyle = config.macosTitlebarStyle self.maximize = config.maximize + self.windowPositionX = config.windowPositionX + self.windowPositionY = config.windowPositionY } } } diff --git a/macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift b/macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift index 5bb8d2f10..ce9f5f4cf 100644 --- a/macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift +++ b/macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift @@ -65,8 +65,7 @@ class TerminalWindow: NSWindow { // fallback to original centering behavior setInitialWindowPosition( x: config.windowPositionX, - y: config.windowPositionY, - windowDecorations: config.windowDecorations) + y: config.windowPositionY) // If our traffic buttons should be hidden, then hide them if config.macosWindowButtons == .hidden { @@ -425,7 +424,7 @@ class TerminalWindow: NSWindow { return derivedConfig.backgroundColor.withAlphaComponent(alpha) } - private func setInitialWindowPosition(x: Int16?, y: Int16?, windowDecorations: Bool) { + private func setInitialWindowPosition(x: Int16?, y: Int16?) { // If we don't have an X/Y then we try to use the previously saved window pos. guard let x, let y else { if (!LastWindowPosition.shared.restore(self)) { @@ -441,19 +440,14 @@ class TerminalWindow: NSWindow { return } - // Convert top-left coordinates to bottom-left origin using our utility extension - let origin = screen.origin( - fromTopLeftOffsetX: CGFloat(x), - offsetY: CGFloat(y), - windowSize: frame.size) + // We have an X/Y, use our controller function to set it up. + guard let terminalController else { + center() + return + } - // Clamp the origin to ensure the window stays fully visible on screen - var safeOrigin = origin - let vf = screen.visibleFrame - safeOrigin.x = min(max(safeOrigin.x, vf.minX), vf.maxX - frame.width) - safeOrigin.y = min(max(safeOrigin.y, vf.minY), vf.maxY - frame.height) - - setFrameOrigin(safeOrigin) + let frame = terminalController.adjustForWindowPosition(frame: frame, on: screen) + setFrameOrigin(frame.origin) } private func hideWindowButtons() {