aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/synthetic
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-07-16 23:28:05 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-07-16 23:29:28 +0300
commit4a3302d19a2baa3a33e910e0088c24e780905b90 (patch)
treedcaf76fae1c82102d933c9225b17ca7ac5d3a847 /vendor/libghostty-vt/src/synthetic
parent550e9f8cc085f428429f5cf2da0bb2dba897e8b8 (diff)
chore: update vendored libghostty-vt
refs #276
Diffstat (limited to 'vendor/libghostty-vt/src/synthetic')
-rw-r--r--vendor/libghostty-vt/src/synthetic/Kitty.zig88
-rw-r--r--vendor/libghostty-vt/src/synthetic/cli.zig2
-rw-r--r--vendor/libghostty-vt/src/synthetic/cli/Kitty.zig70
-rw-r--r--vendor/libghostty-vt/src/synthetic/main.zig1
4 files changed, 161 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/synthetic/Kitty.zig b/vendor/libghostty-vt/src/synthetic/Kitty.zig
new file mode 100644
index 00000000..c2c649d7
--- /dev/null
+++ b/vendor/libghostty-vt/src/synthetic/Kitty.zig
@@ -0,0 +1,88 @@
+//! Generates random Kitty graphics protocol APC sequences.
+//!
+//! The payload is random base64, NOT a valid image: decoding it as
+//! PNG will fail. This corpus is meant for benchmarking the stream,
+//! APC, and Kitty command parsing paths, which never decode the
+//! image data. It does not exercise successful image loading.
+const Kitty = @This();
+
+const std = @import("std");
+const assert = std.debug.assert;
+const Generator = @import("Generator.zig");
+const Bytes = @import("Bytes.zig");
+
+/// Random number generator.
+rand: std.Random,
+
+/// The base64 payload length for each generated command. This is
+/// rounded down to a multiple of four so the payload is always valid
+/// base64 without requiring padding. Kitty clients typically chunk
+/// payloads at 4096 bytes.
+data_len: usize = 4096,
+
+fn checkBase64Alphabet(c: u8) bool {
+ return switch (c) {
+ 'A'...'Z', 'a'...'z', '0'...'9', '+', '/' => true,
+ else => false,
+ };
+}
+
+/// The base64 alphabet, without the padding character.
+pub const base64_alphabet = Bytes.generateAlphabet(checkBase64Alphabet);
+
+pub fn generator(self: *Kitty) Generator {
+ return .init(self, next);
+}
+
+const prefix = "\x1b_G";
+const st = "\x1b\\";
+
+/// Get the next Kitty graphics APC sequence: a well-formed transmit
+/// command with a random base64 payload (not a valid image; see the
+/// module comment), including the APC prefix and the ST terminator.
+pub fn next(
+ self: *Kitty,
+ writer: *std.Io.Writer,
+ max_len: usize,
+) Generator.Error!void {
+ var control_buf: [64]u8 = undefined;
+ const control = std.fmt.bufPrint(
+ &control_buf,
+ "a=t,f=100,i={d};",
+ .{self.rand.intRangeAtMost(u32, 1, 1_000_000)},
+ ) catch unreachable;
+
+ const overhead = prefix.len + control.len + st.len;
+ assert(max_len > overhead);
+
+ const avail = @min(self.data_len, max_len - overhead);
+ const payload_len = avail - (avail % 4);
+
+ try writer.writeAll(prefix);
+ try writer.writeAll(control);
+ if (payload_len > 0) {
+ const bytes: Bytes = .{
+ .rand = self.rand,
+ .alphabet = base64_alphabet,
+ .min_len = payload_len,
+ .max_len = payload_len,
+ };
+ _ = try bytes.write(writer);
+ }
+ try writer.writeAll(st);
+}
+
+test "kitty" {
+ const testing = std.testing;
+ var prng = std.Random.DefaultPrng.init(0);
+
+ var buf: [8192]u8 = undefined;
+ var writer: std.Io.Writer = .fixed(&buf);
+ var v: Kitty = .{ .rand = prng.random() };
+ const gen = v.generator();
+ try gen.next(&writer, buf.len);
+
+ const data = writer.buffered();
+ try testing.expect(std.mem.startsWith(u8, data, "\x1b_Ga=t,f=100,i="));
+ try testing.expect(std.mem.endsWith(u8, data, "\x1b\\"));
+}
diff --git a/vendor/libghostty-vt/src/synthetic/cli.zig b/vendor/libghostty-vt/src/synthetic/cli.zig
index d9b6a659..d6ec028f 100644
--- a/vendor/libghostty-vt/src/synthetic/cli.zig
+++ b/vendor/libghostty-vt/src/synthetic/cli.zig
@@ -7,6 +7,7 @@ const cli = @import("../cli.zig");
/// predictably named files under `cli/`.
pub const Action = enum {
ascii,
+ kitty,
osc,
utf8,
@@ -21,6 +22,7 @@ pub const Action = enum {
pub fn Struct(comptime action: Action) type {
return switch (action) {
.ascii => @import("cli/Ascii.zig"),
+ .kitty => @import("cli/Kitty.zig"),
.osc => @import("cli/Osc.zig"),
.utf8 => @import("cli/Utf8.zig"),
};
diff --git a/vendor/libghostty-vt/src/synthetic/cli/Kitty.zig b/vendor/libghostty-vt/src/synthetic/cli/Kitty.zig
new file mode 100644
index 00000000..99a92798
--- /dev/null
+++ b/vendor/libghostty-vt/src/synthetic/cli/Kitty.zig
@@ -0,0 +1,70 @@
+const Kitty = @This();
+
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const synthetic = @import("../main.zig");
+
+const log = std.log.scoped(.@"kitty-gen");
+
+pub const Options = struct {
+ /// The base64 payload length of each command in bytes, rounded
+ /// down to a multiple of four. Kitty clients typically chunk
+ /// payloads at 4096 bytes.
+ @"data-len": usize = 4096,
+};
+
+opts: Options,
+
+/// The buffer a single generated sequence is written into. Sized to
+/// the payload length plus room for the control data and terminator.
+buf: []u8,
+
+/// Create a new Kitty graphics sequence generator for the given arguments.
+pub fn create(
+ alloc: Allocator,
+ opts: Options,
+) !*Kitty {
+ const ptr = try alloc.create(Kitty);
+ errdefer alloc.destroy(ptr);
+
+ const buf = try alloc.alloc(u8, opts.@"data-len" + 128);
+ errdefer alloc.free(buf);
+
+ ptr.* = .{ .opts = opts, .buf = buf };
+ return ptr;
+}
+
+pub fn destroy(self: *Kitty, alloc: Allocator) void {
+ alloc.free(self.buf);
+ alloc.destroy(self);
+}
+
+pub fn run(self: *Kitty, writer: *std.Io.Writer, rand: std.Random) !void {
+ var gen: synthetic.Kitty = .{
+ .rand = rand,
+ .data_len = self.opts.@"data-len",
+ };
+
+ while (true) {
+ var fixed: std.Io.Writer = .fixed(self.buf);
+ try gen.next(&fixed, self.buf.len);
+ writer.writeAll(fixed.buffered()) catch |err| switch (err) {
+ error.WriteFailed => return,
+ };
+ }
+}
+
+test Kitty {
+ const testing = std.testing;
+ const alloc = testing.allocator;
+
+ const impl: *Kitty = try .create(alloc, .{});
+ defer impl.destroy(alloc);
+
+ var prng = std.Random.DefaultPrng.init(1);
+ const rand = prng.random();
+
+ var buf: [8192]u8 = undefined;
+ var writer: std.Io.Writer = .fixed(&buf);
+ try impl.run(&writer, rand);
+}
diff --git a/vendor/libghostty-vt/src/synthetic/main.zig b/vendor/libghostty-vt/src/synthetic/main.zig
index 85f9f7d3..1c26090d 100644
--- a/vendor/libghostty-vt/src/synthetic/main.zig
+++ b/vendor/libghostty-vt/src/synthetic/main.zig
@@ -18,6 +18,7 @@ pub const cli = @import("cli.zig");
pub const Generator = @import("Generator.zig");
pub const Bytes = @import("Bytes.zig");
pub const Utf8 = @import("Utf8.zig");
+pub const Kitty = @import("Kitty.zig");
pub const Osc = @import("Osc.zig");
test {