From 2d6d0270971e1c3be8c420f8d2c9c7ba34e19b0b Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 7 Aug 2022 10:46:35 -0700 Subject: [PATCH] Screen.region to get a region of contiguous memory for a tag --- src/terminal/Screen.zig | 70 ++++++++++++++++++++++++++++++++++----- src/terminal/Terminal.zig | 5 +-- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/src/terminal/Screen.zig b/src/terminal/Screen.zig index d32551d5b..245b91ee3 100644 --- a/src/terminal/Screen.zig +++ b/src/terminal/Screen.zig @@ -86,7 +86,7 @@ pub const RowIterator = struct { value: usize = 0, pub fn next(self: *RowIterator) ?Row { - if (self.value >= self.tag.max(self.screen)) return null; + if (self.value > self.tag.max(self.screen)) return null; const idx = self.tag.index(self.value); const res = self.screen.getRow(idx); self.value += 1; @@ -129,7 +129,7 @@ pub const RowIndexTag = enum { .screen => screen.totalRows(), .viewport => screen.rows, .active => screen.rows, - }; + } - 1; } /// Construct a RowIndex from a tag. @@ -216,9 +216,31 @@ pub fn rowIterator(self: *const Screen, tag: RowIndexTag) RowIterator { return .{ .screen = self, .tag = tag }; } -/// Get the visible portion of the screen. -pub fn getVisible(self: Screen) []Cell { - return self.storage; +/// Region gets the contiguous portions of memory that constitute an +/// entire region. This is an efficient way to clear regions, for example +/// since you can memcpy directly into it. +/// +/// This has two elements because internally we use a ring buffer and +/// so any region can be split into two if it crosses the ring buffer +/// boundary. +pub fn region(self: *const Screen, tag: RowIndexTag) [2][]Cell { + const top = self.rowIndex(tag.index(0)); + const bot = self.rowIndex(tag.index(tag.max(self))); + + // The bottom and top are available in one contiguous slice. + if (bot >= top) { + return .{ + self.storage[top .. bot + self.cols], + self.storage[0..0], // just so its a valid slice, but zero length + }; + } + + // The bottom and top are split into two slices, so we slice to the + // bottom of the storage, then from the top. + return .{ + self.storage[top..self.storage.len], + self.storage[0 .. bot + self.cols], + }; } /// Get a single row in the active area by index (0-indexed). @@ -649,9 +671,11 @@ test "Screen" { // Sanity check that our test helpers work const str = "1ABCD\n2EFGH\n3IJKL"; s.testWriteString(str); - var contents = try s.testString(alloc); - defer alloc.free(contents); - try testing.expectEqualStrings(str, contents); + { + var contents = try s.testString(alloc); + defer alloc.free(contents); + try testing.expectEqualStrings(str, contents); + } // Test the row iterator var count: usize = 0; @@ -665,6 +689,16 @@ test "Screen" { // Should go through all rows try testing.expectEqual(@as(usize, 3), count); + + // Should be able to easily clear screen + const reg = s.region(.viewport); + std.mem.set(Cell, reg[0], .{ .char = 'A' }); + std.mem.set(Cell, reg[1], .{ .char = 'A' }); + { + var contents = try s.testString(alloc); + defer alloc.free(contents); + try testing.expectEqualStrings("AAAAA\nAAAAA\nAAAAA", contents); + } } test "Screen: scrolling" { @@ -825,6 +859,26 @@ test "Screen: scrollback" { defer alloc.free(contents); try testing.expectEqualStrings("1ABCD\n2EFGH\n3IJKL", contents); } + + // Should be able to easily clear active area only + const reg = s.region(.active); + std.mem.set(Cell, reg[0], .{ .char = 0 }); + std.mem.set(Cell, reg[1], .{ .char = 0 }); + { + var contents = try s.testString(alloc); + defer alloc.free(contents); + try testing.expectEqualStrings("1ABCD", contents); + } + + // Scrolling to the bottom + s.scroll(.{ .bottom = {} }); + + { + // Test our contents rotated + var contents = try s.testString(alloc); + defer alloc.free(contents); + try testing.expectEqualStrings("", contents); + } } test "Screen: scrollback empty" { diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 91e9dd450..775ca0452 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -539,8 +539,9 @@ pub fn eraseDisplay( switch (mode) { .complete => { - const all = self.screen.getVisible(); - std.mem.set(Screen.Cell, all, self.screen.cursor.pen); + const region = self.screen.region(.active); + std.mem.set(Screen.Cell, region[0], self.screen.cursor.pen); + std.mem.set(Screen.Cell, region[1], self.screen.cursor.pen); // Unsets pending wrap state self.screen.cursor.pending_wrap = false;