aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/font
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-09-10 21:51:57 +0300
committerGitHub <noreply@github.com>2026-09-10 21:51:57 +0300
commit425c86179791cd32e2d4ce0ae7f269940b4b0663 (patch)
treee52c4383a6d69216cbe2a32638bf90841d7e2189 /vendor/libghostty-vt/src/font
parent90e947a6e26e02f10ec7f1694c4eb5b78a86406b (diff)
chore: upgrade libghostty and preserve terminal behavior (#3906)
Diffstat (limited to 'vendor/libghostty-vt/src/font')
-rw-r--r--vendor/libghostty-vt/src/font/Atlas.zig4
-rw-r--r--vendor/libghostty-vt/src/font/Collection.zig7
-rw-r--r--vendor/libghostty-vt/src/font/DeferredFace.zig3
-rw-r--r--vendor/libghostty-vt/src/font/Metrics.zig22
-rw-r--r--vendor/libghostty-vt/src/font/SharedGrid.zig160
-rw-r--r--vendor/libghostty-vt/src/font/SharedGridSet.zig33
-rw-r--r--vendor/libghostty-vt/src/font/discovery.zig85
-rw-r--r--vendor/libghostty-vt/src/font/face/coretext.zig1
-rw-r--r--vendor/libghostty-vt/src/font/face/freetype.zig45
-rw-r--r--vendor/libghostty-vt/src/font/glyf_rasterize.zig12
-rw-r--r--vendor/libghostty-vt/src/font/glyf_rasterize_png_test.zig28
-rw-r--r--vendor/libghostty-vt/src/font/library.zig6
-rw-r--r--vendor/libghostty-vt/src/font/opentype/glyf.zig60
-rw-r--r--vendor/libghostty-vt/src/font/opentype/head.zig8
-rw-r--r--vendor/libghostty-vt/src/font/opentype/hhea.zig7
-rw-r--r--vendor/libghostty-vt/src/font/opentype/os2.zig21
-rw-r--r--vendor/libghostty-vt/src/font/opentype/post.zig8
-rw-r--r--vendor/libghostty-vt/src/font/opentype/sfnt.zig7
-rw-r--r--vendor/libghostty-vt/src/font/opentype/svg.zig37
-rw-r--r--vendor/libghostty-vt/src/font/shaper/coretext.zig93
-rw-r--r--vendor/libghostty-vt/src/font/shaper/feature.zig33
-rw-r--r--vendor/libghostty-vt/src/font/shaper/harfbuzz.zig128
-rw-r--r--vendor/libghostty-vt/src/font/shaper/run.zig53
-rw-r--r--vendor/libghostty-vt/src/font/sprite/Face.zig32
-rw-r--r--vendor/libghostty-vt/src/font/sprite/canvas.zig83
-rw-r--r--vendor/libghostty-vt/src/font/sprite/draw/powerline.zig10
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-11x21+2.pngbin1032 -> 941 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-12x24+3.pngbin1295 -> 1151 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-18x36+4.pngbin2193 -> 1994 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-9x17+1.pngbin794 -> 717 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-11x21+2.pngbin1280 -> 2146 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-12x24+3.pngbin1870 -> 2529 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-18x36+4.pngbin3411 -> 4398 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-9x17+1.pngbin1103 -> 1625 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-11x21+2.pngbin1006 -> 881 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-12x24+3.pngbin1255 -> 1082 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-18x36+4.pngbin2247 -> 2371 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-9x17+1.pngbin751 -> 693 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-11x21+2.pngbin5427 -> 5871 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-12x24+3.pngbin5724 -> 6354 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-18x36+4.pngbin9975 -> 11167 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-9x17+1.pngbin4341 -> 4598 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-11x21+2.pngbin2225 -> 2412 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-12x24+3.pngbin2635 -> 2839 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-18x36+4.pngbin4570 -> 5323 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-9x17+1.pngbin1853 -> 1792 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-11x21+2.pngbin1022 -> 1010 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-12x24+3.pngbin1541 -> 1276 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-18x36+4.pngbin2501 -> 1993 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-9x17+1.pngbin917 -> 953 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-11x21+2.pngbin1102 -> 1030 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-12x24+3.pngbin1255 -> 1144 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-18x36+4.pngbin2217 -> 1978 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-9x17+1.pngbin894 -> 834 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-11x21+2.pngbin1114 -> 1044 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-12x24+3.pngbin1421 -> 1279 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-18x36+4.pngbin2473 -> 2073 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-9x17+1.pngbin872 -> 831 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-11x21+2.pngbin495 -> 359 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-12x24+3.pngbin637 -> 438 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-18x36+4.pngbin1210 -> 752 bytes
-rw-r--r--vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-9x17+1.pngbin393 -> 288 bytes
-rw-r--r--vendor/libghostty-vt/src/font/testdata/glyf_rasterize.pngbin1215 -> 1291 bytes
63 files changed, 595 insertions, 391 deletions
diff --git a/vendor/libghostty-vt/src/font/Atlas.zig b/vendor/libghostty-vt/src/font/Atlas.zig
index d1206457..66e91c8f 100644
--- a/vendor/libghostty-vt/src/font/Atlas.zig
+++ b/vendor/libghostty-vt/src/font/Atlas.zig
@@ -32,7 +32,7 @@ data: []u8,
size: u32 = 0,
/// The nodes (rectangles) of available space.
-nodes: std.ArrayListUnmanaged(Node) = .{},
+nodes: std.ArrayList(Node) = .empty,
/// The format of the texture data being written into the Atlas. This must be
/// uniform for all textures in the Atlas. If you have some textures with
@@ -104,7 +104,7 @@ pub fn init(alloc: Allocator, size: u32, format: Format) Allocator.Error!Atlas {
var result = Atlas{
.data = try alloc.alloc(u8, size * size * format.depth()),
.size = size,
- .nodes = .{},
+ .nodes = .empty,
.format = format,
};
errdefer result.deinit(alloc);
diff --git a/vendor/libghostty-vt/src/font/Collection.zig b/vendor/libghostty-vt/src/font/Collection.zig
index 5d7bfa51..6c646d9f 100644
--- a/vendor/libghostty-vt/src/font/Collection.zig
+++ b/vendor/libghostty-vt/src/font/Collection.zig
@@ -28,6 +28,7 @@ const Library = font.Library;
const Metrics = font.Metrics;
const Presentation = font.Presentation;
const Style = font.Style;
+const SegmentedList = @import("../datastruct/segmented_list.zig").SegmentedList;
const log = std.log.scoped(.font_collection);
@@ -662,7 +663,7 @@ fn scaleFactor(
return primary_metric / face_metric;
}
-const UpdateMetricsError = error{
+pub const UpdateMetricsError = error{
CannotLoadPrimaryFont,
};
@@ -694,7 +695,7 @@ pub fn updateMetrics(self: *Collection) UpdateMetricsError!void {
///
/// WARNING: We cannot use any prealloc yet for the segmented list because
/// the collection is copied around by value and pointers aren't stable.
-const StyleArray = std.EnumArray(Style, std.SegmentedList(EntryOrAlias, 0));
+const StyleArray = std.EnumArray(Style, SegmentedList(EntryOrAlias, 0));
/// Load options are used to configure all the details a Collection
/// needs to load deferred faces.
@@ -894,7 +895,7 @@ pub const Index = packed struct(Index.Backing) {
/// The number of bits we use for the index.
const idx_bits = backing_bits - @typeInfo(@typeInfo(Style).@"enum".tag_type).int.bits;
- pub const IndexInt = @Type(.{ .int = .{ .signedness = .unsigned, .bits = idx_bits } });
+ pub const IndexInt = @Int(.unsigned, idx_bits);
/// The special-case fonts that we support.
pub const Special = enum(IndexInt) {
diff --git a/vendor/libghostty-vt/src/font/DeferredFace.zig b/vendor/libghostty-vt/src/font/DeferredFace.zig
index 425f3c28..9a8124c9 100644
--- a/vendor/libghostty-vt/src/font/DeferredFace.zig
+++ b/vendor/libghostty-vt/src/font/DeferredFace.zig
@@ -184,7 +184,8 @@ pub fn name(self: DeferredFace, buf: []u8) ![]const u8 {
.coretext_harfbuzz,
.coretext_noshape,
=> if (self.ct) |ct| {
- const display_name = ct.font.copyDisplayName();
+ const display_name = ct.font.copyDisplayName() orelse
+ return self.familyName(buf);
return display_name.cstringPtr(.utf8) orelse unsupported: {
// "NULL if the internal storage of theString does not allow
// this to be returned efficiently." In this case, we need
diff --git a/vendor/libghostty-vt/src/font/Metrics.zig b/vendor/libghostty-vt/src/font/Metrics.zig
index 60a300b0..36cfe009 100644
--- a/vendor/libghostty-vt/src/font/Metrics.zig
+++ b/vendor/libghostty-vt/src/font/Metrics.zig
@@ -589,23 +589,21 @@ pub const Modifier = union(enum) {
/// Key is an enum of all the available metrics keys.
pub const Key = key: {
const field_infos = std.meta.fields(Metrics);
- var enumFields: [field_infos.len]std.builtin.Type.EnumField = undefined;
+ var names: [field_infos.len][]const u8 = undefined;
+ var raw_values: [field_infos.len]comptime_int = undefined;
var count: usize = 0;
- for (field_infos, 0..) |field, i| {
+ for (field_infos, &names, &raw_values, 0..) |field, *name, *raw, i| {
if (field.type != u32 and field.type != i32 and field.type != f64) continue;
- enumFields[i] = .{ .name = field.name, .value = i };
+ name.* = field.name;
+ raw.* = i;
count += 1;
}
- var decls = [_]std.builtin.Type.Declaration{};
- break :key @Type(.{
- .@"enum" = .{
- .tag_type = std.math.IntFittingRange(0, count - 1),
- .fields = enumFields[0..count],
- .decls = &decls,
- .is_exhaustive = true,
- },
- });
+ const TagInt = std.math.IntFittingRange(0, count - 1);
+ var values: [count]TagInt = undefined;
+ for (raw_values, &values) |raw, *v| v.* = raw;
+
+ break :key @Enum(TagInt, .exhaustive, names[0..count], &values);
};
// NOTE: This is purposely not pub because we want to force outside callers
diff --git a/vendor/libghostty-vt/src/font/SharedGrid.zig b/vendor/libghostty-vt/src/font/SharedGrid.zig
index 18870aab..92cf0500 100644
--- a/vendor/libghostty-vt/src/font/SharedGrid.zig
+++ b/vendor/libghostty-vt/src/font/SharedGrid.zig
@@ -34,6 +34,7 @@ const Metrics = font.Metrics;
const Presentation = font.Presentation;
const Style = font.Style;
const RenderOptions = font.Glyph.RenderOptions;
+const global = @import("../global.zig");
const log = std.log.scoped(.font_shared_grid);
@@ -60,7 +61,7 @@ metrics: Metrics,
/// this directly if they need to i.e. access the atlas directly. Because
/// callers can use this lock directly, maintainers need to be extra careful
/// to review call sites to ensure they are using the lock correctly.
-lock: std.Thread.RwLock,
+lock: std.Io.RwLock,
pub const init_tw = tripwire.module(enum {
codepoints_capacity,
@@ -68,6 +69,8 @@ pub const init_tw = tripwire.module(enum {
reload_metrics,
}, init);
+pub const InitError = std.mem.Allocator.Error || Collection.UpdateMetricsError;
+
/// Initialize the grid.
///
/// The resolver must have a collection that supports deferred loading
@@ -80,7 +83,7 @@ pub const init_tw = tripwire.module(enum {
pub fn init(
alloc: Allocator,
resolver: CodepointResolver,
-) !SharedGrid {
+) InitError!SharedGrid {
const tw = init_tw;
// We need to support loading options since we use the size data
@@ -95,7 +98,7 @@ pub fn init(
.resolver = resolver,
.atlas_grayscale = atlas_grayscale,
.atlas_color = atlas_color,
- .lock = .{},
+ .lock = .init,
.metrics = undefined, // Loaded below
};
@@ -162,14 +165,14 @@ pub fn getIndex(
// Fast path: the cache has the value. This is almost always true and
// only requires a read lock.
{
- self.lock.lockShared();
- defer self.lock.unlockShared();
+ self.lock.lockSharedUncancelable(global.io());
+ defer self.lock.unlockShared(global.io());
if (self.codepoints.get(key)) |v| return v;
}
// Slow path: we need to search this codepoint
- self.lock.lock();
- defer self.lock.unlock();
+ self.lock.lockUncancelable(global.io());
+ defer self.lock.unlock(global.io());
// Try to get it, if it is now in the cache another thread beat us to it.
const gop = try self.codepoints.getOrPut(alloc, key);
@@ -201,8 +204,8 @@ pub fn hasCodepoint(
cp: u32,
p: ?Presentation,
) bool {
- self.lock.lockShared();
- defer self.lock.unlockShared();
+ self.lock.lockSharedUncancelable(global.io());
+ defer self.lock.unlockShared(global.io());
return self.resolver.collection.hasCodepoint(
idx,
cp,
@@ -236,8 +239,8 @@ pub fn renderCodepoint(
// Get the glyph for the font
const glyph_index = glyph_index: {
- self.lock.lockShared();
- defer self.lock.unlockShared();
+ self.lock.lockSharedUncancelable(global.io());
+ defer self.lock.unlockShared(global.io());
const face = try self.resolver.collection.getFace(index);
break :glyph_index face.glyphIndex(cp) orelse return null;
};
@@ -250,6 +253,127 @@ pub const renderGlyph_tw = tripwire.module(enum {
get_presentation,
}, renderGlyph);
+// FIXME: This gets a massive manual error set for now because there is an
+// equally massive call stack in the get_presentation tracepoint. This should
+// be consolidated into something better ASAP.
+pub const RenderGlyphError = error{
+ ArrayTooLarge,
+ AtlasFull,
+ BadArgument,
+ BbxTooBig,
+ BitmapHandlingError,
+ CMapTableMissing,
+ CannotOpenResource,
+ CannotOpenStream,
+ CannotRenderGlyph,
+ CodeOverflow,
+ CorruptedFontGlyphs,
+ CorruptedFontHeader,
+ CouldNotFindContext,
+ DebugOpCode,
+ DeferredLoadingUnavailable,
+ DivideByZero,
+ ENDFInExecStream,
+ ExecutionTooLong,
+ FontHasNoFile,
+ FontPathCantDecode,
+ FontconfigFailed,
+ FontconfigNoId,
+ FontconfigNoMatch,
+ FontconfigTypeMismatch,
+ GlyphResizeFailed,
+ HarfbuzzFailed,
+ HmtxTableMissing,
+ HorizHeaderMissing,
+ Ignore,
+ IndexOutOfBounds,
+ InvalidArgument,
+ InvalidAtlasFormat,
+ InvalidCacheHandle,
+ InvalidCharMapFormat,
+ InvalidCharMapHandle,
+ InvalidCharacterCode,
+ InvalidCodeRange,
+ InvalidComposite,
+ InvalidDriverHandle,
+ InvalidFaceHandle,
+ InvalidFileFormat,
+ InvalidFrameOperation,
+ InvalidFrameRead,
+ InvalidGlyphFormat,
+ InvalidGlyphIndex,
+ InvalidHandle,
+ InvalidHeight,
+ InvalidHorizMetrics,
+ InvalidLibraryHandle,
+ InvalidMatrix,
+ InvalidOffset,
+ InvalidOpcode,
+ InvalidOutline,
+ InvalidPPem,
+ InvalidPixelSize,
+ InvalidPostTable,
+ InvalidPostTableFormat,
+ InvalidReference,
+ InvalidSVGTable,
+ InvalidSizeHandle,
+ InvalidSlotHandle,
+ InvalidState,
+ InvalidStreamHandle,
+ InvalidStreamOperation,
+ InvalidStreamRead,
+ InvalidStreamSeek,
+ InvalidStreamSkip,
+ InvalidTable,
+ InvalidVersion,
+ InvalidVertMetrics,
+ InvalidWidth,
+ LocationsMissing,
+ LowerModuleVersion,
+ MathError,
+ MissingBbxField,
+ MissingCharsField,
+ MissingEncodingField,
+ MissingFontField,
+ MissingFontboundingboxField,
+ MissingModule,
+ MissingProperty,
+ MissingSizeField,
+ MissingStartcharField,
+ MissingStartfontField,
+ NameTableMissing,
+ NestedDEFS,
+ NestedFrameAccess,
+ NoCurrentPoint,
+ NoUnicodeGlyphName,
+ OutOfMemory,
+ PathNotClosed,
+ PixelSourceNotPreMultiplied,
+ PostTableMissing,
+ RasterCorrupted,
+ RasterNegativeHeight,
+ RasterOverflow,
+ RasterUninitialized,
+ SpecialHasNoFace,
+ StackOverflow,
+ StackUnderflow,
+ Syntax,
+ TableMissing,
+ TooFewArguments,
+ TooManyCaches,
+ TooManyDrivers,
+ TooManyExtensions,
+ TooManyFunctionDefs,
+ TooManyHints,
+ TooManyInstructionDefs,
+ UnimplementedFeature,
+ UnknownFileFormat,
+ UnknownFreetypeError,
+ UnlistedObject,
+ UnsupportedGlyphFormat,
+ WrongAtlas,
+};
+
/// Render a glyph index. This automatically determines the correct texture
/// atlas to use and caches the result.
pub fn renderGlyph(
@@ -258,7 +382,7 @@ pub fn renderGlyph(
index: Collection.Index,
glyph_index: u32,
opts: RenderOptions,
-) !Render {
+) RenderGlyphError!Render {
const tw = renderGlyph_tw;
const key: GlyphKey = .{ .index = index, .glyph = glyph_index, .opts = opts };
@@ -266,14 +390,14 @@ pub fn renderGlyph(
// Fast path: the cache has the value. This is almost always true and
// only requires a read lock.
{
- self.lock.lockShared();
- defer self.lock.unlockShared();
+ self.lock.lockSharedUncancelable(global.io());
+ defer self.lock.unlockShared(global.io());
if (self.glyphs.get(key)) |v| return v;
}
// Slow path: we need to search this codepoint
- self.lock.lock();
- defer self.lock.unlock();
+ self.lock.lockUncancelable(global.io());
+ defer self.lock.unlock(global.io());
const gop = try self.glyphs.getOrPut(alloc, key);
if (gop.found_existing) return gop.value_ptr.*;
@@ -468,8 +592,8 @@ test "renderGlyph error after cache insert rolls back cache entry" {
// Get the glyph index for 'A'
const glyph_index = glyph_index: {
- grid.lock.lockShared();
- defer grid.lock.unlockShared();
+ grid.lock.lockSharedUncancelable(testing.io);
+ defer grid.lock.unlockShared(testing.io);
const face = try grid.resolver.collection.getFace(idx);
break :glyph_index face.glyphIndex('A').?;
};
diff --git a/vendor/libghostty-vt/src/font/SharedGridSet.zig b/vendor/libghostty-vt/src/font/SharedGridSet.zig
index 9d8148bd..dc7d8dd7 100644
--- a/vendor/libghostty-vt/src/font/SharedGridSet.zig
+++ b/vendor/libghostty-vt/src/font/SharedGridSet.zig
@@ -28,6 +28,7 @@ const SharedGrid = font.SharedGrid;
const discovery = @import("discovery.zig");
const configpkg = @import("../config.zig");
const Config = configpkg.Config;
+const global = @import("../global.zig");
const log = std.log.scoped(.font_shared_grid_set);
@@ -44,7 +45,7 @@ font_lib: Library,
font_discover: ?Discover = null,
/// Lock to protect multi-threaded access to the map.
-lock: std.Thread.Mutex = .{},
+lock: std.Io.Mutex = .init,
pub const InitError = Library.InitError;
@@ -79,8 +80,8 @@ pub fn deinit(self: *SharedGridSet) void {
/// Returns the number of cached grids.
pub fn count(self: *SharedGridSet) usize {
- self.lock.lock();
- defer self.lock.unlock();
+ self.lock.lockUncancelable(global.io());
+ defer self.lock.unlock(global.io());
return self.map.count();
}
@@ -100,8 +101,8 @@ pub fn ref(
var key = try Key.init(self.alloc, config, font_size);
errdefer key.deinit();
- self.lock.lock();
- defer self.lock.unlock();
+ self.lock.lockUncancelable(global.io());
+ defer self.lock.unlock(global.io());
const gop = try self.map.getOrPut(self.alloc, key);
if (gop.found_existing) {
@@ -339,6 +340,24 @@ fn collection(
// specifying a font-family for emoji.
if (comptime builtin.target.os.tag.isDarwin() and Discover != void) apple_emoji: {
const disco = try self.discover() orelse break :apple_emoji;
+
+ // Fast path: we know the exact name of the font we want so we
+ // can look it up directly, which is sometimes significantly faster than
+ // full discovery (e.g. CoreText).
+ if (@hasDecl(Discover, "discoverExactFamily")) {
+ if (try disco.discoverExactFamily(
+ "Apple Color Emoji",
+ )) |face| {
+ _ = try c.addDeferred(self.alloc, face, .{
+ .style = .regular,
+ .fallback = true,
+ // No size adjustment for emojis.
+ .size_adjustment = .none,
+ });
+ break :apple_emoji;
+ }
+ }
+
var disco_it = try disco.discover(self.alloc, .{
.family = "Apple Color Emoji",
});
@@ -392,8 +411,8 @@ fn collection(
/// Decrement the ref count for the given key. If the ref count is zero,
/// the grid will be deinitialized and removed from the map.j:w
pub fn deref(self: *SharedGridSet, key: Key) void {
- self.lock.lock();
- defer self.lock.unlock();
+ self.lock.lockUncancelable(global.io());
+ defer self.lock.unlock(global.io());
const entry = self.map.getEntry(key) orelse return;
assert(entry.value_ptr.ref >= 1);
diff --git a/vendor/libghostty-vt/src/font/discovery.zig b/vendor/libghostty-vt/src/font/discovery.zig
index b945aa01..1847693a 100644
--- a/vendor/libghostty-vt/src/font/discovery.zig
+++ b/vendor/libghostty-vt/src/font/discovery.zig
@@ -12,6 +12,7 @@ const Face = @import("main.zig").Face;
const Library = @import("main.zig").Library;
const Presentation = @import("main.zig").Presentation;
const Variation = @import("main.zig").face.Variation;
+const global = @import("../global.zig");
const log = std.log.scoped(.discovery);
@@ -349,6 +350,22 @@ pub const CoreText = struct {
_ = self;
}
+ /// Warm up the system font registry.
+ ///
+ /// The first CoreText query in a process initializes the system font
+ /// database, which takes multiple milliseconds, while subsequent
+ /// queries are microseconds.
+ pub fn warmup() void {
+ const name = macos.foundation.String.createWithBytes(
+ "AppleColorEmoji",
+ .utf8,
+ false,
+ ) catch return;
+ defer name.release();
+ const ct_font = macos.text.Font.createWithName(name, 12) catch return;
+ ct_font.release();
+ }
+
/// Discover fonts from a descriptor. This returns an iterator that can
/// be used to build up the deferred fonts.
pub fn discover(self: *const CoreText, alloc: Allocator, desc: Descriptor) !DiscoverIterator {
@@ -382,6 +399,54 @@ pub const CoreText = struct {
};
}
+ /// Discover a font by its exact name (family, full, or PostScript
+ /// name). This is significantly faster than `discover` because it
+ /// avoids the system-wide font matching that CTFontCollection does
+ /// (which takes multiple milliseconds). This should be preferred
+ /// when the desired font is known exactly, e.g. system fonts such
+ /// as Apple Color Emoji.
+ ///
+ /// Returns null if no font with this exact family name exists;
+ /// CoreText fallback fonts are never returned.
+ pub fn discoverExactFamily(
+ self: *const CoreText,
+ family: []const u8,
+ ) !?DeferredFace {
+ _ = self;
+
+ const family_str = try macos.foundation.String.createWithBytes(
+ family,
+ .utf8,
+ false,
+ );
+ defer family_str.release();
+
+ // Create our font. We need a size to initialize it so we use size
+ // 12 but we will alter the size later (same as DiscoverIterator).
+ const ct_font = try macos.text.Font.createWithName(family_str, 12);
+
+ // CTFontCreateWithName never returns null: if the requested font
+ // isn't installed it returns a substitute font. Verify we got
+ // the family we asked for, otherwise report not found.
+ const found: bool = found: {
+ const actual = ct_font.copyFamilyName();
+ defer actual.release();
+ var buf: [256]u8 = undefined;
+ const actual_slice = actual.cstring(&buf, .utf8) orelse
+ break :found false;
+ break :found std.mem.eql(u8, actual_slice, family);
+ };
+ if (!found) {
+ ct_font.release();
+ return null;
+ }
+
+ return .{ .ct = .{
+ .font = ct_font,
+ .variations = &.{},
+ } };
+ }
+
pub fn discoverFallback(
self: *const CoreText,
alloc: Allocator,
@@ -942,15 +1007,15 @@ pub const Windows = struct {
desc: Descriptor,
variations: []const Variation,
state: State,
- dir: ?std.fs.Dir,
- iter: ?std.fs.Dir.Iterator,
+ dir: ?std.Io.Dir,
+ iter: ?std.Io.Dir.Iterator,
system_path: ?[:0]const u8,
user_path: ?[:0]const u8,
const State = enum { system, user, done };
pub fn deinit(self: *DiscoverIterator) void {
- if (self.dir) |*d| d.close();
+ if (self.dir) |*d| d.close(global.io());
if (self.system_path) |p| self.alloc.free(p);
if (self.user_path) |p| self.alloc.free(p);
self.* = undefined;
@@ -967,7 +1032,8 @@ pub const Windows = struct {
continue;
};
self.system_path = path;
- self.dir = std.fs.openDirAbsoluteZ(
+ self.dir = std.Io.Dir.openDirAbsolute(
+ global.io(),
path,
.{ .iterate = true },
) catch {
@@ -982,7 +1048,8 @@ pub const Windows = struct {
continue;
};
self.user_path = path;
- self.dir = std.fs.openDirAbsoluteZ(
+ self.dir = std.Io.Dir.openDirAbsolute(
+ global.io(),
path,
.{ .iterate = true },
) catch {
@@ -995,9 +1062,9 @@ pub const Windows = struct {
}
}
- const entry = (self.iter.?.next() catch null) orelse {
+ const entry = (self.iter.?.next(global.io()) catch null) orelse {
// Finished this directory; advance state.
- if (self.dir) |*d| d.close();
+ if (self.dir) |*d| d.close(global.io());
self.dir = null;
self.iter = null;
self.state = switch (self.state) {
@@ -1020,7 +1087,7 @@ pub const Windows = struct {
/// Windows install but we just skip the directory rather than
/// falling back to a hardcoded drive letter.
fn systemFontsPath(self: *DiscoverIterator) ?[:0]const u8 {
- const systemroot = std.process.getEnvVarOwned(
+ const systemroot = global.environ().getAlloc(
self.alloc,
"SYSTEMROOT",
) catch return null;
@@ -1034,7 +1101,7 @@ pub const Windows = struct {
}
fn userFontsPath(self: *DiscoverIterator) ?[:0]const u8 {
- const local_appdata = std.process.getEnvVarOwned(
+ const local_appdata = global.environ().getAlloc(
self.alloc,
"LOCALAPPDATA",
) catch return null;
diff --git a/vendor/libghostty-vt/src/font/face/coretext.zig b/vendor/libghostty-vt/src/font/face/coretext.zig
index cd8bc7d8..4b2af153 100644
--- a/vendor/libghostty-vt/src/font/face/coretext.zig
+++ b/vendor/libghostty-vt/src/font/face/coretext.zig
@@ -926,6 +926,7 @@ const ColorState = struct {
const svg = opentype.SVG.init(ptr[0..len]) catch |err| {
return switch (err) {
error.EndOfStream,
+ error.ReadFailed,
error.SVGVersionNotSupported,
=> error.InvalidSVGTable,
};
diff --git a/vendor/libghostty-vt/src/font/face/freetype.zig b/vendor/libghostty-vt/src/font/face/freetype.zig
index d04fe180..14a378e5 100644
--- a/vendor/libghostty-vt/src/font/face/freetype.zig
+++ b/vendor/libghostty-vt/src/font/face/freetype.zig
@@ -18,6 +18,7 @@ const Library = font.Library;
const opentype = @import("../opentype.zig");
const quirks = @import("../../quirks.zig");
const config = @import("../../config.zig");
+const global = @import("../../global.zig");
const F26Dot6 = opentype.sfnt.F26Dot6;
@@ -41,7 +42,7 @@ pub const Face = struct {
///
/// This means that anywhere where `self.face.loadGlyph`
/// is called, this mutex must be held.
- ft_mutex: *std.Thread.Mutex,
+ ft_mutex: *std.Io.Mutex,
/// Harfbuzz font corresponding to this face.
hb_font: harfbuzz.Font,
@@ -68,8 +69,8 @@ pub const Face = struct {
index: i32,
opts: font.face.Options,
) !Face {
- lib.mutex.lock();
- defer lib.mutex.unlock();
+ lib.mutex.lockUncancelable(global.io());
+ defer lib.mutex.unlock(global.io());
const face = try lib.lib.initFace(path, index);
errdefer face.deinit();
return try initFace(lib, face, opts);
@@ -81,8 +82,8 @@ pub const Face = struct {
source: [:0]const u8,
opts: font.face.Options,
) !Face {
- lib.mutex.lock();
- defer lib.mutex.unlock();
+ lib.mutex.lockUncancelable(global.io());
+ defer lib.mutex.unlock(global.io());
const face = try lib.lib.initMemoryFace(source, 0);
errdefer face.deinit();
return try initFace(lib, face, opts);
@@ -99,9 +100,9 @@ pub const Face = struct {
var hb_font = try harfbuzz.freetype.createFont(face.handle);
errdefer hb_font.destroy();
- const ft_mutex = try lib.alloc.create(std.Thread.Mutex);
+ const ft_mutex = try lib.alloc.create(std.Io.Mutex);
errdefer lib.alloc.destroy(ft_mutex);
- ft_mutex.* = .{};
+ ft_mutex.* = .init;
var result: Face = .{
.lib = lib,
@@ -142,8 +143,8 @@ pub const Face = struct {
pub fn deinit(self: *Face) void {
self.lib.alloc.destroy(self.ft_mutex);
{
- self.lib.mutex.lock();
- defer self.lib.mutex.unlock();
+ self.lib.mutex.lockUncancelable(global.io());
+ defer self.lib.mutex.unlock(global.io());
self.face.deinit();
}
@@ -330,8 +331,8 @@ pub const Face = struct {
/// Returns true if the given glyph ID is colorized.
pub fn isColorGlyph(self: *const Face, glyph_id: u32) bool {
- self.ft_mutex.lock();
- defer self.ft_mutex.unlock();
+ self.ft_mutex.lockUncancelable(global.io());
+ defer self.ft_mutex.unlock(global.io());
// Load the glyph and see what pixel mode it renders with.
// All modes other than BGRA are non-color.
@@ -429,8 +430,8 @@ pub const Face = struct {
glyph_index: u32,
opts: font.Glyph.RenderOptions,
) !Glyph {
- self.ft_mutex.lock();
- defer self.ft_mutex.unlock();
+ self.ft_mutex.lockUncancelable(global.io());
+ defer self.ft_mutex.unlock(global.io());
// Load the glyph.
try self.face.loadGlyph(glyph_index, self.glyphLoadFlags(opts.constraint.doesAnything()));
@@ -541,7 +542,7 @@ pub const Face = struct {
else
0.0;
- const outline = &glyph.*.outline;
+ const outline: *freetype.c.FT_Outline = &glyph.*.outline;
for (outline.points[0..@intCast(outline.n_points)]) |*p| {
// Convert to f64 for processing
var px = f26dot6ToF64(p.x);
@@ -971,8 +972,8 @@ pub const Face = struct {
// the metrics provided by FreeType, and set ascii_height to null as
// it's optional.
const cell_width: f64, const ascii_height: ?f64 = measurements: {
- self.ft_mutex.lock();
- defer self.ft_mutex.unlock();
+ self.ft_mutex.lockUncancelable(global.io());
+ defer self.ft_mutex.unlock(global.io());
var max: f64 = 0.0;
var top: f64 = 0.0;
@@ -1024,8 +1025,8 @@ pub const Face = struct {
break :heights .{
cap: {
- self.ft_mutex.lock();
- defer self.ft_mutex.unlock();
+ self.ft_mutex.lockUncancelable(global.io());
+ defer self.ft_mutex.unlock(global.io());
if (face.getCharIndex('H')) |glyph_index| {
if (face.loadGlyph(glyph_index, self.glyphLoadFlags(false))) {
break :cap getGlyphSize(face.handle.*.glyph).height;
@@ -1034,8 +1035,8 @@ pub const Face = struct {
break :cap null;
},
ex: {
- self.ft_mutex.lock();
- defer self.ft_mutex.unlock();
+ self.ft_mutex.lockUncancelable(global.io());
+ defer self.ft_mutex.unlock(global.io());
if (face.getCharIndex('x')) |glyph_index| {
if (face.loadGlyph(glyph_index, self.glyphLoadFlags(false))) {
break :ex getGlyphSize(face.handle.*.glyph).height;
@@ -1048,8 +1049,8 @@ pub const Face = struct {
// Measure "水" (CJK water ideograph, U+6C34) for our ic width.
const ic_width: ?f64 = ic_width: {
- self.ft_mutex.lock();
- defer self.ft_mutex.unlock();
+ self.ft_mutex.lockUncancelable(global.io());
+ defer self.ft_mutex.unlock(global.io());
const glyph = face.getCharIndex('水') orelse break :ic_width null;
diff --git a/vendor/libghostty-vt/src/font/glyf_rasterize.zig b/vendor/libghostty-vt/src/font/glyf_rasterize.zig
index f321214b..f288ff0a 100644
--- a/vendor/libghostty-vt/src/font/glyf_rasterize.zig
+++ b/vendor/libghostty-vt/src/font/glyf_rasterize.zig
@@ -99,14 +99,16 @@ pub fn rasterize(
// bitmap just like an empty outline.
if (bounds.width() == 0 or bounds.height() == 0) return Bitmap.initEmpty(alloc, width, height);
- // Build the surface we'll draw on. This is a simple alpha8 drawing.
- var sfc: z2d.Surface = try .init(
+ // Build the surface directly over the bitmap we'll return.
+ const data = try alloc.alloc(u8, @as(usize, width) * @as(usize, height));
+ errdefer alloc.free(data);
+ var sfc: z2d.Surface = .initBuffer(
.image_surface_alpha8,
- alloc,
+ null,
+ std.mem.bytesAsSlice(z2d.pixel.Alpha8, data),
@intCast(width),
@intCast(height),
);
- defer sfc.deinit(alloc);
var path: z2d.Path = .empty;
defer path.deinit(alloc);
@@ -133,7 +135,7 @@ pub fn rasterize(
return .{
.width = width,
.height = height,
- .data = try alloc.dupe(u8, std.mem.sliceAsBytes(sfc.image_surface_alpha8.buf)),
+ .data = data,
};
}
diff --git a/vendor/libghostty-vt/src/font/glyf_rasterize_png_test.zig b/vendor/libghostty-vt/src/font/glyf_rasterize_png_test.zig
index e8ad70fd..881c0ed9 100644
--- a/vendor/libghostty-vt/src/font/glyf_rasterize_png_test.zig
+++ b/vendor/libghostty-vt/src/font/glyf_rasterize_png_test.zig
@@ -2,6 +2,7 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const wuffs = @import("wuffs");
const z2d = @import("z2d");
+const compat_file = @import("../lib/compat/file.zig");
const glyf_rasterize = @import("glyf_rasterize.zig");
const glyf = @import("opentype/glyf.zig");
@@ -107,37 +108,38 @@ fn drawCellBoxes(atlas: *z2d.Surface, y: usize, cell_width: usize, cell_height:
/// `glyf_rasterize_diff.png`, where red is reference-only coverage and green is
/// newly generated coverage. Returns true when a difference was found.
fn diffAtlas(
+ io: std.Io,
alloc: Allocator,
atlas: *z2d.Surface,
generated_path: []const u8,
) !bool {
const ref_path = "src/font/testdata/glyf_rasterize.png";
- const generated_file = try std.fs.openFileAbsolute(generated_path, .{ .mode = .read_only });
- defer generated_file.close();
- const generated_bytes = try generated_file.readToEndAlloc(alloc, std.math.maxInt(usize));
+ const generated_file = try std.Io.Dir.openFileAbsolute(io, generated_path, .{ .mode = .read_only });
+ defer generated_file.close(io);
+ const generated_bytes = try compat_file.readToEndAlloc(generated_file, alloc, std.math.maxInt(usize));
defer alloc.free(generated_bytes);
- const cwd_absolute = try std.fs.cwd().realpathAlloc(alloc, ".");
+ const cwd_absolute = try std.Io.Dir.cwd().realPathFileAlloc(io, ".", alloc);
defer alloc.free(cwd_absolute);
- const ref_file = std.fs.cwd().openFile(ref_path, .{ .mode = .read_only }) catch |err| {
+ const ref_file = std.Io.Dir.cwd().openFile(io, ref_path, .{ .mode = .read_only }) catch |err| {
log.err("Can't open reference file {s}: {}", .{ ref_path, err });
const test_path = try std.fmt.allocPrint(alloc, "{s}/glyf_rasterize_test.png", .{cwd_absolute});
defer alloc.free(test_path);
- try std.fs.copyFileAbsolute(generated_path, test_path, .{});
+ try std.Io.Dir.copyFileAbsolute(generated_path, test_path, io, .{});
return true;
};
- defer ref_file.close();
- const ref_bytes = try ref_file.readToEndAlloc(alloc, std.math.maxInt(usize));
+ defer ref_file.close(io);
+ const ref_bytes = try compat_file.readToEndAlloc(ref_file, alloc, std.math.maxInt(usize));
defer alloc.free(ref_bytes);
if (std.mem.eql(u8, generated_bytes, ref_bytes)) return false;
const test_path = try std.fmt.allocPrint(alloc, "{s}/glyf_rasterize_test.png", .{cwd_absolute});
defer alloc.free(test_path);
- try std.fs.copyFileAbsolute(generated_path, test_path, .{});
+ try std.Io.Dir.copyFileAbsolute(generated_path, test_path, io, .{});
const ref_rgba = try wuffs.png.decode(alloc, ref_bytes);
defer alloc.free(ref_rgba.data);
@@ -185,7 +187,7 @@ fn diffAtlas(
}
const diff_path = "./glyf_rasterize_diff.png";
- try z2d.png_exporter.writeToPNGFile(diff, diff_path, .{});
+ try z2d.png_exporter.writeToPNGFile(io, diff, diff_path, .{});
log.err(
"glyf rasterize visual output differs from reference: test={s}, reference={s}, diff={s}",
.{ test_path, ref_path, diff_path },
@@ -290,12 +292,12 @@ test "glyf_rasterize: bubbletea glyph protocol examples match reference image" {
var dir = testing.tmpDir(.{});
defer dir.cleanup();
- const tmp_dir = try dir.dir.realpathAlloc(alloc, ".");
+ const tmp_dir = try dir.dir.realPathFileAlloc(testing.io, ".", alloc);
defer alloc.free(tmp_dir);
const generated_path = try std.fmt.allocPrint(alloc, "{s}/glyf_rasterize.png", .{tmp_dir});
defer alloc.free(generated_path);
- try z2d.png_exporter.writeToPNGFile(atlas, generated_path, .{});
+ try z2d.png_exporter.writeToPNGFile(testing.io, atlas, generated_path, .{});
- try testing.expect(!try diffAtlas(alloc, &atlas, generated_path));
+ try testing.expect(!try diffAtlas(testing.io, alloc, &atlas, generated_path));
}
diff --git a/vendor/libghostty-vt/src/font/library.zig b/vendor/libghostty-vt/src/font/library.zig
index 56946343..49c2e880 100644
--- a/vendor/libghostty-vt/src/font/library.zig
+++ b/vendor/libghostty-vt/src/font/library.zig
@@ -30,7 +30,7 @@ pub const FreetypeLibrary = struct {
/// Mutex to be held any time the library is
/// being used to create or destroy a face.
- mutex: *std.Thread.Mutex,
+ mutex: *std.Io.Mutex,
pub const InitError = freetype.Error || Allocator.Error;
@@ -38,8 +38,8 @@ pub const FreetypeLibrary = struct {
const lib = try freetype.Library.init();
errdefer lib.deinit();
- const mutex = try alloc.create(std.Thread.Mutex);
- mutex.* = .{};
+ const mutex = try alloc.create(std.Io.Mutex);
+ mutex.* = .init;
return Library{ .lib = lib, .alloc = alloc, .mutex = mutex };
}
diff --git a/vendor/libghostty-vt/src/font/opentype/glyf.zig b/vendor/libghostty-vt/src/font/opentype/glyf.zig
index 912b5fe8..d95b7ece 100644
--- a/vendor/libghostty-vt/src/font/opentype/glyf.zig
+++ b/vendor/libghostty-vt/src/font/opentype/glyf.zig
@@ -1,6 +1,7 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const sfnt = @import("sfnt.zig");
+const compat_reader = @import("../../lib/compat/reader.zig");
/// Glyph Data Table
///
@@ -245,11 +246,10 @@ pub const Glyf = struct {
///
/// The lifetime of this struct, then, is the same as the
/// lifetime of the data that is used to initialize it.
- pub fn init(data: []const u8) error{EndOfStream}!Entry {
- var fbs = std.io.fixedBufferStream(data);
- const reader = fbs.reader();
- const header = try reader.readStructEndian(Header, .big);
- return .{ .header = header, .data = data[fbs.pos..] };
+ pub fn init(data: []const u8) std.Io.Reader.Error!Entry {
+ var reader: std.Io.Reader = .fixed(data);
+ const header = try compat_reader.readStructEndian(&reader, Header, .big);
+ return .{ .header = header, .data = data[@sizeOf(Header)..] };
}
/// Identifies what type (simple or composite) of entry this is.
@@ -262,10 +262,6 @@ pub const Glyf = struct {
/// Errors that can be returned from `Entry.size()`.
pub const SizeError = error{
- /// The entry's data wasn't large enough, ran
- /// out of bytes before we were done reading.
- EndOfStream,
-
/// The entry contains hinting instructions,
/// which we don't currently support.
InstructionsNotSupported,
@@ -283,7 +279,7 @@ pub const Glyf = struct {
/// This entry defines points past the index determined
/// by the final element of the endPtsOfContours array.
TooManyPoints,
- };
+ } || std.Io.Reader.Error;
/// Errors that can be returned from `Entry.decode()`.
pub const DecodeError = SizeError || Allocator.Error || error{
@@ -299,8 +295,7 @@ pub const Glyf = struct {
/// NOTE: Currently produces errors when given composite glyphs
/// or any glyphs that have hinting instructions included.
pub fn size(self: Entry) SizeError!usize {
- var fbs = std.io.fixedBufferStream(self.data);
- const reader = fbs.reader();
+ var reader: std.Io.Reader = .fixed(self.data);
switch (self.entryType()) {
// https://learn.microsoft.com/en-us/typography/opentype/spec/glyf#simple-glyph-description
.simple => {
@@ -329,7 +324,7 @@ pub const Glyf = struct {
// of each contour, in increasing numeric order.
var max_point_index: isize = -1;
for (0..num_contours) |_| {
- const index = try reader.readInt(sfnt.uint16, .big);
+ const index = try compat_reader.readerInt(&reader, sfnt.uint16, .big);
// The endpoints are supposed to monotonically increase.
if (index <= max_point_index) return error.EndPointsOutOfOrder;
max_point_index = index;
@@ -342,7 +337,7 @@ pub const Glyf = struct {
// If instructionLength is zero, no instructions
// are present for this glyph, and this field is
// followed directly by the flags field.
- const instructions_length = try reader.readInt(sfnt.uint16, .big);
+ const instructions_length = try compat_reader.readerInt(&reader, sfnt.uint16, .big);
// Since we don't have code that validates instruction
// byte code, we just reject all glyphs that contain any.
@@ -373,7 +368,7 @@ pub const Glyf = struct {
var x_coords_len: usize = 0;
var y_coords_len: usize = 0;
while (i <= max_point_index) : (i += 1) {
- const flag: SimpleFlags = @bitCast(try reader.readByte());
+ const flag: SimpleFlags = @bitCast(try compat_reader.readByte(&reader));
// Determine how many bytes the x and y coordinates will
// be represented with in the corresponding arrays, add
@@ -393,7 +388,7 @@ pub const Glyf = struct {
// that count, and the x_coords_len and y_coords_len
// must be increased by the correct number of bytes
// as well.
- const repeat_count: usize = try reader.readByte();
+ const repeat_count: usize = try compat_reader.readByte(&reader);
i += repeat_count;
x_coords_len += repeat_count * flag.xBytes();
y_coords_len += repeat_count * flag.yBytes();
@@ -415,7 +410,7 @@ pub const Glyf = struct {
// We determined the length of this section (in bytes)
// above while processing the flags, so that we can just
// skip that many bytes to validate this field.
- try reader.skipBytes(x_coords_len, .{});
+ try compat_reader.readSkipBytes(&reader, x_coords_len);
// uint8 or int16 yCoordinates[variable]
//
@@ -426,7 +421,7 @@ pub const Glyf = struct {
// We determined the length of this section (in bytes)
// above while processing the flags, so that we can just
// skip that many bytes to validate this field.
- try reader.skipBytes(y_coords_len, .{});
+ try compat_reader.readSkipBytes(&reader, y_coords_len);
},
.composite => {
@@ -441,7 +436,7 @@ pub const Glyf = struct {
}
// No issues found, the glyf entry is valid, return its length.
- return @sizeOf(Header) + fbs.pos;
+ return @sizeOf(Header) + reader.seek;
}
/// Decode this simple glyph entry into an owned outline.
@@ -455,8 +450,7 @@ pub const Glyf = struct {
.composite => return error.CompositeNotSupported,
}
- var fbs = std.io.fixedBufferStream(self.data);
- const reader = fbs.reader();
+ var reader: std.Io.Reader = .fixed(self.data);
// A zero-contour glyph may be header-only. See size for the
// reason for the hardcoded 2 here.
@@ -473,7 +467,7 @@ pub const Glyf = struct {
// If we have no contours, then the only possible remaining
// field is instructionLength. Instructions are not supported.
if (num_contours == 0) {
- const instructions_length = try reader.readInt(sfnt.uint16, .big);
+ const instructions_length = try compat_reader.readerInt(&reader, sfnt.uint16, .big);
if (instructions_length > 0) return error.InstructionsNotSupported;
return .{ .points = &.{}, .contours = end_points };
}
@@ -488,7 +482,7 @@ pub const Glyf = struct {
// with the valid index. The final endpoint tells us our point
// count, since endpoints are stored as inclusive point indices.
for (0..end_points.len) |i| {
- const index = try reader.readInt(sfnt.uint16, .big);
+ const index = try compat_reader.readerInt(&reader, sfnt.uint16, .big);
if (index <= prev_end_point) return error.EndPointsOutOfOrder;
prev_end_point = index;
end_points[i] = index;
@@ -499,7 +493,7 @@ pub const Glyf = struct {
};
// Instructions are not supported.
- const instructions_length = try reader.readInt(sfnt.uint16, .big);
+ const instructions_length = try compat_reader.readerInt(&reader, sfnt.uint16, .big);
if (instructions_length > 0) return error.InstructionsNotSupported;
// Allocate our points right away even though the next entries
@@ -522,12 +516,12 @@ pub const Glyf = struct {
{
var point_i: usize = 0;
while (point_i < point_count) {
- const flag: SimpleFlags = @bitCast(try reader.readByte());
+ const flag: SimpleFlags = @bitCast(try compat_reader.readByte(&reader));
flags[point_i] = flag;
point_i += 1;
if (flag.repeat) {
- const repeat_count: usize = try reader.readByte();
+ const repeat_count: usize = try compat_reader.readByte(&reader);
if (point_i + repeat_count > point_count) return error.TooManyPoints;
for (0..repeat_count) |_| {
@@ -543,11 +537,11 @@ pub const Glyf = struct {
for (flags, points) |flag, *point| {
const dx: i32 = if (flag.x_short) short: {
break :short if (flag.x_repeat_or_sign)
- @as(i32, try reader.readByte())
+ @as(i32, try compat_reader.readByte(&reader))
else
- -@as(i32, try reader.readByte());
+ -@as(i32, try compat_reader.readByte(&reader));
} else if (!flag.x_repeat_or_sign)
- @as(i32, try reader.readInt(sfnt.int16, .big))
+ @as(i32, try compat_reader.readerInt(&reader, sfnt.int16, .big))
else
0;
@@ -564,11 +558,11 @@ pub const Glyf = struct {
for (flags, points) |flag, *point| {
const dy: i32 = if (flag.y_short) short: {
break :short if (flag.y_repeat_or_sign)
- @as(i32, try reader.readByte())
+ @as(i32, try compat_reader.readByte(&reader))
else
- -@as(i32, try reader.readByte());
+ -@as(i32, try compat_reader.readByte(&reader));
} else if (!flag.y_repeat_or_sign)
- @as(i32, try reader.readInt(sfnt.int16, .big))
+ @as(i32, try compat_reader.readerInt(&reader, sfnt.int16, .big))
else
0;
@@ -599,7 +593,7 @@ pub const Glyf = struct {
}
/// Retrieve the entry at the provided offset.
- pub fn entry(self: Glyf, index: usize) error{EndOfStream}!Entry {
+ pub fn entry(self: Glyf, index: usize) std.Io.Reader.Error!Entry {
return try Entry.init(self.data[index..]);
}
};
diff --git a/vendor/libghostty-vt/src/font/opentype/head.zig b/vendor/libghostty-vt/src/font/opentype/head.zig
index 38284d9c..009fd144 100644
--- a/vendor/libghostty-vt/src/font/opentype/head.zig
+++ b/vendor/libghostty-vt/src/font/opentype/head.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const sfnt = @import("sfnt.zig");
+const compat_reader = @import("../../lib/compat/reader.zig");
/// Font Header Table
///
@@ -134,10 +135,9 @@ pub const Head = extern struct {
glyphDataFormat: sfnt.int16 align(1),
/// Parse the table from raw data.
- pub fn init(data: []const u8) error{EndOfStream}!Head {
- var fbs = std.io.fixedBufferStream(data);
- const reader = fbs.reader();
- return try reader.readStructEndian(Head, .big);
+ pub fn init(data: []const u8) std.Io.Reader.Error!Head {
+ var reader: std.Io.Reader = .fixed(data);
+ return try compat_reader.readStructEndian(&reader, Head, .big);
}
};
diff --git a/vendor/libghostty-vt/src/font/opentype/hhea.zig b/vendor/libghostty-vt/src/font/opentype/hhea.zig
index b2b3f3e2..7ad77054 100644
--- a/vendor/libghostty-vt/src/font/opentype/hhea.zig
+++ b/vendor/libghostty-vt/src/font/opentype/hhea.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const sfnt = @import("sfnt.zig");
+const compat_reader = @import("../../lib/compat/reader.zig");
/// Horizontal Header Table
///
@@ -71,10 +72,8 @@ pub const Hhea = extern struct {
/// Parse the table from raw data.
pub fn init(data: []const u8) !Hhea {
- var fbs = std.io.fixedBufferStream(data);
- const reader = fbs.reader();
-
- return try reader.readStructEndian(Hhea, .big);
+ var reader: std.Io.Reader = .fixed(data);
+ return try compat_reader.readStructEndian(&reader, Hhea, .big);
}
};
diff --git a/vendor/libghostty-vt/src/font/opentype/os2.zig b/vendor/libghostty-vt/src/font/opentype/os2.zig
index 1cd11f35..5bb0b42b 100644
--- a/vendor/libghostty-vt/src/font/opentype/os2.zig
+++ b/vendor/libghostty-vt/src/font/opentype/os2.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const sfnt = @import("sfnt.zig");
+const compat_reader = @import("../../lib/compat/reader.zig");
pub const FSSelection = packed struct(sfnt.uint16) {
/// Font contains italic or oblique glyphs, otherwise they are upright.
@@ -348,21 +349,17 @@ pub const OS2 = struct {
usUpperOpticalPointSize: ?u16 = null,
/// Parse the table from raw data.
- pub fn init(data: []const u8) error{
- EndOfStream,
- OS2VersionNotSupported,
- }!OS2 {
- var fbs = std.io.fixedBufferStream(data);
- const reader = fbs.reader();
+ pub fn init(data: []const u8) (error{OS2VersionNotSupported} || std.Io.Reader.Error)!OS2 {
+ var reader: std.Io.Reader = .fixed(data);
- const version = try reader.readInt(sfnt.uint16, .big);
+ const version = try compat_reader.readerInt(&reader, sfnt.uint16, .big);
// Return to the start, cause the version is part of the struct.
- try fbs.seekTo(0);
+ reader.seek = 0;
switch (version) {
5 => {
- const table = try reader.readStructEndian(OS2v5, .big);
+ const table = try compat_reader.readStructEndian(&reader, OS2v5, .big);
return .{
.version = table.version,
.xAvgCharWidth = table.xAvgCharWidth,
@@ -406,7 +403,7 @@ pub const OS2 = struct {
};
},
4, 3, 2 => {
- const table = try reader.readStructEndian(OS2v4_3_2, .big);
+ const table = try compat_reader.readStructEndian(&reader, OS2v4_3_2, .big);
return .{
.version = table.version,
.xAvgCharWidth = table.xAvgCharWidth,
@@ -448,7 +445,7 @@ pub const OS2 = struct {
};
},
1 => {
- const table = try reader.readStructEndian(OS2v1, .big);
+ const table = try compat_reader.readStructEndian(&reader, OS2v1, .big);
return .{
.version = table.version,
.xAvgCharWidth = table.xAvgCharWidth,
@@ -485,7 +482,7 @@ pub const OS2 = struct {
};
},
0 => {
- const table = try reader.readStructEndian(OS2v0, .big);
+ const table = try compat_reader.readStructEndian(&reader, OS2v0, .big);
return .{
.version = table.version,
.xAvgCharWidth = table.xAvgCharWidth,
diff --git a/vendor/libghostty-vt/src/font/opentype/post.zig b/vendor/libghostty-vt/src/font/opentype/post.zig
index 8031a0a4..776bf8a3 100644
--- a/vendor/libghostty-vt/src/font/opentype/post.zig
+++ b/vendor/libghostty-vt/src/font/opentype/post.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const sfnt = @import("sfnt.zig");
+const compat_reader = @import("../../lib/compat/reader.zig");
/// PostScript Table
///
@@ -46,10 +47,9 @@ pub const Post = extern struct {
maxMemType1: sfnt.uint32 align(1),
/// Parse the table from raw data.
- pub fn init(data: []const u8) error{EndOfStream}!Post {
- var fbs = std.io.fixedBufferStream(data);
- const reader = fbs.reader();
- return try reader.readStructEndian(Post, .big);
+ pub fn init(data: []const u8) std.Io.Reader.Error!Post {
+ var reader: std.Io.Reader = .fixed(data);
+ return try compat_reader.readStructEndian(&reader, Post, .big);
}
};
diff --git a/vendor/libghostty-vt/src/font/opentype/sfnt.zig b/vendor/libghostty-vt/src/font/opentype/sfnt.zig
index 55a9a0bd..8b182e69 100644
--- a/vendor/libghostty-vt/src/font/opentype/sfnt.zig
+++ b/vendor/libghostty-vt/src/font/opentype/sfnt.zig
@@ -235,8 +235,7 @@ pub const SFNT = struct {
/// Parse a font from raw data. The struct will keep a
/// reference to `data` and use it for future operations.
pub fn init(data: []const u8, alloc: Allocator) !SFNT {
- var fbs = std.io.fixedBufferStream(data);
- const reader = fbs.reader();
+ var reader: std.Io.Reader = .fixed(data);
// SFNT files use big endian, if our native endian is
// not big we'll need to byte swap the values we read.
@@ -244,7 +243,7 @@ pub const SFNT = struct {
var directory: Directory = undefined;
- try reader.readNoEof(std.mem.asBytes(&directory.offset));
+ try reader.readSliceAll(std.mem.asBytes(&directory.offset));
if (byte_swap) std.mem.byteSwapAllFields(
Directory.OffsetSubtable,
&directory.offset,
@@ -252,7 +251,7 @@ pub const SFNT = struct {
directory.records = try alloc.alloc(Directory.TableRecord, directory.offset.num_tables);
- try reader.readNoEof(std.mem.sliceAsBytes(directory.records));
+ try reader.readSliceAll(std.mem.sliceAsBytes(directory.records));
if (byte_swap) for (directory.records) |*record| {
std.mem.byteSwapAllFields(
Directory.TableRecord,
diff --git a/vendor/libghostty-vt/src/font/opentype/svg.zig b/vendor/libghostty-vt/src/font/opentype/svg.zig
index b4d9ccaa..9115fe62 100644
--- a/vendor/libghostty-vt/src/font/opentype/svg.zig
+++ b/vendor/libghostty-vt/src/font/opentype/svg.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const font = @import("../main.zig");
+const compat_reader = @import("../../lib/compat/reader.zig");
/// SVG glyphs description table.
///
@@ -21,27 +22,24 @@ pub const SVG = struct {
/// All records in the table.
records: []const [12]u8,
- pub fn init(data: []const u8) error{
- EndOfStream,
- SVGVersionNotSupported,
- }!SVG {
- var fbs = std.io.fixedBufferStream(data);
- const reader = fbs.reader();
+ pub fn init(data: []const u8) (error{SVGVersionNotSupported} || std.Io.Reader.Error)!SVG {
+ var reader: std.Io.Reader = .fixed(data);
// Version
- if (try reader.readInt(u16, .big) != 0) {
+ if (try compat_reader.readerInt(&reader, u16, .big) != 0) {
return error.SVGVersionNotSupported;
}
// Offset
- const offset = try reader.readInt(u32, .big);
+ const offset = try compat_reader.readerInt(&reader, u32, .big);
// Seek to the offset to get our document list
- try fbs.seekTo(offset);
+ if (offset > data.len) return error.EndOfStream;
+ reader.seek = offset;
// Get our document records along with the start/end glyph range.
- const len = try reader.readInt(u16, .big);
- const records: [*]const [12]u8 = @ptrCast(data[try fbs.getPos()..]);
+ const len = try compat_reader.readerInt(&reader, u16, .big);
+ const records: [*]const [12]u8 = @ptrCast(data[reader.seek..]);
const start_range = try glyphRange(&records[0]);
const end_range = if (len == 1) start_range else try glyphRange(&records[(len - 1)]);
@@ -84,11 +82,10 @@ pub const SVG = struct {
}
fn glyphRange(record: []const u8) !struct { u16, u16 } {
- var fbs = std.io.fixedBufferStream(record);
- const reader = fbs.reader();
+ var reader: std.Io.Reader = .fixed(record);
return .{
- try reader.readInt(u16, .big),
- try reader.readInt(u16, .big),
+ try compat_reader.readerInt(&reader, u16, .big),
+ try compat_reader.readerInt(&reader, u16, .big),
};
}
};
@@ -112,3 +109,13 @@ test "SVG" {
try testing.expectEqual(11482, svg.end_glyph_id);
try testing.expect(svg.hasGlyph(11482));
}
+
+test "SVG document list offset past end" {
+ const testing = std.testing;
+ const table = [_]u8{
+ 0, 0, // Version
+ 0, 0, 0, 7, // Document list offset, one byte past end
+ };
+
+ try testing.expectError(error.EndOfStream, SVG.init(&table));
+}
diff --git a/vendor/libghostty-vt/src/font/shaper/coretext.zig b/vendor/libghostty-vt/src/font/shaper/coretext.zig
index 8a9374bd..5172744d 100644
--- a/vendor/libghostty-vt/src/font/shaper/coretext.zig
+++ b/vendor/libghostty-vt/src/font/shaper/coretext.zig
@@ -20,6 +20,7 @@ const SharedGrid = font.SharedGrid;
const Style = font.Style;
const Presentation = font.Presentation;
const CFReleaseThread = os.CFReleaseThread;
+const global = @import("../../global.zig");
const log = std.log.scoped(.font_shaper);
@@ -90,7 +91,7 @@ pub const Shaper = struct {
unichars: std.ArrayListUnmanaged(u16),
fn init() RunState {
- return .{ .codepoints = .{}, .unichars = .{} };
+ return .{ .codepoints = .empty, .unichars = .empty };
}
fn deinit(self: *RunState, alloc: Allocator) void {
@@ -213,18 +214,18 @@ pub const Shaper = struct {
CFReleaseThread.threadMain,
.{cf_release_thread},
);
- cf_release_thr.setName("cf_release") catch {};
+ cf_release_thr.setName(global.io(), "cf_release") catch {};
return .{
.alloc = alloc,
- .cell_buf = .{},
+ .cell_buf = .empty,
.run_state = run_state,
.features = features,
.features_no_default = features_no_default,
.typesetter_attr_dict = typesetter_attr_dict,
- .cached_fonts = .{},
+ .cached_fonts = .empty,
.cached_font_grid = 0,
- .cf_release_pool = .{},
+ .cf_release_pool = .empty,
.cf_release_thread = cf_release_thread,
.cf_release_thr = cf_release_thr,
};
@@ -282,7 +283,7 @@ pub const Shaper = struct {
// Send the items. If the send succeeds then we wake up the
// thread to process the items. If the send fails then do a manual
// cleanup.
- if (self.cf_release_thread.mailbox.push(.{ .release = .{
+ if (self.cf_release_thread.mailbox.push(global.io(), .{ .release = .{
.refs = items,
.alloc = self.alloc,
} }, .{ .forever = {} }) != 0) {
@@ -360,7 +361,7 @@ pub const Shaper = struct {
// Make room for the attributed string, CTTypesetter, and CTLine.
try self.cf_release_pool.ensureUnusedCapacity(self.alloc, 4);
- const str = macos.foundation.String.createWithCharactersNoCopy(state.unichars.items);
+ const str = try macos.foundation.String.createWithCharactersNoCopy(state.unichars.items);
self.cf_release_pool.appendAssumeCapacity(str);
// Create an attributed string from our string
@@ -380,7 +381,7 @@ pub const Shaper = struct {
self.cf_release_pool.appendAssumeCapacity(typesetter);
// Create a line from the typesetter
- const line = typesetter.createLine(.{ .location = 0, .length = 0 });
+ const line = try typesetter.createLine(.{ .location = 0, .length = 0 });
self.cf_release_pool.appendAssumeCapacity(line);
// This keeps track of the current x offset (sum of advance.width) and
@@ -606,8 +607,8 @@ pub const Shaper = struct {
//
// Because of this, we only acquire the read lock to grab the
// face and set it up, then release it.
- grid.lock.lockShared();
- defer grid.lock.unlockShared();
+ grid.lock.lockSharedUncancelable(global.io());
+ defer grid.lock.unlockShared(global.io());
const face = try grid.resolver.collection.getFace(index);
const original = face.font;
@@ -842,7 +843,7 @@ test "run iterator" {
defer testdata.deinit();
{
- var t: terminal.Terminal = try .init(alloc, .{
+ var t: terminal.Terminal = try .init(testing.io, alloc, .{
.cols = 5,
.rows = 3,
});
@@ -869,7 +870,7 @@ test "run iterator" {
// Spaces should be part of a run
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -892,7 +893,7 @@ test "run iterator" {
{
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -917,7 +918,7 @@ test "run iterator" {
// Bad ligatures
for (&[_][]const u8{ "fl", "fi", "st" }) |bad| {
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -949,7 +950,7 @@ test "run iterator: empty cells with background set" {
{
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1009,7 +1010,7 @@ test "shape" {
buf_idx += try std.unicode.utf8Encode(0x1F3FD, buf[buf_idx..]); // Medium skin tone
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1048,7 +1049,7 @@ test "shape nerd fonts" {
buf_idx += try std.unicode.utf8Encode(' ', buf[buf_idx..]); // space
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1081,7 +1082,7 @@ test "shape inconsolata ligs" {
defer testdata.deinit();
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1110,7 +1111,7 @@ test "shape inconsolata ligs" {
}
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1147,7 +1148,7 @@ test "shape monaspace ligs" {
defer testdata.deinit();
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1185,7 +1186,7 @@ test "shape left-replaced lig in last run" {
defer testdata.deinit();
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1223,7 +1224,7 @@ test "shape left-replaced lig in early run" {
defer testdata.deinit();
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1258,7 +1259,7 @@ test "shape U+3C9 with JB Mono" {
defer testdata.deinit();
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1295,7 +1296,7 @@ test "shape emoji width" {
defer testdata.deinit();
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1330,7 +1331,7 @@ test "shape emoji width long" {
defer testdata.deinit();
// Make a screen and add a long emoji sequence to it.
- var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 });
defer t.deinit(alloc);
var page = t.screens.active.pages.pages.first.?.page();
@@ -1338,7 +1339,7 @@ test "shape emoji width long" {
const cell = &row.cells.ptr(page.memory)[0];
cell.* = .{
.content_tag = .codepoint,
- .content = .{ .codepoint = 0x1F9D4 }, // Person with beard
+ .content = .{ .codepoint = .{ .data = 0x1F9D4 } }, // Person with beard
};
var graphemes = [_]u21{
0x1F3FB, // Light skin tone (Fitz 1-2)
@@ -1385,7 +1386,7 @@ test "shape variation selector VS15" {
buf_idx += try std.unicode.utf8Encode(0xFE0E, buf[buf_idx..]); // ZWJ to force text
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1424,7 +1425,7 @@ test "shape variation selector VS16" {
buf_idx += try std.unicode.utf8Encode(0xFE0F, buf[buf_idx..]); // ZWJ to force color
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1458,7 +1459,7 @@ test "shape with empty cells in between" {
defer testdata.deinit();
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1502,7 +1503,7 @@ test "shape Combining characters" {
buf_idx += try std.unicode.utf8Encode('a', buf[buf_idx..]);
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 });
defer t.deinit(alloc);
// Enable grapheme clustering
@@ -1552,7 +1553,7 @@ test "shape Devanagari string" {
defer testdata.deinit();
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 });
defer t.deinit(alloc);
// Disable grapheme clustering
@@ -1611,7 +1612,7 @@ test "shape Tai Tham vowels (position differs from advance)" {
buf_idx += try std.unicode.utf8Encode(0x1a70, buf[buf_idx..]); // ᩰ
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 });
defer t.deinit(alloc);
// Enable grapheme clustering
@@ -1672,7 +1673,7 @@ test "shape Tai Tham letters (position.y differs from advance)" {
buf_idx += try std.unicode.utf8Encode(0x1a69, buf[buf_idx..]); // U
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 });
defer t.deinit(alloc);
// Enable grapheme clustering
@@ -1732,7 +1733,7 @@ test "shape Javanese ligatures" {
buf_idx += try std.unicode.utf8Encode(0xa9b8, buf[buf_idx..]); // Vowel sign SUKU
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 });
defer t.deinit(alloc);
// Enable grapheme clustering
@@ -1795,7 +1796,7 @@ test "shape Chakma vowel sign with ligature (vowel sign renders first)" {
buf_idx += try std.unicode.utf8Encode(0x1112c, buf[buf_idx..]); // Vowel Sign U
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 });
defer t.deinit(alloc);
// Enable grapheme clustering
@@ -1866,7 +1867,7 @@ test "shape Bengali ligatures with out of order vowels" {
buf_idx += try std.unicode.utf8Encode(0x09c7, buf[buf_idx..]); // Vowel sign E
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 });
defer t.deinit(alloc);
// Enable grapheme clustering
@@ -1924,7 +1925,7 @@ test "shape box glyphs" {
buf_idx += try std.unicode.utf8Encode(0x2501, buf[buf_idx..]); //
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1962,7 +1963,7 @@ test "shape selection boundary" {
defer testdata.deinit();
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -2067,7 +2068,7 @@ test "shape cursor boundary" {
defer testdata.deinit();
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -2204,7 +2205,7 @@ test "shape cursor boundary and colored emoji" {
defer testdata.deinit();
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 3, .rows = 10 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -2301,7 +2302,7 @@ test "shape cell attribute change" {
// Plain >= should shape into 1 run
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -2327,7 +2328,7 @@ test "shape cell attribute change" {
// Bold vs regular should split
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 3, .rows = 10 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -2355,7 +2356,7 @@ test "shape cell attribute change" {
// Changing fg color should split
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 3, .rows = 10 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -2386,7 +2387,7 @@ test "shape cell attribute change" {
// Changing bg color should NOT split
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 3, .rows = 10 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -2417,7 +2418,7 @@ test "shape cell attribute change" {
// Same bg color should not split
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 3, .rows = 10 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -2462,7 +2463,7 @@ test "shape high plane sprite font codepoint" {
var testdata = try testShaper(alloc);
defer testdata.deinit();
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
diff --git a/vendor/libghostty-vt/src/font/shaper/feature.zig b/vendor/libghostty-vt/src/font/shaper/feature.zig
index 5bd73f97..c60eb049 100644
--- a/vendor/libghostty-vt/src/font/shaper/feature.zig
+++ b/vendor/libghostty-vt/src/font/shaper/feature.zig
@@ -1,6 +1,7 @@
const std = @import("std");
const assert = @import("../../quirks.zig").inlineAssert;
const Allocator = std.mem.Allocator;
+const compat_reader = @import("../../lib/compat/reader.zig");
const log = std.log.scoped(.font_shaper);
@@ -18,9 +19,8 @@ pub const Feature = struct {
value: u32,
pub fn fromString(str: []const u8) ?Feature {
- var fbs = std.io.fixedBufferStream(str);
- const reader = fbs.reader();
- return .fromReader(reader);
+ var reader: std.Io.Reader = .fixed(str);
+ return .fromReader(&reader);
}
/// Parse a single font feature setting from a std.io.Reader, with a version
@@ -55,7 +55,7 @@ pub const Feature = struct {
done,
}).start) {
// If we're done then we skip whitespace until we see a ','.
- .done => while (true) switch (reader.readByte() catch ',') {
+ .done => while (true) switch (compat_reader.readByte(reader) catch ',') {
' ', '\t' => continue,
',' => break,
// If we see something other than whitespace or a ','
@@ -66,11 +66,11 @@ pub const Feature = struct {
// If we're fast-forwarding from an error we just wanna
// stop at the first boundary and ignore all other bytes.
.err => {
- reader.skipUntilDelimiterOrEof(',') catch {};
+ compat_reader.readSkipUntilDelimiterOrEof(reader, ',') catch {};
return null;
},
- .start => while (true) switch (reader.readByte() catch ',') {
+ .start => while (true) switch (compat_reader.readByte(reader) catch ',') {
// Ignore leading whitespace.
' ', '\t' => continue,
// Empty feature string.
@@ -97,7 +97,7 @@ pub const Feature = struct {
},
},
- .tag => while (true) switch (reader.readByte() catch ',') {
+ .tag => while (true) switch (compat_reader.readByte(reader) catch ',') {
// If the tag is interrupted by a comma it's invalid.
',' => return null,
// Ignore quote marks. This does technically ignore cases like
@@ -112,7 +112,7 @@ pub const Feature = struct {
},
},
- .space => while (true) switch (reader.readByte() catch ',') {
+ .space => while (true) switch (compat_reader.readByte(reader) catch ',') {
' ', '\t' => continue,
// Ignore quote marks since we might have a
// closing quote from the tag still ahead.
@@ -142,7 +142,7 @@ pub const Feature = struct {
else => continue :state .err,
},
- .int => while (true) switch (reader.readByte() catch ',') {
+ .int => while (true) switch (compat_reader.readByte(reader) catch ',') {
',' => break,
'0'...'9' => |byte| {
// If our value gets too big while
@@ -155,7 +155,7 @@ pub const Feature = struct {
else => continue :state .err,
},
- .bool => while (true) switch (reader.readByte() catch ',') {
+ .bool => while (true) switch (compat_reader.readByte(reader) catch ',') {
',' => return null,
'n', 'N' => {
// "ofn"
@@ -222,7 +222,7 @@ pub const Feature = struct {
/// A list of font feature settings (see `Feature` for more documentation).
pub const FeatureList = struct {
- features: std.ArrayListUnmanaged(Feature) = .{},
+ features: std.ArrayList(Feature) = .empty,
pub fn deinit(self: *FeatureList, alloc: Allocator) void {
self.features.deinit(alloc);
@@ -243,15 +243,14 @@ pub const FeatureList = struct {
alloc: Allocator,
str: []const u8,
) !void {
- var fbs = std.io.fixedBufferStream(str);
- const reader = fbs.reader();
- while (fbs.pos < fbs.buffer.len) {
- const i = fbs.pos;
- if (Feature.fromReader(reader)) |feature| {
+ var reader: std.Io.Reader = .fixed(str);
+ while (reader.seek < reader.end) {
+ const i = reader.seek;
+ if (Feature.fromReader(&reader)) |feature| {
try self.features.append(alloc, feature);
} else log.warn(
"failed to parse font feature setting: \"{s}\"",
- .{fbs.buffer[i..fbs.pos]},
+ .{reader.buffer[i..reader.seek]},
);
}
}
diff --git a/vendor/libghostty-vt/src/font/shaper/harfbuzz.zig b/vendor/libghostty-vt/src/font/shaper/harfbuzz.zig
index 996aabff..11f9fd23 100644
--- a/vendor/libghostty-vt/src/font/shaper/harfbuzz.zig
+++ b/vendor/libghostty-vt/src/font/shaper/harfbuzz.zig
@@ -15,6 +15,7 @@ const Library = font.Library;
const SharedGrid = font.SharedGrid;
const Style = font.Style;
const Presentation = font.Presentation;
+const global = @import("../../global.zig");
const log = std.log.scoped(.font_shaper);
@@ -36,7 +37,7 @@ pub const Shaper = struct {
/// The codepoints added to the buffer before shaping. We need to keep
/// these separately because after shaping, HarfBuzz replaces codepoints
/// with glyph indices in the buffer.
- codepoints: std.ArrayListUnmanaged(Codepoint) = .{},
+ codepoints: std.ArrayList(Codepoint) = .empty,
const Codepoint = struct {
cluster: u32,
@@ -87,7 +88,7 @@ pub const Shaper = struct {
return Shaper{
.alloc = alloc,
.hb_buf = try harfbuzz.Buffer.create(),
- .cell_buf = .{},
+ .cell_buf = .empty,
.hb_feats = hb_feats,
};
}
@@ -134,8 +135,8 @@ pub const Shaper = struct {
// We have to lock the grid to get the face and unfortunately
// freetype faces (typically used with harfbuzz) are not thread
// safe so this has to be an exclusive lock.
- run.grid.lock.lock();
- defer run.grid.lock.unlock();
+ run.grid.lock.lockUncancelable(global.io());
+ defer run.grid.lock.unlock(global.io());
const face = try run.grid.resolver.collection.getFace(run.font_index);
const i = if (!face.quirks_disable_default_font_features) 0 else i: {
@@ -437,13 +438,14 @@ pub const Shaper = struct {
test "run iterator" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaper(alloc);
defer testdata.deinit();
{
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 5, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -467,7 +469,7 @@ test "run iterator" {
// Spaces should be part of a run
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -490,7 +492,7 @@ test "run iterator" {
{
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 5, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -521,13 +523,14 @@ test "run iterator" {
test "run iterator: empty cells with background set" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaper(alloc);
defer testdata.deinit();
{
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 5, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -576,6 +579,7 @@ test "run iterator: empty cells with background set" {
test "shape" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaper(alloc);
defer testdata.deinit();
@@ -587,7 +591,7 @@ test "shape" {
buf_idx += try std.unicode.utf8Encode(0x1F3FD, buf[buf_idx..]); // Medium skin tone
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -616,12 +620,13 @@ test "shape" {
test "shape inconsolata ligs" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaper(alloc);
defer testdata.deinit();
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 5, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -650,7 +655,7 @@ test "shape inconsolata ligs" {
}
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 5, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -682,12 +687,13 @@ test "shape inconsolata ligs" {
test "shape monaspace ligs" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaperWithFont(alloc, .monaspace_neon);
defer testdata.deinit();
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 5, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -723,11 +729,12 @@ test "shape monaspace ligs" {
test "shape arabic forced LTR" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaperWithFont(alloc, .arabic);
defer testdata.deinit();
- var t = try terminal.Terminal.init(alloc, .{ .cols = 120, .rows = 30 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 120, .rows = 30 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -763,12 +770,13 @@ test "shape arabic forced LTR" {
test "shape emoji width" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaper(alloc);
defer testdata.deinit();
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 5, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -800,12 +808,14 @@ test "shape emoji width" {
test "shape emoji width long" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaper(alloc);
defer testdata.deinit();
// Make a screen and add a long emoji sequence to it.
var t = try terminal.Terminal.init(
+ io,
alloc,
.{ .cols = 30, .rows = 3 },
);
@@ -816,7 +826,7 @@ test "shape emoji width long" {
const cell = &row.cells.ptr(page.memory)[0];
cell.* = .{
.content_tag = .codepoint,
- .content = .{ .codepoint = 0x1F9D4 }, // Person with beard
+ .content = .{ .codepoint = @bitCast(@as(u24, 0x1F9D4)) }, // Person with beard
};
var graphemes = [_]u21{
0x1F3FB, // Light skin tone (Fitz 1-2)
@@ -852,9 +862,45 @@ test "shape emoji width long" {
try testing.expectEqual(@as(usize, 1), count);
}
+test "shaper selects font for entire grapheme" {
+ const testing = std.testing;
+ const alloc = testing.allocator;
+ const io = testing.io;
+
+ var testdata = try testShaper(alloc);
+ defer testdata.deinit();
+
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 5, .rows = 3 });
+ defer t.deinit(alloc);
+
+ var s = t.vtStream();
+ defer s.deinit();
+ s.nextSlice("#\u{20E3}"); // Combining enclosing keycap
+
+ const primary = (try testdata.grid.getIndex(alloc, '#', .regular, null)).?;
+ try testing.expect(testdata.grid.hasCodepoint(primary, '#', null));
+ try testing.expect(!testdata.grid.hasCodepoint(primary, 0x20E3, null));
+
+ const additional = (try testdata.grid.getIndex(alloc, 0x20E3, .regular, null)).?;
+ try testing.expect(testdata.grid.hasCodepoint(additional, '#', null));
+ try testing.expect(testdata.grid.hasCodepoint(additional, 0x20E3, null));
+
+ var state: terminal.RenderState = .empty;
+ defer state.deinit(alloc);
+ try state.update(alloc, &t);
+
+ var it = testdata.shaper.runIterator(.{
+ .grid = testdata.grid,
+ .cells = state.row_data.get(0).cells.slice(),
+ });
+ const run = (try it.next(alloc)).?;
+ try testing.expectEqual(additional, run.font_index);
+}
+
test "shape variation selector VS15" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaper(alloc);
defer testdata.deinit();
@@ -865,7 +911,7 @@ test "shape variation selector VS15" {
buf_idx += try std.unicode.utf8Encode(0xFE0E, buf[buf_idx..]); // ZWJ to force text
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -896,6 +942,7 @@ test "shape variation selector VS15" {
test "shape variation selector VS16" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaper(alloc);
defer testdata.deinit();
@@ -906,7 +953,7 @@ test "shape variation selector VS16" {
buf_idx += try std.unicode.utf8Encode(0xFE0F, buf[buf_idx..]); // ZWJ to force color
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -937,12 +984,14 @@ test "shape variation selector VS16" {
test "shape with empty cells in between" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaper(alloc);
defer testdata.deinit();
// Make a screen with some data
var t = try terminal.Terminal.init(
+ io,
alloc,
.{ .cols = 30, .rows = 3 },
);
@@ -977,6 +1026,7 @@ test "shape with empty cells in between" {
test "shape Combining characters" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaper(alloc);
defer testdata.deinit();
@@ -990,6 +1040,7 @@ test "shape Combining characters" {
// Make a screen with some data
var t = try terminal.Terminal.init(
+ io,
alloc,
.{ .cols = 30, .rows = 3 },
);
@@ -1029,6 +1080,7 @@ test "shape Combining characters" {
test "shape Devanagari string" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
// We need a font that supports devanagari for this to work, if we can't
// find Arial Unicode MS, which is a system font on macOS, we just skip
@@ -1040,7 +1092,7 @@ test "shape Devanagari string" {
defer testdata.deinit();
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 30, .rows = 3 });
defer t.deinit(alloc);
// Disable grapheme clustering
@@ -1087,6 +1139,7 @@ test "shape Tai Tham vowels (position differs from advance)" {
// // behavior.
// const testing = std.testing;
// const alloc = testing.allocator;
+ // const io = testing.io;
// // We need a font that supports Tai Tham for this to work, if we can't find
// // Noto Sans Tai Tham, which is a system font on macOS, we just skip the
@@ -1103,7 +1156,7 @@ test "shape Tai Tham vowels (position differs from advance)" {
// buf_idx += try std.unicode.utf8Encode(0x1a70, buf[buf_idx..]); // ᩰ
// // Make a screen with some data
- // var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 });
+ // var t = try terminal.Terminal.init(io, alloc, .{ .cols = 30, .rows = 3 });
// defer t.deinit(alloc);
// // Enable grapheme clustering
@@ -1147,6 +1200,7 @@ test "shape Tai Tham vowels (position differs from advance)" {
test "shape Tibetan characters" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
// We need a font that has multiple glyphs for this codepoint to reproduce
// the old broken behavior, and Noto Serif Tibetan is one of them. It's not
@@ -1162,7 +1216,7 @@ test "shape Tibetan characters" {
buf_idx += try std.unicode.utf8Encode(0x0f00, buf[buf_idx..]); // ༀ
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 30, .rows = 3 });
defer t.deinit(alloc);
// Enable grapheme clustering
@@ -1203,6 +1257,7 @@ test "shape Tai Tham letters (run_offset.y differs from zero)" {
return error.SkipZigTest;
// const testing = std.testing;
// const alloc = testing.allocator;
+ // const io = testing.io;
// // We need a font that supports Tai Tham for this to work, if we can't find
// // Noto Sans Tai Tham, which is a system font on macOS, we just skip the
@@ -1224,7 +1279,7 @@ test "shape Tai Tham letters (run_offset.y differs from zero)" {
// buf_idx += try std.unicode.utf8Encode(0x1a69, buf[buf_idx..]); // U
// // Make a screen with some data
- // var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 });
+ // var t = try terminal.Terminal.init(io, alloc, .{ .cols = 30, .rows = 3 });
// defer t.deinit(alloc);
// // Enable grapheme clustering
@@ -1266,6 +1321,7 @@ test "shape Javanese ligatures" {
return error.SkipZigTest;
// const testing = std.testing;
// const alloc = testing.allocator;
+ // const io = testing.io;
// // We need a font that supports Javanese for this to work, if we can't find
// // Noto Sans Javanese Regular, which is a system font on macOS, we just
@@ -1287,7 +1343,7 @@ test "shape Javanese ligatures" {
// buf_idx += try std.unicode.utf8Encode(0xa9b8, buf[buf_idx..]); // Vowel sign SUKU
// // Make a screen with some data
- // var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 });
+ // var t = try terminal.Terminal.init(io, alloc, .{ .cols = 30, .rows = 3 });
// defer t.deinit(alloc);
// // Enable grapheme clustering
@@ -1327,6 +1383,7 @@ test "shape Javanese ligatures" {
test "shape Chakma vowel sign with ligature (vowel sign renders first)" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
// We need a font that supports Chakma for this to work, if we can't find
// Noto Sans Chakma Regular, which is a system font on macOS, we just skip
@@ -1350,7 +1407,7 @@ test "shape Chakma vowel sign with ligature (vowel sign renders first)" {
buf_idx += try std.unicode.utf8Encode(0x1112c, buf[buf_idx..]); // Vowel Sign U
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 30, .rows = 3 });
defer t.deinit(alloc);
// Enable grapheme clustering
@@ -1398,6 +1455,7 @@ test "shape Bengali ligatures with out of order vowels" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
// We need a font that supports Bengali for this to work, if we can't find
// Arial Unicode MS, which is a system font on macOS, we just skip the
@@ -1425,7 +1483,7 @@ test "shape Bengali ligatures with out of order vowels" {
buf_idx += try std.unicode.utf8Encode(0x09c7, buf[buf_idx..]); // Vowel sign E
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 30, .rows = 3 });
defer t.deinit(alloc);
// Enable grapheme clustering
@@ -1472,6 +1530,7 @@ test "shape Bengali ligatures with out of order vowels" {
test "shape box glyphs" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaper(alloc);
defer testdata.deinit();
@@ -1482,7 +1541,7 @@ test "shape box glyphs" {
buf_idx += try std.unicode.utf8Encode(0x2501, buf[buf_idx..]); //
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1516,12 +1575,13 @@ test "shape box glyphs" {
test "shape selection boundary" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaper(alloc);
defer testdata.deinit();
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1621,12 +1681,13 @@ test "shape selection boundary" {
test "shape cursor boundary" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaper(alloc);
defer testdata.deinit();
// Make a screen with some data
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1758,12 +1819,14 @@ test "shape cursor boundary" {
test "shape cursor boundary and colored emoji" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaper(alloc);
defer testdata.deinit();
// Make a screen with some data
var t = try terminal.Terminal.init(
+ io,
alloc,
.{ .cols = 3, .rows = 10 },
);
@@ -1857,13 +1920,14 @@ test "shape cursor boundary and colored emoji" {
test "shape cell attribute change" {
const testing = std.testing;
const alloc = testing.allocator;
+ const io = testing.io;
var testdata = try testShaper(alloc);
defer testdata.deinit();
// Plain >= should shape into 1 run
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 10, .rows = 3 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1889,7 +1953,7 @@ test "shape cell attribute change" {
// Bold vs regular should split
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 3, .rows = 10 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1917,7 +1981,7 @@ test "shape cell attribute change" {
// Changing fg color should split
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 3, .rows = 10 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1948,7 +2012,7 @@ test "shape cell attribute change" {
// Changing bg color should not split
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 3, .rows = 10 });
defer t.deinit(alloc);
var s = t.vtStream();
@@ -1979,7 +2043,7 @@ test "shape cell attribute change" {
// Same bg color should not split
{
- var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 });
+ var t = try terminal.Terminal.init(io, alloc, .{ .cols = 3, .rows = 10 });
defer t.deinit(alloc);
var s = t.vtStream();
diff --git a/vendor/libghostty-vt/src/font/shaper/run.zig b/vendor/libghostty-vt/src/font/shaper/run.zig
index 45c5d38c..9f33ba32 100644
--- a/vendor/libghostty-vt/src/font/shaper/run.zig
+++ b/vendor/libghostty-vt/src/font/shaper/run.zig
@@ -348,47 +348,34 @@ pub const RunIterator = struct {
// we just return whatever index for the cell codepoint.
if (!cell.hasGrapheme()) return primary;
- // If this is a grapheme, we need to find a font that supports
- // all of the codepoints in the grapheme.
- var candidates: std.ArrayList(font.Collection.Index) = try .initCapacity(
- alloc,
- graphemes.len + 1,
- );
- defer candidates.deinit(alloc);
- candidates.appendAssumeCapacity(primary);
+ // Try the primary font followed by the font selected for each
+ // additional codepoint until one supports the entire grapheme.
+ candidate: for (0..graphemes.len + 1) |i| {
+ const idx = if (i == 0) primary else idx: {
+ const cp = graphemes[i - 1];
- for (graphemes) |cp| {
- // Ignore Emoji ZWJs
- if (cp == 0xFE0E or cp == 0xFE0F or cp == 0x200D) continue;
+ // Ignore Emoji ZWJs
+ if (cp == 0xFE0E or cp == 0xFE0F or cp == 0x200D) continue :candidate;
- // Find a font that supports this codepoint. If none support this
- // then the whole grapheme can't be rendered so we return null.
- //
- // We explicitly do not require the additional grapheme components
- // to support the base presentation, since it is common for emoji
- // fonts to support the base emoji with emoji presentation but not
- // certain ZWJ-combined characters like the male and female signs.
- const idx = try self.opts.grid.getIndex(
- alloc,
- cp,
- style,
- null,
- ) orelse return null;
- candidates.appendAssumeCapacity(idx);
- }
+ // We explicitly do not require the additional grapheme components
+ // to support the base presentation, since it is common for emoji
+ // fonts to support the base emoji with emoji presentation but not
+ // certain ZWJ-combined characters like the male and female signs.
+ break :idx try self.opts.grid.getIndex(
+ alloc,
+ cp,
+ style,
+ null,
+ ) orelse return null;
+ };
- // We need to find a candidate that has ALL of our codepoints
- for (candidates.items) |idx| {
if (!self.opts.grid.hasCodepoint(idx, primary_cp, presentation)) continue;
for (graphemes) |cp| {
// Ignore Emoji ZWJs
if (cp == 0xFE0E or cp == 0xFE0F or cp == 0x200D) continue;
- if (!self.opts.grid.hasCodepoint(idx, cp, null)) break;
- } else {
- // If the while completed, then we have a candidate that
- // supports all of our codepoints.
- return idx;
+ if (!self.opts.grid.hasCodepoint(idx, cp, null)) continue :candidate;
}
+ return idx;
}
return null;
diff --git a/vendor/libghostty-vt/src/font/sprite/Face.zig b/vendor/libghostty-vt/src/font/sprite/Face.zig
index fd029d13..e2e37d2a 100644
--- a/vendor/libghostty-vt/src/font/sprite/Face.zig
+++ b/vendor/libghostty-vt/src/font/sprite/Face.zig
@@ -273,15 +273,16 @@ fn testDiffAtlas(
// Get the file contents, we compare the PNG data first in
// order to ensure that no one smuggles arbitrary binary
// data in to the reference PNGs.
- const test_file = try std.fs.openFileAbsolute(path, .{ .mode = .read_only });
- defer test_file.close();
- const test_bytes = try test_file.readToEndAlloc(
+ const test_file = try std.Io.Dir.openFileAbsolute(std.testing.io, path, .{ .mode = .read_only });
+ defer test_file.close(std.testing.io);
+ var test_file_reader = test_file.reader(std.testing.io, &.{});
+ const test_bytes = try test_file_reader.interface.readAlloc(
alloc,
- std.math.maxInt(usize),
+ (try test_file.stat(std.testing.io)).size,
);
defer alloc.free(test_bytes);
- const cwd_absolute = try std.fs.cwd().realpathAlloc(alloc, ".");
+ const cwd_absolute = try std.Io.Dir.cwd().realPathFileAlloc(std.testing.io, ".", alloc);
defer alloc.free(cwd_absolute);
// Get the reference file contents to compare.
@@ -292,7 +293,7 @@ fn testDiffAtlas(
);
defer alloc.free(ref_path);
const ref_file =
- std.fs.cwd().openFile(ref_path, .{ .mode = .read_only }) catch |err| {
+ std.Io.Dir.cwd().openFile(std.testing.io, ref_path, .{ .mode = .read_only }) catch |err| {
log.err("Can't open reference file {s}: {}\n", .{
ref_path,
err,
@@ -306,14 +307,15 @@ fn testDiffAtlas(
.{ cwd_absolute, i, i + 0xFF, width, height, thickness },
);
defer alloc.free(test_path);
- try std.fs.copyFileAbsolute(path, test_path, .{});
+ try std.Io.Dir.copyFileAbsolute(path, test_path, std.testing.io, .{});
return true;
};
- defer ref_file.close();
- const ref_bytes = try ref_file.readToEndAlloc(
+ defer ref_file.close(std.testing.io);
+ var ref_file_reader = ref_file.reader(std.testing.io, &.{});
+ const ref_bytes = try ref_file_reader.interface.readAlloc(
alloc,
- std.math.maxInt(usize),
+ (try ref_file.stat(std.testing.io)).size,
);
defer alloc.free(ref_bytes);
@@ -330,7 +332,7 @@ fn testDiffAtlas(
.{ cwd_absolute, i, i + 0xFF, width, height, thickness },
);
defer alloc.free(test_path);
- try std.fs.copyFileAbsolute(path, test_path, .{});
+ try std.Io.Dir.copyFileAbsolute(path, test_path, std.testing.io, .{});
// Use wuffs to decode the reference PNG to raw pixels.
// These will be RGBA, so when diffing we can just compare
@@ -394,7 +396,7 @@ fn testDiffAtlas(
.{ i, i + 0xFF, width, height, thickness },
);
defer alloc.free(diff_path);
- try z2d.png_exporter.writeToPNGFile(diff, diff_path, .{});
+ try z2d.png_exporter.writeToPNGFile(std.testing.io, diff, diff_path, .{});
log.err(
"One or more glyphs differ from reference file in range U+{X}...U+{X}! " ++
"test={s}, reference={s}, diff={s}",
@@ -462,7 +464,7 @@ fn testDrawRanges(
// Try to make the sprite_face_test folder if it doesn't already exist.
var dir = testing.tmpDir(.{});
defer dir.cleanup();
- const tmp_dir = try dir.dir.realpathAlloc(alloc, ".");
+ const tmp_dir = try dir.dir.realPathFileAlloc(testing.io, ".", alloc);
defer alloc.free(tmp_dir);
// We set this to true if we have any fails so we can
@@ -481,7 +483,7 @@ fn testDrawRanges(
.{ tmp_dir, i, i + 0xFF, width, height, thickness },
);
defer alloc.free(path);
- try z2d.png_exporter.writeToPNGFile(atlas, path, .{});
+ try z2d.png_exporter.writeToPNGFile(testing.io, atlas, path, .{});
if (try testDiffAtlas(
alloc,
@@ -526,7 +528,7 @@ fn testDrawRanges(
.{ tmp_dir, i, i + 0xFF, width, height, thickness },
);
defer alloc.free(path);
- try z2d.png_exporter.writeToPNGFile(atlas, path, .{});
+ try z2d.png_exporter.writeToPNGFile(std.testing.io, atlas, path, .{});
if (try testDiffAtlas(
alloc,
&atlas,
diff --git a/vendor/libghostty-vt/src/font/sprite/canvas.zig b/vendor/libghostty-vt/src/font/sprite/canvas.zig
index 7904f20a..da54ac27 100644
--- a/vendor/libghostty-vt/src/font/sprite/canvas.zig
+++ b/vendor/libghostty-vt/src/font/sprite/canvas.zig
@@ -5,6 +5,7 @@ const assert = @import("../../quirks.zig").inlineAssert;
const Allocator = std.mem.Allocator;
const z2d = @import("z2d");
const font = @import("../main.zig");
+const global = @import("../../global.zig");
pub fn Point(comptime T: type) type {
return struct {
@@ -255,7 +256,7 @@ pub const Canvas = struct {
/// Acquires a z2d drawing context, caller MUST deinit context.
pub fn getContext(self: *Canvas) z2d.Context {
- var ctx = z2d.Context.init(self.alloc, &self.sfc);
+ var ctx = z2d.Context.init(global.io(), self.alloc, &self.sfc);
// Offset by our padding to keep
// coordinates relative to the cell.
ctx.setTransformation(self.transformation());
@@ -374,85 +375,17 @@ pub const Canvas = struct {
);
}
- /// Do an inner stroke on a z2d path, right now this involves a pretty
- /// heavy workaround that uses two extra surfaces; in the future, z2d
- /// should add inner and outer strokes natively.
+ /// Do an inner stroke using a 1/2 stroke width inset of the supplied
+ /// `path`.
pub fn innerStrokePath(
self: *Canvas,
path: z2d.Path,
opts: z2d.painter.StrokeOptions,
color: Color,
- ) (z2d.painter.StrokeError || z2d.painter.FillError)!void {
- // On one surface we fill the shape, this will be a mask we
- // multiply with the double-width stroke so that only the
- // part inside is used.
- var fill_sfc: z2d.Surface = try .init(
- .image_surface_alpha8,
- self.alloc,
- self.sfc.getWidth(),
- self.sfc.getHeight(),
- );
- defer fill_sfc.deinit(self.alloc);
-
- // On the other we'll do the double width stroke.
- var stroke_sfc: z2d.Surface = try .init(
- .image_surface_alpha8,
- self.alloc,
- self.sfc.getWidth(),
- self.sfc.getHeight(),
- );
- defer stroke_sfc.deinit(self.alloc);
-
- // Make a closed version of the path for our fill, so
- // that we can support open paths for inner stroke.
- var closed_path = path;
- closed_path.nodes = try path.nodes.clone(self.alloc);
- defer closed_path.deinit(self.alloc);
- try closed_path.close(self.alloc);
-
- // Fill the shape in white to the fill surface, we use
- // white because this is a mask that we'll multiply with
- // the stroke, we want everything inside to be the stroke
- // color.
- try z2d.painter.fill(
- self.alloc,
- &fill_sfc,
- &.{ .opaque_pattern = .{
- .pixel = .{ .alpha8 = .{ .a = 255 } },
- } },
- closed_path.nodes.items,
- .{},
- );
-
- // Stroke the shape with double the desired width.
- var mut_opts = opts;
- mut_opts.line_width *= 2;
- try z2d.painter.stroke(
- self.alloc,
- &stroke_sfc,
- &.{ .opaque_pattern = .{
- .pixel = .{ .alpha8 = .{ .a = @intFromEnum(color) } },
- } },
- path.nodes.items,
- mut_opts,
- );
-
- // We multiply the stroke sfc on to the fill surface.
- // The z2d composite operation doesn't seem to work for
- // this with alpha8 surfaces, so we have to do it manually.
- for (
- std.mem.sliceAsBytes(fill_sfc.image_surface_alpha8.buf),
- std.mem.sliceAsBytes(stroke_sfc.image_surface_alpha8.buf),
- ) |*d, s| {
- d.* = @intFromFloat(@round(
- 255.0 *
- (@as(f64, @floatFromInt(s)) / 255.0) *
- (@as(f64, @floatFromInt(d.*)) / 255.0),
- ));
- }
-
- // Then we composite the result on to the main surface.
- self.sfc.composite(&fill_sfc, .src_over, 0, 0, .{});
+ ) (z2d.Path.OffsetError || z2d.painter.StrokeError)!void {
+ var inset_path = try path.offset(self.alloc, -opts.line_width / 2.0);
+ defer inset_path.deinit(self.alloc);
+ return self.strokePath(inset_path, opts, color);
}
/// Fill a z2d path.
diff --git a/vendor/libghostty-vt/src/font/sprite/draw/powerline.zig b/vendor/libghostty-vt/src/font/sprite/draw/powerline.zig
index 8658d855..35445cdd 100644
--- a/vendor/libghostty-vt/src/font/sprite/draw/powerline.zig
+++ b/vendor/libghostty-vt/src/font/sprite/draw/powerline.zig
@@ -292,8 +292,13 @@ pub fn drawE0B5(
const radius: f64 = @min(float_width, float_height / 2);
- var path = canvas.staticPath(4);
+ // Note on the first/final lineTo moves: this is to enforce horizontal
+ // lines on both ends. This ensures that when the path is offset and
+ // stroked, the ends are butt-capped entirely perpendicularly; curveTo
+ // lerping would produce very slight slopes otherwise.
+ var path = canvas.staticPath(6);
path.moveTo(0, 0);
+ path.lineTo(1, 0);
path.curveTo(
radius * c,
0,
@@ -308,9 +313,10 @@ pub fn drawE0B5(
float_height - radius + radius * c,
radius * c,
float_height,
- 0,
+ 1,
float_height,
);
+ path.lineTo(0, float_height);
try canvas.innerStrokePath(path.wrapped_path, .{
.line_width = @floatFromInt(metrics.box_thickness),
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-11x21+2.png
index e04e7726..00ee592b 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-11x21+2.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-11x21+2.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-12x24+3.png
index ce1b1c42..7b37e566 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-12x24+3.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-12x24+3.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-18x36+4.png
index 1c21f688..6b19f819 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-18x36+4.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-18x36+4.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-9x17+1.png
index 459822e6..04c9ac6a 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-9x17+1.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-9x17+1.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-11x21+2.png
index 8d7de36a..898264f3 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-11x21+2.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-11x21+2.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-12x24+3.png
index ab6bec96..59da98ba 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-12x24+3.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-12x24+3.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-18x36+4.png
index 43035aef..e3dff1f3 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-18x36+4.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-18x36+4.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-9x17+1.png
index fc111e2d..6069996b 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-9x17+1.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-9x17+1.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-11x21+2.png
index b3cda82d..3cbf0934 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-11x21+2.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-11x21+2.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-12x24+3.png
index e076da7c..9f62fc4b 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-12x24+3.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-12x24+3.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-18x36+4.png
index 366be386..ded30f0f 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-18x36+4.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-18x36+4.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-9x17+1.png
index 5cd7a4ef..260e536d 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-9x17+1.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-9x17+1.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-11x21+2.png
index 023396fe..6f408b37 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-11x21+2.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-11x21+2.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-12x24+3.png
index 1a4dcb01..99da9148 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-12x24+3.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-12x24+3.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-18x36+4.png
index 73ead485..e2136d7d 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-18x36+4.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-18x36+4.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-9x17+1.png
index b58ded46..2db1f743 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-9x17+1.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-9x17+1.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-11x21+2.png
index 9ae5b45f..eb0032eb 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-11x21+2.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-11x21+2.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-12x24+3.png
index 89011df4..99032356 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-12x24+3.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-12x24+3.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-18x36+4.png
index 05ff2a5f..9d6fc664 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-18x36+4.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-18x36+4.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-9x17+1.png
index cf96b7d1..5c6ef4db 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-9x17+1.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-9x17+1.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-11x21+2.png
index 804e3017..f171dd24 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-11x21+2.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-11x21+2.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-12x24+3.png
index aa3e2f99..4ccdfc4a 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-12x24+3.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-12x24+3.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-18x36+4.png
index 49f431a4..47e70eee 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-18x36+4.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-18x36+4.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-9x17+1.png
index 4308c536..5f0928e2 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-9x17+1.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-9x17+1.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-11x21+2.png
index addd1f49..905adfdc 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-11x21+2.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-11x21+2.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-12x24+3.png
index 70ae8102..c19a7bc0 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-12x24+3.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-12x24+3.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-18x36+4.png
index 32a42097..052ee7e7 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-18x36+4.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-18x36+4.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-9x17+1.png
index 46151697..159c7721 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-9x17+1.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-9x17+1.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-11x21+2.png
index 10385379..94deceb4 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-11x21+2.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-11x21+2.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-12x24+3.png
index b1bb803a..59323c67 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-12x24+3.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-12x24+3.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-18x36+4.png
index e7415b7a..05a124ec 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-18x36+4.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-18x36+4.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-9x17+1.png
index 730a4c42..dcb16b3d 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-9x17+1.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-9x17+1.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-11x21+2.png
index b216174e..de2772e7 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-11x21+2.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-11x21+2.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-12x24+3.png
index 2243248b..8cf94a51 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-12x24+3.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-12x24+3.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-18x36+4.png
index f3a887d2..9e93a93a 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-18x36+4.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-18x36+4.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-9x17+1.png
index 82ce8246..7bfd9db2 100644
--- a/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-9x17+1.png
+++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-9x17+1.png
Binary files differ
diff --git a/vendor/libghostty-vt/src/font/testdata/glyf_rasterize.png b/vendor/libghostty-vt/src/font/testdata/glyf_rasterize.png
index f5f8fb81..689e3e91 100644
--- a/vendor/libghostty-vt/src/font/testdata/glyf_rasterize.png
+++ b/vendor/libghostty-vt/src/font/testdata/glyf_rasterize.png
Binary files differ