From 5c8c9d8e3ce5715e3cc58705ac51e2979b88ef25 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 24 Jul 2022 16:06:04 -0700 Subject: [PATCH] support DECCOLM This gets vttest page 1 and page 2 now FULL passing. We now crash on page 3. This is a lingering bug in our grid code though and we need to find it anyways so we'll keep the crash in. --- src/Window.zig | 25 ++++++------- src/terminal/Terminal.zig | 54 +++++++++++++++++++++++++-- src/terminal/stream.zig | 6 +-- test/cases/vttest/1_1.sh.ghostty.png | Bin 10457 -> 10462 bytes test/cases/vttest/1_2.sh.ghostty.png | Bin 10693 -> 10703 bytes 5 files changed, 65 insertions(+), 20 deletions(-) diff --git a/src/Window.zig b/src/Window.zig index b190b75de..8a34a4053 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -809,23 +809,20 @@ pub fn setMode(self: *Window, mode: terminal.Mode, enabled: bool) !void { .bracketed_paste => self.bracketed_paste = true, - .enable_mode_3 => self.terminal.modes.enable_mode_3 = @boolToInt(enabled), - .@"132_column" => mode3: { - // TODO: test this + .enable_mode_3 => { + // Disable deccolm + self.terminal.setDeccolmSupported(enabled); - // Do nothing if "enable mode 3" is not set. - if (self.terminal.modes.enable_mode_3 == 0) break :mode3; - - // Set it - self.terminal.modes.@"132_column" = @boolToInt(enabled); - - // TODO: do not clear screen flag mode - self.terminal.eraseDisplay(.complete); - self.terminal.setCursorPos(1, 1); - - // TODO: left/right margins + // Force resize back to the window size + self.terminal.resize(self.alloc, self.grid.size.columns, self.grid.size.rows) catch |err| + log.err("error updating terminal size: {}", .{err}); }, + .@"132_column" => try self.terminal.deccolm( + self.alloc, + if (enabled) .@"132_cols" else .@"80_cols", + ), + else => if (enabled) log.warn("unimplemented mode: {}", .{mode}), } } diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index d9615ce17..4ce1511fd 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -52,8 +52,8 @@ modes: packed struct { origin: u1 = 0, // 6 autowrap: u1 = 1, // 7 - @"132_column": u1 = 0, // 3, - enable_mode_3: u1 = 0, // 40 + deccolm: u1 = 0, // 3, + deccolm_supported: u1 = 0, // 40 } = .{}, /// Scrolling region is the area of the screen designated where scrolling @@ -142,10 +142,58 @@ pub fn primaryScreen(self: *Terminal, options: AlternateScreenOptions) void { if (options.cursor_save) self.restoreCursor(); } +/// The modes for DECCOLM. +pub const DeccolmMode = enum(u1) { + @"80_cols" = 0, + @"132_cols" = 1, +}; + +/// DECCOLM changes the terminal width between 80 and 132 columns. This +/// function call will do NOTHING unless `setDeccolmSupported` has been +/// called with "true". +/// +/// This breaks the expectation around modern terminals that they resize +/// with the window. This will fix the grid at either 80 or 132 columns. +/// The rows will continue to be variable. +pub fn deccolm(self: *Terminal, alloc: Allocator, mode: DeccolmMode) !void { + // 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.deccolm_supported == 0) return; + + // Enable it + self.modes.deccolm = @enumToInt(mode); + + // Resize -- we can set cols to 0 because deccolm will force it + try self.resize(alloc, 0, self.rows); + + // TODO: do not clear screen flag mode + self.eraseDisplay(.complete); + self.setCursorPos(1, 1); + + // TODO: left/right margins +} + +/// Allows or disallows deccolm. +pub fn setDeccolmSupported(self: *Terminal, v: bool) void { + self.modes.deccolm_supported = @boolToInt(v); +} + /// Resize the underlying terminal. -pub fn resize(self: *Terminal, alloc: Allocator, cols: usize, rows: usize) !void { +pub fn resize(self: *Terminal, alloc: Allocator, cols_req: usize, rows: usize) !void { // TODO: test, wrapping, etc. + // 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.deccolm_supported == 1) + @as(usize, if (self.modes.deccolm == 1) 132 else 80) + else + cols_req; + // Resize our tabstops // TODO: use resize, but it doesn't set new tabstops if (self.cols != cols) { diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index 9c2b8cd55..2b6a357f4 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -48,9 +48,9 @@ pub fn Stream(comptime Handler: type) type { //log.debug("char: {x}", .{c}); const actions = self.parser.next(c); for (actions) |action_opt| { - // if (action_opt) |action| { - // log.info("action: {}", .{action}); - // } + if (action_opt) |action| { + log.info("action: {}", .{action}); + } switch (action_opt orelse continue) { .print => |p| if (@hasDecl(T, "print")) try self.handler.print(p), .execute => |code| try self.execute(code), diff --git a/test/cases/vttest/1_1.sh.ghostty.png b/test/cases/vttest/1_1.sh.ghostty.png index 128fcbfd096f0477ff7c81c777afc2dd50dcd58c..77bd0764e22afa5f882851aaac7f85b211be499c 100644 GIT binary patch delta 235 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 J%Q~loCIB1NnHB&5