From 19ddbbc7d6c0dca7425ab413aca7728ba5ff65c2 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 16 Apr 2024 14:20:18 -0700 Subject: [PATCH] terminal: eraseRowBounded dirty tracking --- src/terminal/PageList.zig | 44 +++++++++++++++++++++++++++++++++++++++ src/terminal/Terminal.zig | 9 ++++---- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/terminal/PageList.zig b/src/terminal/PageList.zig index 0f1d7ea8a..5a7dbf271 100644 --- a/src/terminal/PageList.zig +++ b/src/terminal/PageList.zig @@ -2102,6 +2102,10 @@ pub fn eraseRowBounded( page.data.clearCells(&rows[pn.y], 0, page.data.size.cols); fastmem.rotateOnce(Row, rows[pn.y..][0 .. limit + 1]); + // Set all the rows as dirty + var dirty = page.data.dirtyBitSet(); + dirty.setRangeValue(.{ .start = pn.y, .end = pn.y + limit }, true); + // Update pins in the shifted region. var pin_it = self.tracked_pins.keyIterator(); while (pin_it.next()) |p_ptr| { @@ -2123,6 +2127,12 @@ pub fn eraseRowBounded( fastmem.rotateOnce(Row, rows[pn.y..page.data.size.rows]); + // All the rows in the page are dirty below the erased row. + { + var dirty = page.data.dirtyBitSet(); + dirty.setRangeValue(.{ .start = pn.y, .end = page.data.size.rows }, true); + } + // We need to keep track of how many rows we've shifted so that we can // determine at what point we need to do a partial shift on subsequent // pages. @@ -2165,6 +2175,10 @@ pub fn eraseRowBounded( page.data.clearCells(&rows[0], 0, page.data.size.cols); fastmem.rotateOnce(Row, rows[0 .. shifted_limit + 1]); + // Set all the rows as dirty + var dirty = page.data.dirtyBitSet(); + dirty.setRangeValue(.{ .start = 0, .end = shifted_limit }, true); + // Update pins in the shifted region. var pin_it = self.tracked_pins.keyIterator(); while (pin_it.next()) |p_ptr| { @@ -2183,6 +2197,10 @@ pub fn eraseRowBounded( fastmem.rotateOnce(Row, rows[0..page.data.size.rows]); + // Set all the rows as dirty + var dirty = page.data.dirtyBitSet(); + dirty.setRangeValue(.{ .start = 0, .end = page.data.size.rows }, true); + // Account for the rows shifted in this page. shifted += page.data.size.rows; @@ -2939,6 +2957,11 @@ pub fn clearDirty(self: *PageList) void { } } +/// Returns true if the point is dirty, used for testing. +pub fn isDirty(self: *const PageList, pt: point.Point) bool { + return self.getCell(pt).?.isDirty(); +} + /// Represents an exact x/y coordinate within the screen. This is called /// a "pin" because it is a fixed point within the pagelist direct to /// a specific page pointer and memory offset. The benefit is that this @@ -4513,6 +4536,13 @@ test "PageList eraseRowBounded less than full row" { try s.eraseRowBounded(.{ .active = .{ .y = 5 } }, 3); try testing.expectEqual(s.rows, s.totalRows()); + // The erased rows should be dirty + try testing.expect(!s.isDirty(.{ .active = .{ .x = 0, .y = 4 } })); + try testing.expect(s.isDirty(.{ .active = .{ .x = 0, .y = 5 } })); + try testing.expect(s.isDirty(.{ .active = .{ .x = 0, .y = 6 } })); + try testing.expect(s.isDirty(.{ .active = .{ .x = 0, .y = 7 } })); + try testing.expect(!s.isDirty(.{ .active = .{ .x = 0, .y = 8 } })); + try testing.expectEqual(s.pages.first.?, p_top.page); try testing.expectEqual(@as(usize, 4), p_top.y); try testing.expectEqual(@as(usize, 0), p_top.x); @@ -4541,6 +4571,12 @@ test "PageList eraseRowBounded with pin at top" { try s.eraseRowBounded(.{ .active = .{ .y = 0 } }, 3); try testing.expectEqual(s.rows, s.totalRows()); + // The erased rows should be dirty + try testing.expect(s.isDirty(.{ .active = .{ .x = 0, .y = 0 } })); + try testing.expect(s.isDirty(.{ .active = .{ .x = 0, .y = 1 } })); + try testing.expect(s.isDirty(.{ .active = .{ .x = 0, .y = 2 } })); + try testing.expect(!s.isDirty(.{ .active = .{ .x = 0, .y = 3 } })); + try testing.expectEqual(s.pages.first.?, p_top.page); try testing.expectEqual(@as(usize, 0), p_top.y); try testing.expectEqual(@as(usize, 0), p_top.x); @@ -4563,6 +4599,10 @@ test "PageList eraseRowBounded full rows single page" { try s.eraseRowBounded(.{ .active = .{ .y = 5 } }, 10); try testing.expectEqual(s.rows, s.totalRows()); + // The erased rows should be dirty + try testing.expect(!s.isDirty(.{ .active = .{ .x = 0, .y = 4 } })); + for (5..10) |y| try testing.expect(s.isDirty(.{ .active = .{ .x = 0, .y = y } })); + // Our pin should move to the first page try testing.expectEqual(s.pages.first.?, p_in.page); try testing.expectEqual(@as(usize, 6), p_in.y); @@ -4620,6 +4660,10 @@ test "PageList eraseRowBounded full rows two pages" { // Erase only a few rows in our active try s.eraseRowBounded(.{ .active = .{ .y = 4 } }, 4); + // The erased rows should be dirty + try testing.expect(!s.isDirty(.{ .active = .{ .x = 0, .y = 3 } })); + for (4..8) |y| try testing.expect(s.isDirty(.{ .active = .{ .x = 0, .y = y } })); + // In page in first page is shifted try testing.expectEqual(s.pages.last.?.prev.?, p_first.page); try testing.expectEqual(@as(usize, p_first.page.data.size.rows - 2), p_first.y); diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 19cd5621c..c2acf8238 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -5881,11 +5881,10 @@ test "Terminal: index bottom of scroll region" { try t.index(); try t.print('X'); - // TODO(dirty) - // try testing.expect(t.isDirty(.{ .active = .{ .x = 0, .y = 0 } })); - // try testing.expect(t.isDirty(.{ .active = .{ .x = 0, .y = 1 } })); - // try testing.expect(t.isDirty(.{ .active = .{ .x = 0, .y = 2 } })); - // try testing.expect(!t.isDirty(.{ .active = .{ .x = 0, .y = 3 } })); + try testing.expect(t.isDirty(.{ .active = .{ .x = 0, .y = 0 } })); + try testing.expect(t.isDirty(.{ .active = .{ .x = 0, .y = 1 } })); + try testing.expect(t.isDirty(.{ .active = .{ .x = 0, .y = 2 } })); + try testing.expect(!t.isDirty(.{ .active = .{ .x = 0, .y = 3 } })); { const str = try t.plainString(testing.allocator);