fill in some gaps and add an example
This commit is contained in:
parent
0e9d395a55
commit
bd361e40ee
4 changed files with 314 additions and 17 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
/result*
|
/result*
|
||||||
/zig-cache
|
/zig-cache
|
||||||
/zig-out
|
/zig-out
|
||||||
|
/perf.data*
|
||||||
|
|
17
build.zig
17
build.zig
|
@ -4,17 +4,17 @@ pub fn build(b: *std.Build) void {
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
_ = b.addModule(
|
const anzi = b.addModule(
|
||||||
"anzi",
|
"anzi",
|
||||||
.{
|
.{
|
||||||
.root_source_file = .{
|
.root_source_file = .{
|
||||||
.path = "src/main.zig",
|
.path = "src/root.zig",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const unit_tests = b.addTest(.{
|
const unit_tests = b.addTest(.{
|
||||||
.root_source_file = .{ .path = "src/main.zig" },
|
.root_source_file = .{ .path = "src/root.zig" },
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
@ -22,4 +22,15 @@ pub fn build(b: *std.Build) void {
|
||||||
const run_unit_tests = b.addRunArtifact(unit_tests);
|
const run_unit_tests = b.addRunArtifact(unit_tests);
|
||||||
const test_step = b.step("test", "Run unit tests");
|
const test_step = b.step("test", "Run unit tests");
|
||||||
test_step.dependOn(&run_unit_tests.step);
|
test_step.dependOn(&run_unit_tests.step);
|
||||||
|
|
||||||
|
const random_sgr = b.addExecutable(.{
|
||||||
|
.name = "random_sgr",
|
||||||
|
.root_source_file = .{ .path = "examples/random_sgr.zig" },
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
random_sgr.root_module.addImport("anzi", anzi);
|
||||||
|
|
||||||
|
b.installArtifact(random_sgr);
|
||||||
}
|
}
|
||||||
|
|
192
examples/random_sgr.zig
Normal file
192
examples/random_sgr.zig
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const anzi = @import("anzi");
|
||||||
|
|
||||||
|
var done: bool = false;
|
||||||
|
|
||||||
|
fn signal(_: c_int) callconv(.C) void {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var column: u8 = 0;
|
||||||
|
var line: u8 = 0;
|
||||||
|
|
||||||
|
fn nextColumn() u8 {
|
||||||
|
const c = column;
|
||||||
|
column = (column + 7) % 80;
|
||||||
|
return c + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nextLine() u8 {
|
||||||
|
const l = line;
|
||||||
|
line = (line + 27) % 24;
|
||||||
|
return l + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var red: u8 = 0;
|
||||||
|
var green: u8 = 0;
|
||||||
|
var blue: u8 = 0;
|
||||||
|
|
||||||
|
inline fn nextRed() u8 {
|
||||||
|
const r = red;
|
||||||
|
red +%= 31;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn nextGreen() u8 {
|
||||||
|
const g = green;
|
||||||
|
green +%= 43;
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn nextBlue() u8 {
|
||||||
|
const b = blue;
|
||||||
|
blue +%= 67;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
const alloc = gpa.allocator();
|
||||||
|
|
||||||
|
const action = std.os.Sigaction{
|
||||||
|
.handler = .{
|
||||||
|
.handler = signal,
|
||||||
|
},
|
||||||
|
.flags = 0,
|
||||||
|
.mask = std.os.empty_sigset,
|
||||||
|
};
|
||||||
|
|
||||||
|
try std.os.sigaction(std.os.SIG.INT, &action, null);
|
||||||
|
|
||||||
|
var generator = std.rand.DefaultPrng.init(@intCast(std.time.timestamp()));
|
||||||
|
const random = generator.random();
|
||||||
|
|
||||||
|
const a = anzi.ANSI(.{});
|
||||||
|
|
||||||
|
std.debug.print("preparing buffer...\n", .{});
|
||||||
|
|
||||||
|
const buffer = try alloc.alloc(u8, 1024 * 1024 * 1024);
|
||||||
|
defer alloc.free(buffer);
|
||||||
|
|
||||||
|
var fbs = std.io.fixedBufferStream(buffer);
|
||||||
|
|
||||||
|
{
|
||||||
|
const writer = fbs.writer();
|
||||||
|
|
||||||
|
try writer.print("{}{}", .{
|
||||||
|
a.ScreenMode{ .mode = .alternate_screen_save_cursor_clear_enter, .action = .set },
|
||||||
|
a.ScreenMode{ .mode = .cursor_visible, .action = .reset },
|
||||||
|
});
|
||||||
|
errdefer {
|
||||||
|
writer.print("{}{}", .{
|
||||||
|
a.ScreenMode{ .mode = .cursor_visible, .action = .set },
|
||||||
|
a.ScreenMode{ .mode = .alternate_screen_save_cursor_clear_enter, .action = .reset },
|
||||||
|
}) catch unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
try a.Erase.entireScreen.write(writer);
|
||||||
|
|
||||||
|
var index: usize = 0;
|
||||||
|
while (!done and index < 10000) : (index += 1) {
|
||||||
|
// try (a.ScreenMode{ .mode = .synchronized_output, .action = .set }).write(writer);
|
||||||
|
for (1..80) |_| {
|
||||||
|
for (1..24) |_| {
|
||||||
|
try (a.CursorMove{
|
||||||
|
.to = .{
|
||||||
|
.column = random.intRangeAtMost(u8, 1, 80),
|
||||||
|
.line = random.intRangeAtMost(u8, 1, 24),
|
||||||
|
},
|
||||||
|
}).write(writer);
|
||||||
|
try (a.GraphicsRenditions{
|
||||||
|
.graphics = &[_]a.GraphicsRenditions.GraphicsRendition{
|
||||||
|
.{
|
||||||
|
.color = .{
|
||||||
|
.layer = .foreground,
|
||||||
|
.color = .{
|
||||||
|
.colorRGB = .{
|
||||||
|
.red = random.int(u8),
|
||||||
|
.green = random.int(u8),
|
||||||
|
.blue = random.int(u8),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.color = .{
|
||||||
|
.layer = .background,
|
||||||
|
.color = .{
|
||||||
|
.colorRGB = .{
|
||||||
|
.red = random.int(u8),
|
||||||
|
.green = random.int(u8),
|
||||||
|
.blue = random.int(u8),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).write(writer);
|
||||||
|
try writer.writeByte('J');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// try (a.ScreenMode{ .mode = .synchronized_output, .action = .reset }).write(writer);
|
||||||
|
}
|
||||||
|
try a.reset.write(writer);
|
||||||
|
try writer.print("{}{}", .{
|
||||||
|
a.ScreenMode{ .mode = .cursor_visible, .action = .set },
|
||||||
|
a.ScreenMode{ .mode = .alternate_screen_save_cursor_clear_enter, .action = .reset },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var elapsed: u64 = undefined;
|
||||||
|
const written = fbs.getWritten();
|
||||||
|
|
||||||
|
{
|
||||||
|
const stdout = std.io.getStdOut();
|
||||||
|
const writer = stdout.writer();
|
||||||
|
|
||||||
|
var iter = std.mem.window(u8, written, 4 * 1024 * 1024, 4 * 1024 * 1024);
|
||||||
|
|
||||||
|
std.debug.print("starting to send data...\n", .{});
|
||||||
|
|
||||||
|
var timer = try std.time.Timer.start();
|
||||||
|
|
||||||
|
while (iter.next()) |buf| {
|
||||||
|
try writer.writeAll(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
elapsed = timer.read();
|
||||||
|
|
||||||
|
std.debug.print("finished sending data...\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
const units = [_]struct {
|
||||||
|
name: []const u8,
|
||||||
|
factor: u64,
|
||||||
|
}{
|
||||||
|
.{ .name = "y", .factor = 365 * std.time.ns_per_day },
|
||||||
|
.{ .name = "w", .factor = std.time.ns_per_week },
|
||||||
|
.{ .name = "d", .factor = std.time.ns_per_day },
|
||||||
|
.{ .name = "h", .factor = std.time.ns_per_hour },
|
||||||
|
.{ .name = "m", .factor = std.time.ns_per_min },
|
||||||
|
.{ .name = "s", .factor = std.time.ns_per_s },
|
||||||
|
.{ .name = "ms", .factor = std.time.ns_per_ms },
|
||||||
|
.{ .name = "µs", .factor = std.time.ns_per_us },
|
||||||
|
.{ .name = "ns", .factor = 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
for (units) |unit| {
|
||||||
|
if (elapsed > unit.factor) {
|
||||||
|
if (i > 0) std.debug.print(" ", .{});
|
||||||
|
const r = elapsed % unit.factor;
|
||||||
|
const x = (elapsed - r) / unit.factor;
|
||||||
|
std.debug.print("{d}{s}", .{ x, unit.name });
|
||||||
|
elapsed = r;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std.debug.print("\n", .{});
|
||||||
|
std.debug.print("{d}\n", .{written.len});
|
||||||
|
}
|
|
@ -174,7 +174,7 @@ pub fn ANSI(comptime options: Options) type {
|
||||||
/// moves cursor to column #
|
/// moves cursor to column #
|
||||||
toColumn: u8,
|
toColumn: u8,
|
||||||
|
|
||||||
pub fn format(self: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
pub fn write(self: @This(), writer: anytype) !void {
|
||||||
try writer.writeAll(Self.RL_PROMPT_START_IGNORE ++ Self.CSI);
|
try writer.writeAll(Self.RL_PROMPT_START_IGNORE ++ Self.CSI);
|
||||||
switch (self) {
|
switch (self) {
|
||||||
.home => try writer.writeAll("H"),
|
.home => try writer.writeAll("H"),
|
||||||
|
@ -194,12 +194,17 @@ pub fn ANSI(comptime options: Options) type {
|
||||||
.downBOL => "E",
|
.downBOL => "E",
|
||||||
.upBOL => "F",
|
.upBOL => "F",
|
||||||
.toColumn => "G",
|
.toColumn => "G",
|
||||||
|
else => unreachable,
|
||||||
};
|
};
|
||||||
try writer.writeAll(v);
|
try writer.writeAll(v);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
try writer.writeAll(Self.RL_PROMPT_END_IGNORE);
|
try writer.writeAll(Self.RL_PROMPT_END_IGNORE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn format(self: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
||||||
|
try self.write(writer);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// request cursor position (reports as ESC[#;#R)
|
/// request cursor position (reports as ESC[#;#R)
|
||||||
|
@ -248,8 +253,8 @@ pub fn ANSI(comptime options: Options) type {
|
||||||
fromStartOfLineToCursor,
|
fromStartOfLineToCursor,
|
||||||
entireLine,
|
entireLine,
|
||||||
|
|
||||||
pub fn format(self: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
pub fn write(self: @This(), writer: anytype) !void {
|
||||||
try writer.writeAll(Self.RL_PROMPT_START_IGNORE ++
|
try writer.writeAll(Self.RL_PROMPT_START_IGNORE ++ Self.CSI ++
|
||||||
switch (self) {
|
switch (self) {
|
||||||
.fromCursorToEndOfScreen => "0J",
|
.fromCursorToEndOfScreen => "0J",
|
||||||
.fromStartOfScreenToCursor => "1J",
|
.fromStartOfScreenToCursor => "1J",
|
||||||
|
@ -260,21 +265,26 @@ pub fn ANSI(comptime options: Options) type {
|
||||||
.entireLine => "2K",
|
.entireLine => "2K",
|
||||||
} ++ Self.RL_PROMPT_END_IGNORE);
|
} ++ Self.RL_PROMPT_END_IGNORE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn format(self: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
||||||
|
try self.write(writer);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const ColorType = union(enum) {
|
pub const ColorType = union(enum) {
|
||||||
color8: Color8,
|
color8: Color8,
|
||||||
color256: Color256,
|
color256: Color256,
|
||||||
colorRGB: ColorRGB,
|
colorRGB: ColorRGB,
|
||||||
};
|
};
|
||||||
|
|
||||||
const GraphicsRenditions = struct {
|
pub const GraphicsRenditions = struct {
|
||||||
const GraphicsRendition = union(enum) {
|
pub const GraphicsRendition = union(enum) {
|
||||||
reset: void,
|
reset: void,
|
||||||
style: struct {
|
style: struct {
|
||||||
mode: enum { enable, disable },
|
mode: enum { enable, disable },
|
||||||
style: Style,
|
style: Style,
|
||||||
pub fn format(self: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
|
||||||
|
pub fn write(self: @This(), writer: anytype) !void {
|
||||||
switch (self.mode) {
|
switch (self.mode) {
|
||||||
.enable => try std.fmt.formatInt(@intFromEnum(self.style), 10, .lower, .{}, writer),
|
.enable => try std.fmt.formatInt(@intFromEnum(self.style), 10, .lower, .{}, writer),
|
||||||
.disable => switch (self.style) {
|
.disable => switch (self.style) {
|
||||||
|
@ -283,11 +293,16 @@ pub fn ANSI(comptime options: Options) type {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn format(self: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
||||||
|
try self.write(writer);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
color: struct {
|
color: struct {
|
||||||
layer: Layer,
|
layer: Layer,
|
||||||
color: ColorType,
|
color: ColorType,
|
||||||
pub fn format(self: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
|
||||||
|
pub fn write(self: @This(), writer: anytype) !void {
|
||||||
switch (self.color) {
|
switch (self.color) {
|
||||||
.color8 => |v| {
|
.color8 => |v| {
|
||||||
var offset: u8 = 30;
|
var offset: u8 = 30;
|
||||||
|
@ -315,29 +330,37 @@ pub fn ANSI(comptime options: Options) type {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn format(self: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
||||||
|
try self.write(writer);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
graphics: []const GraphicsRendition,
|
graphics: []const GraphicsRendition,
|
||||||
|
|
||||||
pub fn format(self: @This(), comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void {
|
pub fn write(self: @This(), writer: anytype) !void {
|
||||||
try writer.writeAll(Self.RL_PROMPT_START_IGNORE ++ Self.CSI);
|
try writer.writeAll(Self.RL_PROMPT_START_IGNORE ++ Self.CSI);
|
||||||
|
|
||||||
for (self.graphics, 0..) |graphic, index| {
|
for (self.graphics, 0..) |graphic, index| {
|
||||||
if (index > 0) try writer.writeAll(";");
|
if (index > 0) try writer.writeAll(";");
|
||||||
switch (graphic) {
|
switch (graphic) {
|
||||||
.reset => try writer.writeAll("0"),
|
.reset => try writer.writeAll("0"),
|
||||||
.style => |s| try s.format(fmt, opt, writer),
|
.style => |s| try s.write(writer),
|
||||||
.color => |c| try c.format(fmt, opt, writer),
|
.color => |c| try c.write(writer),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try writer.writeAll("m" ++ Self.RL_PROMPT_END_IGNORE);
|
try writer.writeAll("m" ++ Self.RL_PROMPT_END_IGNORE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn format(self: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
||||||
|
try self.write(writer);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Reset all styles and colors to the default
|
/// Reset all styles and colors to the default
|
||||||
const reset = GraphicsRenditions{
|
pub const reset = GraphicsRenditions{
|
||||||
.graphics = &.{
|
.graphics = &.{
|
||||||
.{
|
.{
|
||||||
.reset = {},
|
.reset = {},
|
||||||
|
@ -536,13 +559,83 @@ pub fn ANSI(comptime options: Options) type {
|
||||||
HOST_WRITABLE = 2,
|
HOST_WRITABLE = 2,
|
||||||
|
|
||||||
pub fn format(self: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
pub fn format(self: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
||||||
try writer.writeAll(Self.RL_PROMPT_END_IGNORE_START_IGNORE ++ Self.CSI);
|
try writer.writeAll(Self.RL_PROMPT_START_IGNORE ++ Self.CSI);
|
||||||
try std.fmt.formatInt(@intFromEnum(self), 10, .lower, .{}, writer);
|
try std.fmt.formatInt(@intFromEnum(self), 10, .lower, .{}, writer);
|
||||||
try writer.writeAll("$~" ++ Self.RL_PROMPT_END_IGNORE);
|
try writer.writeAll("$~" ++ Self.RL_PROMPT_END_IGNORE);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ScreenMode = struct {};
|
pub const ScreenMode = struct {
|
||||||
|
mode: enum(u16) {
|
||||||
|
cursor_visible = 25,
|
||||||
|
alternate_screen = 1047,
|
||||||
|
alternate_screen_save_cursor_clear_enter = 1049,
|
||||||
|
bracketed_paste = 2004,
|
||||||
|
synchronized_output = 2026,
|
||||||
|
grapheme_cluster = 2027,
|
||||||
|
report_color_scheme = 2031,
|
||||||
|
},
|
||||||
|
action: enum {
|
||||||
|
set,
|
||||||
|
reset,
|
||||||
|
},
|
||||||
|
|
||||||
|
pub fn write(self: @This(), writer: anytype) !void {
|
||||||
|
try writer.writeAll(Self.RL_PROMPT_START_IGNORE ++ Self.CSI ++ "?");
|
||||||
|
try std.fmt.formatInt(@intFromEnum(self.mode), 10, .lower, .{}, writer);
|
||||||
|
try writer.writeAll(switch (self.action) {
|
||||||
|
.set => "h",
|
||||||
|
.reset => "l",
|
||||||
|
});
|
||||||
|
try writer.writeAll(Self.RL_PROMPT_END_IGNORE);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format(self: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
||||||
|
try self.write(writer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const KittyGraphicsFormat = enum(u8) {
|
||||||
|
rgb = 24,
|
||||||
|
rgba = 32,
|
||||||
|
png = 100,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const KittyGraphicsMedium =
|
||||||
|
enum {
|
||||||
|
direct,
|
||||||
|
simple_file,
|
||||||
|
temporary_file,
|
||||||
|
shared_memory_object,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const KittyGraphicsData = struct {
|
||||||
|
format: KittyGraphicsFormat = .rgba,
|
||||||
|
medium: KittyGraphicsMedium = .direct,
|
||||||
|
width: ?u32,
|
||||||
|
height: ?u32,
|
||||||
|
compression: enum {
|
||||||
|
none,
|
||||||
|
zstd,
|
||||||
|
},
|
||||||
|
size: ?u32,
|
||||||
|
offset: ?u32,
|
||||||
|
data: []const u8,
|
||||||
|
|
||||||
|
// pub fn format(self: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
||||||
|
// var first = false;
|
||||||
|
// const encoded_size = std.base64.standard.Encoder.calcSize(self.data);
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const TransmitKittyGraphicsData = struct {
|
||||||
|
image_id: ?u32,
|
||||||
|
image_number: ?u32,
|
||||||
|
placement_id: ?u32,
|
||||||
|
action: union {
|
||||||
|
transmit: KittyGraphicsData,
|
||||||
|
},
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue