mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-21 03:06:15 +03:00
terminal/new
This commit is contained in:
@ -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"
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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" {
|
||||
|
@ -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;
|
||||
|
@ -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" {
|
||||
|
Reference in New Issue
Block a user