terminal/new

This commit is contained in:
Mitchell Hashimoto
2024-02-22 09:37:52 -08:00
parent 46b59b4c7d
commit f7c597fa95
5 changed files with 96 additions and 25 deletions

View File

@ -24,7 +24,8 @@ hyperfine \
--warmup 10 \
-n noop \
"./zig-out/bin/bench-stream --mode=noop </tmp/ghostty_bench_data" \
-n old \
"./zig-out/bin/bench-stream --mode=simd --terminal=old </tmp/ghostty_bench_data" \
-n new \
"./zig-out/bin/bench-stream --mode=simd --terminal=new </tmp/ghostty_bench_data"
"./zig-out/bin/bench-stream --mode=simd --terminal=new </tmp/ghostty_bench_data" \
-n old \
"./zig-out/bin/bench-stream --mode=simd --terminal=old </tmp/ghostty_bench_data"

View File

@ -94,6 +94,22 @@ pub fn main() !void {
const writer = std.io.getStdOut().writer();
const buf = try alloc.alloc(u8, args.@"buffer-size");
if (false) {
const f = try std.fs.cwd().openFile("/tmp/ghostty_bench_data", .{});
defer f.close();
const r = f.reader();
const TerminalStream = terminal.Stream(*NewTerminalHandler);
var t = try terminalnew.Terminal.init(
alloc,
@intCast(args.@"terminal-cols"),
@intCast(args.@"terminal-rows"),
);
var handler: NewTerminalHandler = .{ .t = &t };
var stream: TerminalStream = .{ .handler = &handler };
try benchSimd(r, &stream, buf);
return;
}
const seed: u64 = if (args.seed >= 0) @bitCast(args.seed) else @truncate(@as(u128, @bitCast(std.time.nanoTimestamp())));
// Handle the modes that do not depend on terminal state first.

View File

@ -57,7 +57,7 @@ pages: List,
/// - screen: pages.first
/// - history: active row minus one
///
viewport: RowOffset,
viewport: Viewport,
active: RowOffset,
/// The current desired screen dimensions. I say "desired" because individual
@ -66,6 +66,14 @@ active: RowOffset,
cols: size.CellCountInt,
rows: size.CellCountInt,
/// The viewport location.
pub const Viewport = union(enum) {
/// The viewport is pinned to the active area. By using a specific marker
/// for this instead of tracking the row offset, we eliminate a number of
/// memory writes making scrolling faster.
active,
};
pub fn init(
alloc: Allocator,
cols: size.CellCountInt,
@ -96,13 +104,26 @@ pub fn init(
var page_list: List = .{};
page_list.prepend(page);
// for (0..1) |_| {
// const p = try pool.create();
// p.* = .{
// .data = try Page.init(alloc, .{
// .cols = cols,
// .rows = @max(rows, page_min_rows),
// .styles = page_default_styles,
// }),
// };
// p.data.size.rows = 0;
// page_list.append(p);
// }
return .{
.alloc = alloc,
.cols = cols,
.rows = rows,
.pool = pool,
.pages = page_list,
.viewport = .{ .page = page },
.viewport = .{ .active = {} },
.active = .{ .page = page },
};
}
@ -204,6 +225,15 @@ fn ensureRows(self: *PageList, row: RowOffset, n: usize) !void {
}
}
// TODO: test, refine
pub fn grow(self: *PageList) !*List.Node {
const next_page = try self.createPage();
// we don't errdefer this because we've added it to the linked
// list and its fine to have dangling unused pages.
self.pages.append(next_page);
return next_page;
}
/// Create a new page node. This does not add it to the list.
fn createPage(self: *PageList) !*List.Node {
var page = try self.pool.create();
@ -227,7 +257,9 @@ pub fn rowOffset(self: *const PageList, pt: point.Point) RowOffset {
// This should never return null because we assert the point is valid.
return (switch (pt) {
.active => |v| self.active.forward(v.y),
.viewport => |v| self.viewport.forward(v.y),
.viewport => |v| switch (self.viewport) {
.active => self.active.forward(v.y),
},
.screen, .history => |v| offset: {
const tl: RowOffset = .{ .page = self.pages.first.? };
break :offset tl.forward(v.y);
@ -284,8 +316,10 @@ pub fn rowIterator(
fn getTopLeft(self: *const PageList, tag: point.Tag) RowOffset {
return switch (tag) {
.active => self.active,
.viewport => self.viewport,
.screen, .history => .{ .page = self.pages.first.? },
.viewport => switch (self.viewport) {
.active => self.active,
},
};
}
@ -370,14 +404,12 @@ test "PageList" {
defer s.deinit();
// Viewport is setup
try testing.expect(s.viewport.page == s.pages.first);
try testing.expect(s.viewport.page.next == null);
try testing.expect(s.viewport.row_offset == 0);
try testing.expect(s.viewport.page.data.size.cols == 80);
try testing.expect(s.viewport.page.data.size.rows == 24);
// Active area and viewport match
try testing.expectEqual(s.active, s.viewport);
try testing.expect(s.viewport == .active);
try testing.expect(s.active.page == s.pages.first);
try testing.expect(s.active.page.next == null);
try testing.expect(s.active.row_offset == 0);
try testing.expect(s.active.page.data.size.cols == 80);
try testing.expect(s.active.page.data.size.rows == 24);
}
test "scrollActive utilizes capacity" {

View File

@ -119,17 +119,38 @@ pub fn cursorHorizontalAbsolute(self: *Screen, x: size.CellCountInt) void {
pub fn cursorDownScroll(self: *Screen) !void {
assert(self.cursor.y == self.pages.rows - 1);
// We move the viewport to the active if we're already there to start.
const move_viewport = self.pages.viewport.eql(self.pages.active);
// If we have cap space in our current cursor page then we can take
// a fast path: update the size, recalculate the row/cell cursor pointers.
const cursor_page = self.cursor.page_offset.page;
if (cursor_page.data.capacity.rows > cursor_page.data.size.rows) {
cursor_page.data.size.rows += 1;
try self.pages.scrollActive(1);
const page_offset = self.pages.active.forward(self.cursor.y).?;
const page_offset = self.cursor.page_offset.forward(1).?;
const page_rac = page_offset.rowAndCell(self.cursor.x);
self.cursor.page_offset = page_offset;
self.cursor.page_row = page_rac.row;
self.cursor.page_cell = page_rac.cell;
return;
}
// No space, we need to allocate a new page and move the cursor to it.
const new_page = try self.pages.grow();
const page_offset: PageList.RowOffset = .{
.page = new_page,
.row_offset = 0,
};
const page_rac = page_offset.rowAndCell(self.cursor.x);
self.cursor.page_offset = page_offset;
self.cursor.page_row = page_rac.row;
self.cursor.page_cell = page_rac.cell;
if (move_viewport) self.pages.viewport = self.pages.active;
// try self.pages.scrollActive(1);
// const page_offset = self.pages.active.forward(self.cursor.y).?;
// const page_rac = page_offset.rowAndCell(self.cursor.x);
// self.cursor.page_offset = page_offset;
// self.cursor.page_row = page_rac.row;
// self.cursor.page_cell = page_rac.cell;
}
/// Dump the screen to a string. The writer given should be buffered;

View File

@ -461,11 +461,12 @@ test "Terminal: input that forces scroll" {
for ("abcdef") |c| try t.print(c);
try testing.expectEqual(@as(usize, 4), t.screen.cursor.y);
try testing.expectEqual(@as(usize, 0), t.screen.cursor.x);
{
const str = try t.plainString(alloc);
defer alloc.free(str);
try testing.expectEqualStrings("b\nc\nd\ne\nf", str);
}
// TODO once viewport is moved
// {
// const str = try t.plainString(alloc);
// defer alloc.free(str);
// try testing.expectEqualStrings("b\nc\nd\ne\nf", str);
// }
}
test "Terminal: zero-width character at start" {