diff --git a/include/ghostty.h b/include/ghostty.h index 22d42449c..890e5f50e 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -379,7 +379,7 @@ typedef struct { ghostty_platform_u platform; void* userdata; double scale_factor; - uint8_t font_size; + float font_size; const char* working_directory; const char* command; } ghostty_surface_config_s; diff --git a/macos/Sources/Ghostty/SurfaceView.swift b/macos/Sources/Ghostty/SurfaceView.swift index d007a898c..4d755e70e 100644 --- a/macos/Sources/Ghostty/SurfaceView.swift +++ b/macos/Sources/Ghostty/SurfaceView.swift @@ -252,7 +252,7 @@ extension Ghostty { /// libghostty, usually from the Ghostty configuration. struct SurfaceConfiguration { /// Explicit font size to use in points - var fontSize: UInt8? = nil + var fontSize: Float32? = nil /// Explicit working directory to set var workingDirectory: String? = nil diff --git a/pkg/harfbuzz/build.zig b/pkg/harfbuzz/build.zig index 1329250ba..45d242699 100644 --- a/pkg/harfbuzz/build.zig +++ b/pkg/harfbuzz/build.zig @@ -41,7 +41,11 @@ pub fn build(b: *std.Build) !void { try apple_sdk.addPaths(b, module); } - const freetype_dep = b.dependency("freetype", .{ .target = target, .optimize = optimize }); + const freetype_dep = b.dependency("freetype", .{ + .target = target, + .optimize = optimize, + .@"enable-libpng" = true, + }); lib.linkLibrary(freetype_dep.artifact("freetype")); module.addIncludePath(freetype_dep.builder.dependency("freetype", .{}).path("include")); diff --git a/src/Surface.zig b/src/Surface.zig index 8266f3dbf..fea791be1 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -194,7 +194,7 @@ const DerivedConfig = struct { arena: ArenaAllocator, /// For docs for these, see the associated config they are derived from. - original_font_size: u8, + original_font_size: f32, keybind: configpkg.Keybinds, clipboard_read: configpkg.ClipboardAccess, clipboard_write: configpkg.ClipboardAccess, @@ -3008,18 +3008,25 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool ), .increase_font_size => |delta| { - log.debug("increase font size={}", .{delta}); + // Max delta is somewhat arbitrary. + const clamped_delta = @max(0, @min(255, delta)); + + log.debug("increase font size={}", .{clamped_delta}); var size = self.font_size; - size.points +|= delta; + // Max point size is somewhat arbitrary. + size.points = @min(size.points + clamped_delta, 255); try self.setFontSize(size); }, .decrease_font_size => |delta| { - log.debug("decrease font size={}", .{delta}); + // Max delta is somewhat arbitrary. + const clamped_delta = @max(0, @min(255, delta)); + + log.debug("decrease font size={}", .{clamped_delta}); var size = self.font_size; - size.points = @max(1, size.points -| delta); + size.points = @max(1, size.points - clamped_delta); try self.setFontSize(size); }, diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index 341de5eb7..4c9db1988 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -314,7 +314,7 @@ pub const Surface = struct { scale_factor: f64 = 1, /// The font size to inherit. If 0, default font size will be used. - font_size: u8 = 0, + font_size: f32 = 0, /// The working directory to load into. working_directory: [*:0]const u8 = "", @@ -1049,7 +1049,7 @@ pub const Surface = struct { } fn newSurfaceOptions(self: *const Surface) apprt.Surface.Options { - const font_size: u8 = font_size: { + const font_size: f32 = font_size: { if (!self.app.config.@"window-inherit-font-size") break :font_size 0; break :font_size self.core_surface.font_size.points; }; diff --git a/src/cli/args.zig b/src/cli/args.zig index 49c5152ac..707416e38 100644 --- a/src/cli/args.zig +++ b/src/cli/args.zig @@ -250,8 +250,10 @@ fn parseIntoField( 0, ) catch return error.InvalidValue, - f64 => std.fmt.parseFloat( - f64, + f32, + f64, + => |Float| std.fmt.parseFloat( + Float, value orelse return error.ValueRequired, ) catch return error.InvalidValue, diff --git a/src/config/Config.zig b/src/config/Config.zig index a2d6f39a8..c565d538d 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -99,8 +99,12 @@ const c = @cImport({ /// separate repetitive entries in your config). @"font-feature": RepeatableString = .{}, -/// Font size in points -@"font-size": u8 = switch (builtin.os.tag) { +/// Font size in points. This value can be a non-integer and the nearest integer +/// pixel size will be selected. If you have a high dpi display where 1pt = 2px +/// then you can get an odd numbered pixel size by specifying a half point. +/// +/// For example, 13.5pt @ 2px/pt = 27px +@"font-size": f32 = switch (builtin.os.tag) { // On macOS we default a little bigger since this tends to look better. This // is purely subjective but this is easy to modify. .macos => 13, diff --git a/src/config/c_get.zig b/src/config/c_get.zig index 442d5e6a3..ff3523c29 100644 --- a/src/config/c_get.zig +++ b/src/config/c_get.zig @@ -42,8 +42,8 @@ fn getValue(ptr_raw: *anyopaque, value: anytype) bool { ptr.* = @intCast(value); }, - f32, f64 => { - const ptr: *f64 = @ptrCast(@alignCast(ptr_raw)); + f32, f64 => |Float| { + const ptr: *Float = @ptrCast(@alignCast(ptr_raw)); ptr.* = @floatCast(value); }, @@ -102,9 +102,9 @@ test "u8" { defer c.deinit(); c.@"font-size" = 24; - var cval: c_uint = undefined; + var cval: f32 = undefined; try testing.expect(get(&c, .@"font-size", &cval)); - try testing.expectEqual(@as(c_uint, 24), cval); + try testing.expectEqual(@as(f32, 24), cval); } test "enum" { diff --git a/src/font/DeferredFace.zig b/src/font/DeferredFace.zig index de4293ba1..8051895a4 100644 --- a/src/font/DeferredFace.zig +++ b/src/font/DeferredFace.zig @@ -371,7 +371,7 @@ pub const Wasm = struct { } } - export fn deferred_face_load(self: *DeferredFace, pts: u16) void { + export fn deferred_face_load(self: *DeferredFace, pts: f32) void { self.load(.{}, .{ .points = pts }) catch |err| { log.warn("error loading deferred face err={}", .{err}); return; diff --git a/src/font/SharedGridSet.zig b/src/font/SharedGridSet.zig index daf39b54f..857368a13 100644 --- a/src/font/SharedGridSet.zig +++ b/src/font/SharedGridSet.zig @@ -575,7 +575,9 @@ pub const Key = struct { /// Hash the key with the given hasher. pub fn hash(self: Key, hasher: anytype) void { const autoHash = std.hash.autoHash; - autoHash(hasher, self.font_size); + autoHash(hasher, @as(u32, @bitCast(self.font_size.points))); + autoHash(hasher, self.font_size.xdpi); + autoHash(hasher, self.font_size.ydpi); autoHash(hasher, self.descriptors.len); for (self.descriptors) |d| d.hash(hasher); self.codepoint_map.hash(hasher); diff --git a/src/font/discovery.zig b/src/font/discovery.zig index a93574e07..c9176adc6 100644 --- a/src/font/discovery.zig +++ b/src/font/discovery.zig @@ -49,7 +49,7 @@ pub const Descriptor = struct { /// Font size in points that the font should support. For conversion /// to pixels, we will use 72 DPI for Mac and 96 DPI for everything else. /// (If pixel conversion is necessary, i.e. emoji fonts) - size: u16 = 0, + size: f32 = 0, /// True if we want to search specifically for a font that supports /// specific styles. @@ -69,7 +69,7 @@ pub const Descriptor = struct { autoHashStrat(hasher, self.family, .Deep); autoHashStrat(hasher, self.style, .Deep); autoHash(hasher, self.codepoint); - autoHash(hasher, self.size); + autoHash(hasher, @as(u32, @bitCast(self.size))); autoHash(hasher, self.bold); autoHash(hasher, self.italic); autoHash(hasher, self.monospace); @@ -125,7 +125,7 @@ pub const Descriptor = struct { } if (self.size > 0) assert(pat.add( .size, - .{ .integer = self.size }, + .{ .integer = @intFromFloat(@round(self.size)) }, false, )); if (self.bold) assert(pat.add( @@ -183,7 +183,7 @@ pub const Descriptor = struct { // Set our size attribute if set if (self.size > 0) { - const size32 = @as(i32, @intCast(self.size)); + const size32: i32 = @intFromFloat(@round(self.size)); const size = try macos.foundation.Number.create( .sint32, &size32, diff --git a/src/font/face.zig b/src/font/face.zig index 815629b44..8bcfb8209 100644 --- a/src/font/face.zig +++ b/src/font/face.zig @@ -35,7 +35,7 @@ pub const Options = struct { /// The desired size for loading a font. pub const DesiredSize = struct { // Desired size in points - points: u8, + points: f32, // The DPI of the screen so we can convert points to pixels. xdpi: u16 = default_dpi, @@ -44,8 +44,7 @@ pub const DesiredSize = struct { // Converts points to pixels pub fn pixels(self: DesiredSize) u16 { // 1 point = 1/72 inch - const points_u16: u16 = @intCast(self.points); - return (points_u16 * self.ydpi) / 72; + return @intFromFloat(@round((self.points * @as(f32, @floatFromInt(self.ydpi))) / 72)); } }; diff --git a/src/font/face/freetype.zig b/src/font/face/freetype.zig index d32217a02..2860503b4 100644 --- a/src/font/face/freetype.zig +++ b/src/font/face/freetype.zig @@ -137,7 +137,7 @@ pub const Face = struct { // to what the user requested. Otherwise, we can choose an arbitrary // pixel size. if (face.isScalable()) { - const size_26dot6 = @as(i32, @intCast(size.points)) << 6; // mult by 64 + const size_26dot6: i32 = @intFromFloat(@round(size.points * 64)); try face.setCharSize(0, size_26dot6, size.xdpi, size.ydpi); } else try selectSizeNearest(face, size.pixels()); } diff --git a/src/font/face/web_canvas.zig b/src/font/face/web_canvas.zig index 6470f6a8b..036dfa921 100644 --- a/src/font/face/web_canvas.zig +++ b/src/font/face/web_canvas.zig @@ -497,7 +497,7 @@ pub const Wasm = struct { return face_new_(ptr, len, pts, p) catch null; } - fn face_new_(ptr: [*]const u8, len: usize, pts: u16, presentation: u16) !*Face { + fn face_new_(ptr: [*]const u8, len: usize, pts: f32, presentation: u16) !*Face { var face = try Face.initNamed( alloc, ptr[0..len], diff --git a/src/input/Binding.zig b/src/input/Binding.zig index 4a153035d..200fd6d58 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -158,8 +158,8 @@ pub const Action = union(enum) { paste_from_selection: void, /// Increase/decrease the font size by a certain amount. - increase_font_size: u8, - decrease_font_size: u8, + increase_font_size: f32, + decrease_font_size: f32, /// Reset the font size to the original configured size. reset_font_size: void,