xterm audit: DECCOLM and 132COLS

This commit is contained in:
Mitchell Hashimoto
2023-11-12 22:05:26 -08:00
parent 8d0404066f
commit f00c87e805
2 changed files with 165 additions and 21 deletions

View File

@ -290,40 +290,37 @@ pub fn deccolm(self: *Terminal, alloc: Allocator, mode: DeccolmMode) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
// TODO: test // 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
// We need to support this. This corresponds to xterm's private mode 40 // set mode.
// bit. If the mode "?40" is set, then "?3" (DECCOLM) is supported. This if (!self.modes.get(.enable_mode_3)) {
// doesn't exactly match VT100 semantics but modern terminals no longer self.modes.set(.@"132_column", false);
// blindly accept mode 3 since its so weird in modern practice. return;
if (!self.modes.get(.enable_mode_3)) return; }
// Enable it // Enable it
self.modes.set(.@"132_column", mode == .@"132_cols"); self.modes.set(.@"132_column", mode == .@"132_cols");
// Resize -- we can set cols to 0 because deccolm will force it // Resize to the requested size
try self.resize(alloc, 0, self.rows); 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.eraseDisplay(alloc, .complete, false);
self.setCursorPos(1, 1); self.setCursorPos(1, 1);
// TODO: left/right margins
} }
/// Resize the underlying terminal. /// 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()); const tracy = trace(@src());
defer tracy.end(); 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 our cols/rows didn't change then we're done
if (self.cols == cols and self.rows == rows) return; if (self.cols == cols and self.rows == rows) return;
@ -6777,3 +6774,80 @@ test "Terminal: printRepeat no previous character" {
try testing.expectEqualStrings("", str); 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);
}

View File

@ -0,0 +1,70 @@
import VTMode from "@/components/VTMode";
# Select 80 or 132 Columns per Page (DECCOLM)
<VTMode value={3} />
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.