mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-13 19:15:48 +00:00
fix: calculate cell size before presenting gtk window (#10459)
Fixes #7937 Added `computeInitialSize` to GTK `Surface` and call it in GTK `Application` before the first `present()`, so the window manager centers the correct size on initial show. The issue occurs because the core `Surface.recomputeInitialSize()` runs only after the renderer is initialized. In GTK, the `GLArea` isn’t realized until after `present()`, so the initial size arrives too late for WM centering. **Limitations**: when we precompute size before `present()` we do not have access to padding, so the sizing will be very slightly off... but since it is only off a few pixels I was unable to tell visually that it wasn't perfectly centered. **Other thoughts**: I was hesitant to make changes to core `Surface` because the issue is Linux-specific, but it may make sense to extract a helper from `recomputeInitialSize` to avoid duplicating the sizing math. **AI Disclosure:** I used AI to explore the project, help with any language / API questions (I've never used zig before and rarely use gtk), and make implementation suggestions.
This commit is contained in:
@@ -46,8 +46,8 @@ const Renderer = rendererpkg.Renderer;
|
||||
/// being resized to a size that is too small to be useful. These defaults
|
||||
/// are chosen to match the default size of Mac's Terminal.app, but is
|
||||
/// otherwise somewhat arbitrary.
|
||||
const min_window_width_cells: u32 = 10;
|
||||
const min_window_height_cells: u32 = 4;
|
||||
pub const min_window_width_cells: u32 = 10;
|
||||
pub const min_window_height_cells: u32 = 4;
|
||||
|
||||
/// The maximum number of key tables that can be active at any
|
||||
/// given time. `activate_key_table` calls after this are ignored.
|
||||
|
||||
@@ -2182,6 +2182,18 @@ const Action = struct {
|
||||
// Create a new tab with window context (first tab in new window)
|
||||
win.newTabForWindow(parent);
|
||||
|
||||
// Estimate the initial window size before presenting so the window
|
||||
// manager can position it correctly.
|
||||
if (win.getActiveSurface()) |surface| {
|
||||
surface.estimateInitialSize();
|
||||
if (surface.getDefaultSize()) |size| {
|
||||
win.as(gtk.Window).setDefaultSize(
|
||||
@intCast(size.width),
|
||||
@intCast(size.height),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Show the window
|
||||
gtk.Window.present(win.as(gtk.Window));
|
||||
}
|
||||
|
||||
@@ -2005,6 +2005,55 @@ pub const Surface = extern struct {
|
||||
self.as(gobject.Object).notifyByPspec(properties.@"default-size".impl.param_spec);
|
||||
}
|
||||
|
||||
/// Estimate and set the initial window size from config and font metrics.
|
||||
/// This can be called before the core surface exists to set up the window
|
||||
/// size before presenting. This is an estimate because it does not take
|
||||
/// into account any padding that may need to be added to the window.
|
||||
pub fn estimateInitialSize(self: *Self) void {
|
||||
const priv: *Private = self.private();
|
||||
const config_obj = priv.config orelse return;
|
||||
const config = config_obj.get();
|
||||
|
||||
// Both dimensions must be configured
|
||||
if (config.@"window-height" <= 0 or config.@"window-width" <= 0) return;
|
||||
|
||||
const app = Application.default();
|
||||
const alloc = app.allocator();
|
||||
|
||||
// Get content scale and compute DPI
|
||||
const content_scale = self.getContentScale();
|
||||
const x_dpi = content_scale.x * font.face.default_dpi;
|
||||
const y_dpi = content_scale.y * font.face.default_dpi;
|
||||
|
||||
const font_size: font.face.DesiredSize = .{
|
||||
.points = config.@"font-size",
|
||||
.xdpi = @intFromFloat(x_dpi),
|
||||
.ydpi = @intFromFloat(y_dpi),
|
||||
};
|
||||
|
||||
// Get font grid for cell metrics
|
||||
var derived_config = font.SharedGridSet.DerivedConfig.init(alloc, config) catch return;
|
||||
defer derived_config.deinit();
|
||||
|
||||
const font_grid_key, const font_grid = app.core().font_grid_set.ref(
|
||||
&derived_config,
|
||||
font_size,
|
||||
) catch return;
|
||||
defer app.core().font_grid_set.deref(font_grid_key);
|
||||
|
||||
const cell = font_grid.cellSize();
|
||||
|
||||
const width = @max(CoreSurface.min_window_width_cells, config.@"window-width") * cell.width;
|
||||
const height = @max(CoreSurface.min_window_height_cells, config.@"window-height") * cell.height;
|
||||
const width_f32: f32 = @floatFromInt(width);
|
||||
const height_f32: f32 = @floatFromInt(height);
|
||||
|
||||
const final_width: u32 = @intFromFloat(@ceil(width_f32 / content_scale.x));
|
||||
const final_height: u32 = @intFromFloat(@ceil(height_f32 / content_scale.y));
|
||||
|
||||
self.setDefaultSize(.{ .width = final_width, .height = final_height });
|
||||
}
|
||||
|
||||
/// Get the key sequence list. Full transfer.
|
||||
fn getKeySequence(self: *Self) ?*ext.StringList {
|
||||
const priv = self.private();
|
||||
|
||||
Reference in New Issue
Block a user