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 128fcbfd0..77bd0764e 100644 Binary files a/test/cases/vttest/1_1.sh.ghostty.png and b/test/cases/vttest/1_1.sh.ghostty.png differ diff --git a/test/cases/vttest/1_2.sh.ghostty.png b/test/cases/vttest/1_2.sh.ghostty.png index 95992194d..78139efac 100644 Binary files a/test/cases/vttest/1_2.sh.ghostty.png and b/test/cases/vttest/1_2.sh.ghostty.png differ