From f00c87e805bf5e970162a627e57eccab77537ab9 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 12 Nov 2023 22:05:26 -0800 Subject: [PATCH 1/4] xterm audit: DECCOLM and 132COLS --- src/terminal/Terminal.zig | 116 +++++++++++++++++++++----- website/app/vt/modes/deccolm/page.mdx | 70 ++++++++++++++++ 2 files changed, 165 insertions(+), 21 deletions(-) create mode 100644 website/app/vt/modes/deccolm/page.mdx diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index c5f777006..5b3052099 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -290,40 +290,37 @@ pub fn deccolm(self: *Terminal, alloc: Allocator, mode: DeccolmMode) !void { const tracy = trace(@src()); defer tracy.end(); - // TODO: test - - // We need to support this. This corresponds to xterm's private mode 40 - // bit. If the mode "?40" is set, then "?3" (DECCOLM) is supported. This - // doesn't exactly match VT100 semantics but modern terminals no longer - // blindly accept mode 3 since its so weird in modern practice. - if (!self.modes.get(.enable_mode_3)) return; + // If DEC mode 40 isn't enabled, then this is ignored. We also make + // sure that we don't have deccolm set because we want to fully ignore + // set mode. + if (!self.modes.get(.enable_mode_3)) { + self.modes.set(.@"132_column", false); + return; + } // Enable it self.modes.set(.@"132_column", mode == .@"132_cols"); - // Resize -- we can set cols to 0 because deccolm will force it - try self.resize(alloc, 0, self.rows); + // Resize to the requested size + try self.resize( + alloc, + switch (mode) { + .@"132_cols" => 132, + .@"80_cols" => 80, + }, + self.rows, + ); - // TODO: do not clear screen flag mode + // Erase our display and move our cursor. self.eraseDisplay(alloc, .complete, false); self.setCursorPos(1, 1); - - // TODO: left/right margins } /// Resize the underlying terminal. -pub fn resize(self: *Terminal, alloc: Allocator, cols_req: usize, rows: usize) !void { +pub fn resize(self: *Terminal, alloc: Allocator, cols: usize, rows: usize) !void { const tracy = trace(@src()); defer tracy.end(); - // If we have deccolm supported then we are fixed at either 80 or 132 - // columns depending on if mode 3 is set or not. - // TODO: test - const cols: usize = if (self.modes.get(.enable_mode_3)) - if (self.modes.get(.@"132_column")) 132 else 80 - else - cols_req; - // If our cols/rows didn't change then we're done if (self.cols == cols and self.rows == rows) return; @@ -6777,3 +6774,80 @@ test "Terminal: printRepeat no previous character" { try testing.expectEqualStrings("", str); } } + +test "Terminal: DECCOLM without DEC mode 40" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + t.modes.set(.@"132_column", true); + try t.deccolm(alloc, .@"132_cols"); + try testing.expectEqual(@as(usize, 5), t.cols); + try testing.expectEqual(@as(usize, 5), t.rows); + try testing.expect(!t.modes.get(.@"132_column")); +} + +test "Terminal: DECCOLM unset" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + t.modes.set(.enable_mode_3, true); + try t.deccolm(alloc, .@"80_cols"); + try testing.expectEqual(@as(usize, 80), t.cols); + try testing.expectEqual(@as(usize, 5), t.rows); +} + +test "Terminal: DECCOLM resets pending wrap" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + for ("ABCDE") |c| try t.print(c); + try testing.expect(t.screen.cursor.pending_wrap); + + t.modes.set(.enable_mode_3, true); + try t.deccolm(alloc, .@"80_cols"); + try testing.expectEqual(@as(usize, 80), t.cols); + try testing.expectEqual(@as(usize, 5), t.rows); + try testing.expect(!t.screen.cursor.pending_wrap); +} + +test "Terminal: DECCOLM preserves SGR bg" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + const pen: Screen.Cell = .{ + .bg = .{ .r = 0xFF, .g = 0x00, .b = 0x00 }, + .attrs = .{ .has_bg = true }, + }; + + t.screen.cursor.pen = pen; + t.modes.set(.enable_mode_3, true); + try t.deccolm(alloc, .@"80_cols"); + + { + const cell = t.screen.getCell(.active, 0, 0); + try testing.expectEqual(pen, cell); + } +} + +test "Terminal: DECCOLM resets scroll region" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + t.modes.set(.enable_left_and_right_margin, true); + t.setTopAndBottomMargin(2, 3); + t.setLeftAndRightMargin(3, 5); + + t.modes.set(.enable_mode_3, true); + try t.deccolm(alloc, .@"80_cols"); + + try testing.expect(t.modes.get(.enable_left_and_right_margin)); + try testing.expectEqual(@as(usize, 0), t.scrolling_region.top); + try testing.expectEqual(@as(usize, 4), t.scrolling_region.bottom); + try testing.expectEqual(@as(usize, 0), t.scrolling_region.left); + try testing.expectEqual(@as(usize, 79), t.scrolling_region.right); +} diff --git a/website/app/vt/modes/deccolm/page.mdx b/website/app/vt/modes/deccolm/page.mdx new file mode 100644 index 000000000..a8fce6c99 --- /dev/null +++ b/website/app/vt/modes/deccolm/page.mdx @@ -0,0 +1,70 @@ +import VTMode from "@/components/VTMode"; + +# Select 80 or 132 Columns per Page (DECCOLM) + + + +Sets the screen to 132 columns if set or 80 columns if unset. + +This requires [`132COLS` (DEC mode 40)](/vt/modes/132cols) to be set +to have any effect. If `132COLS` is not set, then setting or unsetting +this mode does nothing. + +When this mode changes, the screen is resized to the given column amount, +performing reflow if necessary. If the GUI window is too narrow or too wide, +it is typically resized to fit the explicit column count or a scrollbar is +used. If the GUI window is manually resized (i.e. with the mouse), the column +width of DECCOLM is not enforced. + +The scroll margins are reset to their default values given the new screen size. +The cursor is moved to the top-left. The screen is erased using +[erase display (ED) with command 2](/vt/ed). + +## Validation + +### DECCOLM V-1: Disabled + +```bash +printf "ABC\n" +printf "\033[?40l" # disable mode 3 +printf "\033[?3h" +printf "X" +``` + +``` +|ABC_____| +|Xc______| +|________| +``` + +The command should be completely ignored. + +### DECCOLM V-2: Unset (80 Column) + +```bash +printf "ABC\n" +printf "\033[?40h" # enable mode 3 +printf "\033[?3l" # unset the mode +printf "X" +``` + +``` +|X_______| +``` + +The screen should be 80 columns wide. + +### DECCOLM V-3: Set (132 Column) + +```bash +printf "ABC\n" +printf "\033[?40h" # enable mode 3 +printf "\033[?3h" +printf "X" +``` + +``` +|X_______| +``` + +The screen should be 132 columns wide. From 8783f6c925183a09c747d62bf123163a531e2ee1 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 12 Nov 2023 22:11:11 -0800 Subject: [PATCH 2/4] xterm audit: slow scroll (DECSCLM) --- src/terminal/modes.zig | 1 + website/app/vt/modes/decsclm/page.mdx | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 website/app/vt/modes/decsclm/page.mdx diff --git a/src/terminal/modes.zig b/src/terminal/modes.zig index 906b6f610..cd2e1f45a 100644 --- a/src/terminal/modes.zig +++ b/src/terminal/modes.zig @@ -185,6 +185,7 @@ const entries: []const ModeEntry = &.{ // DEC .{ .name = "cursor_keys", .value = 1 }, // DECCKM .{ .name = "132_column", .value = 3 }, + .{ .name = "slow_scroll", .value = 4 }, .{ .name = "reverse_colors", .value = 5 }, .{ .name = "origin", .value = 6 }, .{ .name = "wraparound", .value = 7, .default = true }, diff --git a/website/app/vt/modes/decsclm/page.mdx b/website/app/vt/modes/decsclm/page.mdx new file mode 100644 index 000000000..809521607 --- /dev/null +++ b/website/app/vt/modes/decsclm/page.mdx @@ -0,0 +1,12 @@ +import VTMode from "@/components/VTMode"; + +# Slow Scroll (DECSCLM) + + + +Enable slow or smooth scrolling. + +Typically, slow scrolling will scroll line by line when using scroll +functions (arrow keys, scrollbar, etc.). With this disabling, scrolling +jumps by more lines. This is purely up to the terminal to implement how it +sees fit. From df800fb0b32c81ac93548053b42172a4583d5ad3 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 12 Nov 2023 22:18:53 -0800 Subject: [PATCH 3/4] xterm audit: reverse video (DECSCNM) --- website/app/vt/modes/decscnm/page.mdx | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 website/app/vt/modes/decscnm/page.mdx diff --git a/website/app/vt/modes/decscnm/page.mdx b/website/app/vt/modes/decscnm/page.mdx new file mode 100644 index 000000000..f68e69019 --- /dev/null +++ b/website/app/vt/modes/decscnm/page.mdx @@ -0,0 +1,11 @@ +import VTMode from "@/components/VTMode"; + +# Reverse Video (DECSCNM) + + + +Swap the foreground/background colors of cells. + +This swaps the foreground and background color of cells when displayed. +This does not physically alter the cell state or cell contents; only the +rendered state is affected. From 3192b1354686f6e6a76b125577f8587df72a160b Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 12 Nov 2023 22:20:28 -0800 Subject: [PATCH 4/4] terminal: our mode size changed --- src/terminal/modes.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/terminal/modes.zig b/src/terminal/modes.zig index cd2e1f45a..28a4d5a33 100644 --- a/src/terminal/modes.zig +++ b/src/terminal/modes.zig @@ -69,7 +69,7 @@ pub const ModeState = struct { // We have this here so that we explicitly fail when we change the // size of modes. The size of modes is NOT particularly important, // we just want to be mentally aware when it happens. - try std.testing.expectEqual(4, @sizeOf(ModePacked)); + try std.testing.expectEqual(8, @sizeOf(ModePacked)); } };