From 604eeceb0344043ad0479f050550bf380f810272 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 22 Sep 2023 18:54:58 -0700 Subject: [PATCH 1/6] apprt/glfw: support window-width, window-height configurations --- src/apprt/glfw.zig | 9 +++++++++ src/config/Config.zig | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/apprt/glfw.zig b/src/apprt/glfw.zig index e4acab289..e69aa1479 100644 --- a/src/apprt/glfw.zig +++ b/src/apprt/glfw.zig @@ -376,6 +376,15 @@ pub const Surface = struct { self, ); errdefer self.core_surface.deinit(); + + // If we have a desired window size, we can now calculate the size + // because we have the cell size. + if (config.@"window-height" > 0 or config.@"window-width" > 0) { + self.window.setSize(.{ + .height = @max(config.@"window-height" * self.core_surface.cell_size.height, 480), + .width = @max(config.@"window-width" * self.core_surface.cell_size.width, 640), + }); + } } pub fn deinit(self: *Surface) void { diff --git a/src/config/Config.zig b/src/config/Config.zig index 1b6e8d84b..8ba3b02c1 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -300,6 +300,26 @@ keybind: Keybinds = .{}, /// This is currently only supported on macOS. @"window-theme": WindowTheme = .system, +/// The initial window size. This size is in terminal grid cells by default. +/// +/// We don't currently support specifying a size in pixels but a future +/// change can enable that. If this isn't specified, the app runtime will +/// determine some default size. +/// +/// Note that the window manager may put limits on the size or override +/// the size. For example, a tiling window manager may force the window +/// to be a certain size to fit within the grid. There is nothing Ghostty +/// will do about this, but it will make an effort. +/// +/// This will not affect new tabs, splits, or other nested terminal +/// elements. This only affects the initial window size of any new window. +/// Changing this value will not affect the size of the window after +/// it has been created. This is only used for the initial size. +/// +/// Windows smaller than 10 wide by 4 high are not allowed. +@"window-height": u32 = 0, +@"window-width": u32 = 0, + /// Whether to allow programs running in the terminal to read/write to /// the system clipboard (OSC 52, for googling). The default is to /// disallow clipboard reading but allow writing. @@ -1007,6 +1027,10 @@ pub fn finalize(self: *Config) !void { // Clamp our split opacity self.@"unfocused-split-opacity" = @min(1.0, @max(0.15, self.@"unfocused-split-opacity")); + + // Minimmum window size + if (self.@"window-width" > 0) self.@"window-width" = @max(10, self.@"window-width"); + if (self.@"window-height" > 0) self.@"window-height" = @max(4, self.@"window-height"); } /// Create a shallow copy of this config. This will share all the memory From 8cb96a28c1a78e00f9546eb805529f6cd2e4f9a0 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 30 Sep 2023 20:15:07 -0700 Subject: [PATCH 2/6] config: increase eval branches for new configs --- src/config/CAPI.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/CAPI.zig b/src/config/CAPI.zig index 1912403bc..09ea27c70 100644 --- a/src/config/CAPI.zig +++ b/src/config/CAPI.zig @@ -86,6 +86,7 @@ export fn ghostty_config_get( key_str: [*]const u8, len: usize, ) bool { + @setEvalBranchQuota(10_000); const key = std.meta.stringToEnum(Key, key_str[0..len]) orelse return false; return c_get.get(self, key, ptr); } From cc8e1cd936195648badd6c61fa0381a86a044a60 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 30 Sep 2023 20:47:31 -0700 Subject: [PATCH 3/6] macos: support initial window size --- include/ghostty.h | 10 +++++ .../PrimaryWindowController.swift | 3 ++ .../Features/Settings/ConfigurationErrors.xib | 4 +- macos/Sources/Ghostty/Package.swift | 4 ++ macos/Sources/Ghostty/SurfaceView.swift | 45 ++++++++++++++++++- src/apprt/embedded.zig | 25 +++++++++++ 6 files changed, 88 insertions(+), 3 deletions(-) diff --git a/include/ghostty.h b/include/ghostty.h index 42e83db5f..198fe08dc 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -291,6 +291,15 @@ typedef enum { GHOSTTY_BUILD_MODE_RELEASE_SMALL, } ghostty_build_mode_e; +typedef struct { + bool origin; + bool size; + uint32_t x; + uint32_t y; + uint32_t w; + uint32_t h; +} ghostty_rect_s; + // Fully defined types. This MUST be kept in sync with equivalent Zig // structs. To find the Zig struct, grep for this type name. The documentation // for all of these types is available in the Zig source. @@ -380,6 +389,7 @@ ghostty_surface_t ghostty_surface_new(ghostty_app_t, ghostty_surface_config_s*); void ghostty_surface_free(ghostty_surface_t); ghostty_app_t ghostty_surface_app(ghostty_surface_t); bool ghostty_surface_transparent(ghostty_surface_t); +ghostty_rect_s ghostty_surface_window_frame(ghostty_surface_t); void ghostty_surface_refresh(ghostty_surface_t); void ghostty_surface_set_content_scale(ghostty_surface_t, double, double); void ghostty_surface_set_focus(ghostty_surface_t, bool); diff --git a/macos/Sources/Features/Primary Window/PrimaryWindowController.swift b/macos/Sources/Features/Primary Window/PrimaryWindowController.swift index 0d2389cd7..6add783e3 100644 --- a/macos/Sources/Features/Primary Window/PrimaryWindowController.swift +++ b/macos/Sources/Features/Primary Window/PrimaryWindowController.swift @@ -4,6 +4,9 @@ class PrimaryWindowController: NSWindowController, NSWindowDelegate { // This is used to programmatically control tabs. weak var windowManager: PrimaryWindowManager? + // This should be set to true once a surface has been initialized once. + var didInitializeFromSurface: Bool = false + // This is required for the "+" button to show up in the tab bar to add a // new tab. override func newWindowForTab(_ sender: Any?) { diff --git a/macos/Sources/Features/Settings/ConfigurationErrors.xib b/macos/Sources/Features/Settings/ConfigurationErrors.xib index d3f94b14d..dcd40ad21 100644 --- a/macos/Sources/Features/Settings/ConfigurationErrors.xib +++ b/macos/Sources/Features/Settings/ConfigurationErrors.xib @@ -1,8 +1,8 @@ - + - + diff --git a/macos/Sources/Ghostty/Package.swift b/macos/Sources/Ghostty/Package.swift index 81404fbfb..8f97a090d 100644 --- a/macos/Sources/Ghostty/Package.swift +++ b/macos/Sources/Ghostty/Package.swift @@ -98,6 +98,10 @@ extension Ghostty.Notification { /// Notification sent to toggle split maximize/unmaximize. static let didToggleSplitZoom = Notification.Name("com.mitchellh.ghostty.didToggleSplitZoom") + + /// Notification + static let didReceiveInitialWindowFrame = Notification.Name("com.mitchellh.ghostty.didReceiveInitialWindowFrame") + static let FrameKey = "com.mitchellh.ghostty.frame" } // Make the input enum hashable. diff --git a/macos/Sources/Ghostty/SurfaceView.swift b/macos/Sources/Ghostty/SurfaceView.swift index d33563ff2..ac2788796 100644 --- a/macos/Sources/Ghostty/SurfaceView.swift +++ b/macos/Sources/Ghostty/SurfaceView.swift @@ -71,6 +71,7 @@ extension Ghostty { // We use these notifications to determine when the window our surface is // attached to is or is not focused. let pubBecomeFocused = NotificationCenter.default.publisher(for: Notification.didBecomeFocusedSurface, object: surfaceView) + let pubInitialFrame = NotificationCenter.default.publisher(for: Notification.didReceiveInitialWindowFrame, object: surfaceView) let pubBecomeKey = NotificationCenter.default.publisher(for: NSWindow.didBecomeKeyNotification) let pubResign = NotificationCenter.default.publisher(for: NSWindow.didResignKeyNotification) @@ -100,6 +101,26 @@ extension Ghostty { surfaceFocus = true } } + .onReceive(pubInitialFrame) { notification in + // We never set the initial frame if we're a split + guard !isSplit else { return } + + // We need a window to set the frame + guard let surfaceWindow = surfaceView.window else { return } + guard let frameAny = notification.userInfo?[Ghostty.Notification.FrameKey] else { return } + guard let frame = frameAny as? NSRect else { return } + + // If we have tabs, then do not change the window size + guard let windowControllerRaw = surfaceWindow.windowController else { return } + guard let windowController = windowControllerRaw as? PrimaryWindowController else { return } + guard !windowController.didInitializeFromSurface else { return } + + // We have no tabs and we are not a split, so set the initial size of the window. + surfaceWindow.setFrame(frame, display: true) + + // Note that we did initialize + windowController.didInitializeFromSurface = true + } .onAppear() { // Welcome to the SwiftUI bug house of horrors. On macOS 12 (at least // 12.5.1, didn't test other versions), the order in which the view @@ -191,7 +212,7 @@ extension Ghostty { // changed with escape codes. This is public because the callbacks go // to the app level and it is set from there. @Published var title: String = "👻" - + private(set) var surface: ghostty_surface_t? var error: Error? = nil @@ -407,6 +428,28 @@ extension Ghostty { // If we have a blur, set the blur ghostty_set_window_background_blur(surface, Unmanaged.passUnretained(window).toOpaque()) + + // Set the window size + let rect = ghostty_surface_window_frame(surface) + if (rect.size || rect.origin) { + var frame = window.frame + if (rect.origin) { + frame.origin.x = Double(rect.x) + frame.origin.y = Double(rect.y) + } + if (rect.size) { + frame.size.width = Double(rect.w) + frame.size.height = Double(rect.h) + } + + NotificationCenter.default.post( + name: Notification.didReceiveInitialWindowFrame, + object: self, + userInfo: [ + Notification.FrameKey: frame, + ] + ) + } } override func becomeFirstResponder() -> Bool { diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index 36240b7f0..ecfb08012 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -757,6 +757,16 @@ pub const Surface = struct { pub const CAPI = struct { const global = &@import("../main.zig").state; + /// ghostty_rect_s + const Rect = extern struct { + origin: bool = false, + size: bool = false, + x: u32 = 0, + y: u32 = 0, + w: u32 = 0, + h: u32 = 0, + }; + /// Create a new app. export fn ghostty_app_new( opts: *const apprt.runtime.App.Options, @@ -864,6 +874,21 @@ pub const CAPI = struct { return surface.app.config.@"background-opacity" < 1.0; } + /// The desired window frame for a new window. + export fn ghostty_surface_window_frame(surface: *Surface) Rect { + const config = surface.app.config; + + // If the desired height/width isn't configured, return 0. + if (config.@"window-height" == 0 or config.@"window-width" == 0) return .{}; + + // Return the desired rect + return .{ + .size = true, + .h = @max(config.@"window-height" * surface.core_surface.cell_size.height, 480), + .w = @max(config.@"window-width" * surface.core_surface.cell_size.width, 640), + }; + } + /// Tell the surface that it needs to schedule a render export fn ghostty_surface_refresh(surface: *Surface) void { surface.refresh(); From a1a8aeb104e9fb47f1396ddb0572b056533ffeaa Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 30 Sep 2023 21:35:50 -0700 Subject: [PATCH 4/6] initial window size needs to take into account window chrome --- include/ghostty.h | 12 +--- macos/Sources/Ghostty/AppState.swift | 9 ++- macos/Sources/Ghostty/SurfaceView.swift | 79 ++++++++++++------------- src/Surface.zig | 30 ++++++++++ src/apprt/embedded.zig | 38 ++++-------- src/apprt/glfw.zig | 16 +++-- 6 files changed, 97 insertions(+), 87 deletions(-) diff --git a/include/ghostty.h b/include/ghostty.h index 198fe08dc..1a0a7c9c6 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -291,15 +291,6 @@ typedef enum { GHOSTTY_BUILD_MODE_RELEASE_SMALL, } ghostty_build_mode_e; -typedef struct { - bool origin; - bool size; - uint32_t x; - uint32_t y; - uint32_t w; - uint32_t h; -} ghostty_rect_s; - // Fully defined types. This MUST be kept in sync with equivalent Zig // structs. To find the Zig struct, grep for this type name. The documentation // for all of these types is available in the Zig source. @@ -335,6 +326,7 @@ typedef void (*ghostty_runtime_focus_split_cb)(void *, ghostty_split_focus_direc typedef void (*ghostty_runtime_toggle_split_zoom_cb)(void *); typedef void (*ghostty_runtime_goto_tab_cb)(void *, int32_t); typedef void (*ghostty_runtime_toggle_fullscreen_cb)(void *, ghostty_non_native_fullscreen_e); +typedef void (*ghostty_runtime_set_initial_window_size_cb)(void *, uint32_t, uint32_t); typedef struct { void *userdata; @@ -354,6 +346,7 @@ typedef struct { ghostty_runtime_toggle_split_zoom_cb toggle_split_zoom_cb; ghostty_runtime_goto_tab_cb goto_tab_cb; ghostty_runtime_toggle_fullscreen_cb toggle_fullscreen_cb; + ghostty_runtime_set_initial_window_size_cb set_initial_window_size_cb; } ghostty_runtime_config_s; //------------------------------------------------------------------- @@ -389,7 +382,6 @@ ghostty_surface_t ghostty_surface_new(ghostty_app_t, ghostty_surface_config_s*); void ghostty_surface_free(ghostty_surface_t); ghostty_app_t ghostty_surface_app(ghostty_surface_t); bool ghostty_surface_transparent(ghostty_surface_t); -ghostty_rect_s ghostty_surface_window_frame(ghostty_surface_t); void ghostty_surface_refresh(ghostty_surface_t); void ghostty_surface_set_content_scale(ghostty_surface_t, double, double); void ghostty_surface_set_focus(ghostty_surface_t, bool); diff --git a/macos/Sources/Ghostty/AppState.swift b/macos/Sources/Ghostty/AppState.swift index 5cf39f044..e01ce96af 100644 --- a/macos/Sources/Ghostty/AppState.swift +++ b/macos/Sources/Ghostty/AppState.swift @@ -125,7 +125,8 @@ extension Ghostty { focus_split_cb: { userdata, direction in AppState.focusSplit(userdata, direction: direction) }, toggle_split_zoom_cb: { userdata in AppState.toggleSplitZoom(userdata) }, goto_tab_cb: { userdata, n in AppState.gotoTab(userdata, n: n) }, - toggle_fullscreen_cb: { userdata, nonNativeFullscreen in AppState.toggleFullscreen(userdata, nonNativeFullscreen: nonNativeFullscreen) } + toggle_fullscreen_cb: { userdata, nonNativeFullscreen in AppState.toggleFullscreen(userdata, nonNativeFullscreen: nonNativeFullscreen) }, + set_initial_window_size_cb: { userdata, width, height in AppState.setInitialWindowSize(userdata, width: width, height: height) } ) // Create the ghostty app. @@ -413,6 +414,12 @@ extension Ghostty { ] ) } + + static func setInitialWindowSize(_ userdata: UnsafeMutableRawPointer?, width: UInt32, height: UInt32) { + // We need a window to set the frame + guard let surfaceView = self.surfaceUserdata(from: userdata) else { return } + surfaceView.initialSize = NSMakeSize(Double(width), Double(height)) + } static func newTab(_ userdata: UnsafeMutableRawPointer?, config: ghostty_surface_config_s) { guard let surface = self.surfaceUserdata(from: userdata) else { return } diff --git a/macos/Sources/Ghostty/SurfaceView.swift b/macos/Sources/Ghostty/SurfaceView.swift index ac2788796..d686055c9 100644 --- a/macos/Sources/Ghostty/SurfaceView.swift +++ b/macos/Sources/Ghostty/SurfaceView.swift @@ -71,7 +71,6 @@ extension Ghostty { // We use these notifications to determine when the window our surface is // attached to is or is not focused. let pubBecomeFocused = NotificationCenter.default.publisher(for: Notification.didBecomeFocusedSurface, object: surfaceView) - let pubInitialFrame = NotificationCenter.default.publisher(for: Notification.didReceiveInitialWindowFrame, object: surfaceView) let pubBecomeKey = NotificationCenter.default.publisher(for: NSWindow.didBecomeKeyNotification) let pubResign = NotificationCenter.default.publisher(for: NSWindow.didResignKeyNotification) @@ -101,26 +100,6 @@ extension Ghostty { surfaceFocus = true } } - .onReceive(pubInitialFrame) { notification in - // We never set the initial frame if we're a split - guard !isSplit else { return } - - // We need a window to set the frame - guard let surfaceWindow = surfaceView.window else { return } - guard let frameAny = notification.userInfo?[Ghostty.Notification.FrameKey] else { return } - guard let frame = frameAny as? NSRect else { return } - - // If we have tabs, then do not change the window size - guard let windowControllerRaw = surfaceWindow.windowController else { return } - guard let windowController = windowControllerRaw as? PrimaryWindowController else { return } - guard !windowController.didInitializeFromSurface else { return } - - // We have no tabs and we are not a split, so set the initial size of the window. - surfaceWindow.setFrame(frame, display: true) - - // Note that we did initialize - windowController.didInitializeFromSurface = true - } .onAppear() { // Welcome to the SwiftUI bug house of horrors. On macOS 12 (at least // 12.5.1, didn't test other versions), the order in which the view @@ -213,6 +192,10 @@ extension Ghostty { // to the app level and it is set from there. @Published var title: String = "👻" + // An initial size to request for a window. This will only affect + // then the view is moved to a new window. + var initialSize: NSSize? = nil + private(set) var surface: ghostty_surface_t? var error: Error? = nil @@ -429,27 +412,39 @@ extension Ghostty { // If we have a blur, set the blur ghostty_set_window_background_blur(surface, Unmanaged.passUnretained(window).toOpaque()) - // Set the window size - let rect = ghostty_surface_window_frame(surface) - if (rect.size || rect.origin) { - var frame = window.frame - if (rect.origin) { - frame.origin.x = Double(rect.x) - frame.origin.y = Double(rect.y) - } - if (rect.size) { - frame.size.width = Double(rect.w) - frame.size.height = Double(rect.h) - } - - NotificationCenter.default.post( - name: Notification.didReceiveInitialWindowFrame, - object: self, - userInfo: [ - Notification.FrameKey: frame, - ] - ) - } + // Try to set the initial window size if we have one + setInitialWindowSize() + } + + /// Sets the initial window size requested by the Ghostty config. + /// + /// This only works under certain conditions: + /// - The window must be "uninitialized" + /// - The window must have no tabs + /// - Ghostty must have requested an initial size + /// + private func setInitialWindowSize() { + guard let initialSize = initialSize else { return } + + // If we have tabs, then do not change the window size + guard let window = self.window else { return } + guard let windowControllerRaw = window.windowController else { return } + guard let windowController = windowControllerRaw as? PrimaryWindowController else { return } + guard !windowController.didInitializeFromSurface else { return } + + // Setup our frame. We need to first subtract the views frame so that we can + // just get the chrome frame so that we only affect the surface view size. + var frame = window.frame + frame.size.width -= self.frame.size.width + frame.size.height -= self.frame.size.height + frame.size.width += initialSize.width + frame.size.height += initialSize.height + + // We have no tabs and we are not a split, so set the initial size of the window. + window.setFrame(frame, display: true) + + // Note that we did initialize + windowController.didInitializeFromSurface = true } override func becomeFirstResponder() -> Bool { diff --git a/src/Surface.zig b/src/Surface.zig index 5711edf08..172d2495a 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -471,6 +471,36 @@ pub fn init( .{&self.io_thread}, ); self.io_thr.setName("io") catch {}; + + // Determine our initial window size if configured. We need to do this + // quite late in the process because our height/width are in grid dimensions, + // so we need to know our cell sizes first. + // + // Note: it is important to do this after the renderer is setup above. + // This allows the apprt to fully initialize the surface before we + // start messing with the window. + if (config.@"window-height" > 0 and config.@"window-width" > 0) init: { + const scale = rt_surface.getContentScale() catch break :init; + const height = @max(config.@"window-height" * cell_size.height, 480); + const width = @max(config.@"window-width" * cell_size.width, 640); + const width_f32: f32 = @floatFromInt(width); + const height_f32: f32 = @floatFromInt(height); + + // The final values are affected by content scale and we need to + // account for the padding so we get the exact correct grid size. + const final_width: u32 = + @as(u32, @intFromFloat(@ceil(width_f32 / scale.x))) + + padding.left + + padding.right; + const final_height: u32 = + @as(u32, @intFromFloat(@ceil(height_f32 / scale.y))) + + padding.top + + padding.bottom; + + rt_surface.setInitialWindowSize(final_width, final_height) catch |err| { + log.warn("unable to set initial window size: {s}", .{err}); + }; + } } pub fn deinit(self: *Surface) void { diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index ecfb08012..da5a60273 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -87,6 +87,10 @@ pub const App = struct { /// Toggle fullscreen for current window. toggle_fullscreen: ?*const fn (SurfaceUD, configpkg.NonNativeFullscreen) callconv(.C) void = null, + + /// Set the initial window size. It is up to the user of libghostty to + /// determine if it is the initial window and set this appropriately. + set_initial_window_size: ?*const fn (SurfaceUD, u32, u32) callconv(.C) void = null, }; /// Special values for the goto_tab callback. @@ -734,6 +738,15 @@ pub const Surface = struct { func(self.opts.userdata, options); } + pub fn setInitialWindowSize(self: *const Surface, width: u32, height: u32) !void { + const func = self.app.opts.set_initial_window_size orelse { + log.info("runtime embedder does not set_initial_window_size", .{}); + return; + }; + + func(self.opts.userdata, width, height); + } + fn newSurfaceOptions(self: *const Surface) apprt.Surface.Options { const font_size: u16 = font_size: { if (!self.app.config.@"window-inherit-font-size") break :font_size 0; @@ -757,16 +770,6 @@ pub const Surface = struct { pub const CAPI = struct { const global = &@import("../main.zig").state; - /// ghostty_rect_s - const Rect = extern struct { - origin: bool = false, - size: bool = false, - x: u32 = 0, - y: u32 = 0, - w: u32 = 0, - h: u32 = 0, - }; - /// Create a new app. export fn ghostty_app_new( opts: *const apprt.runtime.App.Options, @@ -874,21 +877,6 @@ pub const CAPI = struct { return surface.app.config.@"background-opacity" < 1.0; } - /// The desired window frame for a new window. - export fn ghostty_surface_window_frame(surface: *Surface) Rect { - const config = surface.app.config; - - // If the desired height/width isn't configured, return 0. - if (config.@"window-height" == 0 or config.@"window-width" == 0) return .{}; - - // Return the desired rect - return .{ - .size = true, - .h = @max(config.@"window-height" * surface.core_surface.cell_size.height, 480), - .w = @max(config.@"window-width" * surface.core_surface.cell_size.width, 640), - }; - } - /// Tell the surface that it needs to schedule a render export fn ghostty_surface_refresh(surface: *Surface) void { surface.refresh(); diff --git a/src/apprt/glfw.zig b/src/apprt/glfw.zig index e69aa1479..4a40ed22e 100644 --- a/src/apprt/glfw.zig +++ b/src/apprt/glfw.zig @@ -376,15 +376,6 @@ pub const Surface = struct { self, ); errdefer self.core_surface.deinit(); - - // If we have a desired window size, we can now calculate the size - // because we have the cell size. - if (config.@"window-height" > 0 or config.@"window-width" > 0) { - self.window.setSize(.{ - .height = @max(config.@"window-height" * self.core_surface.cell_size.height, 480), - .width = @max(config.@"window-width" * self.core_surface.cell_size.width, 640), - }); - } } pub fn deinit(self: *Surface) void { @@ -456,6 +447,13 @@ pub const Surface = struct { self.app.app.alloc.destroy(self); } + /// Set the initial window size. This is called exactly once at + /// surface initialization time. This may be called before "self" + /// is fully initialized. + pub fn setInitialWindowSize(self: *const Surface, width: u32, height: u32) !void { + self.window.setSize(.{ .width = width, .height = height }); + } + /// Set the size limits of the window. /// Note: this interface is not good, we should redo it if we plan /// to use this more. i.e. you can't set max width but no max height, From 769e7df675107da99bc6e803295f343ae8d28c7b Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 30 Sep 2023 21:41:17 -0700 Subject: [PATCH 5/6] apprt/gtk: implement default window size --- src/apprt/gtk/Surface.zig | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index b66c053e9..2638eca9d 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -325,6 +325,16 @@ pub fn getSize(self: *const Surface) !apprt.SurfaceSize { return self.size; } +pub fn setInitialWindowSize(self: *const Surface, width: u32, height: u32) !void { + // Note: this doesn't properly take into account the window decorations. + // I'm not currently sure how to do that. + c.gtk_window_set_default_size( + @ptrCast(self.window.window), + @intCast(width), + @intCast(height), + ); +} + pub fn setSizeLimits(self: *Surface, min: apprt.SurfaceSize, max_: ?apprt.SurfaceSize) !void { _ = self; _ = min; From cec45de8423f1806a9f32be8e23bbe735c9bd181 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 30 Sep 2023 21:48:22 -0700 Subject: [PATCH 6/6] config: note gtk bug --- src/config/Config.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/config/Config.zig b/src/config/Config.zig index 8ba3b02c1..f9913e440 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -316,6 +316,11 @@ keybind: Keybinds = .{}, /// Changing this value will not affect the size of the window after /// it has been created. This is only used for the initial size. /// +/// BUG: On Linux with GTK, the calculated window size will not properly +/// take into account window decorations. As a result, the grid dimensions +/// will not exactly match this configuration. If window decorations are +/// disabled (see window-decorations), then this will work as expected. +/// /// Windows smaller than 10 wide by 4 high are not allowed. @"window-height": u32 = 0, @"window-width": u32 = 0,