diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 2b5d358c3..b3e3b33a7 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -866,16 +866,37 @@ pub fn decaln(self: *Terminal) !void { defer tracy.end(); // Reset margins, also sets cursor to top-left - self.setTopAndBottomMargin(0, 0); + self.scrolling_region = .{ + .top = 0, + .bottom = self.rows - 1, + .left = 0, + .right = self.cols - 1, + }; - // Fill with Es, does not move cursor. We reset fg/bg so we can just - // optimize here by doing row copies. - const filled = self.screen.getRow(.{ .active = 0 }); - filled.fill(.{ .char = 'E' }); + // Origin mode is disabled + self.modes.set(.origin, false); - var row: usize = 1; - while (row < self.rows) : (row += 1) { - try self.screen.getRow(.{ .active = row }).copyRow(filled); + // Move our cursor to the top-left + self.setCursorPos(1, 1); + + // Clear our stylistic attributes + self.screen.cursor.pen = .{ + .bg = self.screen.cursor.pen.bg, + .fg = self.screen.cursor.pen.fg, + .attrs = .{ + .has_bg = self.screen.cursor.pen.attrs.has_bg, + .has_fg = self.screen.cursor.pen.attrs.has_fg, + .protected = self.screen.cursor.pen.attrs.protected, + }, + }; + + // Our pen has the letter E + var pen: Screen.Cell = .{ .char = 'E' }; + + // Fill with Es, does not move cursor. + for (0..self.rows) |y| { + const filled = self.screen.getRow(.{ .active = y }); + filled.fill(pen); } } @@ -3397,6 +3418,50 @@ test "Terminal: DECALN" { } } +test "Terminal: decaln reset margins" { + const alloc = testing.allocator; + var t = try init(alloc, 3, 3); + defer t.deinit(alloc); + + // Initial value + t.modes.set(.origin, true); + t.setTopAndBottomMargin(2, 3); + try t.decaln(); + try t.scrollDown(1); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("\nEEE\nEEE", str); + } +} + +test "Terminal: decaln preserves color" { + const alloc = testing.allocator; + var t = try init(alloc, 3, 3); + defer t.deinit(alloc); + + const pen: Screen.Cell = .{ + .bg = .{ .r = 0xFF, .g = 0x00, .b = 0x00 }, + .attrs = .{ .has_bg = true }, + }; + + // Initial value + t.screen.cursor.pen = pen; + t.modes.set(.origin, true); + t.setTopAndBottomMargin(2, 3); + try t.decaln(); + try t.scrollDown(1); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("\nEEE\nEEE", str); + const cell = t.screen.getCell(.active, 0, 0); + try testing.expectEqual(pen, cell); + } +} + test "Terminal: insertBlanks" { // NOTE: this is not verified with conformance tests, so these // tests might actually be verifying wrong behavior. diff --git a/website/app/vt/decaln/page.mdx b/website/app/vt/decaln/page.mdx new file mode 100644 index 000000000..5b036c7e2 --- /dev/null +++ b/website/app/vt/decaln/page.mdx @@ -0,0 +1,45 @@ +import VTSequence from "@/components/VTSequence"; + +# Screen Alignment Test (DECALN) + + + +Reset margins, move cursor to the top left, and fill the screen with `E`. + +Reset the top, bottom, left, and right margins and unset [origin mode](#TODO). +The cursor is moved to the top-left corner of the screen. + +All stylistic SGR attributes are unset, such as bold, blink, etc. +SGR foreground and background colors are preserved. +The [protected attribute](#TODO) is not unset. + +The entire screen is filled with the character `E`. The letter `E` ignores +the current SGR settings and is written with no styling. + +## Validation + +### DECALN V-1: Simple Usage + +```bash +printf "\033#8" +``` + +``` +|EEEEEEEE| +|EEEEEEEE| +|EEEEEEEE| +``` + +### DECALN V-2: Reset Margins + +```bash +printf "\033[2;3r" # scroll region top/bottom +printf "\033#8" +printf "\033[T" +``` + +``` +|c_______| +|EEEEEEEE| +|EEEEEEEE| +```