terminal: reset should preserve desired default mode values

Fixes #2857

Some terminal modes always reset, but there are others that should be
conditional based on how the terminal's default state is configured.
Primarily from #2857 is the grapheme clustering mode (mode 2027) which
was always resetting to false but should be conditional based on the
the `grapheme-width-method` configuration.
This commit is contained in:
Mitchell Hashimoto
2024-11-29 14:39:22 -08:00
parent ffb5fff555
commit 853ba9e3c7
4 changed files with 51 additions and 14 deletions

View File

@ -193,6 +193,10 @@ pub const Options = struct {
cols: size.CellCountInt,
rows: size.CellCountInt,
max_scrollback: usize = 10_000,
/// The default mode state. When the terminal gets a reset, it
/// will revert back to this state.
default_modes: modes.ModePacked = .{},
};
/// Initialize a new terminal.
@ -216,6 +220,10 @@ pub fn init(
.right = cols - 1,
},
.pwd = std.ArrayList(u8).init(alloc),
.modes = .{
.values = opts.default_modes,
.default = opts.default_modes,
},
};
}
@ -2680,7 +2688,7 @@ fn resetCommonState(self: *Terminal) void {
self.screen.endHyperlink();
self.screen.charset = .{};
self.modes = .{};
self.modes.reset();
self.flags = .{};
self.tabstops.reset(TABSTOP_INTERVAL);
self.screen.kitty_keyboard = .{};
@ -10555,6 +10563,18 @@ test "Terminal: fullReset clears alt screen kitty keyboard state" {
try testing.expectEqual(0, t.secondary_screen.kitty_keyboard.current().int());
}
test "Terminal: fullReset default modes" {
var t = try init(testing.allocator, .{
.cols = 10,
.rows = 10,
.default_modes = .{ .grapheme_cluster = true },
});
defer t.deinit(testing.allocator);
try testing.expect(t.modes.get(.grapheme_cluster));
t.fullReset();
try testing.expect(t.modes.get(.grapheme_cluster));
}
// https://github.com/mitchellh/ghostty/issues/272
// This is also tested in depth in screen resize tests but I want to keep
// this test around to ensure we don't regress at multiple layers.

View File

@ -47,6 +47,7 @@ pub const CursorStyle = Screen.CursorStyle;
pub const CursorStyleReq = ansi.CursorStyle;
pub const DeviceAttributeReq = ansi.DeviceAttributeReq;
pub const Mode = modes.Mode;
pub const ModePacked = modes.ModePacked;
pub const ModifyKeyFormat = ansi.ModifyKeyFormat;
pub const ProtectedMode = ansi.ProtectedMode;
pub const StatusLineType = ansi.StatusLineType;

View File

@ -21,6 +21,17 @@ pub const ModeState = struct {
/// a real-world issue but we need to be aware of a DoS vector.
saved: ModePacked = .{},
/// The default values for the modes. This is used to reset
/// the modes to their default values during reset.
default: ModePacked = .{},
/// Reset the modes to their default values. This also clears the
/// saved state.
pub fn reset(self: *ModeState) void {
self.values = self.default;
self.saved = .{};
}
/// Set a mode to a value.
pub fn set(self: *ModeState, mode: Mode, value: bool) void {
switch (mode) {

View File

@ -127,6 +127,23 @@ pub const DerivedConfig = struct {
/// This will also start the child process if the termio is configured
/// to run a child process.
pub fn init(self: *Termio, alloc: Allocator, opts: termio.Options) !void {
// The default terminal modes based on our config.
const default_modes: terminal.ModePacked = modes: {
var modes: terminal.ModePacked = .{};
// Setup our initial grapheme cluster support if enabled. We use a
// switch to ensure we get a compiler error if more cases are added.
switch (opts.full_config.@"grapheme-width-method") {
.unicode => modes.grapheme_cluster = true,
.legacy => {},
}
// Set default cursor blink settings
modes.cursor_blinking = opts.config.cursor_blink orelse true;
break :modes modes;
};
// Create our terminal
var term = try terminal.Terminal.init(alloc, opts: {
const grid_size = opts.size.grid();
@ -134,19 +151,13 @@ pub fn init(self: *Termio, alloc: Allocator, opts: termio.Options) !void {
.cols = grid_size.columns,
.rows = grid_size.rows,
.max_scrollback = opts.full_config.@"scrollback-limit",
.default_modes = default_modes,
};
});
errdefer term.deinit(alloc);
term.default_palette = opts.config.palette;
term.color_palette.colors = opts.config.palette;
// Setup our initial grapheme cluster support if enabled. We use a
// switch to ensure we get a compiler error if more cases are added.
switch (opts.full_config.@"grapheme-width-method") {
.unicode => term.modes.set(.grapheme_cluster, true),
.legacy => {},
}
// Set the image size limits
try term.screen.kitty_images.setLimit(
alloc,
@ -159,12 +170,6 @@ pub fn init(self: *Termio, alloc: Allocator, opts: termio.Options) !void {
opts.config.image_storage_limit,
);
// Set default cursor blink settings
term.modes.set(
.cursor_blinking,
opts.config.cursor_blink orelse true,
);
// Set our default cursor style
term.screen.cursor.cursor_style = opts.config.cursor_style;