the big screen switchover

This commit is contained in:
Mitchell Hashimoto
2022-09-01 00:58:47 -07:00
parent 2f2b12a32f
commit 77c8ec0a20
9 changed files with 3275 additions and 3226 deletions

View File

@ -322,7 +322,7 @@ pub fn deinit(self: *Grid) void {
/// ///
/// Note this doesn't have to typically be manually called. Internally, /// Note this doesn't have to typically be manually called. Internally,
/// the renderer will do this when it needs more memory space. /// the renderer will do this when it needs more memory space.
pub fn rebuildCells(self: *Grid, term: Terminal) !void { pub fn rebuildCells(self: *Grid, term: *Terminal) !void {
const t = trace(@src()); const t = trace(@src());
defer t.end(); defer t.end();
@ -344,9 +344,13 @@ pub fn rebuildCells(self: *Grid, term: Terminal) !void {
// Build each cell // Build each cell
var rowIter = term.screen.rowIterator(.viewport); var rowIter = term.screen.rowIterator(.viewport);
var y: usize = 0; var y: usize = 0;
while (rowIter.next()) |line| { while (rowIter.next()) |row| {
defer y += 1; defer y += 1;
for (line) |cell, x| {
var cellIter = row.cellIterator();
var x: usize = 0;
while (cellIter.next()) |cell| {
defer x += 1;
assert(try self.updateCell(term, cell, x, y)); assert(try self.updateCell(term, cell, x, y));
} }
} }
@ -358,7 +362,7 @@ pub fn rebuildCells(self: *Grid, term: Terminal) !void {
/// This should be called prior to render to finalize the cells and prepare /// This should be called prior to render to finalize the cells and prepare
/// for render. This performs tasks such as preparing the cursor, refreshing /// for render. This performs tasks such as preparing the cursor, refreshing
/// the cells if necessary, etc. /// the cells if necessary, etc.
pub fn finalizeCells(self: *Grid, term: Terminal) !void { pub fn finalizeCells(self: *Grid, term: *Terminal) !void {
// Add the cursor // Add the cursor
// TODO: only add cursor if it changed // TODO: only add cursor if it changed
if (self.cells.items.len < self.cells.capacity) if (self.cells.items.len < self.cells.capacity)
@ -375,10 +379,11 @@ pub fn finalizeCells(self: *Grid, term: Terminal) !void {
try self.flushAtlas(); try self.flushAtlas();
} }
fn addCursor(self: *Grid, term: Terminal) void { fn addCursor(self: *Grid, term: *Terminal) void {
// Add the cursor // Add the cursor
if (self.cursor_visible and term.screen.viewportIsBottom()) { if (self.cursor_visible and term.screen.viewportIsBottom()) {
const cell = term.screen.getCell( const cell = term.screen.getCell(
.active,
term.screen.cursor.y, term.screen.cursor.y,
term.screen.cursor.x, term.screen.cursor.x,
); );
@ -410,7 +415,7 @@ fn addCursor(self: *Grid, term: Terminal) void {
/// needed. /// needed.
pub fn updateCell( pub fn updateCell(
self: *Grid, self: *Grid,
term: Terminal, term: *Terminal,
cell: terminal.Screen.Cell, cell: terminal.Screen.Cell,
x: usize, x: usize,
y: usize, y: usize,
@ -454,14 +459,14 @@ pub fn updateCell(
const res: BgFg = if (!cell.attrs.inverse) .{ const res: BgFg = if (!cell.attrs.inverse) .{
// In normal mode, background and fg match the cell. We // In normal mode, background and fg match the cell. We
// un-optionalize the fg by defaulting to our fg color. // un-optionalize the fg by defaulting to our fg color.
.bg = cell.bg, .bg = if (cell.attrs.has_bg) cell.bg else null,
.fg = cell.fg orelse self.foreground, .fg = if (cell.attrs.has_fg) cell.fg else self.foreground,
} else .{ } else .{
// In inverted mode, the background MUST be set to something // In inverted mode, the background MUST be set to something
// (is never null) so it is either the fg or default fg. The // (is never null) so it is either the fg or default fg. The
// fg is either the bg or default background. // fg is either the bg or default background.
.bg = cell.fg orelse self.foreground, .bg = if (cell.attrs.has_fg) cell.fg else self.foreground,
.fg = cell.bg orelse self.background, .fg = if (cell.attrs.has_bg) cell.bg else self.background,
}; };
break :colors res; break :colors res;
}; };

View File

@ -529,7 +529,8 @@ fn charCallback(window: glfw.Window, codepoint: u21) void {
// We want to scroll to the bottom // We want to scroll to the bottom
// TODO: detect if we're at the bottom to avoid the render call here. // TODO: detect if we're at the bottom to avoid the render call here.
win.terminal.scrollViewport(.{ .bottom = {} }); win.terminal.scrollViewport(.{ .bottom = {} }) catch |err|
log.err("error scrolling viewport err={}", .{err});
win.render_timer.schedule() catch |err| win.render_timer.schedule() catch |err|
log.err("error scheduling render in charCallback err={}", .{err}); log.err("error scheduling render in charCallback err={}", .{err});
@ -785,7 +786,8 @@ fn scrollCallback(window: glfw.Window, xoff: f64, yoff: f64) void {
const sign: isize = if (yoff > 0) -1 else 1; const sign: isize = if (yoff > 0) -1 else 1;
const delta: isize = sign * @maximum(@divFloor(win.grid.size.rows, 15), 1); const delta: isize = sign * @maximum(@divFloor(win.grid.size.rows, 15), 1);
log.info("scroll: delta={}", .{delta}); log.info("scroll: delta={}", .{delta});
win.terminal.scrollViewport(.{ .delta = delta }); win.terminal.scrollViewport(.{ .delta = delta }) catch |err|
log.err("error scrolling viewport err={}", .{err});
// Schedule render since scrolling usually does something. // Schedule render since scrolling usually does something.
// TODO(perf): we can only schedule render if we know scrolling // TODO(perf): we can only schedule render if we know scrolling
@ -1338,11 +1340,11 @@ fn renderTimerCallback(t: *libuv.Timer) void {
gl.clear(gl.c.GL_COLOR_BUFFER_BIT); gl.clear(gl.c.GL_COLOR_BUFFER_BIT);
// For now, rebuild all cells // For now, rebuild all cells
win.grid.rebuildCells(win.terminal) catch |err| win.grid.rebuildCells(&win.terminal) catch |err|
log.err("error calling rebuildCells in render timer err={}", .{err}); log.err("error calling rebuildCells in render timer err={}", .{err});
// Finalize the cells prior to render // Finalize the cells prior to render
win.grid.finalizeCells(win.terminal) catch |err| win.grid.finalizeCells(&win.terminal) catch |err|
log.err("error calling updateCells in render timer err={}", .{err}); log.err("error calling updateCells in render timer err={}", .{err});
// Render the grid // Render the grid
@ -1382,7 +1384,7 @@ pub fn horizontalTab(self: *Window) !void {
} }
pub fn linefeed(self: *Window) !void { pub fn linefeed(self: *Window) !void {
self.terminal.linefeed(); try self.terminal.linefeed();
} }
pub fn carriageReturn(self: *Window) !void { pub fn carriageReturn(self: *Window) !void {
@ -1426,7 +1428,7 @@ pub fn setCursorPos(self: *Window, row: u16, col: u16) !void {
pub fn eraseDisplay(self: *Window, mode: terminal.EraseDisplay) !void { pub fn eraseDisplay(self: *Window, mode: terminal.EraseDisplay) !void {
if (mode == .complete) { if (mode == .complete) {
// Whenever we erase the full display, scroll to bottom. // Whenever we erase the full display, scroll to bottom.
self.terminal.scrollViewport(.{ .bottom = {} }); try self.terminal.scrollViewport(.{ .bottom = {} });
try self.render_timer.schedule(); try self.render_timer.schedule();
} }
@ -1462,12 +1464,12 @@ pub fn reverseIndex(self: *Window) !void {
} }
pub fn index(self: *Window) !void { pub fn index(self: *Window) !void {
self.terminal.index(); try self.terminal.index();
} }
pub fn nextLine(self: *Window) !void { pub fn nextLine(self: *Window) !void {
self.terminal.carriageReturn(); self.terminal.carriageReturn();
self.terminal.index(); try self.terminal.index();
} }
pub fn setTopAndBottomMargin(self: *Window, top: u16, bot: u16) !void { pub fn setTopAndBottomMargin(self: *Window, top: u16, bot: u16) !void {

View File

@ -74,7 +74,7 @@ pub const RunIterator = struct {
i: usize = 0, i: usize = 0,
pub fn next(self: *RunIterator, alloc: Allocator) !?TextRun { pub fn next(self: *RunIterator, alloc: Allocator) !?TextRun {
if (self.i >= self.row.len) return null; if (self.i >= self.row.lenCells()) return null;
// Track the font for our curent run // Track the font for our curent run
var current_font: Group.FontIndex = .{}; var current_font: Group.FontIndex = .{};
@ -85,8 +85,8 @@ pub const RunIterator = struct {
// Go through cell by cell and accumulate while we build our run. // Go through cell by cell and accumulate while we build our run.
var j: usize = self.i; var j: usize = self.i;
while (j < self.row.len) : (j += 1) { while (j < self.row.lenCells()) : (j += 1) {
const cell = self.row[j]; const cell = self.row.getCell(j);
// Ignore tailing wide spacers, this will get fixed up by the shaper // Ignore tailing wide spacers, this will get fixed up by the shaper
if (cell.empty() or cell.attrs.wide_spacer_tail) continue; if (cell.empty() or cell.attrs.wide_spacer_tail) continue;
@ -129,8 +129,8 @@ test "run iterator" {
{ {
// Make a screen with some data // Make a screen with some data
var screen = try terminal.Screen.init(alloc, 3, 5, 0); var screen = try terminal.Screen.init(alloc, 3, 5, 0);
defer screen.deinit(alloc); defer screen.deinit();
screen.testWriteString("ABCD"); try screen.testWriteString("ABCD");
// Get our run iterator // Get our run iterator
var shaper = testdata.shaper; var shaper = testdata.shaper;
@ -143,8 +143,8 @@ test "run iterator" {
{ {
// Make a screen with some data // Make a screen with some data
var screen = try terminal.Screen.init(alloc, 3, 5, 0); var screen = try terminal.Screen.init(alloc, 3, 5, 0);
defer screen.deinit(alloc); defer screen.deinit();
screen.testWriteString("A😃D"); try screen.testWriteString("A😃D");
// Get our run iterator // Get our run iterator
var shaper = testdata.shaper; var shaper = testdata.shaper;
@ -175,8 +175,8 @@ test "shape" {
// Make a screen with some data // Make a screen with some data
var screen = try terminal.Screen.init(alloc, 3, 10, 0); var screen = try terminal.Screen.init(alloc, 3, 10, 0);
defer screen.deinit(alloc); defer screen.deinit();
screen.testWriteString(buf[0..buf_idx]); try screen.testWriteString(buf[0..buf_idx]);
// Get our run iterator // Get our run iterator
var shaper = testdata.shaper; var shaper = testdata.shaper;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2248
src/terminal/ScreenOld.zig Normal file

File diff suppressed because it is too large Load Diff

View File

@ -149,8 +149,8 @@ pub fn init(alloc: Allocator, cols: usize, rows: usize) !Terminal {
pub fn deinit(self: *Terminal, alloc: Allocator) void { pub fn deinit(self: *Terminal, alloc: Allocator) void {
self.tabstops.deinit(alloc); self.tabstops.deinit(alloc);
self.screen.deinit(alloc); self.screen.deinit();
self.secondary_screen.deinit(alloc); self.secondary_screen.deinit();
self.* = undefined; self.* = undefined;
} }
@ -286,11 +286,11 @@ pub fn resize(self: *Terminal, alloc: Allocator, cols_req: usize, rows: usize) !
// If we're making the screen smaller, dealloc the unused items. // If we're making the screen smaller, dealloc the unused items.
if (self.active_screen == .primary) { if (self.active_screen == .primary) {
try self.screen.resize(alloc, rows, cols); try self.screen.resize(rows, cols);
try self.secondary_screen.resizeWithoutReflow(alloc, rows, cols); try self.secondary_screen.resizeWithoutReflow(rows, cols);
} else { } else {
try self.screen.resizeWithoutReflow(alloc, rows, cols); try self.screen.resizeWithoutReflow(rows, cols);
try self.secondary_screen.resize(alloc, rows, cols); try self.secondary_screen.resize(rows, cols);
} }
// Set our size // Set our size
@ -308,7 +308,7 @@ pub fn resize(self: *Terminal, alloc: Allocator, cols_req: usize, rows: usize) !
/// encoded as "\n". This omits any formatting such as fg/bg. /// encoded as "\n". This omits any formatting such as fg/bg.
/// ///
/// The caller must free the string. /// The caller must free the string.
pub fn plainString(self: Terminal, alloc: Allocator) ![]const u8 { pub fn plainString(self: *Terminal, alloc: Allocator) ![]const u8 {
return try self.screen.testString(alloc, .viewport); return try self.screen.testString(alloc, .viewport);
} }
@ -336,8 +336,8 @@ pub fn setAttribute(self: *Terminal, attr: sgr.Attribute) !void {
switch (attr) { switch (attr) {
.unset => { .unset => {
self.screen.cursor.pen.fg = null; self.screen.cursor.pen.attrs.has_fg = false;
self.screen.cursor.pen.bg = null; self.screen.cursor.pen.attrs.has_bg = false;
self.screen.cursor.pen.attrs = .{}; self.screen.cursor.pen.attrs = .{};
}, },
@ -362,6 +362,7 @@ pub fn setAttribute(self: *Terminal, attr: sgr.Attribute) !void {
}, },
.direct_color_fg => |rgb| { .direct_color_fg => |rgb| {
self.screen.cursor.pen.attrs.has_fg = true;
self.screen.cursor.pen.fg = .{ self.screen.cursor.pen.fg = .{
.r = rgb.r, .r = rgb.r,
.g = rgb.g, .g = rgb.g,
@ -370,6 +371,7 @@ pub fn setAttribute(self: *Terminal, attr: sgr.Attribute) !void {
}, },
.direct_color_bg => |rgb| { .direct_color_bg => |rgb| {
self.screen.cursor.pen.attrs.has_bg = true;
self.screen.cursor.pen.bg = .{ self.screen.cursor.pen.bg = .{
.r = rgb.r, .r = rgb.r,
.g = rgb.g, .g = rgb.g,
@ -377,21 +379,39 @@ pub fn setAttribute(self: *Terminal, attr: sgr.Attribute) !void {
}; };
}, },
.@"8_fg" => |n| self.screen.cursor.pen.fg = color.default[@enumToInt(n)], .@"8_fg" => |n| {
self.screen.cursor.pen.attrs.has_fg = true;
self.screen.cursor.pen.fg = color.default[@enumToInt(n)];
},
.@"8_bg" => |n| self.screen.cursor.pen.bg = color.default[@enumToInt(n)], .@"8_bg" => |n| {
self.screen.cursor.pen.attrs.has_bg = true;
self.screen.cursor.pen.bg = color.default[@enumToInt(n)];
},
.reset_fg => self.screen.cursor.pen.fg = null, .reset_fg => self.screen.cursor.pen.attrs.has_fg = false,
.reset_bg => self.screen.cursor.pen.bg = null, .reset_bg => self.screen.cursor.pen.attrs.has_bg = false,
.@"8_bright_fg" => |n| self.screen.cursor.pen.fg = color.default[@enumToInt(n)], .@"8_bright_fg" => |n| {
self.screen.cursor.pen.attrs.has_fg = true;
self.screen.cursor.pen.fg = color.default[@enumToInt(n)];
},
.@"8_bright_bg" => |n| self.screen.cursor.pen.bg = color.default[@enumToInt(n)], .@"8_bright_bg" => |n| {
self.screen.cursor.pen.attrs.has_bg = true;
self.screen.cursor.pen.bg = color.default[@enumToInt(n)];
},
.@"256_fg" => |idx| self.screen.cursor.pen.fg = color.default[idx], .@"256_fg" => |idx| {
self.screen.cursor.pen.attrs.has_fg = true;
self.screen.cursor.pen.fg = color.default[idx];
},
.@"256_bg" => |idx| self.screen.cursor.pen.bg = color.default[idx], .@"256_bg" => |idx| {
self.screen.cursor.pen.attrs.has_bg = true;
self.screen.cursor.pen.bg = color.default[idx];
},
else => return error.InvalidAttribute, else => return error.InvalidAttribute,
} }
@ -440,7 +460,7 @@ pub fn print(self: *Terminal, c: u21) !void {
// If we're soft-wrapping, then handle that first. // If we're soft-wrapping, then handle that first.
if (self.screen.cursor.pending_wrap and self.modes.autowrap) if (self.screen.cursor.pending_wrap and self.modes.autowrap)
_ = self.printWrap(); try self.printWrap();
switch (width) { switch (width) {
// Single cell is very easy: just write in the cell // Single cell is very easy: just write in the cell
@ -457,7 +477,7 @@ pub fn print(self: *Terminal, c: u21) !void {
if (self.screen.cursor.x == self.cols - 1) { if (self.screen.cursor.x == self.cols - 1) {
const spacer_head = self.printCell(' '); const spacer_head = self.printCell(' ');
spacer_head.attrs.wide_spacer_head = true; spacer_head.attrs.wide_spacer_head = true;
_ = self.printWrap(); try self.printWrap();
} }
const wide_cell = self.printCell(c); const wide_cell = self.printCell(c);
@ -503,10 +523,8 @@ fn printCell(self: *Terminal, unmapped_c: u21) *Screen.Cell {
break :c @intCast(u21, table[@intCast(u8, unmapped_c)]); break :c @intCast(u21, table[@intCast(u8, unmapped_c)]);
}; };
const cell = self.screen.getCell( const row = self.screen.getRow(.{ .active = self.screen.cursor.y });
self.screen.cursor.y, const cell = row.getCellPtr(self.screen.cursor.x);
self.screen.cursor.x,
);
// If this cell is wide char then we need to clear it. // If this cell is wide char then we need to clear it.
// We ignore wide spacer HEADS because we can just write // We ignore wide spacer HEADS because we can just write
@ -515,7 +533,7 @@ fn printCell(self: *Terminal, unmapped_c: u21) *Screen.Cell {
const x = self.screen.cursor.x + 1; const x = self.screen.cursor.x + 1;
assert(x < self.cols); assert(x < self.cols);
const spacer_cell = self.screen.getCell(self.screen.cursor.y, x); const spacer_cell = row.getCellPtr(x);
spacer_cell.attrs.wide_spacer_tail = false; spacer_cell.attrs.wide_spacer_tail = false;
if (self.screen.cursor.x <= 1) { if (self.screen.cursor.x <= 1) {
@ -525,7 +543,7 @@ fn printCell(self: *Terminal, unmapped_c: u21) *Screen.Cell {
assert(self.screen.cursor.x > 0); assert(self.screen.cursor.x > 0);
const x = self.screen.cursor.x - 1; const x = self.screen.cursor.x - 1;
const wide_cell = self.screen.getCell(self.screen.cursor.y, x); const wide_cell = row.getCellPtr(x);
wide_cell.attrs.wide = false; wide_cell.attrs.wide = false;
if (self.screen.cursor.x <= 1) { if (self.screen.cursor.x <= 1) {
@ -539,26 +557,20 @@ fn printCell(self: *Terminal, unmapped_c: u21) *Screen.Cell {
return cell; return cell;
} }
fn printWrap(self: *Terminal) *Screen.Cell { fn printWrap(self: *Terminal) !void {
// Mark that the cell is wrapped, which guarantees that there is const row = self.screen.getRow(.{ .active = self.screen.cursor.y });
// at least one cell after it in the next row. row.setWrapped(true);
const cell = self.screen.getCell(
self.screen.cursor.y,
self.screen.cursor.x,
);
cell.attrs.wrap = true;
// Move to the next line // Move to the next line
self.index(); try self.index();
self.screen.cursor.x = 0; self.screen.cursor.x = 0;
return cell;
} }
fn clearWideSpacerHead(self: *Terminal) void { fn clearWideSpacerHead(self: *Terminal) void {
// TODO: handle deleting wide char on row 0 of active // TODO: handle deleting wide char on row 0 of active
assert(self.screen.cursor.y >= 1); assert(self.screen.cursor.y >= 1);
const cell = self.screen.getCell( const cell = self.screen.getCellPtr(
.active,
self.screen.cursor.y - 1, self.screen.cursor.y - 1,
self.cols - 1, self.cols - 1,
); );
@ -578,14 +590,11 @@ pub fn decaln(self: *Terminal) void {
// Fill with Es, does not move cursor. We reset fg/bg so we can just // Fill with Es, does not move cursor. We reset fg/bg so we can just
// optimize here by doing row copies. // optimize here by doing row copies.
const filled = self.screen.getRow(.{ .active = 0 }); const filled = self.screen.getRow(.{ .active = 0 });
var col: usize = 0; filled.fill(.{ .char = 'E' });
while (col < self.cols) : (col += 1) {
filled[col] = .{ .char = 'E' };
}
var row: usize = 1; var row: usize = 1;
while (row < self.rows) : (row += 1) { while (row < self.rows) : (row += 1) {
std.mem.copy(Screen.Cell, self.screen.getRow(.{ .active = row }), filled); self.screen.getRow(.{ .active = row }).copyRow(filled);
} }
} }
@ -601,7 +610,7 @@ pub fn decaln(self: *Terminal) void {
/// move the cursor one line down /// move the cursor one line down
/// ///
/// This unsets the pending wrap state without wrapping. /// This unsets the pending wrap state without wrapping.
pub fn index(self: *Terminal) void { pub fn index(self: *Terminal) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
@ -625,7 +634,7 @@ pub fn index(self: *Terminal) void {
if (self.scrolling_region.top == 0 and if (self.scrolling_region.top == 0 and
self.scrolling_region.bottom == self.rows - 1) self.scrolling_region.bottom == self.rows - 1)
{ {
self.screen.scroll(.{ .delta = 1 }); try self.screen.scroll(.{ .delta = 1 });
} else { } else {
// TODO: test // TODO: test
self.scrollUp(1); self.scrollUp(1);
@ -736,9 +745,8 @@ pub fn eraseDisplay(
switch (mode) { switch (mode) {
.complete => { .complete => {
const region = self.screen.region(.active); var it = self.screen.rowIterator(.active);
std.mem.set(Screen.Cell, region[0], self.screen.cursor.pen); while (it.next()) |row| row.clear(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;
@ -748,7 +756,7 @@ pub fn eraseDisplay(
// All lines to the right (including the cursor) // All lines to the right (including the cursor)
var x: usize = self.screen.cursor.x; var x: usize = self.screen.cursor.x;
while (x < self.cols) : (x += 1) { while (x < self.cols) : (x += 1) {
const cell = self.getOrPutCell(x, self.screen.cursor.y); const cell = self.screen.getCellPtr(.active, self.screen.cursor.y, x);
cell.* = self.screen.cursor.pen; cell.* = self.screen.cursor.pen;
cell.char = 0; cell.char = 0;
} }
@ -758,7 +766,7 @@ pub fn eraseDisplay(
while (y < self.rows) : (y += 1) { while (y < self.rows) : (y += 1) {
x = 0; x = 0;
while (x < self.cols) : (x += 1) { while (x < self.cols) : (x += 1) {
const cell = self.getOrPutCell(x, y); const cell = self.screen.getCellPtr(.active, y, x);
cell.* = self.screen.cursor.pen; cell.* = self.screen.cursor.pen;
cell.char = 0; cell.char = 0;
} }
@ -772,7 +780,7 @@ pub fn eraseDisplay(
// Erase to the left (including the cursor) // Erase to the left (including the cursor)
var x: usize = 0; var x: usize = 0;
while (x <= self.screen.cursor.x) : (x += 1) { while (x <= self.screen.cursor.x) : (x += 1) {
const cell = self.getOrPutCell(x, self.screen.cursor.y); const cell = self.screen.getCellPtr(.active, self.screen.cursor.y, x);
cell.* = self.screen.cursor.pen; cell.* = self.screen.cursor.pen;
cell.char = 0; cell.char = 0;
} }
@ -782,7 +790,7 @@ pub fn eraseDisplay(
while (y < self.screen.cursor.y) : (y += 1) { while (y < self.screen.cursor.y) : (y += 1) {
x = 0; x = 0;
while (x < self.cols) : (x += 1) { while (x < self.cols) : (x += 1) {
const cell = self.getOrPutCell(x, y); const cell = self.screen.getCellPtr(.active, y, x);
cell.* = self.screen.cursor.pen; cell.* = self.screen.cursor.pen;
cell.char = 0; cell.char = 0;
} }
@ -793,14 +801,9 @@ pub fn eraseDisplay(
}, },
.scrollback => { .scrollback => {
const region = self.screen.region(.history); var it = self.screen.rowIterator(.history);
std.mem.set(Screen.Cell, region[0], self.screen.cursor.pen); while (it.next()) |row| row.clear(self.screen.cursor.pen);
std.mem.set(Screen.Cell, region[1], self.screen.cursor.pen); @panic("MOVE TO SCREEN SO CIRC BUF IS CORRECT");
// TODO: move this logic to the Screen implementation
self.screen.top = self.screen.visible_offset;
self.screen.bottom = self.screen.bottom - self.screen.visible_offset;
self.screen.visible_offset = 0;
}, },
} }
} }
@ -817,12 +820,12 @@ pub fn eraseLine(
switch (mode) { switch (mode) {
.right => { .right => {
const row = self.screen.getRow(.{ .active = self.screen.cursor.y }); const row = self.screen.getRow(.{ .active = self.screen.cursor.y });
std.mem.set(Screen.Cell, row[self.screen.cursor.x..], self.screen.cursor.pen); row.fillSlice(self.screen.cursor.pen, self.screen.cursor.x, self.cols);
}, },
.left => { .left => {
const row = self.screen.getRow(.{ .active = self.screen.cursor.y }); const row = self.screen.getRow(.{ .active = self.screen.cursor.y });
std.mem.set(Screen.Cell, row[0 .. self.screen.cursor.x + 1], self.screen.cursor.pen); row.fillSlice(self.screen.cursor.pen, 0, self.screen.cursor.x + 1);
// Unsets pending wrap state // Unsets pending wrap state
self.screen.cursor.pending_wrap = false; self.screen.cursor.pending_wrap = false;
@ -830,7 +833,7 @@ pub fn eraseLine(
.complete => { .complete => {
const row = self.screen.getRow(.{ .active = self.screen.cursor.y }); const row = self.screen.getRow(.{ .active = self.screen.cursor.y });
std.mem.set(Screen.Cell, row, self.screen.cursor.pen); row.fill(self.screen.cursor.pen);
}, },
else => { else => {
@ -864,8 +867,9 @@ pub fn deleteChars(self: *Terminal, count: usize) !void {
var i: usize = self.screen.cursor.x; var i: usize = self.screen.cursor.x;
while (i < end) : (i += 1) { while (i < end) : (i += 1) {
const j = i + count; const j = i + count;
line[i] = line[j]; const j_cell = line.getCellPtr(j);
line[j].char = 0; line.getCellPtr(i).* = j_cell.*;
j_cell.char = 0;
} }
} }
@ -879,12 +883,10 @@ pub fn eraseChars(self: *Terminal, count: usize) void {
const end = @minimum(self.cols, self.screen.cursor.x + count); const end = @minimum(self.cols, self.screen.cursor.x + count);
// Shift // Shift
var x: usize = self.screen.cursor.x; var pen = self.screen.cursor.pen;
while (x < end) : (x += 1) { pen.char = 0;
const cell = self.getOrPutCell(x, self.screen.cursor.y); const row = self.screen.getRow(.{ .active = self.screen.cursor.y });
cell.* = self.screen.cursor.pen; row.fillSlice(pen, self.screen.cursor.x, end);
cell.char = 0;
}
} }
/// Move the cursor to the left amount cells. If amount is 0, adjust it to 1. /// Move the cursor to the left amount cells. If amount is 0, adjust it to 1.
@ -994,11 +996,11 @@ pub fn carriageReturn(self: *Terminal) void {
} }
/// Linefeed moves the cursor to the next line. /// Linefeed moves the cursor to the next line.
pub fn linefeed(self: *Terminal) void { pub fn linefeed(self: *Terminal) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
self.index(); try self.index();
} }
/// Inserts spaces at current cursor position moving existing cell contents /// Inserts spaces at current cursor position moving existing cell contents
@ -1031,7 +1033,7 @@ pub fn insertBlanks(self: *Terminal, count: usize) void {
// This is the number of spaces we have left to shift existing data. // This is the number of spaces we have left to shift existing data.
// If count is bigger than the available space left after the cursor, // If count is bigger than the available space left after the cursor,
// we may have no space at all for copying. // we may have no space at all for copying.
const copyable = row.len - pivot; const copyable = self.screen.cols - pivot;
if (copyable > 0) { if (copyable > 0) {
// This is the index of the final copyable value that we need to copy. // This is the index of the final copyable value that we need to copy.
const copyable_end = start + copyable - 1; const copyable_end = start + copyable - 1;
@ -1040,16 +1042,16 @@ pub fn insertBlanks(self: *Terminal, count: usize) void {
// allocated new space, otherwise we'll copy duplicates. // allocated new space, otherwise we'll copy duplicates.
var i: usize = 0; var i: usize = 0;
while (i < copyable) : (i += 1) { while (i < copyable) : (i += 1) {
const to = row.len - 1 - i; const to = self.screen.cols - 1 - i;
const from = copyable_end - i; const from = copyable_end - i;
row[to] = row[from]; row.getCellPtr(to).* = row.getCell(from);
} }
} }
// Insert zero // Insert zero
var pen = self.screen.cursor.pen; var pen = self.screen.cursor.pen;
pen.char = ' '; // NOTE: this should be 0 but we need space for tests pen.char = ' '; // NOTE: this should be 0 but we need space for tests
std.mem.set(Screen.Cell, row[start..pivot], pen); row.fillSlice(pen, start, pivot);
} }
/// Insert amount lines at the current cursor row. The contents of the line /// Insert amount lines at the current cursor row. The contents of the line
@ -1091,18 +1093,14 @@ pub fn insertLines(self: *Terminal, count: usize) void {
// Ensure we have the lines populated to the end // Ensure we have the lines populated to the end
while (y > top) : (y -= 1) { while (y > top) : (y -= 1) {
self.screen.copyRow(y, y - adjusted_count); self.screen.copyRow(.{ .active = y }, .{ .active = y - adjusted_count });
} }
// Insert count blank lines // Insert count blank lines
y = self.screen.cursor.y; y = self.screen.cursor.y;
while (y < self.screen.cursor.y + adjusted_count) : (y += 1) { while (y < self.screen.cursor.y + adjusted_count) : (y += 1) {
var x: usize = 0; const row = self.screen.getRow(.{ .active = y });
while (x < self.cols) : (x += 1) { row.clear(self.screen.cursor.pen);
const cell = self.getOrPutCell(x, y);
cell.* = self.screen.cursor.pen;
cell.char = 0;
}
} }
} }
@ -1140,12 +1138,12 @@ pub fn deleteLines(self: *Terminal, count: usize) void {
// Scroll up the count amount. // Scroll up the count amount.
var y: usize = self.screen.cursor.y; var y: usize = self.screen.cursor.y;
while (y <= self.scrolling_region.bottom - adjusted_count) : (y += 1) { while (y <= self.scrolling_region.bottom - adjusted_count) : (y += 1) {
self.screen.copyRow(y, y + adjusted_count); self.screen.copyRow(.{ .active = y }, .{ .active = y + adjusted_count });
} }
while (y <= self.scrolling_region.bottom) : (y += 1) { while (y <= self.scrolling_region.bottom) : (y += 1) {
const row = self.screen.getRow(.{ .active = y }); const row = self.screen.getRow(.{ .active = y });
std.mem.set(Screen.Cell, row, self.screen.cursor.pen); row.fill(self.screen.cursor.pen);
} }
} }
@ -1194,11 +1192,11 @@ pub const ScrollViewport = union(enum) {
}; };
/// Scroll the viewport of the terminal grid. /// Scroll the viewport of the terminal grid.
pub fn scrollViewport(self: *Terminal, behavior: ScrollViewport) void { pub fn scrollViewport(self: *Terminal, behavior: ScrollViewport) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
self.screen.scroll(switch (behavior) { try self.screen.scroll(switch (behavior) {
.top => .{ .top = {} }, .top => .{ .top = {} },
.bottom => .{ .bottom = {} }, .bottom => .{ .bottom = {} },
.delta => |delta| .{ .delta_no_grow = delta }, .delta => |delta| .{ .delta_no_grow = delta },
@ -1236,13 +1234,6 @@ pub fn setScrollingRegion(self: *Terminal, top: usize, bottom: usize) void {
self.setCursorPos(1, 1); self.setCursorPos(1, 1);
} }
fn getOrPutCell(self: *Terminal, x: usize, y: usize) *Screen.Cell {
const tracy = trace(@src());
defer tracy.end();
return self.screen.getCell(y, x);
}
test "Terminal: input with no control characters" { test "Terminal: input with no control characters" {
var t = try init(testing.allocator, 80, 80); var t = try init(testing.allocator, 80, 80);
defer t.deinit(testing.allocator); defer t.deinit(testing.allocator);
@ -1282,9 +1273,9 @@ test "Terminal: print writes to bottom if scrolled" {
// Make newlines so we create scrollback // Make newlines so we create scrollback
// 3 pushes hello off the screen // 3 pushes hello off the screen
t.index(); try t.index();
t.index(); try t.index();
t.index(); try t.index();
{ {
var str = try t.plainString(testing.allocator); var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str); defer testing.allocator.free(str);
@ -1292,7 +1283,7 @@ test "Terminal: print writes to bottom if scrolled" {
} }
// Scroll to the top // Scroll to the top
t.scrollViewport(.{ .top = {} }); try t.scrollViewport(.{ .top = {} });
{ {
var str = try t.plainString(testing.allocator); var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str); defer testing.allocator.free(str);
@ -1301,7 +1292,7 @@ test "Terminal: print writes to bottom if scrolled" {
// Type // Type
try t.print('A'); try t.print('A');
t.scrollViewport(.{ .bottom = {} }); try t.scrollViewport(.{ .bottom = {} });
{ {
var str = try t.plainString(testing.allocator); var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str); defer testing.allocator.free(str);
@ -1378,7 +1369,7 @@ test "Terminal: linefeed and carriage return" {
// Basic grid writing // Basic grid writing
for ("hello") |c| try t.print(c); for ("hello") |c| try t.print(c);
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
for ("world") |c| try t.print(c); for ("world") |c| try t.print(c);
try testing.expectEqual(@as(usize, 1), t.screen.cursor.y); try testing.expectEqual(@as(usize, 1), t.screen.cursor.y);
try testing.expectEqual(@as(usize, 5), t.screen.cursor.x); try testing.expectEqual(@as(usize, 5), t.screen.cursor.x);
@ -1396,7 +1387,7 @@ test "Terminal: linefeed unsets pending wrap" {
// Basic grid writing // Basic grid writing
for ("hello") |c| try t.print(c); for ("hello") |c| try t.print(c);
try testing.expect(t.screen.cursor.pending_wrap == true); try testing.expect(t.screen.cursor.pending_wrap == true);
t.linefeed(); try t.linefeed();
try testing.expect(t.screen.cursor.pending_wrap == false); try testing.expect(t.screen.cursor.pending_wrap == false);
} }
@ -1540,13 +1531,13 @@ test "Terminal: deleteLines" {
// Initial value // Initial value
try t.print('A'); try t.print('A');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('B'); try t.print('B');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('C'); try t.print('C');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('D'); try t.print('D');
t.cursorUp(2); t.cursorUp(2);
@ -1554,7 +1545,7 @@ test "Terminal: deleteLines" {
try t.print('E'); try t.print('E');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
// We should be // We should be
try testing.expectEqual(@as(usize, 0), t.screen.cursor.x); try testing.expectEqual(@as(usize, 0), t.screen.cursor.x);
@ -1575,13 +1566,13 @@ test "Terminal: deleteLines with scroll region" {
// Initial value // Initial value
try t.print('A'); try t.print('A');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('B'); try t.print('B');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('C'); try t.print('C');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('D'); try t.print('D');
t.setScrollingRegion(1, 3); t.setScrollingRegion(1, 3);
@ -1590,7 +1581,7 @@ test "Terminal: deleteLines with scroll region" {
try t.print('E'); try t.print('E');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
// We should be // We should be
// try testing.expectEqual(@as(usize, 0), t.screen.cursor.x); // try testing.expectEqual(@as(usize, 0), t.screen.cursor.x);
@ -1611,16 +1602,16 @@ test "Terminal: insertLines" {
// Initial value // Initial value
try t.print('A'); try t.print('A');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('B'); try t.print('B');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('C'); try t.print('C');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('D'); try t.print('D');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('E'); try t.print('E');
// Move to row 2 // Move to row 2
@ -1644,16 +1635,16 @@ test "Terminal: insertLines with scroll region" {
// Initial value // Initial value
try t.print('A'); try t.print('A');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('B'); try t.print('B');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('C'); try t.print('C');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('D'); try t.print('D');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('E'); try t.print('E');
t.setScrollingRegion(1, 2); t.setScrollingRegion(1, 2);
@ -1677,16 +1668,16 @@ test "Terminal: insertLines more than remaining" {
// Initial value // Initial value
try t.print('A'); try t.print('A');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('B'); try t.print('B');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('C'); try t.print('C');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('D'); try t.print('D');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('E'); try t.print('E');
// Move to row 2 // Move to row 2
@ -1710,17 +1701,17 @@ test "Terminal: reverseIndex" {
// Initial value // Initial value
try t.print('A'); try t.print('A');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('B'); try t.print('B');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('C'); try t.print('C');
try t.reverseIndex(); try t.reverseIndex();
try t.print('D'); try t.print('D');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
{ {
var str = try t.plainString(testing.allocator); var str = try t.plainString(testing.allocator);
@ -1736,24 +1727,24 @@ test "Terminal: reverseIndex from the top" {
try t.print('A'); try t.print('A');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('B'); try t.print('B');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
t.setCursorPos(1, 1); t.setCursorPos(1, 1);
try t.reverseIndex(); try t.reverseIndex();
try t.print('D'); try t.print('D');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
t.setCursorPos(1, 1); t.setCursorPos(1, 1);
try t.reverseIndex(); try t.reverseIndex();
try t.print('E'); try t.print('E');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
{ {
var str = try t.plainString(testing.allocator); var str = try t.plainString(testing.allocator);
@ -1767,7 +1758,7 @@ test "Terminal: index" {
var t = try init(alloc, 2, 5); var t = try init(alloc, 2, 5);
defer t.deinit(alloc); defer t.deinit(alloc);
t.index(); try t.index();
try t.print('A'); try t.print('A');
{ {
@ -1784,7 +1775,7 @@ test "Terminal: index from the bottom" {
t.setCursorPos(5, 1); t.setCursorPos(5, 1);
try t.print('A'); try t.print('A');
t.index(); try t.index();
try t.print('B'); try t.print('B');
@ -1802,7 +1793,7 @@ test "Terminal: index outside of scrolling region" {
try testing.expectEqual(@as(usize, 0), t.screen.cursor.y); try testing.expectEqual(@as(usize, 0), t.screen.cursor.y);
t.setScrollingRegion(2, 5); t.setScrollingRegion(2, 5);
t.index(); try t.index();
try testing.expectEqual(@as(usize, 1), t.screen.cursor.y); try testing.expectEqual(@as(usize, 1), t.screen.cursor.y);
} }
@ -1814,7 +1805,7 @@ test "Terminal: index from the bottom outside of scroll region" {
t.setScrollingRegion(1, 2); t.setScrollingRegion(1, 2);
t.setCursorPos(5, 1); t.setCursorPos(5, 1);
try t.print('A'); try t.print('A');
t.index(); try t.index();
try t.print('B'); try t.print('B');
{ {
@ -1832,7 +1823,7 @@ test "Terminal: DECALN" {
// Initial value // Initial value
try t.print('A'); try t.print('A');
t.carriageReturn(); t.carriageReturn();
t.linefeed(); try t.linefeed();
try t.print('B'); try t.print('B');
t.decaln(); t.decaln();

View File

@ -27,9 +27,6 @@ pub const EraseLine = csi.EraseLine;
pub const TabClear = csi.TabClear; pub const TabClear = csi.TabClear;
pub const Attribute = sgr.Attribute; pub const Attribute = sgr.Attribute;
pub const Screen2 = @import("Screen2.zig");
test { test {
@import("std").testing.refAllDecls(@This()); @import("std").testing.refAllDecls(@This());
_ = @import("circ_buf.zig");
} }

View File

@ -16,7 +16,7 @@ pub const Viewport = struct {
// get the full offset from the top. // get the full offset from the top.
return .{ return .{
.x = self.x, .x = self.x,
.y = screen.visible_offset + self.y, .y = screen.viewport + self.y,
}; };
} }
@ -25,7 +25,7 @@ pub const Viewport = struct {
const alloc = testing.allocator; const alloc = testing.allocator;
var s = try Screen.init(alloc, 3, 5, 0); var s = try Screen.init(alloc, 3, 5, 0);
defer s.deinit(alloc); defer s.deinit();
try testing.expectEqual(ScreenPoint{ try testing.expectEqual(ScreenPoint{
.x = 1, .x = 1,
@ -38,24 +38,24 @@ pub const Viewport = struct {
const alloc = testing.allocator; const alloc = testing.allocator;
var s = try Screen.init(alloc, 3, 5, 3); var s = try Screen.init(alloc, 3, 5, 3);
defer s.deinit(alloc); defer s.deinit();
// At the bottom // At the bottom
s.scroll(.{ .delta = 6 }); try s.scroll(.{ .delta = 6 });
try testing.expectEqual(ScreenPoint{ try testing.expectEqual(ScreenPoint{
.x = 0, .x = 0,
.y = 3, .y = 6,
}, (Viewport{ .x = 0, .y = 0 }).toScreen(&s)); }, (Viewport{ .x = 0, .y = 0 }).toScreen(&s));
// Move the viewport a bit up // Move the viewport a bit up
s.scroll(.{ .delta = -1 }); try s.scroll(.{ .delta = -1 });
try testing.expectEqual(ScreenPoint{ try testing.expectEqual(ScreenPoint{
.x = 0, .x = 0,
.y = 2, .y = 5,
}, (Viewport{ .x = 0, .y = 0 }).toScreen(&s)); }, (Viewport{ .x = 0, .y = 0 }).toScreen(&s));
// Move the viewport to top // Move the viewport to top
s.scroll(.{ .top = {} }); try s.scroll(.{ .top = {} });
try testing.expectEqual(ScreenPoint{ try testing.expectEqual(ScreenPoint{
.x = 0, .x = 0,
.y = 0, .y = 0,