vaxis: add sync and refresh

Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
This commit is contained in:
Tim Culverhouse 2024-01-23 07:36:44 -06:00
parent 5d8f78ccd2
commit 59470f18e8
3 changed files with 50 additions and 17 deletions

View file

@ -34,6 +34,8 @@ pub fn main() !void {
var text_input = TextInput.init(alloc); var text_input = TextInput.init(alloc);
defer text_input.deinit(); defer text_input.deinit();
try vx.queryTerminal();
// The main event loop. Vaxis provides a thread safe, blocking, buffered // The main event loop. Vaxis provides a thread safe, blocking, buffered
// queue which can serve as the primary event queue for an application // queue which can serve as the primary event queue for an application
outer: while (true) { outer: while (true) {
@ -49,9 +51,12 @@ pub fn main() !void {
else => color_idx + 1, else => color_idx + 1,
}; };
try text_input.update(.{ .key_press = key }); try text_input.update(.{ .key_press = key });
if (key.codepoint == 'c' and key.mods.ctrl) { if (key.matches('c', .{ .ctrl = true })) {
break :outer; break :outer;
} }
if (key.matches('l', .{ .ctrl = true })) {
vx.queueRefresh();
}
}, },
.winsize => |ws| { .winsize => |ws| {
try vx.resize(alloc, ws); try vx.resize(alloc, ws);

View file

@ -10,6 +10,10 @@ pub const csi_u_query = "\x1b[?u";
pub const kitty_graphics_query = "\x1b_Gi=1,a=q\x1b\\"; pub const kitty_graphics_query = "\x1b_Gi=1,a=q\x1b\\";
pub const sixel_geometry_query = "\x1b[?2;1;0S"; pub const sixel_geometry_query = "\x1b[?2;1;0S";
// sync
pub const sync_set = "\x1b[?2026h";
pub const sync_reset = "\x1b[?2026l";
// Key encoding // Key encoding
pub const csi_u_push = "\x1b[>{d}u"; pub const csi_u_push = "\x1b[>{d}u";
pub const csi_u_pop = "\x1b[<u"; pub const csi_u_pop = "\x1b[<u";

View file

@ -36,13 +36,18 @@ pub fn Vaxis(comptime T: type) type {
tty: ?Tty, tty: ?Tty,
/// the screen we write to
screen: Screen, screen: Screen,
// The last screen we drew. We keep this so we can efficiently update on /// The last screen we drew. We keep this so we can efficiently update on
// the next render /// the next render
screen_last: InternalScreen = undefined, screen_last: InternalScreen = undefined,
/// alt_screen state. We track so we can exit on deinit
alt_screen: bool, alt_screen: bool,
/// if we should redraw the entire screen on the next render
refresh: bool = false,
// statistics // statistics
renders: usize = 0, renders: usize = 0,
render_dur: i128 = 0, render_dur: i128 = 0,
@ -161,26 +166,37 @@ pub fn Vaxis(comptime T: type) type {
var tty = self.tty orelse return; var tty = self.tty orelse return;
const colorterm = std.os.getenv("COLORTERM") orelse ""; const colorterm = std.os.getenv("COLORTERM") orelse "";
if (std.mem.eql(u8, colorterm, "truecolor" or if (std.mem.eql(u8, colorterm, "truecolor") or
std.mem.eql(u8, colorterm, "24bit"))) std.mem.eql(u8, colorterm, "24bit"))
{ {
// TODO: Notify rgb support // TODO: Notify rgb support
} }
const writer = tty.buffered_writer.writer(); // TODO: decide if we actually want to query for focus and sync. It
_ = try writer.write(ctlseqs.decrqm_focus); // doesn't hurt to blindly use them
_ = try writer.write(ctlseqs.decrqm_sync); // _ = try tty.write(ctlseqs.decrqm_focus);
_ = try writer.write(ctlseqs.decrqm_unicode); // _ = try tty.write(ctlseqs.decrqm_sync);
_ = try writer.write(ctlseqs.decrqm_color_theme); _ = try tty.write(ctlseqs.decrqm_unicode);
_ = try writer.write(ctlseqs.xtversion); _ = try tty.write(ctlseqs.decrqm_color_theme);
_ = try writer.write(ctlseqs.csi_u_query); // TODO: XTVERSION has a DCS response. uncomment when we can parse
_ = try writer.write(ctlseqs.kitty_graphics_query); // that
_ = try writer.write(ctlseqs.sixel_geometry_query); // _ = try tty.write(ctlseqs.xtversion);
_ = try tty.write(ctlseqs.csi_u_query);
// TODO: KITTY_GRAPHICS has an APC response. uncomment when we can
// parse that
// that
// _ = try tty.write(ctlseqs.kitty_graphics_query);
_ = try tty.write(ctlseqs.sixel_geometry_query);
// TODO: XTGETTCAP queries ("RGB", "Smulx") // TODO: XTGETTCAP queries ("RGB", "Smulx")
_ = try writer.write(ctlseqs.primary_device_attrs); _ = try tty.write(ctlseqs.primary_device_attrs);
try writer.flush(); try tty.flush();
}
// the next render call will refresh the entire screen
pub fn queueRefresh(self: *Self) void {
self.refresh = true;
} }
/// draws the screen to the terminal /// draws the screen to the terminal
@ -192,8 +208,16 @@ pub fn Vaxis(comptime T: type) type {
self.render_dur += std.time.microTimestamp() - timer_start; self.render_dur += std.time.microTimestamp() - timer_start;
} }
defer self.refresh = false;
defer tty.flush() catch {}; defer tty.flush() catch {};
// Set up sync before we write anything
// TODO: optimize sync so we only sync _when we have changes_. This
// requires a smarter buffered writer, we'll probably have to write
// our own
_ = try tty.write(ctlseqs.sync_set);
defer _ = tty.write(ctlseqs.sync_reset) catch {};
// Send the cursor to 0,0 // Send the cursor to 0,0
// TODO: this needs to move after we optimize writes. We only do // 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 // this if we have an update to make. We also need to hide cursor
@ -218,7 +242,7 @@ pub fn Vaxis(comptime T: type) type {
} }
// If cell is the same as our last frame, we don't need to do // If cell is the same as our last frame, we don't need to do
// anything // anything
if (self.screen_last.buf[i].eql(cell)) { if (!self.refresh and self.screen_last.buf[i].eql(cell)) {
reposition = true; reposition = true;
// Close any osc8 sequence we might be in before // Close any osc8 sequence we might be in before
// repositioning // repositioning