diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-16 23:28:05 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-16 23:29:28 +0300 |
| commit | 4a3302d19a2baa3a33e910e0088c24e780905b90 (patch) | |
| tree | dcaf76fae1c82102d933c9225b17ca7ac5d3a847 /vendor/libghostty-vt/src/synthetic | |
| parent | 550e9f8cc085f428429f5cf2da0bb2dba897e8b8 (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.zig | 88 | ||||
| -rw-r--r-- | vendor/libghostty-vt/src/synthetic/cli.zig | 2 | ||||
| -rw-r--r-- | vendor/libghostty-vt/src/synthetic/cli/Kitty.zig | 70 | ||||
| -rw-r--r-- | vendor/libghostty-vt/src/synthetic/main.zig | 1 |
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 { |
