mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +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,
|
texture_color: gl.Texture,
|
||||||
|
|
||||||
/// The font structures.
|
/// The font structures.
|
||||||
font_lib: font.Library,
|
font_group: *font.GroupCache,
|
||||||
font_group: font.GroupCache,
|
|
||||||
font_shaper: font.Shaper,
|
font_shaper: font.Shaper,
|
||||||
|
|
||||||
/// Whether the cursor is visible or not. This is used to control cursor
|
/// Whether the cursor is visible or not. This is used to control cursor
|
||||||
@ -152,100 +151,7 @@ const GPUCellMode = enum(u8) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Grid {
|
||||||
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);
|
|
||||||
|
|
||||||
// Create the initial font shaper
|
// Create the initial font shaper
|
||||||
var shape_buf = try alloc.alloc(font.Shaper.Cell, 1);
|
var shape_buf = try alloc.alloc(font.Shaper.Cell, 1);
|
||||||
errdefer alloc.free(shape_buf);
|
errdefer alloc.free(shape_buf);
|
||||||
@ -381,7 +287,6 @@ pub fn init(
|
|||||||
.vbo = vbo,
|
.vbo = vbo,
|
||||||
.texture = tex,
|
.texture = tex,
|
||||||
.texture_color = tex_color,
|
.texture_color = tex_color,
|
||||||
.font_lib = font_lib,
|
|
||||||
.font_group = font_group,
|
.font_group = font_group,
|
||||||
.font_shaper = shaper,
|
.font_shaper = shaper,
|
||||||
.cursor_visible = true,
|
.cursor_visible = true,
|
||||||
@ -394,8 +299,6 @@ pub fn init(
|
|||||||
pub fn deinit(self: *Grid) void {
|
pub fn deinit(self: *Grid) void {
|
||||||
self.font_shaper.deinit();
|
self.font_shaper.deinit();
|
||||||
self.alloc.free(self.font_shaper.cell_buf);
|
self.alloc.free(self.font_shaper.cell_buf);
|
||||||
self.font_group.deinit(self.alloc);
|
|
||||||
self.font_lib.deinit();
|
|
||||||
|
|
||||||
self.texture.destroy();
|
self.texture.destroy();
|
||||||
self.texture_color.destroy();
|
self.texture_color.destroy();
|
||||||
@ -478,7 +381,7 @@ pub fn rebuildCells(self: *Grid, term: *Terminal) !void {
|
|||||||
const start = self.cells.items.len;
|
const start = self.cells.items.len;
|
||||||
|
|
||||||
// Split our row into runs and shape each one.
|
// 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| {
|
while (try iter.next(self.alloc)) |run| {
|
||||||
for (try self.font_shaper.shape(run)) |shaper_cell| {
|
for (try self.font_shaper.shape(run)) |shaper_cell| {
|
||||||
assert(try self.updateCell(
|
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, 3), grid.columns);
|
||||||
try testing.expectEqual(@as(GridSize.Unit, 2), grid.rows);
|
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: Allocator,
|
||||||
alloc_io_arena: std.heap.ArenaAllocator,
|
alloc_io_arena: std.heap.ArenaAllocator,
|
||||||
|
|
||||||
|
/// The font structures
|
||||||
|
font_lib: font.Library,
|
||||||
|
font_group: *font.GroupCache,
|
||||||
|
|
||||||
/// The glfw window handle.
|
/// The glfw window handle.
|
||||||
window: glfw.Window,
|
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.glEnable(gl.c.GL_BLEND);
|
||||||
gl.c.glBlendFunc(gl.c.GL_SRC_ALPHA, gl.c.GL_ONE_MINUS_SRC_ALPHA);
|
gl.c.glBlendFunc(gl.c.GL_SRC_ALPHA, gl.c.GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
// Create our terminal grid with the initial window size
|
// The font size we desire along with the DPI determiend for the window
|
||||||
const window_size = try window.getSize();
|
const font_size: font.Face.DesiredSize = .{
|
||||||
var grid = try Grid.init(alloc, .{
|
|
||||||
.points = config.@"font-size",
|
.points = config.@"font-size",
|
||||||
.xdpi = @floatToInt(u16, x_dpi),
|
.xdpi = @floatToInt(u16, x_dpi),
|
||||||
.ydpi = @floatToInt(u16, y_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 });
|
try grid.setScreenSize(.{ .width = window_size.width, .height = window_size.height });
|
||||||
grid.background = .{
|
grid.background = .{
|
||||||
.r = config.background.r,
|
.r = config.background.r,
|
||||||
@ -333,6 +432,8 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo
|
|||||||
self.* = .{
|
self.* = .{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.alloc_io_arena = io_arena,
|
.alloc_io_arena = io_arena,
|
||||||
|
.font_lib = font_lib,
|
||||||
|
.font_group = font_group,
|
||||||
.window = window,
|
.window = window,
|
||||||
.cursor = cursor,
|
.cursor = cursor,
|
||||||
.focused = false,
|
.focused = false,
|
||||||
@ -419,6 +520,9 @@ pub fn destroy(self: *Window) void {
|
|||||||
// windows using it to the default.
|
// windows using it to the default.
|
||||||
self.cursor.destroy();
|
self.cursor.destroy();
|
||||||
|
|
||||||
|
self.font_group.deinit(self.alloc);
|
||||||
|
self.font_lib.deinit();
|
||||||
|
|
||||||
self.alloc_io_arena.deinit();
|
self.alloc_io_arena.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1715,3 +1819,8 @@ pub fn invokeCharset(
|
|||||||
) !void {
|
) !void {
|
||||||
self.terminal.invokeCharset(active, slot, single);
|
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);
|
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.
|
/// Parse a single key/value pair into the destination type T.
|
||||||
@ -193,6 +195,28 @@ test "parse: simple" {
|
|||||||
try testing.expect(!data.@"b-f");
|
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" {
|
test "parseIntoField: string" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
var arena = ArenaAllocator.init(testing.allocator);
|
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.
|
/// CLI flag names hence we use a lot of `@""` syntax to support hyphens.
|
||||||
pub const Config = struct {
|
pub const Config = struct {
|
||||||
/// The font families to use.
|
/// The font families to use.
|
||||||
@"font-family": ?[]const u8 = null,
|
@"font-family": ?[:0]const u8 = null,
|
||||||
@"font-family-bold": ?[]const u8 = null,
|
@"font-family-bold": ?[:0]const u8 = null,
|
||||||
@"font-family-italic": ?[]const u8 = null,
|
@"font-family-italic": ?[:0]const u8 = null,
|
||||||
@"font-family-bold-italic": ?[]const u8 = null,
|
@"font-family-bold-italic": ?[:0]const u8 = null,
|
||||||
|
|
||||||
/// Font size in points
|
/// Font size in points
|
||||||
@"font-size": u8 = 12,
|
@"font-size": u8 = 12,
|
||||||
@ -122,6 +122,25 @@ pub const Config = struct {
|
|||||||
|
|
||||||
return result;
|
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.
|
/// Color represents a color using RGB.
|
||||||
|
Reference in New Issue
Block a user