From 53aab0a163baf726104bb46738f4fe77c95e8412 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 29 Sep 2022 14:51:31 -0700 Subject: [PATCH] --font-family CLI config --- src/Grid.zig | 108 ++------------------------------------------ src/Window.zig | 115 +++++++++++++++++++++++++++++++++++++++++++++-- src/cli_args.zig | 24 ++++++++++ src/config.zig | 27 +++++++++-- 4 files changed, 162 insertions(+), 112 deletions(-) diff --git a/src/Grid.zig b/src/Grid.zig index 1f393cbc6..de0eea79d 100644 --- a/src/Grid.zig +++ b/src/Grid.zig @@ -58,8 +58,7 @@ texture: gl.Texture, texture_color: gl.Texture, /// The font structures. -font_lib: font.Library, -font_group: font.GroupCache, +font_group: *font.GroupCache, font_shaper: font.Shaper, /// Whether the cursor is visible or not. This is used to control cursor @@ -152,100 +151,7 @@ const GPUCellMode = enum(u8) { } }; -pub fn init( - alloc: Allocator, - font_size: font.Face.DesiredSize, -) !Grid { - // Build our font group - var font_lib = try font.Library.init(); - errdefer font_lib.deinit(); - var font_group = try font.GroupCache.init(alloc, group: { - var group = try font.Group.init(alloc, font_lib, font_size); - errdefer group.deinit(alloc); - - // Search for fonts - if (font.Discover != void) { - var disco = font.Discover.init(); - defer disco.deinit(); - - { - var disco_it = try disco.discover(.{ - .family = "Fira Code", - .size = font_size.points, - }); - defer disco_it.deinit(); - if (try disco_it.next()) |face| { - log.debug("font regular: {s}", .{try face.name()}); - try group.addFace(alloc, .regular, face); - } - } - { - var disco_it = try disco.discover(.{ - .family = "Fira Code", - .size = font_size.points, - .bold = true, - }); - defer disco_it.deinit(); - if (try disco_it.next()) |face| { - log.debug("font bold: {s}", .{try face.name()}); - try group.addFace(alloc, .bold, face); - } - } - { - var disco_it = try disco.discover(.{ - .family = "Fira Code", - .size = font_size.points, - .italic = true, - }); - defer disco_it.deinit(); - if (try disco_it.next()) |face| { - log.debug("font italic: {s}", .{try face.name()}); - try group.addFace(alloc, .italic, face); - } - } - { - var disco_it = try disco.discover(.{ - .family = "Fira Code", - .size = font_size.points, - .bold = true, - .italic = true, - }); - defer disco_it.deinit(); - if (try disco_it.next()) |face| { - log.debug("font bold+italic: {s}", .{try face.name()}); - try group.addFace(alloc, .bold_italic, face); - } - } - } - - // Our built-in font will be used as a backup - try group.addFace( - alloc, - .regular, - font.DeferredFace.initLoaded(try font.Face.init(font_lib, face_ttf, font_size)), - ); - try group.addFace( - alloc, - .bold, - font.DeferredFace.initLoaded(try font.Face.init(font_lib, face_bold_ttf, font_size)), - ); - - // Emoji - try group.addFace( - alloc, - .regular, - font.DeferredFace.initLoaded(try font.Face.init(font_lib, face_emoji_ttf, font_size)), - ); - try group.addFace( - alloc, - .regular, - font.DeferredFace.initLoaded(try font.Face.init(font_lib, face_emoji_text_ttf, font_size)), - ); - - break :group group; - }); - errdefer font_group.deinit(alloc); - +pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Grid { // Create the initial font shaper var shape_buf = try alloc.alloc(font.Shaper.Cell, 1); errdefer alloc.free(shape_buf); @@ -381,7 +287,6 @@ pub fn init( .vbo = vbo, .texture = tex, .texture_color = tex_color, - .font_lib = font_lib, .font_group = font_group, .font_shaper = shaper, .cursor_visible = true, @@ -394,8 +299,6 @@ pub fn init( pub fn deinit(self: *Grid) void { self.font_shaper.deinit(); self.alloc.free(self.font_shaper.cell_buf); - self.font_group.deinit(self.alloc); - self.font_lib.deinit(); self.texture.destroy(); self.texture_color.destroy(); @@ -478,7 +381,7 @@ pub fn rebuildCells(self: *Grid, term: *Terminal) !void { const start = self.cells.items.len; // Split our row into runs and shape each one. - var iter = self.font_shaper.runIterator(&self.font_group, row); + var iter = self.font_shaper.runIterator(self.font_group, row); while (try iter.next(self.alloc)) |run| { for (try self.font_shaper.shape(run)) |shaper_cell| { assert(try self.updateCell( @@ -967,8 +870,3 @@ test "GridSize update rounding" { try testing.expectEqual(@as(GridSize.Unit, 3), grid.columns); try testing.expectEqual(@as(GridSize.Unit, 2), grid.rows); } - -const face_ttf = @embedFile("font/res/FiraCode-Regular.ttf"); -const face_bold_ttf = @embedFile("font/res/FiraCode-Bold.ttf"); -const face_emoji_ttf = @embedFile("font/res/NotoColorEmoji.ttf"); -const face_emoji_text_ttf = @embedFile("font/res/NotoEmoji-Regular.ttf"); diff --git a/src/Window.zig b/src/Window.zig index 2d3f91ca0..c84909bfd 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -35,6 +35,10 @@ const WRITE_REQ_PREALLOC = std.math.pow(usize, 2, 5); alloc: Allocator, alloc_io_arena: std.heap.ArenaAllocator, +/// The font structures +font_lib: font.Library, +font_group: *font.GroupCache, + /// The glfw window handle. window: glfw.Window, @@ -237,13 +241,108 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo gl.c.glEnable(gl.c.GL_BLEND); gl.c.glBlendFunc(gl.c.GL_SRC_ALPHA, gl.c.GL_ONE_MINUS_SRC_ALPHA); - // Create our terminal grid with the initial window size - const window_size = try window.getSize(); - var grid = try Grid.init(alloc, .{ + // The font size we desire along with the DPI determiend for the window + const font_size: font.Face.DesiredSize = .{ .points = config.@"font-size", .xdpi = @floatToInt(u16, x_dpi), .ydpi = @floatToInt(u16, y_dpi), + }; + + // Find all the fonts for this window + var font_lib = try font.Library.init(); + errdefer font_lib.deinit(); + var font_group = try alloc.create(font.GroupCache); + errdefer alloc.destroy(font_group); + font_group.* = try font.GroupCache.init(alloc, group: { + var group = try font.Group.init(alloc, font_lib, font_size); + errdefer group.deinit(alloc); + + // Search for fonts + if (font.Discover != void) { + var disco = font.Discover.init(); + defer disco.deinit(); + + if (config.@"font-family") |family| { + var disco_it = try disco.discover(.{ + .family = family, + .size = font_size.points, + }); + defer disco_it.deinit(); + if (try disco_it.next()) |face| { + log.debug("font regular: {s}", .{try face.name()}); + try group.addFace(alloc, .regular, face); + } + } + if (config.@"font-family-bold") |family| { + var disco_it = try disco.discover(.{ + .family = family, + .size = font_size.points, + .bold = true, + }); + defer disco_it.deinit(); + if (try disco_it.next()) |face| { + log.debug("font bold: {s}", .{try face.name()}); + try group.addFace(alloc, .bold, face); + } + } + if (config.@"font-family-italic") |family| { + var disco_it = try disco.discover(.{ + .family = family, + .size = font_size.points, + .italic = true, + }); + defer disco_it.deinit(); + if (try disco_it.next()) |face| { + log.debug("font italic: {s}", .{try face.name()}); + try group.addFace(alloc, .italic, face); + } + } + if (config.@"font-family-bold-italic") |family| { + var disco_it = try disco.discover(.{ + .family = family, + .size = font_size.points, + .bold = true, + .italic = true, + }); + defer disco_it.deinit(); + if (try disco_it.next()) |face| { + log.debug("font bold+italic: {s}", .{try face.name()}); + try group.addFace(alloc, .bold_italic, face); + } + } + } + + // Our built-in font will be used as a backup + try group.addFace( + alloc, + .regular, + font.DeferredFace.initLoaded(try font.Face.init(font_lib, face_ttf, font_size)), + ); + try group.addFace( + alloc, + .bold, + font.DeferredFace.initLoaded(try font.Face.init(font_lib, face_bold_ttf, font_size)), + ); + + // Emoji + try group.addFace( + alloc, + .regular, + font.DeferredFace.initLoaded(try font.Face.init(font_lib, face_emoji_ttf, font_size)), + ); + try group.addFace( + alloc, + .regular, + font.DeferredFace.initLoaded(try font.Face.init(font_lib, face_emoji_text_ttf, font_size)), + ); + + break :group group; }); + errdefer font_group.deinit(alloc); + + // Create our terminal grid with the initial window size + const window_size = try window.getSize(); + var grid = try Grid.init(alloc, font_group); try grid.setScreenSize(.{ .width = window_size.width, .height = window_size.height }); grid.background = .{ .r = config.background.r, @@ -333,6 +432,8 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo self.* = .{ .alloc = alloc, .alloc_io_arena = io_arena, + .font_lib = font_lib, + .font_group = font_group, .window = window, .cursor = cursor, .focused = false, @@ -419,6 +520,9 @@ pub fn destroy(self: *Window) void { // windows using it to the default. self.cursor.destroy(); + self.font_group.deinit(self.alloc); + self.font_lib.deinit(); + self.alloc_io_arena.deinit(); } @@ -1715,3 +1819,8 @@ pub fn invokeCharset( ) !void { self.terminal.invokeCharset(active, slot, single); } + +const face_ttf = @embedFile("font/res/FiraCode-Regular.ttf"); +const face_bold_ttf = @embedFile("font/res/FiraCode-Bold.ttf"); +const face_emoji_ttf = @embedFile("font/res/NotoColorEmoji.ttf"); +const face_emoji_text_ttf = @embedFile("font/res/NotoEmoji-Regular.ttf"); diff --git a/src/cli_args.zig b/src/cli_args.zig index 502cfcf3f..b6263f048 100644 --- a/src/cli_args.zig +++ b/src/cli_args.zig @@ -60,6 +60,8 @@ pub fn parse(comptime T: type, alloc: Allocator, dst: *T, iter: anytype) !void { try parseIntoField(T, arena_alloc, dst, key, value); } } + + if (@hasDecl(T, "finalize")) try dst.finalize(); } /// Parse a single key/value pair into the destination type T. @@ -193,6 +195,28 @@ test "parse: simple" { try testing.expect(!data.@"b-f"); } +test "parse: finalize" { + const testing = std.testing; + + var data: struct { + a: []const u8 = "", + _arena: ?ArenaAllocator = null, + + pub fn finalize(self: *@This()) !void { + self.a = "YO"; + } + } = .{}; + defer if (data._arena) |arena| arena.deinit(); + + var iter = try std.process.ArgIteratorGeneral(.{}).init( + testing.allocator, + "--a=42", + ); + defer iter.deinit(); + try parse(@TypeOf(data), testing.allocator, &data, &iter); + try testing.expectEqualStrings("YO", data.a); +} + test "parseIntoField: string" { const testing = std.testing; var arena = ArenaAllocator.init(testing.allocator); diff --git a/src/config.zig b/src/config.zig index d93f86783..7448f7ae1 100644 --- a/src/config.zig +++ b/src/config.zig @@ -7,10 +7,10 @@ const inputpkg = @import("input.zig"); /// CLI flag names hence we use a lot of `@""` syntax to support hyphens. pub const Config = struct { /// The font families to use. - @"font-family": ?[]const u8 = null, - @"font-family-bold": ?[]const u8 = null, - @"font-family-italic": ?[]const u8 = null, - @"font-family-bold-italic": ?[]const u8 = null, + @"font-family": ?[:0]const u8 = null, + @"font-family-bold": ?[:0]const u8 = null, + @"font-family-italic": ?[:0]const u8 = null, + @"font-family-bold-italic": ?[:0]const u8 = null, /// Font size in points @"font-size": u8 = 12, @@ -122,6 +122,25 @@ pub const Config = struct { return result; } + + pub fn finalize(self: *Config) !void { + // If we have a font-family set and don't set the others, default + // the others to the font family. This way, if someone does + // --font-family=foo, then we try to get the stylized versions of + // "foo" as well. + if (self.@"font-family") |family| { + const fields = &[_][]const u8{ + "font-family-bold", + "font-family-italic", + "font-family-bold-italic", + }; + inline for (fields) |field| { + if (@field(self, field) == null) { + @field(self, field) = family; + } + } + } + } }; /// Color represents a color using RGB.