terminal: move to new modes struct

This commit is contained in:
Mitchell Hashimoto
2023-08-15 11:00:22 -07:00
parent 716c343f07
commit 951aa00c63
7 changed files with 112 additions and 242 deletions

View File

@ -742,7 +742,7 @@ fn clipboardPaste(
log.warn("error scrolling to bottom err={}", .{err}); log.warn("error scrolling to bottom err={}", .{err});
}; };
break :bracketed self.io.terminal.modes.bracketed_paste; break :bracketed self.io.terminal.modes.get(.bracketed_paste);
}; };
if (bracketed) { if (bracketed) {
@ -1024,8 +1024,8 @@ pub fn charCallback(
try self.io.terminal.scrollViewport(.{ .bottom = {} }); try self.io.terminal.scrollViewport(.{ .bottom = {} });
break :critical .{ break :critical .{
.alt_esc_prefix = self.io.terminal.modes.alt_esc_prefix, .alt_esc_prefix = self.io.terminal.modes.get(.alt_esc_prefix),
.modify_other_keys = self.io.terminal.modes.modify_other_keys, .modify_other_keys = self.io.terminal.flags.modify_other_keys_2,
}; };
}; };
@ -1178,9 +1178,9 @@ pub fn keyCallback(
// We'll need to know these values here on. // We'll need to know these values here on.
self.renderer_state.mutex.lock(); self.renderer_state.mutex.lock();
const cursor_key_application = self.io.terminal.modes.cursor_keys; const cursor_key_application = self.io.terminal.modes.get(.cursor_keys);
const keypad_key_application = self.io.terminal.modes.keypad_keys; const keypad_key_application = self.io.terminal.modes.get(.keypad_keys);
const modify_other_keys = self.io.terminal.modes.modify_other_keys; const modify_other_keys = self.io.terminal.flags.modify_other_keys_2;
self.renderer_state.mutex.unlock(); self.renderer_state.mutex.unlock();
// Check if we're processing a function key. // Check if we're processing a function key.
@ -1355,7 +1355,7 @@ pub fn focusCallback(self: *Surface, focused: bool) !void {
// Notify the app about focus in/out if it is requesting it // Notify the app about focus in/out if it is requesting it
{ {
self.renderer_state.mutex.lock(); self.renderer_state.mutex.lock();
const focus_event = self.io.terminal.modes.focus_event; const focus_event = self.io.terminal.modes.get(.focus_event);
self.renderer_state.mutex.unlock(); self.renderer_state.mutex.unlock();
if (focus_event) { if (focus_event) {
@ -1479,7 +1479,7 @@ pub fn scrollCallback(
// If we have an active mouse reporting mode, clear the selection. // If we have an active mouse reporting mode, clear the selection.
// The selection can occur if the user uses the shift mod key to // The selection can occur if the user uses the shift mod key to
// override mouse grabbing from the window. // override mouse grabbing from the window.
if (self.io.terminal.modes.mouse_event != .none) { if (self.io.terminal.flags.mouse_event != .none) {
self.setSelection(null); self.setSelection(null);
} }
@ -1488,8 +1488,8 @@ pub fn scrollCallback(
// (1) alt screen (2) no explicit mouse reporting and (3) alt // (1) alt screen (2) no explicit mouse reporting and (3) alt
// scroll mode enabled. // scroll mode enabled.
if (self.io.terminal.active_screen == .alternate and if (self.io.terminal.active_screen == .alternate and
self.io.terminal.modes.mouse_event == .none and self.io.terminal.flags.mouse_event == .none and
self.io.terminal.modes.mouse_alternate_scroll) self.io.terminal.modes.get(.mouse_alternate_scroll))
{ {
if (y.delta_unsigned > 0) { if (y.delta_unsigned > 0) {
const seq = if (y.delta < 0) "\x1bOA" else "\x1bOB"; const seq = if (y.delta < 0) "\x1bOA" else "\x1bOB";
@ -1550,7 +1550,7 @@ fn mouseReport(
// do we want to not report mouse events at all outside the surface? // do we want to not report mouse events at all outside the surface?
// Depending on the event, we may do nothing at all. // Depending on the event, we may do nothing at all.
switch (self.io.terminal.modes.mouse_event) { switch (self.io.terminal.flags.mouse_event) {
.none => return, .none => return,
// X10 only reports clicks with mouse button 1, 2, 3. We verify // X10 only reports clicks with mouse button 1, 2, 3. We verify
@ -1585,7 +1585,7 @@ fn mouseReport(
if (button == null) { if (button == null) {
// Null button means motion without a button pressed // Null button means motion without a button pressed
acc = 3; acc = 3;
} else if (action == .release and self.io.terminal.modes.mouse_format != .sgr) { } else if (action == .release and self.io.terminal.flags.mouse_format != .sgr) {
// Release is 3. It is NOT 3 in SGR mode because SGR can tell // Release is 3. It is NOT 3 in SGR mode because SGR can tell
// the application what button was released. // the application what button was released.
acc = 3; acc = 3;
@ -1601,7 +1601,7 @@ fn mouseReport(
} }
// X10 doesn't have modifiers // X10 doesn't have modifiers
if (self.io.terminal.modes.mouse_event != .x10) { if (self.io.terminal.flags.mouse_event != .x10) {
if (mods.shift) acc += 4; if (mods.shift) acc += 4;
if (mods.super) acc += 8; if (mods.super) acc += 8;
if (mods.ctrl) acc += 16; if (mods.ctrl) acc += 16;
@ -1613,7 +1613,7 @@ fn mouseReport(
break :code acc; break :code acc;
}; };
switch (self.io.terminal.modes.mouse_format) { switch (self.io.terminal.flags.mouse_format) {
.x10 => { .x10 => {
if (viewport_point.x > 222 or viewport_point.y > 222) { if (viewport_point.x > 222 or viewport_point.y > 222) {
log.info("X10 mouse format can only encode X/Y up to 223", .{}); log.info("X10 mouse format can only encode X/Y up to 223", .{});
@ -1784,7 +1784,7 @@ pub fn mouseButtonCallback(
defer self.renderer_state.mutex.unlock(); defer self.renderer_state.mutex.unlock();
// Report mouse events if enabled // Report mouse events if enabled
if (self.io.terminal.modes.mouse_event != .none) report: { if (self.io.terminal.flags.mouse_event != .none) report: {
// Shift overrides mouse "grabbing" in the window, taken from Kitty. // Shift overrides mouse "grabbing" in the window, taken from Kitty.
if (mods.shift) break :report; if (mods.shift) break :report;
@ -1922,7 +1922,7 @@ pub fn cursorPosCallback(
defer self.renderer_state.mutex.unlock(); defer self.renderer_state.mutex.unlock();
// Do a mouse report // Do a mouse report
if (self.io.terminal.modes.mouse_event != .none) report: { if (self.io.terminal.flags.mouse_event != .none) report: {
// Shift overrides mouse "grabbing" in the window, taken from Kitty. // Shift overrides mouse "grabbing" in the window, taken from Kitty.
if (self.mouse.mods.shift) break :report; if (self.mouse.mods.shift) break :report;
@ -2224,7 +2224,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !void
log.warn("error scrolling to bottom err={}", .{err}); log.warn("error scrolling to bottom err={}", .{err});
}; };
break :normal !self.io.terminal.modes.cursor_keys; break :normal !self.io.terminal.modes.get(.cursor_keys);
}; };
if (normal) { if (normal) {

View File

@ -564,7 +564,7 @@ pub fn render(
self.config.background = bg; self.config.background = bg;
self.config.foreground = fg; self.config.foreground = fg;
} }
if (state.terminal.modes.reverse_colors) { if (state.terminal.modes.get(.reverse_colors)) {
self.config.background = fg; self.config.background = fg;
self.config.foreground = bg; self.config.foreground = bg;
} }

View File

@ -12,6 +12,7 @@ const assert = std.debug.assert;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const ansi = @import("ansi.zig"); const ansi = @import("ansi.zig");
const modes = @import("modes.zig");
const charsets = @import("charsets.zig"); const charsets = @import("charsets.zig");
const csi = @import("csi.zig"); const csi = @import("csi.zig");
const sgr = @import("sgr.zig"); const sgr = @import("sgr.zig");
@ -77,45 +78,27 @@ color_palette: color.Palette = color.default,
/// char CSI (ESC [ <n> b). /// char CSI (ESC [ <n> b).
previous_char: ?u21 = null, previous_char: ?u21 = null,
/// Modes - This isn't exhaustive, since some modes (i.e. cursor origin) /// The modes that this terminal currently has active.
/// are applied to the cursor and others aren't boolean yes/no. modes: modes.ModeState = .{},
modes: packed struct {
const Self = @This();
cursor_keys: bool = false, // 1
insert: bool = false, // 4
reverse_colors: bool = false, // 5,
origin: bool = false, // 6
autowrap: bool = true, // 7
deccolm: bool = false, // 3,
deccolm_supported: bool = false, // 40
keypad_keys: bool = false, // 66
alt_esc_prefix: bool = true, // 1036
focus_event: bool = false, // 1004
mouse_alternate_scroll: bool = true, // 1007
mouse_event: MouseEvents = .none,
mouse_format: MouseFormat = .x10,
bracketed_paste: bool = false, // 2004
// This is set via ESC[4;2m. Any other modify key mode just sets
// this to false.
modify_other_keys: bool = false,
/// These are just a packed set of flags we may set on the terminal.
flags: packed struct {
// This isn't a mode, this is set by OSC 133 using the "A" event. // This isn't a mode, this is set by OSC 133 using the "A" event.
// If this is true, it tells us that the shell supports redrawing // If this is true, it tells us that the shell supports redrawing
// the prompt and that when we resize, if the cursor is at a prompt, // the prompt and that when we resize, if the cursor is at a prompt,
// then we should clear the screen below and allow the shell to redraw. // then we should clear the screen below and allow the shell to redraw.
shell_redraws_prompt: bool = false, shell_redraws_prompt: bool = false,
test { // This is set via ESC[4;2m. Any other modify key mode just sets
// We have this here so that we explicitly fail when we change the // this to false and we act in mode 1 by default.
// size of modes. The size of modes is NOT particularly important, modify_other_keys_2: bool = false,
// we just want to be mentally aware when it happens.
try std.testing.expectEqual(4, @sizeOf(Self)); /// The mouse event mode and format. These are set to the last
} /// set mode in modes. You can't get the right event/format to use
/// based on modes alone because modes don't show you what order
/// this was called so we have to track it separately.
mouse_event: MouseEvents = .none,
mouse_format: MouseFormat = .x10,
} = .{}, } = .{},
/// State required for all charset operations. /// State required for all charset operations.
@ -285,10 +268,10 @@ pub fn deccolm(self: *Terminal, alloc: Allocator, mode: DeccolmMode) !void {
// bit. If the mode "?40" is set, then "?3" (DECCOLM) is supported. This // bit. If the mode "?40" is set, then "?3" (DECCOLM) is supported. This
// doesn't exactly match VT100 semantics but modern terminals no longer // doesn't exactly match VT100 semantics but modern terminals no longer
// blindly accept mode 3 since its so weird in modern practice. // blindly accept mode 3 since its so weird in modern practice.
if (!self.modes.deccolm_supported) return; if (!self.modes.get(.enable_mode_3)) return;
// Enable it // Enable it
self.modes.deccolm = mode == .@"132_cols"; self.modes.set(.@"132_column", mode == .@"132_cols");
// Resize -- we can set cols to 0 because deccolm will force it // Resize -- we can set cols to 0 because deccolm will force it
try self.resize(alloc, 0, self.rows); try self.resize(alloc, 0, self.rows);
@ -300,14 +283,6 @@ pub fn deccolm(self: *Terminal, alloc: Allocator, mode: DeccolmMode) !void {
// TODO: left/right margins // TODO: left/right margins
} }
/// Allows or disallows deccolm.
pub fn setDeccolmSupported(self: *Terminal, v: bool) void {
const tracy = trace(@src());
defer tracy.end();
self.modes.deccolm_supported = v;
}
/// 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_req: usize, rows: usize) !void {
const tracy = trace(@src()); const tracy = trace(@src());
@ -316,8 +291,8 @@ pub fn resize(self: *Terminal, alloc: Allocator, cols_req: usize, rows: usize) !
// If we have deccolm supported then we are fixed at either 80 or 132 // If we have deccolm supported then we are fixed at either 80 or 132
// columns depending on if mode 3 is set or not. // columns depending on if mode 3 is set or not.
// TODO: test // TODO: test
const cols: usize = if (self.modes.deccolm_supported) const cols: usize = if (self.modes.get(.enable_mode_3))
if (self.modes.deccolm) 132 else 80 if (self.modes.get(.@"132_column")) 132 else 80
else else
cols_req; cols_req;
@ -349,13 +324,13 @@ pub fn resize(self: *Terminal, alloc: Allocator, cols_req: usize, rows: usize) !
}; };
} }
/// If modes.shell_redraws_prompt is true and we're on the primary screen, /// If shell_redraws_prompt is true and we're on the primary screen,
/// then this will clear the screen from the cursor down if the cursor is /// then this will clear the screen from the cursor down if the cursor is
/// on a prompt in order to allow the shell to redraw the prompt. /// on a prompt in order to allow the shell to redraw the prompt.
fn clearPromptForResize(self: *Terminal) void { fn clearPromptForResize(self: *Terminal) void {
assert(self.active_screen == .primary); assert(self.active_screen == .primary);
if (!self.modes.shell_redraws_prompt) return; if (!self.flags.shell_redraws_prompt) return;
// We need to find the first y that is a prompt. If we find any line // We need to find the first y that is a prompt. If we find any line
// that is NOT a prompt (or input -- which is part of a prompt) then // that is NOT a prompt (or input -- which is part of a prompt) then
@ -693,13 +668,16 @@ pub fn print(self: *Terminal, c: u21) !void {
self.previous_char = c; self.previous_char = c;
// If we're soft-wrapping, then handle that first. // If we're soft-wrapping, then handle that first.
if (self.screen.cursor.pending_wrap and self.modes.autowrap) if (self.screen.cursor.pending_wrap and self.modes.get(.autowrap))
try self.printWrap(); try self.printWrap();
// If we have insert mode enabled then we need to handle that. We // If we have insert mode enabled then we need to handle that. We
// only do insert mode if we're not at the end of the line. // only do insert mode if we're not at the end of the line.
if (self.modes.insert and self.screen.cursor.x + width < self.cols) if (self.modes.get(.insert) and
self.screen.cursor.x + width < self.cols)
{
self.insertBlanks(width); self.insertBlanks(width);
}
switch (width) { switch (width) {
// Single cell is very easy: just write in the cell // Single cell is very easy: just write in the cell
@ -962,7 +940,7 @@ pub fn setCursorPos(self: *Terminal, row_req: usize, col_req: usize) void {
y_offset: usize = 0, y_offset: usize = 0,
x_max: usize, x_max: usize,
y_max: usize, y_max: usize,
} = if (self.modes.origin) .{ } = if (self.modes.get(.origin)) .{
.x_offset = 0, // TODO: left/right margins .x_offset = 0, // TODO: left/right margins
.y_offset = self.scrolling_region.top, .y_offset = self.scrolling_region.top,
.x_max = self.cols, // TODO: left/right margins .x_max = self.cols, // TODO: left/right margins
@ -994,7 +972,7 @@ pub fn setCursorColAbsolute(self: *Terminal, col_req: usize) void {
// TODO: test // TODO: test
assert(!self.modes.origin); // TODO assert(!self.modes.get(.origin)); // TODO
if (self.status_display != .main) return; // TODO if (self.status_display != .main) return; // TODO
@ -1582,6 +1560,7 @@ pub fn fullReset(self: *Terminal) void {
self.eraseDisplay(.scrollback); self.eraseDisplay(.scrollback);
self.eraseDisplay(.complete); self.eraseDisplay(.complete);
self.modes = .{}; self.modes = .{};
self.flags = .{};
self.tabstops.reset(0); self.tabstops.reset(0);
self.screen.cursor = .{}; self.screen.cursor = .{};
self.screen.saved_cursor = .{}; self.screen.saved_cursor = .{};
@ -1890,7 +1869,7 @@ test "Terminal: setCursorPosition" {
try testing.expect(!t.screen.cursor.pending_wrap); try testing.expect(!t.screen.cursor.pending_wrap);
// Origin mode // Origin mode
t.modes.origin = true; t.modes.set(.origin, true);
// No change without a scroll region // No change without a scroll region
t.setCursorPos(81, 81); t.setCursorPos(81, 81);
@ -2340,7 +2319,7 @@ test "Terminal: insert mode with space" {
for ("hello") |c| try t.print(c); for ("hello") |c| try t.print(c);
t.setCursorPos(1, 2); t.setCursorPos(1, 2);
t.modes.insert = true; t.modes.set(.insert, true);
try t.print('X'); try t.print('X');
{ {
@ -2357,7 +2336,7 @@ test "Terminal: insert mode doesn't wrap pushed characters" {
for ("hello") |c| try t.print(c); for ("hello") |c| try t.print(c);
t.setCursorPos(1, 2); t.setCursorPos(1, 2);
t.modes.insert = true; t.modes.set(.insert, true);
try t.print('X'); try t.print('X');
{ {
@ -2373,7 +2352,7 @@ test "Terminal: insert mode does nothing at the end of the line" {
defer t.deinit(alloc); defer t.deinit(alloc);
for ("hello") |c| try t.print(c); for ("hello") |c| try t.print(c);
t.modes.insert = true; t.modes.set(.insert, true);
try t.print('X'); try t.print('X');
{ {
@ -2390,7 +2369,7 @@ test "Terminal: insert mode with wide characters" {
for ("hello") |c| try t.print(c); for ("hello") |c| try t.print(c);
t.setCursorPos(1, 2); t.setCursorPos(1, 2);
t.modes.insert = true; t.modes.set(.insert, true);
try t.print('😀'); // 0x1F600 try t.print('😀'); // 0x1F600
{ {
@ -2406,7 +2385,7 @@ test "Terminal: insert mode with wide characters at end" {
defer t.deinit(alloc); defer t.deinit(alloc);
for ("well") |c| try t.print(c); for ("well") |c| try t.print(c);
t.modes.insert = true; t.modes.set(.insert, true);
try t.print('😀'); // 0x1F600 try t.print('😀'); // 0x1F600
{ {

View File

@ -42,106 +42,6 @@ pub const RenditionAspect = enum(u16) {
_, _,
}; };
/// Modes that can be set with with Set Mode (SM) (ESC [ h). The enum
/// values correspond to the `?`-prefixed modes, since those are the ones
/// of primary interest. The enum value is the mode value.
pub const Mode = enum(u16) {
/// This control function selects the sequences the arrow keys send.
/// You can use the four arrow keys to move the cursor through the current
/// page or to send special application commands.
///
/// If the DECCKM function is set, then the arrow keys send application
/// sequences to the host.
///
/// If the DECCKM function is reset, then the arrow keys send ANSI cursor
/// sequences to the host.
cursor_keys = 1,
/// Change terminal wide between 80 and 132 column mode. When set
/// (with ?40 set), resizes terminal to 132 columns and keeps it that
/// wide. When unset, resizes to 80 columns.
@"132_column" = 3,
/// Insert mode. This mode writes a character and pushes all existing
/// characters to the right. The existing contents are never wrapped.
insert = 4,
/// Reverses the foreground and background colors of all cells.
reverse_colors = 5,
/// If set, the origin of the coordinate system is relative to the
/// current scroll region. If set the cursor is moved to the top left of
/// the current scroll region.
origin = 6,
/// Enable or disable automatic line wrapping.
autowrap = 7,
/// Click-only (press) mouse reporting.
mouse_event_x10 = 9,
/// Set whether the cursor is visible or not.
cursor_visible = 25,
/// Enables or disables mode ?3. If disabled, the terminal will resize
/// to the size of the window. If enabled, this will take effect when
/// mode ?3 is set or unset.
enable_mode_3 = 40,
/// DECNKM. Sets application keypad mode if enabled.
keypad_keys = 66,
/// "Normal" mouse events: click/release, scroll
mouse_event_normal = 1000,
/// Same as normal mode but also send events for mouse motion
/// while the button is pressed when the cell in the grid changes.
mouse_event_button = 1002,
/// Same as button mode but doesn't require a button to be pressed
/// to track mouse movement.
mouse_event_any = 1003,
/// Send focus in/out events.
focus_event = 1004,
/// Report mouse position in the utf8 format to support larger screens.
mouse_format_utf8 = 1005,
/// Report mouse position in the SGR format.
mouse_format_sgr = 1006,
/// Report mouse scroll events as cursor up/down keys. Any other mouse
/// mode overrides this.
mouse_alternate_scroll = 1007,
/// Report mouse position in the urxvt format.
mouse_format_urxvt = 1015,
/// Report mouse position in the SGR format as pixels, instead of cells.
mouse_format_sgr_pixels = 1016,
/// The alt key sends esc as a prefix before any character. On by default.
alt_esc_prefix = 1036,
/// altSendsEscape xterm (https://invisible-island.net/xterm/manpage/xterm.html)
alt_sends_escape = 1039,
/// Alternate screen mode with save cursor and clear on enter.
alt_screen_save_cursor_clear_enter = 1049,
/// Bracket clipboard paste contents in delimiter sequences.
///
/// When pasting from the (e.g. system) clipboard add "ESC [ 2 0 0 ~"
/// before the clipboard contents and "ESC [ 2 0 1 ~" after the clipboard
/// contents. This allows applications to distinguish clipboard contents
/// from manually typed text.
bracketed_paste = 2004,
// Non-exhaustive so that @intToEnum never fails for unsupported modes.
_,
};
/// The device attribute request type (ESC [ c). /// The device attribute request type (ESC [ c).
pub const DeviceAttributeReq = enum { pub const DeviceAttributeReq = enum {
primary, // Blank primary, // Blank

View File

@ -7,6 +7,7 @@ const csi = @import("csi.zig");
const sgr = @import("sgr.zig"); const sgr = @import("sgr.zig");
pub const point = @import("point.zig"); pub const point = @import("point.zig");
pub const color = @import("color.zig"); pub const color = @import("color.zig");
pub const modes = @import("modes.zig");
pub const parse_table = @import("parse_table.zig"); pub const parse_table = @import("parse_table.zig");
pub const Charset = charsets.Charset; pub const Charset = charsets.Charset;
@ -20,7 +21,7 @@ pub const Stream = stream.Stream;
pub const CursorStyle = ansi.CursorStyle; pub const CursorStyle = ansi.CursorStyle;
pub const DeviceAttributeReq = ansi.DeviceAttributeReq; pub const DeviceAttributeReq = ansi.DeviceAttributeReq;
pub const DeviceStatusReq = ansi.DeviceStatusReq; pub const DeviceStatusReq = ansi.DeviceStatusReq;
pub const Mode = ansi.Mode; pub const Mode = modes.Mode;
pub const ModifyKeyFormat = ansi.ModifyKeyFormat; pub const ModifyKeyFormat = ansi.ModifyKeyFormat;
pub const StatusLineType = ansi.StatusLineType; pub const StatusLineType = ansi.StatusLineType;
pub const StatusDisplay = ansi.StatusDisplay; pub const StatusDisplay = ansi.StatusDisplay;
@ -36,5 +37,4 @@ pub usingnamespace if (builtin.target.isWasm()) struct {
test { test {
@import("std").testing.refAllDecls(@This()); @import("std").testing.refAllDecls(@This());
_ = @import("modes.zig");
} }

View File

@ -4,6 +4,7 @@ const Parser = @import("Parser.zig");
const ansi = @import("ansi.zig"); const ansi = @import("ansi.zig");
const charsets = @import("charsets.zig"); const charsets = @import("charsets.zig");
const csi = @import("csi.zig"); const csi = @import("csi.zig");
const modes = @import("modes.zig");
const osc = @import("osc.zig"); const osc = @import("osc.zig");
const sgr = @import("sgr.zig"); const sgr = @import("sgr.zig");
const trace = @import("tracy").trace; const trace = @import("tracy").trace;
@ -426,14 +427,30 @@ pub fn Stream(comptime Handler: type) type {
// SM - Set Mode // SM - Set Mode
'h' => if (@hasDecl(T, "setMode")) { 'h' => if (@hasDecl(T, "setMode")) {
for (action.params) |mode| for (action.params) |mode| {
try self.handler.setMode(@enumFromInt(mode), true); if (modes.hasSupport(mode)) {
try self.handler.setMode(
@enumFromInt(mode),
true,
);
} else {
log.warn("unimplemented mode: {}", .{mode});
}
}
} else log.warn("unimplemented CSI callback: {}", .{action}), } else log.warn("unimplemented CSI callback: {}", .{action}),
// RM - Reset Mode // RM - Reset Mode
'l' => if (@hasDecl(T, "setMode")) { 'l' => if (@hasDecl(T, "setMode")) {
for (action.params) |mode| for (action.params) |mode| {
try self.handler.setMode(@enumFromInt(mode), false); if (modes.hasSupport(mode)) {
try self.handler.setMode(
@enumFromInt(mode),
false,
);
} else {
log.warn("unimplemented mode: {}", .{mode});
}
}
} else log.warn("unimplemented CSI callback: {}", .{action}), } else log.warn("unimplemented CSI callback: {}", .{action}),
// SGR - Select Graphic Rendition // SGR - Select Graphic Rendition
@ -896,20 +913,19 @@ test "stream: cursor right (CUF)" {
test "stream: set mode (SM) and reset mode (RM)" { test "stream: set mode (SM) and reset mode (RM)" {
const H = struct { const H = struct {
mode: ansi.Mode = @as(ansi.Mode, @enumFromInt(0)), mode: modes.Mode = @as(modes.Mode, @enumFromInt(1)),
pub fn setMode(self: *@This(), mode: modes.Mode, v: bool) !void {
pub fn setMode(self: *@This(), mode: ansi.Mode, v: bool) !void { self.mode = @as(modes.Mode, @enumFromInt(1));
self.mode = @as(ansi.Mode, @enumFromInt(0));
if (v) self.mode = mode; if (v) self.mode = mode;
} }
}; };
var s: Stream(H) = .{ .handler = .{} }; var s: Stream(H) = .{ .handler = .{} };
try s.nextSlice("\x1B[?6h"); try s.nextSlice("\x1B[?6h");
try testing.expectEqual(@as(ansi.Mode, .origin), s.handler.mode); try testing.expectEqual(@as(modes.Mode, .origin), s.handler.mode);
try s.nextSlice("\x1B[?6l"); try s.nextSlice("\x1B[?6l");
try testing.expectEqual(@as(ansi.Mode, @enumFromInt(0)), s.handler.mode); try testing.expectEqual(@as(modes.Mode, @enumFromInt(1)), s.handler.mode);
} }
test "stream: restore mode" { test "stream: restore mode" {

View File

@ -1119,7 +1119,7 @@ const StreamHandler = struct {
} }
pub fn setCursorRow(self: *StreamHandler, row: u16) !void { pub fn setCursorRow(self: *StreamHandler, row: u16) !void {
if (self.terminal.modes.origin) { if (self.terminal.modes.get(.origin)) {
// TODO // TODO
log.err("setCursorRow: implement origin mode", .{}); log.err("setCursorRow: implement origin mode", .{});
unreachable; unreachable;
@ -1184,10 +1184,10 @@ const StreamHandler = struct {
} }
pub fn setModifyKeyFormat(self: *StreamHandler, format: terminal.ModifyKeyFormat) !void { pub fn setModifyKeyFormat(self: *StreamHandler, format: terminal.ModifyKeyFormat) !void {
self.terminal.modes.modify_other_keys = false; self.terminal.flags.modify_other_keys_2 = false;
switch (format) { switch (format) {
.other_keys => |v| switch (v) { .other_keys => |v| switch (v) {
.numeric => self.terminal.modes.modify_other_keys = true, .numeric => self.terminal.flags.modify_other_keys_2 = true,
else => {}, else => {},
}, },
else => {}, else => {},
@ -1199,38 +1199,19 @@ const StreamHandler = struct {
// terminal locks because it is only called from process() which // terminal locks because it is only called from process() which
// grabs the lock. // grabs the lock.
// We first always set the raw mode on our mode state.
self.terminal.modes.set(mode, enabled);
// And then some modes require additional processing.
switch (mode) { switch (mode) {
.cursor_keys => { // Schedule a render since we changed colors
self.terminal.modes.cursor_keys = enabled; .reverse_colors => try self.queueRender(),
},
.keypad_keys => { // Origin resets cursor pos
self.terminal.modes.keypad_keys = enabled; .origin => self.terminal.setCursorPos(1, 1),
},
.insert => { // We need to update our renderer state for this mode
self.terminal.modes.insert = enabled; .cursor_visible => self.ev.renderer_state.cursor.visible = enabled,
},
.reverse_colors => {
self.terminal.modes.reverse_colors = enabled;
// Schedule a render since we changed colors
try self.queueRender();
},
.origin => {
self.terminal.modes.origin = enabled;
self.terminal.setCursorPos(1, 1);
},
.autowrap => {
self.terminal.modes.autowrap = enabled;
},
.cursor_visible => {
self.ev.renderer_state.cursor.visible = enabled;
},
.alt_screen_save_cursor_clear_enter => { .alt_screen_save_cursor_clear_enter => {
const opts: terminal.Terminal.AlternateScreenOptions = .{ const opts: terminal.Terminal.AlternateScreenOptions = .{
@ -1247,15 +1228,13 @@ const StreamHandler = struct {
try self.queueRender(); try self.queueRender();
}, },
.bracketed_paste => self.terminal.modes.bracketed_paste = enabled, // Force resize back to the window size
.enable_mode_3 => self.terminal.resize(
.enable_mode_3 => { self.alloc,
// Disable deccolm self.grid_size.columns,
self.terminal.setDeccolmSupported(enabled); self.grid_size.rows,
) catch |err| {
// Force resize back to the window size log.err("error updating terminal size: {}", .{err});
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( .@"132_column" => try self.terminal.deccolm(
@ -1263,21 +1242,17 @@ const StreamHandler = struct {
if (enabled) .@"132_cols" else .@"80_cols", if (enabled) .@"132_cols" else .@"80_cols",
), ),
.mouse_event_x10 => self.terminal.modes.mouse_event = if (enabled) .x10 else .none, .mouse_event_x10 => self.terminal.flags.mouse_event = if (enabled) .x10 else .none,
.mouse_event_normal => self.terminal.modes.mouse_event = if (enabled) .normal else .none, .mouse_event_normal => self.terminal.flags.mouse_event = if (enabled) .normal else .none,
.mouse_event_button => self.terminal.modes.mouse_event = if (enabled) .button else .none, .mouse_event_button => self.terminal.flags.mouse_event = if (enabled) .button else .none,
.mouse_event_any => self.terminal.modes.mouse_event = if (enabled) .any else .none, .mouse_event_any => self.terminal.flags.mouse_event = if (enabled) .any else .none,
.mouse_format_utf8 => self.terminal.modes.mouse_format = if (enabled) .utf8 else .x10, .mouse_format_utf8 => self.terminal.flags.mouse_format = if (enabled) .utf8 else .x10,
.mouse_format_sgr => self.terminal.modes.mouse_format = if (enabled) .sgr else .x10, .mouse_format_sgr => self.terminal.flags.mouse_format = if (enabled) .sgr else .x10,
.mouse_format_urxvt => self.terminal.modes.mouse_format = if (enabled) .urxvt else .x10, .mouse_format_urxvt => self.terminal.flags.mouse_format = if (enabled) .urxvt else .x10,
.mouse_format_sgr_pixels => self.terminal.modes.mouse_format = if (enabled) .sgr_pixels else .x10, .mouse_format_sgr_pixels => self.terminal.flags.mouse_format = if (enabled) .sgr_pixels else .x10,
.mouse_alternate_scroll => self.terminal.modes.mouse_alternate_scroll = enabled, else => {},
.focus_event => self.terminal.modes.focus_event = enabled,
.alt_esc_prefix => self.terminal.modes.alt_esc_prefix = enabled,
else => if (enabled) log.warn("unimplemented mode: {}", .{mode}),
} }
} }
@ -1323,7 +1298,7 @@ const StreamHandler = struct {
const pos: struct { const pos: struct {
x: usize, x: usize,
y: usize, y: usize,
} = if (self.terminal.modes.origin) .{ } = if (self.terminal.modes.get(.origin)) .{
// TODO: what do we do if cursor is outside scrolling region? // TODO: what do we do if cursor is outside scrolling region?
.x = self.terminal.screen.cursor.x, .x = self.terminal.screen.cursor.x,
.y = self.terminal.screen.cursor.y -| self.terminal.scrolling_region.top, .y = self.terminal.screen.cursor.y -| self.terminal.scrolling_region.top,
@ -1464,7 +1439,7 @@ const StreamHandler = struct {
pub fn promptStart(self: *StreamHandler, aid: ?[]const u8, redraw: bool) !void { pub fn promptStart(self: *StreamHandler, aid: ?[]const u8, redraw: bool) !void {
_ = aid; _ = aid;
self.terminal.markSemanticPrompt(.prompt); self.terminal.markSemanticPrompt(.prompt);
self.terminal.modes.shell_redraws_prompt = redraw; self.terminal.flags.shell_redraws_prompt = redraw;
} }
pub fn promptEnd(self: *StreamHandler) !void { pub fn promptEnd(self: *StreamHandler) !void {