mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
Screen.region to get a region of contiguous memory for a tag
This commit is contained in:
@ -86,7 +86,7 @@ pub const RowIterator = struct {
|
|||||||
value: usize = 0,
|
value: usize = 0,
|
||||||
|
|
||||||
pub fn next(self: *RowIterator) ?Row {
|
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 idx = self.tag.index(self.value);
|
||||||
const res = self.screen.getRow(idx);
|
const res = self.screen.getRow(idx);
|
||||||
self.value += 1;
|
self.value += 1;
|
||||||
@ -129,7 +129,7 @@ pub const RowIndexTag = enum {
|
|||||||
.screen => screen.totalRows(),
|
.screen => screen.totalRows(),
|
||||||
.viewport => screen.rows,
|
.viewport => screen.rows,
|
||||||
.active => screen.rows,
|
.active => screen.rows,
|
||||||
};
|
} - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a RowIndex from a tag.
|
/// Construct a RowIndex from a tag.
|
||||||
@ -216,9 +216,31 @@ pub fn rowIterator(self: *const Screen, tag: RowIndexTag) RowIterator {
|
|||||||
return .{ .screen = self, .tag = tag };
|
return .{ .screen = self, .tag = tag };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the visible portion of the screen.
|
/// Region gets the contiguous portions of memory that constitute an
|
||||||
pub fn getVisible(self: Screen) []Cell {
|
/// entire region. This is an efficient way to clear regions, for example
|
||||||
return self.storage;
|
/// 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).
|
/// 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
|
// Sanity check that our test helpers work
|
||||||
const str = "1ABCD\n2EFGH\n3IJKL";
|
const str = "1ABCD\n2EFGH\n3IJKL";
|
||||||
s.testWriteString(str);
|
s.testWriteString(str);
|
||||||
var contents = try s.testString(alloc);
|
{
|
||||||
defer alloc.free(contents);
|
var contents = try s.testString(alloc);
|
||||||
try testing.expectEqualStrings(str, contents);
|
defer alloc.free(contents);
|
||||||
|
try testing.expectEqualStrings(str, contents);
|
||||||
|
}
|
||||||
|
|
||||||
// Test the row iterator
|
// Test the row iterator
|
||||||
var count: usize = 0;
|
var count: usize = 0;
|
||||||
@ -665,6 +689,16 @@ test "Screen" {
|
|||||||
|
|
||||||
// Should go through all rows
|
// Should go through all rows
|
||||||
try testing.expectEqual(@as(usize, 3), count);
|
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" {
|
test "Screen: scrolling" {
|
||||||
@ -825,6 +859,26 @@ test "Screen: scrollback" {
|
|||||||
defer alloc.free(contents);
|
defer alloc.free(contents);
|
||||||
try testing.expectEqualStrings("1ABCD\n2EFGH\n3IJKL", 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" {
|
test "Screen: scrollback empty" {
|
||||||
|
@ -539,8 +539,9 @@ pub fn eraseDisplay(
|
|||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
.complete => {
|
.complete => {
|
||||||
const all = self.screen.getVisible();
|
const region = self.screen.region(.active);
|
||||||
std.mem.set(Screen.Cell, all, self.screen.cursor.pen);
|
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
|
// Unsets pending wrap state
|
||||||
self.screen.cursor.pending_wrap = false;
|
self.screen.cursor.pending_wrap = false;
|
||||||
|
Reference in New Issue
Block a user