From 29b96be84fb3e1498172e02cdc42a481b4e2e998 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 2 Jan 2025 13:04:30 -0800 Subject: [PATCH] tweaks to window position --- .../Terminal/TerminalController.swift | 54 +++++++++---------- macos/Sources/Ghostty/Ghostty.Config.swift | 8 +-- src/apprt/glfw.zig | 23 ++++---- src/config/Config.zig | 22 ++++++-- 4 files changed, 61 insertions(+), 46 deletions(-) diff --git a/macos/Sources/Features/Terminal/TerminalController.swift b/macos/Sources/Features/Terminal/TerminalController.swift index e3e5ac3f67..1e8e4c2141 100644 --- a/macos/Sources/Features/Terminal/TerminalController.swift +++ b/macos/Sources/Features/Terminal/TerminalController.swift @@ -273,6 +273,28 @@ class TerminalController: BaseTerminalController { } } + private func setInitialWindowPosition(x: Int16?, y: Int16?, windowDecorations: Bool) { + guard let window else { return } + + // If we don't have both an X and Y we center. + guard let x, let y else { + window.center() + return + } + + // Prefer the screen our window is being placed on otherwise our primary screen. + guard let screen = window.screen ?? NSScreen.screens.first else { + window.center() + return + } + + // Orient based on the top left of the primary monitor + let frame = screen.visibleFrame + window.setFrameOrigin(.init( + x: frame.minX + CGFloat(x), + y: frame.maxY - (CGFloat(y) + window.frame.height))) + } + //MARK: - NSWindowController override func windowWillLoad() { @@ -370,8 +392,11 @@ class TerminalController: BaseTerminalController { // Set our window positioning to coordinates if config value exists, otherwise // fallback to original centering behavior - setInitialWindowPosition(window, x: config.windowInitialPositionX, y: config.windowInitialPositionY, windowDecorations: config.windowDecorations) - + setInitialWindowPosition( + x: config.windowPositionX, + y: config.windowPositionY, + windowDecorations: config.windowDecorations) + // Make sure our theme is set on the window so styling is correct. if let windowTheme = config.windowTheme { window.windowTheme = .init(rawValue: windowTheme) @@ -468,31 +493,6 @@ class TerminalController: BaseTerminalController { let data = TerminalRestorableState(from: self) data.encode(with: state) } - - func setInitialWindowPosition(_ window: NSWindow, x: Int16?, y: Int16?, windowDecorations: Bool) { - if let primaryScreen = NSScreen.screens.first { - let frame = primaryScreen.visibleFrame - - if let windowPositionX = x, let windowPositionY = y { - // Offset titlebar if needed, otherwise use default padding of 12 - // NOTE: Not 100% certain where this extra padding comes from but I'd love - // to calculate it dynamically if possible - let titlebarHeight = windowDecorations ? window.frame.height - (window.contentView?.frame.height ?? 0) : 12 - - // Orient based on the top left of the primary monitor - let startPositionX = frame.origin.x + CGFloat(windowPositionX) - let startPositionY = (frame.origin.y + frame.height) - (CGFloat(windowPositionY) + window.frame.height) + titlebarHeight - - window.setFrameOrigin(NSPoint(x: startPositionX, y: startPositionY)) - } else { - // Fallback to original centering behavior - window.center() - } - } else { - // Fallback to original centering behavior - window.center() - } - } // MARK: First Responder diff --git a/macos/Sources/Ghostty/Ghostty.Config.swift b/macos/Sources/Ghostty/Ghostty.Config.swift index b6ae5de966..82d17a2de6 100644 --- a/macos/Sources/Ghostty/Ghostty.Config.swift +++ b/macos/Sources/Ghostty/Ghostty.Config.swift @@ -150,17 +150,17 @@ extension Ghostty { return String(cString: ptr) } - var windowInitialPositionX: Int16? { + var windowPositionX: Int16? { guard let config = self.config else { return nil } var v: Int16 = 0 - let key = "window-initial-position-x" + let key = "window-position-x" return ghostty_config_get(config, &v, key, UInt(key.count)) ? v : nil } - var windowInitialPositionY: Int16? { + var windowPositionY: Int16? { guard let config = self.config else { return nil } var v: Int16 = 0 - let key = "window-initial-position-y" + let key = "window-position-y" return ghostty_config_get(config, &v, key, UInt(key.count)) ? v : nil } diff --git a/src/apprt/glfw.zig b/src/apprt/glfw.zig index 5f48e0dd77..3fbef0f229 100644 --- a/src/apprt/glfw.zig +++ b/src/apprt/glfw.zig @@ -149,14 +149,10 @@ pub const App = struct { value: apprt.Action.Value(action), ) !void { switch (action) { - .new_window => { - var surface = try self.newSurface(switch (target) { - .app => null, - .surface => |v| v, - }); - - try surface.setInitialWindowPosition(self.config.@"window-initial-position-x", self.config.@"window-initial-position-y"); - }, + .new_window => _ = try self.newSurface(switch (target) { + .app => null, + .surface => |v| v, + }), .new_tab => try self.newTab(switch (target) { .app => null, @@ -514,6 +510,13 @@ pub const Surface = struct { ) orelse return glfw.mustGetErrorCode(); errdefer win.destroy(); + // Setup our + setInitialWindowPosition( + win, + app.config.@"window-position-x", + app.config.@"window-position-y", + ); + // Get our physical DPI - debug only because we don't have a use for // this but the logging of it may be useful if (builtin.mode == .Debug) { @@ -670,12 +673,12 @@ pub const Surface = struct { /// Set the initial window position. This is called exactly once at /// surface initialization time. This may be called before "self" /// is fully initialized. - fn setInitialWindowPosition(self: *const Surface, x: ?i16, y: ?i16) !void { + fn setInitialWindowPosition(win: glfw.Window, x: ?i16, y: ?i16) void { const start_position_x = x orelse return; const start_position_y = y orelse return; log.debug("setting initial window position ({},{})", .{ start_position_x, start_position_y }); - self.window.setPos(.{ .x = start_position_x, .y = start_position_y }); + win.setPos(.{ .x = start_position_x, .y = start_position_y }); } /// Set the size limits of the window. diff --git a/src/config/Config.zig b/src/config/Config.zig index 7d8d648df8..153ede2e5d 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -1108,7 +1108,7 @@ keybind: Keybinds = .{}, @"window-height": u32 = 0, @"window-width": u32 = 0, -/// The initial window position. This position is in pixels and is relative +/// The starting window position. This position is in pixels and is relative /// to the top-left corner of the primary monitor. Both values must be set to take /// effect. If only one value is set, it is ignored. /// @@ -1117,10 +1117,22 @@ keybind: Keybinds = .{}, /// to be a certain position to fit within the grid. There is nothing Ghostty /// will do about this, but it will make an effort. /// -/// Important note: Setting this value will only work on macOS and glfw builds -/// on Linux. GTK 4.0 does not support setting the window position. -@"window-initial-position-x": ?i16 = null, -@"window-initial-position-y": ?i16 = null, +/// Also note that negative values are also up to the operating system and +/// window manager. Some window managers may not allow windows to be placed +/// off-screen. +/// +/// Invalid positions are runtime-specific, but generally the positions are +/// clamped to the nearest valid position. +/// +/// On macOS, the window position is relative to the top-left corner of +/// the visible screen area. This means that if the menu bar is visible, the +/// window will be placed below the menu bar. +/// +/// Note: this is only supported on macOS and Linux GLFW builds. The GTK +/// runtime does not support setting the window position (this is a limitation +/// of GTK 4.0). +@"window-position-x": ?i16 = null, +@"window-position-y": ?i16 = null, /// Whether to enable saving and restoring window state. Window state includes /// their position, size, tabs, splits, etc. Some window state requires shell