From b4ed0e6cbeeb3de984c3ddb7d925e65f70c537b4 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 26 Feb 2024 21:38:59 -0800 Subject: [PATCH] terminal/new: saved cursor --- src/terminal/Terminal.zig | 4 ++ src/terminal/new/Screen.zig | 16 ++++- src/terminal/new/Terminal.zig | 125 ++++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 1 deletion(-) diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index aa439d0d9..a9998a037 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -5462,6 +5462,7 @@ test "Terminal: resize with wraparound on" { try testing.expectEqualStrings("01\n23", str); } +// X test "Terminal: saveCursor" { const alloc = testing.allocator; var t = try init(alloc, 3, 3); @@ -5510,6 +5511,7 @@ test "Terminal: saveCursor with screen change" { try testing.expect(t.modes.get(.origin)); } +// X test "Terminal: saveCursor position" { const alloc = testing.allocator; var t = try init(alloc, 10, 5); @@ -5530,6 +5532,7 @@ test "Terminal: saveCursor position" { } } +// X test "Terminal: saveCursor pending wrap state" { const alloc = testing.allocator; var t = try init(alloc, 5, 5); @@ -5550,6 +5553,7 @@ test "Terminal: saveCursor pending wrap state" { } } +// X test "Terminal: saveCursor origin mode" { const alloc = testing.allocator; var t = try init(alloc, 10, 5); diff --git a/src/terminal/new/Screen.zig b/src/terminal/new/Screen.zig index 6fd0f2980..27947ba90 100644 --- a/src/terminal/new/Screen.zig +++ b/src/terminal/new/Screen.zig @@ -22,8 +22,11 @@ pages: PageList, /// The current cursor position cursor: Cursor, +/// The saved cursor +saved_cursor: ?SavedCursor = null, + /// The cursor position. -const Cursor = struct { +pub const Cursor = struct { // The x/y position within the viewport. x: size.CellCountInt, y: size.CellCountInt, @@ -50,6 +53,17 @@ const Cursor = struct { page_cell: *pagepkg.Cell, }; +/// Saved cursor state. +pub const SavedCursor = struct { + x: size.CellCountInt, + y: size.CellCountInt, + style: style.Style, + pending_wrap: bool, + origin: bool, + // TODO + //charset: CharsetState, +}; + /// Initialize a new screen. pub fn init( alloc: Allocator, diff --git a/src/terminal/new/Terminal.zig b/src/terminal/new/Terminal.zig index 816bb6677..c04f48483 100644 --- a/src/terminal/new/Terminal.zig +++ b/src/terminal/new/Terminal.zig @@ -773,6 +773,53 @@ pub fn cursorLeft(self: *Terminal, count_req: usize) void { } } +/// Save cursor position and further state. +/// +/// The primary and alternate screen have distinct save state. One saved state +/// is kept per screen (main / alternative). If for the current screen state +/// was already saved it is overwritten. +pub fn saveCursor(self: *Terminal) void { + self.screen.saved_cursor = .{ + .x = self.screen.cursor.x, + .y = self.screen.cursor.y, + .style = self.screen.cursor.style, + .pending_wrap = self.screen.cursor.pending_wrap, + .origin = self.modes.get(.origin), + //TODO + //.charset = self.screen.charset, + }; +} + +/// Restore cursor position and other state. +/// +/// The primary and alternate screen have distinct save state. +/// If no save was done before values are reset to their initial values. +pub fn restoreCursor(self: *Terminal) !void { + const saved: Screen.SavedCursor = self.screen.saved_cursor orelse .{ + .x = 0, + .y = 0, + .style = .{}, + .pending_wrap = false, + .origin = false, + // TODO + //.charset = .{}, + }; + + // Set the style first because it can fail + const old_style = self.screen.cursor.style; + self.screen.cursor.style = saved.style; + errdefer self.screen.cursor.style = old_style; + try self.screen.manualStyleUpdate(); + + //self.screen.charset = saved.charset; + self.modes.set(.origin, saved.origin); + self.screen.cursor.pending_wrap = saved.pending_wrap; + self.screen.cursorAbsolute( + @min(saved.x, self.cols - 1), + @min(saved.y, self.rows - 1), + ); +} + /// Horizontal tab moves the cursor to the next tabstop, clearing /// the screen to the left the tabstop. pub fn horizontalTab(self: *Terminal) !void { @@ -5461,3 +5508,81 @@ test "Terminal: deleteChars split wide character tail" { try testing.expectEqualStrings("0", str); } } + +test "Terminal: saveCursor" { + const alloc = testing.allocator; + var t = try init(alloc, 3, 3); + defer t.deinit(alloc); + + try t.setAttribute(.{ .bold = {} }); + //t.screen.charset.gr = .G3; + t.modes.set(.origin, true); + t.saveCursor(); + //t.screen.charset.gr = .G0; + try t.setAttribute(.{ .unset = {} }); + t.modes.set(.origin, false); + try t.restoreCursor(); + try testing.expect(t.screen.cursor.style.flags.bold); + //try testing.expect(t.screen.charset.gr == .G3); + try testing.expect(t.modes.get(.origin)); +} + +test "Terminal: saveCursor position" { + const alloc = testing.allocator; + var t = try init(alloc, 10, 5); + defer t.deinit(alloc); + + t.setCursorPos(1, 5); + try t.print('A'); + t.saveCursor(); + t.setCursorPos(1, 1); + try t.print('B'); + try t.restoreCursor(); + try t.print('X'); + + { + const str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("B AX", str); + } +} + +test "Terminal: saveCursor pending wrap state" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + t.setCursorPos(1, 5); + try t.print('A'); + t.saveCursor(); + t.setCursorPos(1, 1); + try t.print('B'); + try t.restoreCursor(); + try t.print('X'); + + { + const str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("B A\nX", str); + } +} + +test "Terminal: saveCursor origin mode" { + const alloc = testing.allocator; + var t = try init(alloc, 10, 5); + defer t.deinit(alloc); + + t.modes.set(.origin, true); + t.saveCursor(); + t.modes.set(.enable_left_and_right_margin, true); + t.setLeftAndRightMargin(3, 5); + t.setTopAndBottomMargin(2, 4); + try t.restoreCursor(); + try t.print('X'); + + { + const str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("X", str); + } +}