From 99224ae2c0337234078157dc94fc70329ea1816f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 13 Nov 2022 22:07:40 -0800 Subject: [PATCH] implement CSI ESC [ b for repeating previously printed char --- src/terminal/Terminal.zig | 17 +++++++++++++++++ src/terminal/stream.zig | 22 +++++++++++++++++----- src/termio/Exec.zig | 4 ++++ 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 50bc40f69..738e876e9 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -60,6 +60,10 @@ scrolling_region: ScrollingRegion, /// The charset state charset: CharsetState = .{}, +/// The previous printed character. This is used for the repeat previous +/// char CSI (ESC [ b). +previous_char: ?u21 = null, + /// Modes - This isn't exhaustive, since some modes (i.e. cursor origin) /// are applied to the cursor and others aren't boolean yes/no. modes: packed struct { @@ -571,6 +575,9 @@ pub fn print(self: *Terminal, c: u21) !void { return; } + // We have a printable character, save it + self.previous_char = c; + // If we're soft-wrapping, then handle that first. if (self.screen.cursor.pending_wrap and self.modes.autowrap) try self.printWrap(); @@ -699,6 +706,15 @@ fn clearWideSpacerHead(self: *Terminal) void { cell.attrs.wide_spacer_head = false; } +/// Print the previous printed character a repeated amount of times. +pub fn printRepeat(self: *Terminal, count: usize) !void { + // TODO: test + if (self.previous_char) |c| { + var i: usize = 0; + while (i < count) : (i += 1) try self.print(c); + } +} + /// Resets all margins and fills the whole screen with the character 'E' /// /// Sets the cursor to the top left corner. @@ -1353,6 +1369,7 @@ pub fn fullReset(self: *Terminal) void { self.screen.cursor = .{}; self.screen.saved_cursor = .{}; self.scrolling_region = .{ .top = 0, .bottom = self.rows - 1 }; + self.previous_char = null; } test "Terminal: input with no control characters" { diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index a7a59698d..ee2cb7457 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -314,6 +314,18 @@ pub fn Stream(comptime Handler: type) type { }, ) else log.warn("unimplemented CSI callback: {}", .{action}), + // Repeat Previous Char (REP) + 'b' => if (@hasDecl(T, "printRepeat")) try self.handler.printRepeat( + switch (action.params.len) { + 0 => 1, + 1 => action.params[0], + else => { + log.warn("invalid print repeat command: {}", .{action}); + return; + }, + }, + ) else log.warn("unimplemented CSI callback: {}", .{action}), + // c - Device Attributes (DA1) 'c' => if (@hasDecl(T, "deviceAttributes")) { const req: ansi.DeviceAttributeReq = switch (action.intermediates.len) { @@ -408,11 +420,6 @@ pub fn Stream(comptime Handler: type) type { else => log.warn("invalid DECSTBM command: {}", .{action}), } else log.warn("unimplemented CSI callback: {}", .{action}), - else => if (@hasDecl(T, "csiUnimplemented")) - try self.handler.csiUnimplemented(action) - else - log.warn("unimplemented CSI action: {}", .{action}), - // ICH - Insert Blanks // TODO: test '@' => if (@hasDecl(T, "insertBlanks")) switch (action.params.len) { @@ -440,6 +447,11 @@ pub fn Stream(comptime Handler: type) type { if (!success) log.warn("unimplemented CSI callback: {}", .{action}); }, + + else => if (@hasDecl(T, "csiUnimplemented")) + try self.handler.csiUnimplemented(action) + else + log.warn("unimplemented CSI action: {}", .{action}), } } diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index ab4fc7cd0..077a5096d 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -478,6 +478,10 @@ const StreamHandler = struct { try self.terminal.print(ch); } + pub fn printRepeat(self: *StreamHandler, count: usize) !void { + try self.terminal.printRepeat(count); + } + pub fn bell(self: StreamHandler) !void { _ = self; log.info("BELL", .{});