mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
--font-family CLI config
This commit is contained in:
108
src/Grid.zig
108
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");
|
||||
|
115
src/Window.zig
115
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");
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
Reference in New Issue
Block a user