render: complete the render loop
This loop matches the go version of Vaxis to a tee. :chefs-kiss: Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
This commit is contained in:
parent
13e9dadbb1
commit
de91cbe2d0
4 changed files with 219 additions and 3 deletions
|
@ -22,12 +22,18 @@ pub fn main() !void {
|
|||
|
||||
try vx.enterAltScreen();
|
||||
|
||||
var color_idx: u8 = 0;
|
||||
const msg = "Hello, world!";
|
||||
outer: while (true) {
|
||||
const event = vx.nextEvent();
|
||||
log.debug("event: {}\r\n", .{event});
|
||||
switch (event) {
|
||||
.key_press => |key| {
|
||||
if (color_idx == 255) {
|
||||
color_idx = 0;
|
||||
} else {
|
||||
color_idx += 1;
|
||||
}
|
||||
if (key.codepoint == 'c' and key.mods.ctrl) {
|
||||
break :outer;
|
||||
}
|
||||
|
@ -42,7 +48,10 @@ pub fn main() !void {
|
|||
win.clear();
|
||||
const child = win.initChild(win.width / 2 - msg.len / 2, win.height / 2, .expand, .expand);
|
||||
for (msg, 0..) |_, i| {
|
||||
const cell: Cell = .{ .char = .{ .grapheme = msg[i .. i + 1] } };
|
||||
const cell: Cell = .{
|
||||
.char = .{ .grapheme = msg[i .. i + 1] },
|
||||
.style = .{ .fg = .{ .index = color_idx } },
|
||||
};
|
||||
child.writeCell(i, 0, cell);
|
||||
}
|
||||
try vx.render();
|
||||
|
|
|
@ -24,6 +24,14 @@ pub const Style = struct {
|
|||
ul_style: Underline = .off,
|
||||
url: ?[]const u8 = null,
|
||||
url_params: ?[]const u8 = null,
|
||||
|
||||
bold: bool = false,
|
||||
dim: bool = false,
|
||||
italic: bool = false,
|
||||
blink: bool = false,
|
||||
reverse: bool = false,
|
||||
invisible: bool = false,
|
||||
strikethrough: bool = false,
|
||||
};
|
||||
|
||||
pub const Color = union(enum) {
|
||||
|
|
|
@ -11,7 +11,52 @@ pub const csi_u_pop = "\x1b[<u";
|
|||
// Cursor
|
||||
pub const home = "\x1b[H";
|
||||
pub const cup = "\x1b[{d};{d}H";
|
||||
pub const hide_cursor = "\x1b[?25l";
|
||||
pub const show_cursor = "\x1b[?25h";
|
||||
|
||||
// alt screen
|
||||
pub const smcup = "\x1b[?1049h";
|
||||
pub const rmcup = "\x1b[?1049l";
|
||||
|
||||
// colors
|
||||
pub const fg_base = "\x1b[3{d}m";
|
||||
pub const fg_bright = "\x1b[9{d}m";
|
||||
pub const bg_base = "\x1b[4{d}m";
|
||||
pub const bg_bright = "\x1b[10{d}m";
|
||||
|
||||
pub const fg_reset = "\x1b[39m";
|
||||
pub const bg_reset = "\x1b[49m";
|
||||
pub const ul_reset = "\x1b[59m";
|
||||
pub const fg_indexed = "\x1b[38;5;{d}m";
|
||||
pub const bg_indexed = "\x1b[48:5:{d}m";
|
||||
pub const ul_indexed = "\x1b[58:5:{d}m";
|
||||
pub const fg_rgb = "\x1b[38:2:{d}:{d}:{d}m";
|
||||
pub const bg_rgb = "\x1b[48:2:{d}:{d}:{d}m";
|
||||
pub const ul_rgb = "\x1b[58:2:{d}:{d}:{d}m";
|
||||
|
||||
// Underlines
|
||||
pub const ul_off = "\x1b[24m"; // NOTE: this could be \x1b[4:0m but is not as widely supported
|
||||
pub const ul_single = "\x1b[4m";
|
||||
pub const ul_double = "\x1b[4:2m";
|
||||
pub const ul_curly = "\x1b[4:3m";
|
||||
pub const ul_dotted = "\x1b[4:4m";
|
||||
pub const ul_dashed = "\x1b[4:5m";
|
||||
|
||||
// Attributes
|
||||
pub const bold_set = "\x1b[1m";
|
||||
pub const dim_set = "\x1b[2m";
|
||||
pub const italic_set = "\x1b[3m";
|
||||
pub const blink_set = "\x1b[5m";
|
||||
pub const reverse_set = "\x1b[7m";
|
||||
pub const invisible_set = "\x1b[8m";
|
||||
pub const strikethrough_set = "\x1b[9m";
|
||||
pub const bold_dim_reset = "\x1b[22m";
|
||||
pub const italic_reset = "\x1b[23m";
|
||||
pub const blink_reset = "\x1b[25m";
|
||||
pub const reverse_reset = "\x1b[27m";
|
||||
pub const invisible_reset = "\x1b[28m";
|
||||
pub const strikethrough_reset = "\x1b[29m";
|
||||
|
||||
// OSC sequences
|
||||
pub const osc8 = "\x1b]8;{s};{s}\x1b\\";
|
||||
pub const osc8_clear = "\x1b]8;;\x1b\\";
|
||||
|
|
158
src/vaxis.zig
158
src/vaxis.zig
|
@ -8,6 +8,7 @@ const Key = @import("Key.zig");
|
|||
const Screen = @import("Screen.zig");
|
||||
const Window = @import("Window.zig");
|
||||
const Options = @import("Options.zig");
|
||||
const Style = @import("cell.zig").Style;
|
||||
|
||||
/// Vaxis is the entrypoint for a Vaxis application. The provided type T should
|
||||
/// be a tagged union which contains all of the events the application will
|
||||
|
@ -135,18 +136,30 @@ pub fn Vaxis(comptime T: type) type {
|
|||
self.alt_screen = false;
|
||||
}
|
||||
|
||||
/// draws the screen to the terminal
|
||||
pub fn render(self: *Self) !void {
|
||||
var tty = self.tty orelse return;
|
||||
|
||||
// TODO: optimize writes
|
||||
|
||||
// Send the cursor to 0,0
|
||||
// TODO: this needs to move after we optimize writes. We only do
|
||||
// this if we have an update to make. We also need to hide cursor
|
||||
// and then reshow it if needed
|
||||
_ = try tty.write(ctlseqs.hide_cursor);
|
||||
_ = try tty.write(ctlseqs.home);
|
||||
|
||||
// initialize some variables
|
||||
var reposition: bool = false;
|
||||
var row: usize = 0;
|
||||
var col: usize = 0;
|
||||
for (self.screen.buf, 0..) |cell, i| {
|
||||
col += 1;
|
||||
var cursor: Style = .{};
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < self.screen.buf.len) : (i += 1) {
|
||||
const cell = self.screen.buf[i];
|
||||
defer col += 1;
|
||||
defer cursor = cell.style;
|
||||
if (col == self.screen.width) {
|
||||
row += 1;
|
||||
col = 0;
|
||||
|
@ -155,13 +168,154 @@ pub fn Vaxis(comptime T: type) type {
|
|||
// anything
|
||||
if (std.meta.eql(cell, self.screen_last.buf[i])) {
|
||||
reposition = true;
|
||||
// Close any osc8 sequence we might be in before
|
||||
// repositioning
|
||||
if (cursor.url) |_| {
|
||||
_ = try tty.write(ctlseqs.osc8_clear);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Set this cell in the last frame
|
||||
self.screen_last.buf[i] = cell;
|
||||
|
||||
// reposition the cursor, if needed
|
||||
if (reposition) {
|
||||
try std.fmt.format(tty.writer(), ctlseqs.cup, .{ row + 1, col + 1 });
|
||||
}
|
||||
|
||||
// something is different, so let's loop throuugh everything and
|
||||
// find out what
|
||||
|
||||
// foreground
|
||||
if (!std.meta.eql(cursor.fg, cell.style.fg)) {
|
||||
switch (cell.style.fg) {
|
||||
.default => _ = try tty.write(ctlseqs.fg_reset),
|
||||
.index => |idx| {
|
||||
switch (idx) {
|
||||
0...7 => try std.fmt.format(tty.writer(), ctlseqs.fg_base, .{idx}),
|
||||
8...15 => try std.fmt.format(tty.writer(), ctlseqs.fg_bright, .{idx}),
|
||||
else => try std.fmt.format(tty.writer(), ctlseqs.fg_indexed, .{idx}),
|
||||
}
|
||||
},
|
||||
.rgb => |rgb| {
|
||||
try std.fmt.format(tty.writer(), ctlseqs.fg_rgb, .{ rgb[0], rgb[1], rgb[2] });
|
||||
},
|
||||
}
|
||||
}
|
||||
// background
|
||||
if (!std.meta.eql(cursor.bg, cell.style.bg)) {
|
||||
switch (cell.style.bg) {
|
||||
.default => _ = try tty.write(ctlseqs.bg_reset),
|
||||
.index => |idx| {
|
||||
switch (idx) {
|
||||
0...7 => try std.fmt.format(tty.writer(), ctlseqs.bg_base, .{idx}),
|
||||
8...15 => try std.fmt.format(tty.writer(), ctlseqs.bg_bright, .{idx}),
|
||||
else => try std.fmt.format(tty.writer(), ctlseqs.bg_indexed, .{idx}),
|
||||
}
|
||||
},
|
||||
.rgb => |rgb| {
|
||||
try std.fmt.format(tty.writer(), ctlseqs.bg_rgb, .{ rgb[0], rgb[1], rgb[2] });
|
||||
},
|
||||
}
|
||||
}
|
||||
// underline color
|
||||
if (!std.meta.eql(cursor.ul, cell.style.ul)) {
|
||||
switch (cell.style.bg) {
|
||||
.default => _ = try tty.write(ctlseqs.ul_reset),
|
||||
.index => |idx| {
|
||||
try std.fmt.format(tty.writer(), ctlseqs.ul_indexed, .{idx});
|
||||
},
|
||||
.rgb => |rgb| {
|
||||
try std.fmt.format(tty.writer(), ctlseqs.ul_rgb, .{ rgb[0], rgb[1], rgb[2] });
|
||||
},
|
||||
}
|
||||
}
|
||||
// underline style
|
||||
if (!std.meta.eql(cursor.ul_style, cell.style.ul_style)) {
|
||||
const seq = switch (cell.style.ul_style) {
|
||||
.off => ctlseqs.ul_off,
|
||||
.single => ctlseqs.ul_single,
|
||||
.double => ctlseqs.ul_double,
|
||||
.curly => ctlseqs.ul_curly,
|
||||
.dotted => ctlseqs.ul_dotted,
|
||||
.dashed => ctlseqs.ul_dashed,
|
||||
};
|
||||
_ = try tty.write(seq);
|
||||
}
|
||||
// bold
|
||||
if (cursor.bold != cell.style.bold) {
|
||||
const seq = switch (cell.style.bold) {
|
||||
true => ctlseqs.bold_set,
|
||||
false => ctlseqs.bold_dim_reset,
|
||||
};
|
||||
_ = try tty.write(seq);
|
||||
if (cell.style.dim) {
|
||||
_ = try tty.write(ctlseqs.dim_set);
|
||||
}
|
||||
}
|
||||
// dim
|
||||
if (cursor.dim != cell.style.dim) {
|
||||
const seq = switch (cell.style.dim) {
|
||||
true => ctlseqs.dim_set,
|
||||
false => ctlseqs.bold_dim_reset,
|
||||
};
|
||||
_ = try tty.write(seq);
|
||||
if (cell.style.bold) {
|
||||
_ = try tty.write(ctlseqs.bold_set);
|
||||
}
|
||||
}
|
||||
// dim
|
||||
if (cursor.italic != cell.style.italic) {
|
||||
const seq = switch (cell.style.italic) {
|
||||
true => ctlseqs.italic_set,
|
||||
false => ctlseqs.italic_reset,
|
||||
};
|
||||
_ = try tty.write(seq);
|
||||
}
|
||||
// dim
|
||||
if (cursor.blink != cell.style.blink) {
|
||||
const seq = switch (cell.style.blink) {
|
||||
true => ctlseqs.blink_set,
|
||||
false => ctlseqs.blink_reset,
|
||||
};
|
||||
_ = try tty.write(seq);
|
||||
}
|
||||
// reverse
|
||||
if (cursor.reverse != cell.style.reverse) {
|
||||
const seq = switch (cell.style.reverse) {
|
||||
true => ctlseqs.reverse_set,
|
||||
false => ctlseqs.reverse_reset,
|
||||
};
|
||||
_ = try tty.write(seq);
|
||||
}
|
||||
// invisible
|
||||
if (cursor.invisible != cell.style.invisible) {
|
||||
const seq = switch (cell.style.invisible) {
|
||||
true => ctlseqs.invisible_set,
|
||||
false => ctlseqs.invisible_reset,
|
||||
};
|
||||
_ = try tty.write(seq);
|
||||
}
|
||||
// strikethrough
|
||||
if (cursor.strikethrough != cell.style.strikethrough) {
|
||||
const seq = switch (cell.style.strikethrough) {
|
||||
true => ctlseqs.strikethrough_set,
|
||||
false => ctlseqs.strikethrough_reset,
|
||||
};
|
||||
_ = try tty.write(seq);
|
||||
}
|
||||
|
||||
// url
|
||||
if (!std.meta.eql(cursor.url, cell.style.url)) {
|
||||
const url = cell.style.url orelse "";
|
||||
var ps = cell.style.url_params orelse "";
|
||||
if (url.len == 0) {
|
||||
// Empty out the params no matter what if we don't have
|
||||
// a url
|
||||
ps = "";
|
||||
}
|
||||
try std.fmt.format(tty.writer(), ctlseqs.osc8, .{ ps, url });
|
||||
}
|
||||
_ = try tty.write(cell.char.grapheme);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue