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/Kitty.zig | |
| parent | 550e9f8cc085f428429f5cf2da0bb2dba897e8b8 (diff) | |
chore: update vendored libghostty-vt
refs #276
Diffstat (limited to 'vendor/libghostty-vt/src/synthetic/Kitty.zig')
| -rw-r--r-- | vendor/libghostty-vt/src/synthetic/Kitty.zig | 88 |
1 files changed, 88 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\\")); +} |
