mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-10-09 03:16:33 +00:00
Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7071a22cb5 | ||
![]() |
a586b47dc9 | ||
![]() |
c8efb2a8c9 | ||
![]() |
62ed472d9e |
@@ -1,6 +1,6 @@
|
||||
.{
|
||||
.name = .ghostty,
|
||||
.version = "1.2.1",
|
||||
.version = "1.2.2",
|
||||
.paths = .{""},
|
||||
.fingerprint = 0x64407a2a0b4147e5,
|
||||
.minimum_zig_version = "0.14.1",
|
||||
|
@@ -52,8 +52,8 @@
|
||||
|
||||
<releases>
|
||||
<!-- TODO: Generate this automatically -->
|
||||
<release version="1.2.1" date="2025-10-06">
|
||||
<url type="details">https://ghostty.org/docs/install/release-notes/1-2-1</url>
|
||||
<release version="1.2.2" date="2025-10-08">
|
||||
<url type="details">https://ghostty.org/docs/install/release-notes/1-2-2</url>
|
||||
</release>
|
||||
</releases>
|
||||
</component>
|
||||
|
@@ -40,7 +40,7 @@
|
||||
in
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "ghostty";
|
||||
version = "1.2.1";
|
||||
version = "1.2.2";
|
||||
|
||||
# We limit source like this to try and reduce the amount of rebuilds as possible
|
||||
# thus we only provide the source that is needed for the build
|
||||
|
@@ -20,7 +20,7 @@ const GitVersion = @import("GitVersion.zig");
|
||||
/// TODO: When Zig 0.14 is released, derive this from build.zig.zon directly.
|
||||
/// Until then this MUST match build.zig.zon and should always be the
|
||||
/// _next_ version to release.
|
||||
const app_version: std.SemanticVersion = .{ .major = 1, .minor = 2, .patch = 1 };
|
||||
const app_version: std.SemanticVersion = .{ .major = 1, .minor = 2, .patch = 2 };
|
||||
|
||||
/// Standard build configuration options.
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
|
147
src/datastruct/comparison.zig
Normal file
147
src/datastruct/comparison.zig
Normal file
@@ -0,0 +1,147 @@
|
||||
// The contents of this file is largely based on testing.zig from the Zig 0.15.1
|
||||
// stdlib, distributed under the MIT license, copyright (c) Zig contributors
|
||||
const std = @import("std");
|
||||
|
||||
/// Generic, recursive equality testing utility using approximate comparison for
|
||||
/// floats and equality for everything else
|
||||
///
|
||||
/// Based on `std.testing.expectEqual` and `std.testing.expectEqualSlices`.
|
||||
///
|
||||
/// The relative tolerance is currently hardcoded to `sqrt(eps(float_type))`.
|
||||
pub inline fn expectApproxEqual(expected: anytype, actual: anytype) !void {
|
||||
const T = @TypeOf(expected, actual);
|
||||
return expectApproxEqualInner(T, expected, actual);
|
||||
}
|
||||
|
||||
fn expectApproxEqualInner(comptime T: type, expected: T, actual: T) !void {
|
||||
switch (@typeInfo(T)) {
|
||||
// check approximate equality for floats
|
||||
.float => {
|
||||
const sqrt_eps = comptime std.math.sqrt(std.math.floatEps(T));
|
||||
if (!std.math.approxEqRel(T, expected, actual, sqrt_eps)) {
|
||||
print("expected approximately {any}, found {any}\n", .{ expected, actual });
|
||||
return error.TestExpectedApproxEqual;
|
||||
}
|
||||
},
|
||||
|
||||
// recurse into containers
|
||||
.array => {
|
||||
const diff_index: usize = diff_index: {
|
||||
const shortest = @min(expected.len, actual.len);
|
||||
var index: usize = 0;
|
||||
while (index < shortest) : (index += 1) {
|
||||
expectApproxEqual(actual[index], expected[index]) catch break :diff_index index;
|
||||
}
|
||||
break :diff_index if (expected.len == actual.len) return else shortest;
|
||||
};
|
||||
print("slices not approximately equal. first significant difference occurs at index {d} (0x{X})\n", .{ diff_index, diff_index });
|
||||
return error.TestExpectedApproxEqual;
|
||||
},
|
||||
.vector => |info| {
|
||||
var i: usize = 0;
|
||||
while (i < info.len) : (i += 1) {
|
||||
expectApproxEqual(expected[i], actual[i]) catch {
|
||||
print("index {d} incorrect. expected approximately {any}, found {any}\n", .{
|
||||
i, expected[i], actual[i],
|
||||
});
|
||||
return error.TestExpectedApproxEqual;
|
||||
};
|
||||
}
|
||||
},
|
||||
.@"struct" => |structType| {
|
||||
inline for (structType.fields) |field| {
|
||||
try expectApproxEqual(@field(expected, field.name), @field(actual, field.name));
|
||||
}
|
||||
},
|
||||
|
||||
// unwrap unions, optionals, and error unions
|
||||
.@"union" => |union_info| {
|
||||
if (union_info.tag_type == null) {
|
||||
// untagged unions can only be compared bitwise,
|
||||
// so expectEqual is all we need
|
||||
std.testing.expectEqual(expected, actual) catch {
|
||||
return error.TestExpectedApproxEqual;
|
||||
};
|
||||
}
|
||||
|
||||
const Tag = std.meta.Tag(@TypeOf(expected));
|
||||
|
||||
const expectedTag = @as(Tag, expected);
|
||||
const actualTag = @as(Tag, actual);
|
||||
|
||||
std.testing.expectEqual(expectedTag, actualTag) catch {
|
||||
return error.TestExpectedApproxEqual;
|
||||
};
|
||||
|
||||
// we only reach this switch if the tags are equal
|
||||
switch (expected) {
|
||||
inline else => |val, tag| try expectApproxEqual(val, @field(actual, @tagName(tag))),
|
||||
}
|
||||
},
|
||||
.optional, .error_union => {
|
||||
if (expected) |expected_payload| if (actual) |actual_payload| {
|
||||
return expectApproxEqual(expected_payload, actual_payload);
|
||||
};
|
||||
// we only reach this point if there's at least one null or error,
|
||||
// in which case expectEqual is all we need
|
||||
std.testing.expectEqual(expected, actual) catch {
|
||||
return error.TestExpectedApproxEqual;
|
||||
};
|
||||
},
|
||||
|
||||
// fall back to expectEqual for everything else
|
||||
else => std.testing.expectEqual(expected, actual) catch {
|
||||
return error.TestExpectedApproxEqual;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy of std.testing.print (not public)
|
||||
fn print(comptime fmt: []const u8, args: anytype) void {
|
||||
if (@inComptime()) {
|
||||
@compileError(std.fmt.comptimePrint(fmt, args));
|
||||
} else if (std.testing.backend_can_print) {
|
||||
std.debug.print(fmt, args);
|
||||
}
|
||||
}
|
||||
|
||||
// Tests based on the `expectEqual` tests in the Zig stdlib
|
||||
test "expectApproxEqual.union(enum)" {
|
||||
const T = union(enum) {
|
||||
a: i32,
|
||||
b: f32,
|
||||
};
|
||||
|
||||
const b10 = T{ .b = 10.0 };
|
||||
const b10plus = T{ .b = 10.000001 };
|
||||
|
||||
try expectApproxEqual(b10, b10plus);
|
||||
}
|
||||
|
||||
test "expectApproxEqual nested array" {
|
||||
const a = [2][2]f32{
|
||||
[_]f32{ 1.0, 0.0 },
|
||||
[_]f32{ 0.0, 1.0 },
|
||||
};
|
||||
|
||||
const b = [2][2]f32{
|
||||
[_]f32{ 1.000001, 0.0 },
|
||||
[_]f32{ 0.0, 0.999999 },
|
||||
};
|
||||
|
||||
try expectApproxEqual(a, b);
|
||||
}
|
||||
|
||||
test "expectApproxEqual vector" {
|
||||
const a: @Vector(4, f32) = @splat(4.0);
|
||||
const b: @Vector(4, f32) = @splat(4.000001);
|
||||
|
||||
try expectApproxEqual(a, b);
|
||||
}
|
||||
|
||||
test "expectApproxEqual struct" {
|
||||
const a = .{ 1, @as(f32, 1.0) };
|
||||
const b = .{ 1, @as(f32, 0.999999) };
|
||||
|
||||
try expectApproxEqual(a, b);
|
||||
}
|
@@ -19,6 +19,7 @@ const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const config = @import("../config.zig");
|
||||
const comparison = @import("../datastruct/comparison.zig");
|
||||
const font = @import("main.zig");
|
||||
const options = font.options;
|
||||
const DeferredFace = font.DeferredFace;
|
||||
@@ -1199,7 +1200,7 @@ test "metrics" {
|
||||
|
||||
try c.updateMetrics();
|
||||
|
||||
try std.testing.expectEqual(font.Metrics{
|
||||
try comparison.expectApproxEqual(font.Metrics{
|
||||
.cell_width = 8,
|
||||
// The cell height is 17 px because the calculation is
|
||||
//
|
||||
@@ -1229,12 +1230,12 @@ test "metrics" {
|
||||
.icon_height = 12.24,
|
||||
.face_width = 8.0,
|
||||
.face_height = 16.784,
|
||||
.face_y = @round(3.04) - @as(f64, 3.04), // use f64, not comptime float, for exact match with runtime value
|
||||
.face_y = -0.04,
|
||||
}, c.metrics);
|
||||
|
||||
// Resize should change metrics
|
||||
try c.setSize(.{ .points = 24, .xdpi = 96, .ydpi = 96 });
|
||||
try std.testing.expectEqual(font.Metrics{
|
||||
try comparison.expectApproxEqual(font.Metrics{
|
||||
.cell_width = 16,
|
||||
.cell_height = 34,
|
||||
.cell_baseline = 6,
|
||||
@@ -1249,7 +1250,7 @@ test "metrics" {
|
||||
.icon_height = 24.48,
|
||||
.face_width = 16.0,
|
||||
.face_height = 33.568,
|
||||
.face_y = @round(6.08) - @as(f64, 6.08), // use f64, not comptime float, for exact match with runtime value
|
||||
.face_y = -0.08,
|
||||
}, c.metrics);
|
||||
}
|
||||
|
||||
@@ -1493,29 +1494,7 @@ test "face metrics" {
|
||||
.{ narrowMetricsExpected, wideMetricsExpected },
|
||||
.{ narrowMetrics, wideMetrics },
|
||||
) |metricsExpected, metricsActual| {
|
||||
inline for (@typeInfo(font.Metrics.FaceMetrics).@"struct".fields) |field| {
|
||||
const expected = @field(metricsExpected, field.name);
|
||||
const actual = @field(metricsActual, field.name);
|
||||
// Unwrap optional fields
|
||||
const expectedValue, const actualValue = unwrap: switch (@typeInfo(field.type)) {
|
||||
.optional => {
|
||||
if (expected) |expectedValue| if (actual) |actualValue| {
|
||||
break :unwrap .{ expectedValue, actualValue };
|
||||
};
|
||||
// Null values can be compared directly
|
||||
try std.testing.expectEqual(expected, actual);
|
||||
continue;
|
||||
},
|
||||
else => break :unwrap .{ expected, actual },
|
||||
};
|
||||
// All non-null values are floats
|
||||
const eps = std.math.floatEps(@TypeOf(actualValue - expectedValue));
|
||||
try std.testing.expectApproxEqRel(
|
||||
expectedValue,
|
||||
actualValue,
|
||||
std.math.sqrt(eps),
|
||||
);
|
||||
}
|
||||
try comparison.expectApproxEqual(metricsExpected, metricsActual);
|
||||
}
|
||||
|
||||
// Verify estimated metrics. icWidth() should equal the smaller of
|
||||
|
@@ -507,3 +507,196 @@ test "Variation.Id: slnt should be 1936486004" {
|
||||
try testing.expectEqual(@as(u32, 1936486004), @as(u32, @bitCast(id)));
|
||||
try testing.expectEqualStrings("slnt", &(id.str()));
|
||||
}
|
||||
|
||||
test "Constraints" {
|
||||
const comparison = @import("../datastruct/comparison.zig");
|
||||
const getConstraint = @import("nerd_font_attributes.zig").getConstraint;
|
||||
|
||||
// Hardcoded data matches metrics from CoreText at size 12 and DPI 96.
|
||||
|
||||
// Define grid metrics (matches font-family = JetBrains Mono)
|
||||
const metrics: Metrics = .{
|
||||
.cell_width = 10,
|
||||
.cell_height = 22,
|
||||
.cell_baseline = 5,
|
||||
.underline_position = 19,
|
||||
.underline_thickness = 1,
|
||||
.strikethrough_position = 12,
|
||||
.strikethrough_thickness = 1,
|
||||
.overline_position = 0,
|
||||
.overline_thickness = 1,
|
||||
.box_thickness = 1,
|
||||
.cursor_thickness = 1,
|
||||
.cursor_height = 22,
|
||||
.icon_height = 44.48 / 3.0,
|
||||
.face_width = 9.6,
|
||||
.face_height = 21.12,
|
||||
.face_y = 0.2,
|
||||
};
|
||||
|
||||
// ASCII (no constraint).
|
||||
{
|
||||
const constraint: RenderOptions.Constraint = .none;
|
||||
|
||||
// BBox of 'x' from JetBrains Mono.
|
||||
const glyph_x: GlyphSize = .{
|
||||
.width = 6.784,
|
||||
.height = 15.28,
|
||||
.x = 1.408,
|
||||
.y = 4.84,
|
||||
};
|
||||
|
||||
// Any constraint width: do nothing.
|
||||
inline for (.{ 1, 2 }) |constraint_width| {
|
||||
try comparison.expectApproxEqual(
|
||||
glyph_x,
|
||||
constraint.constrain(glyph_x, metrics, constraint_width),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Symbol (same constraint as hardcoded in Renderer.addGlyph).
|
||||
{
|
||||
const constraint: RenderOptions.Constraint = .{ .size = .fit };
|
||||
|
||||
// BBox of '■' (0x25A0 black square) from Iosevka.
|
||||
// NOTE: This glyph is designed to span two cells.
|
||||
const glyph_25A0: GlyphSize = .{
|
||||
.width = 10.272,
|
||||
.height = 10.272,
|
||||
.x = 2.864,
|
||||
.y = 5.304,
|
||||
};
|
||||
|
||||
// Constraint width 1: scale down and shift to fit a single cell.
|
||||
try comparison.expectApproxEqual(
|
||||
GlyphSize{
|
||||
.width = metrics.face_width,
|
||||
.height = metrics.face_width,
|
||||
.x = 0,
|
||||
.y = 5.64,
|
||||
},
|
||||
constraint.constrain(glyph_25A0, metrics, 1),
|
||||
);
|
||||
|
||||
// Constraint width 2: do nothing.
|
||||
try comparison.expectApproxEqual(
|
||||
glyph_25A0,
|
||||
constraint.constrain(glyph_25A0, metrics, 2),
|
||||
);
|
||||
}
|
||||
|
||||
// Emoji (same constraint as hardcoded in SharedGrid.renderGlyph).
|
||||
{
|
||||
const constraint: RenderOptions.Constraint = .{
|
||||
.size = .cover,
|
||||
.align_horizontal = .center,
|
||||
.align_vertical = .center,
|
||||
.pad_left = 0.025,
|
||||
.pad_right = 0.025,
|
||||
};
|
||||
|
||||
// BBox of '🥸' (0x1F978) from Apple Color Emoji.
|
||||
const glyph_1F978: GlyphSize = .{
|
||||
.width = 20,
|
||||
.height = 20,
|
||||
.x = 0.46,
|
||||
.y = 1,
|
||||
};
|
||||
|
||||
// Constraint width 2: scale to cover two cells with padding, center;
|
||||
try comparison.expectApproxEqual(
|
||||
GlyphSize{
|
||||
.width = 18.72,
|
||||
.height = 18.72,
|
||||
.x = 0.44,
|
||||
.y = 1.4,
|
||||
},
|
||||
constraint.constrain(glyph_1F978, metrics, 2),
|
||||
);
|
||||
}
|
||||
|
||||
// Nerd Font default.
|
||||
{
|
||||
const constraint = getConstraint(0xea61).?;
|
||||
|
||||
// Verify that this is the constraint we expect.
|
||||
try std.testing.expectEqual(.fit_cover1, constraint.size);
|
||||
try std.testing.expectEqual(.icon, constraint.height);
|
||||
try std.testing.expectEqual(.center1, constraint.align_horizontal);
|
||||
try std.testing.expectEqual(.center1, constraint.align_vertical);
|
||||
|
||||
// BBox of '' (0xEA61 nf-cod-lightbulb) from Symbols Only.
|
||||
// NOTE: This icon is part of a group, so the
|
||||
// constraint applies to a larger bounding box.
|
||||
const glyph_EA61: GlyphSize = .{
|
||||
.width = 9.015625,
|
||||
.height = 13.015625,
|
||||
.x = 3.015625,
|
||||
.y = 3.76525,
|
||||
};
|
||||
|
||||
// Constraint width 1: scale and shift group to fit a single cell.
|
||||
try comparison.expectApproxEqual(
|
||||
GlyphSize{
|
||||
.width = 7.2125,
|
||||
.height = 10.4125,
|
||||
.x = 0.8125,
|
||||
.y = 5.950695224719102,
|
||||
},
|
||||
constraint.constrain(glyph_EA61, metrics, 1),
|
||||
);
|
||||
|
||||
// Constraint width 2: no scaling; left-align and vertically center group.
|
||||
try comparison.expectApproxEqual(
|
||||
GlyphSize{
|
||||
.width = glyph_EA61.width,
|
||||
.height = glyph_EA61.height,
|
||||
.x = 1.015625,
|
||||
.y = 4.7483690308988775,
|
||||
},
|
||||
constraint.constrain(glyph_EA61, metrics, 2),
|
||||
);
|
||||
}
|
||||
|
||||
// Nerd Font stretch.
|
||||
{
|
||||
const constraint = getConstraint(0xe0c0).?;
|
||||
|
||||
// Verify that this is the constraint we expect.
|
||||
try std.testing.expectEqual(.stretch, constraint.size);
|
||||
try std.testing.expectEqual(.cell, constraint.height);
|
||||
try std.testing.expectEqual(.start, constraint.align_horizontal);
|
||||
try std.testing.expectEqual(.center1, constraint.align_vertical);
|
||||
|
||||
// BBox of ' ' (0xE0C0 nf-ple-flame_thick) from Symbols Only.
|
||||
const glyph_E0C0: GlyphSize = .{
|
||||
.width = 16.796875,
|
||||
.height = 16.46875,
|
||||
.x = -0.796875,
|
||||
.y = 1.7109375,
|
||||
};
|
||||
|
||||
// Constraint width 1: stretch and position to exactly cover one cell.
|
||||
try comparison.expectApproxEqual(
|
||||
GlyphSize{
|
||||
.width = @floatFromInt(metrics.cell_width),
|
||||
.height = @floatFromInt(metrics.cell_height),
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
},
|
||||
constraint.constrain(glyph_E0C0, metrics, 1),
|
||||
);
|
||||
|
||||
// Constraint width 1: stretch and position to exactly cover two cells.
|
||||
try comparison.expectApproxEqual(
|
||||
GlyphSize{
|
||||
.width = @floatFromInt(2 * metrics.cell_width),
|
||||
.height = @floatFromInt(metrics.cell_height),
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
},
|
||||
constraint.constrain(glyph_E0C0, metrics, 2),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -363,7 +363,11 @@ pub const Face = struct {
|
||||
// We center all glyphs within the pixel-rounded and adjusted
|
||||
// cell width if it's larger than the face width, so that they
|
||||
// aren't weirdly off to the left.
|
||||
if (metrics.face_width < cell_width) {
|
||||
//
|
||||
// We don't do this if the glyph has a stretch constraint,
|
||||
// since in that case the position was already calculated with the
|
||||
// new cell width in mind.
|
||||
if ((constraint.size != .stretch) and (metrics.face_width < cell_width)) {
|
||||
// We add half the difference to re-center.
|
||||
x += (cell_width - metrics.face_width) / 2;
|
||||
}
|
||||
@@ -378,18 +382,6 @@ pub const Face = struct {
|
||||
y = @round(y);
|
||||
}
|
||||
|
||||
// We center all glyphs within the pixel-rounded and adjusted
|
||||
// cell width if it's larger than the face width, so that they
|
||||
// aren't weirdly off to the left.
|
||||
//
|
||||
// We don't do this if the glyph has a stretch constraint,
|
||||
// since in that case the position was already calculated with the
|
||||
// new cell width in mind.
|
||||
if ((constraint.size != .stretch) and (metrics.face_width < cell_width)) {
|
||||
// We add half the difference to re-center.
|
||||
x += (cell_width - metrics.face_width) / 2;
|
||||
}
|
||||
|
||||
// We make an assumption that font smoothing ("thicken")
|
||||
// adds no more than 1 extra pixel to any edge. We don't
|
||||
// add extra size if it's a sbix color font though, since
|
||||
|
@@ -1177,43 +1177,6 @@ test "color emoji" {
|
||||
const glyph_id = ft_font.glyphIndex('🥸').?;
|
||||
try testing.expect(ft_font.isColorGlyph(glyph_id));
|
||||
}
|
||||
|
||||
// resize
|
||||
// TODO: Comprehensive tests for constraints,
|
||||
// this is just an adapted legacy test.
|
||||
{
|
||||
const glyph = try ft_font.renderGlyph(
|
||||
alloc,
|
||||
&atlas,
|
||||
ft_font.glyphIndex('🥸').?,
|
||||
.{
|
||||
.grid_metrics = .{
|
||||
.cell_width = 13,
|
||||
.cell_height = 24,
|
||||
.cell_baseline = 0,
|
||||
.underline_position = 0,
|
||||
.underline_thickness = 0,
|
||||
.strikethrough_position = 0,
|
||||
.strikethrough_thickness = 0,
|
||||
.overline_position = 0,
|
||||
.overline_thickness = 0,
|
||||
.box_thickness = 0,
|
||||
.cursor_height = 0,
|
||||
.icon_height = 0,
|
||||
.face_width = 13,
|
||||
.face_height = 24,
|
||||
.face_y = 0,
|
||||
},
|
||||
.constraint_width = 2,
|
||||
.constraint = .{
|
||||
.size = .fit,
|
||||
.align_horizontal = .center,
|
||||
.align_vertical = .center,
|
||||
},
|
||||
},
|
||||
);
|
||||
try testing.expectEqual(@as(u32, 24), glyph.height);
|
||||
}
|
||||
}
|
||||
|
||||
test "mono to bgra" {
|
||||
|
@@ -95,9 +95,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
||||
/// Allocator that can be used
|
||||
alloc: std.mem.Allocator,
|
||||
|
||||
/// MemoryPool for PageList pages which we use when cloning the screen.
|
||||
page_pool: terminal.PageList.MemoryPool,
|
||||
|
||||
/// This mutex must be held whenever any state used in `drawFrame` is
|
||||
/// being modified, and also when it's being accessed in `drawFrame`.
|
||||
draw_mutex: std.Thread.Mutex = .{},
|
||||
@@ -679,19 +676,8 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
||||
};
|
||||
errdefer if (display_link) |v| v.release();
|
||||
|
||||
// We preheat the page pool with 4 pages- this is an arbitrary
|
||||
// choice based on what seems reasonable for the number of pages
|
||||
// used by the viewport area.
|
||||
var page_pool: terminal.PageList.MemoryPool = try .init(
|
||||
alloc,
|
||||
std.heap.page_allocator,
|
||||
4,
|
||||
);
|
||||
errdefer page_pool.deinit();
|
||||
|
||||
var result: Self = .{
|
||||
.alloc = alloc,
|
||||
.page_pool = page_pool,
|
||||
.config = options.config,
|
||||
.surface_mailbox = options.surface_mailbox,
|
||||
.grid_metrics = font_critical.metrics,
|
||||
@@ -774,8 +760,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.page_pool.deinit();
|
||||
|
||||
self.swap_chain.deinit();
|
||||
|
||||
if (DisplayLink != void) {
|
||||
@@ -1108,13 +1092,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
||||
full_rebuild: bool,
|
||||
};
|
||||
|
||||
// Empty our page pool, but retain capacity.
|
||||
self.page_pool.reset(.retain_capacity);
|
||||
|
||||
var arena: std.heap.ArenaAllocator = .init(self.alloc);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
// Update all our data as tightly as possible within the mutex.
|
||||
var critical: Critical = critical: {
|
||||
// const start = try std.time.Instant.now();
|
||||
@@ -1171,12 +1148,12 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
||||
// We used to share terminal state, but we've since learned through
|
||||
// analysis that it is faster to copy the terminal state than to
|
||||
// hold the lock while rebuilding GPU cells.
|
||||
const screen_copy = try state.terminal.screen.clonePool(
|
||||
alloc,
|
||||
&self.page_pool,
|
||||
var screen_copy = try state.terminal.screen.clone(
|
||||
self.alloc,
|
||||
.{ .viewport = .{} },
|
||||
null,
|
||||
);
|
||||
errdefer screen_copy.deinit();
|
||||
|
||||
// Whether to draw our cursor or not.
|
||||
const cursor_style = if (state.terminal.flags.password_input)
|
||||
@@ -1192,8 +1169,9 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
||||
const preedit: ?renderer.State.Preedit = preedit: {
|
||||
if (cursor_style == null) break :preedit null;
|
||||
const p = state.preedit orelse break :preedit null;
|
||||
break :preedit try p.clone(alloc);
|
||||
break :preedit try p.clone(self.alloc);
|
||||
};
|
||||
errdefer if (preedit) |p| p.deinit(self.alloc);
|
||||
|
||||
// If we have Kitty graphics data, we enter a SLOW SLOW SLOW path.
|
||||
// We only do this if the Kitty image state is dirty meaning only if
|
||||
@@ -1263,6 +1241,10 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
||||
.full_rebuild = full_rebuild,
|
||||
};
|
||||
};
|
||||
defer {
|
||||
critical.screen.deinit();
|
||||
if (critical.preedit) |p| p.deinit(self.alloc);
|
||||
}
|
||||
|
||||
// Build our GPU cells
|
||||
try self.rebuildCells(
|
||||
|
Reference in New Issue
Block a user