mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
terminal/new: scrollactive
This commit is contained in:
30
src/bench/stream-new.sh
Executable file
30
src/bench/stream-new.sh
Executable file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# This is a trivial helper script to help run the stream benchmark.
|
||||
# You probably want to tweak this script depending on what you're
|
||||
# trying to measure.
|
||||
|
||||
# Options:
|
||||
# - "ascii", uniform random ASCII bytes
|
||||
# - "utf8", uniform random unicode characters, encoded as utf8
|
||||
# - "rand", pure random data, will contain many invalid code sequences.
|
||||
DATA="ascii"
|
||||
SIZE="25000000"
|
||||
|
||||
# Uncomment to test with an active terminal state.
|
||||
# ARGS=" --terminal"
|
||||
|
||||
# Generate the benchmark input ahead of time so it's not included in the time.
|
||||
./zig-out/bin/bench-stream --mode=gen-$DATA | head -c $SIZE > /tmp/ghostty_bench_data
|
||||
|
||||
# Uncomment to instead use the contents of `stream.txt` as input. (Ignores SIZE)
|
||||
# echo $(cat ./stream.txt) > /tmp/ghostty_bench_data
|
||||
|
||||
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"
|
@ -117,7 +117,7 @@ pub fn deinit(self: *PageList) void {
|
||||
/// Scroll the active area down by n lines. If the n lines go beyond the
|
||||
/// end of the screen, this will add new pages as necessary. This does
|
||||
/// not move the viewport.
|
||||
pub fn scrollDown(self: *PageList, n: usize) !void {
|
||||
pub fn scrollActive(self: *PageList, n: usize) !void {
|
||||
// Move our active area down as much as possible towards n. The return
|
||||
// value is the amount of rows we were short in any existing page, and
|
||||
// we must expand at least that much. This does not include the size
|
||||
@ -297,6 +297,10 @@ pub const RowOffset = struct {
|
||||
page: *List.Node,
|
||||
row_offset: usize = 0,
|
||||
|
||||
pub fn eql(self: RowOffset, other: RowOffset) bool {
|
||||
return self.page == other.page and self.row_offset == other.row_offset;
|
||||
}
|
||||
|
||||
pub fn rowAndCell(self: RowOffset, x: usize) struct {
|
||||
row: *pagepkg.Row,
|
||||
cell: *pagepkg.Cell,
|
||||
@ -327,7 +331,7 @@ pub const RowOffset = struct {
|
||||
},
|
||||
} {
|
||||
// Index fits within this page
|
||||
var rows = self.page.data.size.rows - (self.row_offset + 1);
|
||||
const rows = self.page.data.size.rows - (self.row_offset + 1);
|
||||
if (n <= rows) return .{ .offset = .{
|
||||
.page = self.page,
|
||||
.row_offset = n + self.row_offset,
|
||||
@ -335,20 +339,18 @@ pub const RowOffset = struct {
|
||||
|
||||
// Need to traverse page links to find the page
|
||||
var page: *List.Node = self.page;
|
||||
var n_left: usize = n;
|
||||
while (n_left >= rows) {
|
||||
n_left -= rows;
|
||||
var n_left: usize = n - rows;
|
||||
while (true) {
|
||||
page = page.next orelse return .{ .overflow = .{
|
||||
.end = .{ .page = page, .row_offset = page.data.size.rows - 1 },
|
||||
.remaining = n_left,
|
||||
} };
|
||||
rows = page.data.size.rows;
|
||||
if (n_left <= page.data.size.rows) return .{ .offset = .{
|
||||
.page = page,
|
||||
.row_offset = n_left - 1,
|
||||
} };
|
||||
n_left -= page.data.size.rows;
|
||||
}
|
||||
|
||||
return .{ .offset = .{
|
||||
.page = page,
|
||||
.row_offset = n_left,
|
||||
} };
|
||||
}
|
||||
};
|
||||
|
||||
@ -378,7 +380,7 @@ test "PageList" {
|
||||
try testing.expectEqual(s.active, s.viewport);
|
||||
}
|
||||
|
||||
test "scrollDown utilizes capacity" {
|
||||
test "scrollActive utilizes capacity" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
@ -391,7 +393,7 @@ test "scrollDown utilizes capacity" {
|
||||
try testing.expect(s.active.row_offset == 0);
|
||||
try testing.expectEqual(@as(size.CellCountInt, 1), s.active.page.data.size.rows);
|
||||
|
||||
try s.scrollDown(1);
|
||||
try s.scrollActive(1);
|
||||
|
||||
// We should not allocate a new page because we have enough capacity
|
||||
try testing.expect(s.active.page == s.pages.first);
|
||||
@ -400,7 +402,7 @@ test "scrollDown utilizes capacity" {
|
||||
try testing.expectEqual(@as(size.CellCountInt, 2), s.active.page.data.size.rows);
|
||||
}
|
||||
|
||||
test "scrollDown adds new pages" {
|
||||
test "scrollActive adds new pages" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
@ -414,7 +416,7 @@ test "scrollDown adds new pages" {
|
||||
|
||||
// The initial active is a single page so scrolling down even one
|
||||
// should force the allocation of an entire new page.
|
||||
try s.scrollDown(1);
|
||||
try s.scrollActive(1);
|
||||
|
||||
// We should still be on the first page but offset, and we should
|
||||
// have a second page created.
|
||||
|
@ -114,6 +114,24 @@ pub fn cursorHorizontalAbsolute(self: *Screen, x: size.CellCountInt) void {
|
||||
self.cursor.x = x;
|
||||
}
|
||||
|
||||
/// Scroll the active area and keep the cursor at the bottom of the screen.
|
||||
/// This is a very specialized function but it keeps it fast.
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
if (move_viewport) self.pages.viewport = self.pages.active;
|
||||
}
|
||||
|
||||
/// Dump the screen to a string. The writer given should be buffered;
|
||||
/// this function does not attempt to efficiently write and generally writes
|
||||
/// one byte at a time.
|
||||
|
@ -173,9 +173,9 @@ pub fn init(alloc: Allocator, cols: size.CellCountInt, rows: size.CellCountInt)
|
||||
.rows = rows,
|
||||
.active_screen = .primary,
|
||||
// TODO: configurable scrollback
|
||||
.screen = try Screen.init(alloc, rows, cols, 10000),
|
||||
.screen = try Screen.init(alloc, cols, rows, 10000),
|
||||
// No scrollback for the alternate screen
|
||||
.secondary_screen = try Screen.init(alloc, rows, cols, 0),
|
||||
.secondary_screen = try Screen.init(alloc, cols, rows, 0),
|
||||
.tabstops = try Tabstops.init(alloc, cols, TABSTOP_INTERVAL),
|
||||
.scrolling_region = .{
|
||||
.top = 0,
|
||||
@ -396,8 +396,7 @@ pub fn index(self: *Terminal) !void {
|
||||
self.scrolling_region.left == 0 and
|
||||
self.scrolling_region.right == self.cols - 1)
|
||||
{
|
||||
@panic("TODO: scroll screen");
|
||||
//try self.screen.scroll(.{ .screen = 1 });
|
||||
try self.screen.cursorDownScroll();
|
||||
} else {
|
||||
@panic("TODO: scroll up");
|
||||
//try self.scrollUp(1);
|
||||
@ -453,6 +452,22 @@ test "Terminal: input with basic wraparound" {
|
||||
}
|
||||
}
|
||||
|
||||
test "Terminal: input that forces scroll" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try init(alloc, 1, 5);
|
||||
defer t.deinit(alloc);
|
||||
|
||||
// Basic grid writing
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
test "Terminal: zero-width character at start" {
|
||||
var t = try init(testing.allocator, 80, 80);
|
||||
defer t.deinit(testing.allocator);
|
||||
@ -466,11 +481,11 @@ test "Terminal: zero-width character at start" {
|
||||
}
|
||||
|
||||
// https://github.com/mitchellh/ghostty/issues/1400
|
||||
// test "Terminal: print single very long line" {
|
||||
// var t = try init(testing.allocator, 5, 5);
|
||||
// defer t.deinit(testing.allocator);
|
||||
//
|
||||
// // This would crash for issue 1400. So the assertion here is
|
||||
// // that we simply do not crash.
|
||||
// for (0..500) |_| try t.print('x');
|
||||
// }
|
||||
test "Terminal: print single very long line" {
|
||||
var t = try init(testing.allocator, 5, 5);
|
||||
defer t.deinit(testing.allocator);
|
||||
|
||||
// This would crash for issue 1400. So the assertion here is
|
||||
// that we simply do not crash.
|
||||
for (0..1000) |_| try t.print('x');
|
||||
}
|
||||
|
Reference in New Issue
Block a user