--font-family CLI config

This commit is contained in:
Mitchell Hashimoto
2022-09-29 14:51:31 -07:00
parent 7eac0afff2
commit 53aab0a163
4 changed files with 162 additions and 112 deletions

View File

@ -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");

View File

@ -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");

View File

@ -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);

View File

@ -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.