diff --git a/src/build/fish_completions.zig b/src/build/fish_completions.zig index 0ff0a2163..64fbea44e 100644 --- a/src/build/fish_completions.zig +++ b/src/build/fish_completions.zig @@ -12,7 +12,7 @@ pub const fish_completions = comptimeGenerateFishCompletions(); fn comptimeGenerateFishCompletions() []const u8 { comptime { - @setEvalBranchQuota(17000); + @setEvalBranchQuota(18000); var counter = std.io.countingWriter(std.io.null_writer); try writeFishCompletions(&counter.writer()); diff --git a/src/build/mdgen/mdgen.zig b/src/build/mdgen/mdgen.zig index 2e2884f1a..7e05596d7 100644 --- a/src/build/mdgen/mdgen.zig +++ b/src/build/mdgen/mdgen.zig @@ -26,7 +26,7 @@ pub fn genConfig(writer: anytype, cli: bool) !void { \\ ); - @setEvalBranchQuota(2000); + @setEvalBranchQuota(3000); inline for (@typeInfo(Config).Struct.fields) |field| { if (field.name[0] == '_') continue; diff --git a/src/config.zig b/src/config.zig index b9f214fc9..5af7832dd 100644 --- a/src/config.zig +++ b/src/config.zig @@ -16,6 +16,7 @@ pub const CopyOnSelect = Config.CopyOnSelect; pub const CustomShaderAnimation = Config.CustomShaderAnimation; pub const FontSyntheticStyle = Config.FontSyntheticStyle; pub const FontStyle = Config.FontStyle; +pub const FreetypeLoadFlags = Config.FreetypeLoadFlags; pub const Keybinds = Config.Keybinds; pub const MouseShiftCapture = Config.MouseShiftCapture; pub const NonNativeFullscreen = Config.NonNativeFullscreen; diff --git a/src/config/Config.zig b/src/config/Config.zig index e6b9d35ab..d8f007435 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -287,6 +287,29 @@ const c = @cImport({ /// terminals. Only new terminals will use the new configuration. @"grapheme-width-method": GraphemeWidthMethod = .unicode, +/// FreeType load flags to enable. The format of this is a list of flags to +/// enable separated by commas. If you prefix a flag with `no-` then it is +/// disabled. If you omit a flag, it's default value is used, so you must +/// explicitly disable flags you don't want. You can also use `true` or `false` +/// to turn all flags on or off. +/// +/// This configuration only applies to Ghostty builds that use FreeType. +/// This is usually the case only for Linux builds. macOS uses CoreText +/// and does not have an equivalent configuration. +/// +/// Available flags: +/// +/// * `hinting` - Enable or disable hinting, enabled by default. +/// * `force-autohint` - Use the freetype auto-hinter rather than the +/// font's native hinter. Enabled by default. +/// * `monochrome` - Instructs renderer to use 1-bit monochrome +/// rendering. This option doesn't impact the hinter. +/// Enabled by default. +/// * `autohint` - Use the freetype auto-hinter. Enabled by default. +/// +/// Example: `hinting`, `no-hinting`, `force-autohint`, `no-force-autohint` +@"freetype-load-flags": FreetypeLoadFlags = .{}, + /// A theme to use. If the theme is an absolute pathname, Ghostty will attempt /// to load that file as a theme. If that file does not exist or is inaccessible, /// an error will be logged and no other directories will be searched. @@ -4565,6 +4588,17 @@ pub const GraphemeWidthMethod = enum { unicode, }; +/// See freetype-load-flag +pub const FreetypeLoadFlags = packed struct { + // The defaults here at the time of writing this match the defaults + // for Freetype itself. Ghostty hasn't made any opinionated changes + // to these defaults. + hinting: bool = true, + @"force-autohint": bool = true, + monochrome: bool = true, + autohint: bool = true, +}; + /// See linux-cgroup pub const LinuxCgroup = enum { never, diff --git a/src/font/Collection.zig b/src/font/Collection.zig index 476787749..f79c80936 100644 --- a/src/font/Collection.zig +++ b/src/font/Collection.zig @@ -452,6 +452,12 @@ pub const LoadOptions = struct { /// for this is owned by the user and is not freed by the collection. metric_modifiers: Metrics.ModifierSet = .{}, + /// Freetype Load Flags to use when loading glyphs. This is a list of + /// bitfield constants that controls operations to perform during glyph + /// loading. Only a subset is exposed for configuration, for the whole set + /// of flags see `pkg.freetype.face.LoadFlags`. + freetype_load_flags: font.face.FreetypeLoadFlags = font.face.freetype_load_flags_default, + pub fn deinit(self: *LoadOptions, alloc: Allocator) void { _ = self; _ = alloc; @@ -462,6 +468,7 @@ pub const LoadOptions = struct { return .{ .size = self.size, .metric_modifiers = &self.metric_modifiers, + .freetype_load_flags = self.freetype_load_flags, }; } }; diff --git a/src/font/SharedGridSet.zig b/src/font/SharedGridSet.zig index c3067fa6d..ac2fcbf8a 100644 --- a/src/font/SharedGridSet.zig +++ b/src/font/SharedGridSet.zig @@ -168,6 +168,7 @@ fn collection( .library = self.font_lib, .size = size, .metric_modifiers = key.metric_modifiers, + .freetype_load_flags = key.freetype_load_flags, }; var c = Collection.init(); @@ -427,6 +428,7 @@ pub const DerivedConfig = struct { @"adjust-strikethrough-position": ?Metrics.Modifier, @"adjust-strikethrough-thickness": ?Metrics.Modifier, @"adjust-cursor-thickness": ?Metrics.Modifier, + @"freetype-load-flags": font.face.FreetypeLoadFlags, /// Initialize a DerivedConfig. The config should be either a /// config.Config or another DerivedConfig to clone from. @@ -461,6 +463,7 @@ pub const DerivedConfig = struct { .@"adjust-strikethrough-position" = config.@"adjust-strikethrough-position", .@"adjust-strikethrough-thickness" = config.@"adjust-strikethrough-thickness", .@"adjust-cursor-thickness" = config.@"adjust-cursor-thickness", + .@"freetype-load-flags" = if (font.face.FreetypeLoadFlags != void) config.@"freetype-load-flags" else {}, // This must be last so the arena contains all our allocations // from above since Zig does assignment in order. @@ -500,6 +503,10 @@ pub const Key = struct { /// font grid. font_size: DesiredSize = .{ .points = 12 }, + /// The freetype load flags configuration, only non-void if the + /// freetype backend is enabled. + freetype_load_flags: font.face.FreetypeLoadFlags = font.face.freetype_load_flags_default, + const style_offsets_len = std.enums.directEnumArrayLen(Style, 0); const StyleOffsets = [style_offsets_len]usize; @@ -618,6 +625,10 @@ pub const Key = struct { .codepoint_map = codepoint_map, .metric_modifiers = metric_modifiers, .font_size = font_size, + .freetype_load_flags = if (font.face.FreetypeLoadFlags != void) + config.@"freetype-load-flags" + else + font.face.freetype_load_flags_default, }; } @@ -647,6 +658,7 @@ pub const Key = struct { for (self.descriptors) |d| d.hash(hasher); self.codepoint_map.hash(hasher); autoHash(hasher, self.metric_modifiers.count()); + autoHash(hasher, self.freetype_load_flags); if (self.metric_modifiers.count() > 0) { inline for (@typeInfo(Metrics.Key).Enum.fields) |field| { const key = @field(Metrics.Key, field.name); diff --git a/src/font/face.zig b/src/font/face.zig index 8bcfb8209..9f80c5637 100644 --- a/src/font/face.zig +++ b/src/font/face.zig @@ -2,6 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); const options = @import("main.zig").options; pub const Metrics = @import("face/Metrics.zig"); +const config = @import("../config.zig"); const freetype = @import("face/freetype.zig"); const coretext = @import("face/coretext.zig"); pub const web_canvas = @import("face/web_canvas.zig"); @@ -26,10 +27,19 @@ pub const Face = switch (options.backend) { /// using whatever platform method you can. pub const default_dpi = if (builtin.os.tag == .macos) 72 else 96; +/// These are the flags to customize how freetype loads fonts. This is +/// only non-void if the freetype backend is enabled. +pub const FreetypeLoadFlags = if (options.backend.hasFreetype()) + config.FreetypeLoadFlags +else + void; +pub const freetype_load_flags_default = if (FreetypeLoadFlags != void) .{} else {}; + /// Options for initializing a font face. pub const Options = struct { size: DesiredSize, metric_modifiers: ?*const Metrics.ModifierSet = null, + freetype_load_flags: FreetypeLoadFlags = freetype_load_flags_default, }; /// The desired size for loading a font. diff --git a/src/font/face/freetype.zig b/src/font/face/freetype.zig index 7bb9ecbab..683f80cc8 100644 --- a/src/font/face/freetype.zig +++ b/src/font/face/freetype.zig @@ -18,10 +18,16 @@ const Library = font.Library; const convert = @import("freetype_convert.zig"); const fastmem = @import("../../fastmem.zig"); const quirks = @import("../../quirks.zig"); +const config = @import("../../config.zig"); const log = std.log.scoped(.font_face); pub const Face = struct { + comptime { + // If we have the freetype backend, we should have load flags. + assert(font.face.FreetypeLoadFlags != void); + } + /// Our freetype library lib: freetype.Library, @@ -34,6 +40,9 @@ pub const Face = struct { /// Metrics for this font face. These are useful for renderers. metrics: font.face.Metrics, + /// Freetype load flags for this font face. + load_flags: font.face.FreetypeLoadFlags, + /// Set quirks.disableDefaultFontFeatures quirks_disable_default_font_features: bool = false, @@ -77,6 +86,7 @@ pub const Face = struct { .face = face, .hb_font = hb_font, .metrics = calcMetrics(face, opts.metric_modifiers), + .load_flags = opts.freetype_load_flags, }; result.quirks_disable_default_font_features = quirks.disableDefaultFontFeatures(&result); @@ -319,6 +329,12 @@ pub const Face = struct { // This must be enabled for color faces though because those are // often colored bitmaps, which we support. .no_bitmap = !self.face.hasColor(), + + // use options from config + .no_hinting = !self.load_flags.hinting, + .force_autohint = !self.load_flags.@"force-autohint", + .monochrome = !self.load_flags.monochrome, + .no_autohint = !self.load_flags.autohint, }); const glyph = self.face.handle.*.glyph;