From 7e77c6b36928aeea3fb9eb56671620e4660833e8 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 8 May 2022 16:08:07 -0700 Subject: [PATCH] terminal implement clear screen and set cursor, needs tests --- src/Window.zig | 2 +- src/terminal/Terminal.zig | 70 ++++++++++++++++++++++++++++++++++++++- src/terminal/csi.zig | 7 ++++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 src/terminal/csi.zig diff --git a/src/Window.zig b/src/Window.zig index b242898db..df55ace1f 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -139,7 +139,7 @@ pub fn create(alloc: Allocator, loop: libuv.Loop) !*Window { var env = try std.process.getEnvMap(alloc); defer env.deinit(); - try env.put("TERM", "dumb"); + try env.put("TERM", "xterm-256color"); var cmd: Command = .{ .path = path, diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 70a0dc806..566081cb7 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -8,6 +8,7 @@ const builtin = @import("builtin"); const testing = std.testing; const Allocator = std.mem.Allocator; const ansi = @import("ansi.zig"); +const csi = @import("csi.zig"); const Parser = @import("Parser.zig"); const Tabstops = @import("Tabstops.zig"); const trace = @import("../tracy/tracy.zig").trace; @@ -127,11 +128,52 @@ pub fn appendChar(self: *Terminal, alloc: Allocator, c: u8) !void { switch (action_opt orelse continue) { .print => |p| try self.print(alloc, p), .execute => |code| try self.execute(alloc, code), - .csi_dispatch => |csi| log.warn("CSI: {}", .{csi}), + .csi_dispatch => |csi| try self.csiDispatch(alloc, csi), } } } +fn csiDispatch( + self: *Terminal, + alloc: Allocator, + action: Parser.Action.CSI, +) !void { + switch (action.final) { + // Set Cursor Position (TODO: docs) + 'H' => { + switch (action.params.len) { + 0 => try self.setCursorPosition(1, 1), + 1 => try self.setCursorPosition(action.params[0], 1), + 2 => try self.setCursorPosition(action.params[0], action.params[1]), + else => log.warn("unimplemented CSI: {}", .{csi}), + } + }, + + // Erase Display + 'J' => try self.eraseDisplay(alloc, switch (action.params.len) { + 0 => .below, + 1 => mode: { + // TODO: use meta to get enum max + if (action.params[0] > 3) { + log.warn("invalid erase display command: {}", .{csi}); + return; + } + + break :mode @intToEnum( + csi.EraseDisplayMode, + action.params[0], + ); + }, + else => { + log.warn("invalid erase display command: {}", .{csi}); + return; + }, + }), + + else => log.warn("unimplemented CSI: {}", .{csi}), + } +} + fn print(self: *Terminal, alloc: Allocator, c: u8) !void { const tracy = trace(@src()); defer tracy.end(); @@ -171,6 +213,32 @@ pub fn bell(self: *Terminal) void { log.info("bell", .{}); } +/// Set the cursor position. Row and column are 1-based. +/// TODO: test +pub fn setCursorPosition(self: *Terminal, row: usize, col: usize) !void { + const tracy = trace(@src()); + defer tracy.end(); + + self.cursor.x = col - 1; + self.cursor.y = row - 1; +} + +/// Erase the display. +/// TODO: test +pub fn eraseDisplay( + self: *Terminal, + alloc: Allocator, + mode: csi.EraseDisplayMode, +) !void { + switch (mode) { + .complete => { + for (self.screen.items) |*line| line.deinit(alloc); + self.screen.clearRetainingCapacity(); + }, + else => @panic("unimplemented"), + } +} + /// Backspace moves the cursor back a column (but not less than 0). pub fn backspace(self: *Terminal) void { const tracy = trace(@src()); diff --git a/src/terminal/csi.zig b/src/terminal/csi.zig new file mode 100644 index 000000000..d4d237bc3 --- /dev/null +++ b/src/terminal/csi.zig @@ -0,0 +1,7 @@ +// Modes for the ED CSI command. +pub const EraseDisplayMode = enum(u8) { + below = 0, + above = 1, + complete = 2, + scrollback = 3, +};