From 6a065540dd8860120d09bd2d8e413891cd5d7a8a Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 12 Oct 2023 17:07:47 -0700 Subject: [PATCH 1/3] terminal: KAM (mode 2) This has an associated config `vt-kam-allowed` which defaults to "false" since this mode can cause the terminal to become mostly unusable. We include this mode for completions sake however. --- src/Surface.zig | 9 +++++++++ src/config/Config.zig | 8 ++++++++ src/terminal/modes.zig | 1 + website/app/vt/modes/kam/page.mdx | 28 ++++++++++++++++++++++++++++ website/components/VTMode.tsx | 18 ++++++++++++++++++ 5 files changed, 64 insertions(+) create mode 100644 website/app/vt/modes/kam/page.mdx create mode 100644 website/components/VTMode.tsx diff --git a/src/Surface.zig b/src/Surface.zig index c3fd1e9d4..575a7e822 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -145,6 +145,7 @@ const DerivedConfig = struct { mouse_shift_capture: configpkg.MouseShiftCapture, macos_non_native_fullscreen: configpkg.NonNativeFullscreen, macos_option_as_alt: configpkg.OptionAsAlt, + vt_kam_allowed: bool, window_padding_x: u32, window_padding_y: u32, @@ -166,6 +167,7 @@ const DerivedConfig = struct { .mouse_shift_capture = config.@"mouse-shift-capture", .macos_non_native_fullscreen = config.@"macos-non-native-fullscreen", .macos_option_as_alt = config.@"macos-option-as-alt", + .vt_kam_allowed = config.@"vt-kam-allowed", .window_padding_x = config.@"window-padding-x", .window_padding_y = config.@"window-padding-y", @@ -985,6 +987,13 @@ pub fn keyCallback( if (consumed and performed) return true; } + // If we allow KAM and KAM is enabled then we do nothing. + if (self.config.vt_kam_allowed) { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + if (self.io.terminal.modes.get(.disable_keyboard)) return true; + } + // If this input event has text, then we hide the mouse if configured. if (self.config.mouse_hide_while_typing and !self.mouse.hidden and diff --git a/src/config/Config.zig b/src/config/Config.zig index c21564f42..82733bfba 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -461,6 +461,14 @@ keybind: Keybinds = .{}, /// The default value is "16-bit". @"osc-color-report-format": OSCColorReportFormat = .@"16-bit", +/// If true, allows the "KAM" mode (ANSI mode 2) to be used within +/// the terminal. KAM disables keyboard input at the request of the +/// application. This is not a common feature and is not recommended +/// to be enabled. This will not be documented further because +/// if you know you need KAM, you know. If you don't know if you +/// need KAM, you don't need it. +@"vt-kam-allowed": bool = false, + /// If anything other than false, fullscreen mode on macOS will not use the /// native fullscreen, but make the window fullscreen without animations and /// using a new space. It's faster than the native fullscreen mode since it diff --git a/src/terminal/modes.zig b/src/terminal/modes.zig index 25a020e9d..8c49941ba 100644 --- a/src/terminal/modes.zig +++ b/src/terminal/modes.zig @@ -170,6 +170,7 @@ const ModeEntry = struct { /// valuable to redocument them all here. const entries: []const ModeEntry = &.{ // ANSI + .{ .name = "disable_keyboard", .value = 2, .ansi = true }, // KAM .{ .name = "insert", .value = 4, .ansi = true }, // DEC diff --git a/website/app/vt/modes/kam/page.mdx b/website/app/vt/modes/kam/page.mdx new file mode 100644 index 000000000..3efeca79c --- /dev/null +++ b/website/app/vt/modes/kam/page.mdx @@ -0,0 +1,28 @@ +import VTMode from "@/components/VTMode"; + +# Keyboard Action Mode (KAM) + + + +Disable all keyboard input. + +This mode is unset as part of both [full reset (RIS)](/vt/ris) +and [soft reset (DECSTR)](/vt/decstr). + +A poorly behaved terminal program can lock the terminal emulator +using this command. Terminal emulators should provide a mechanism +to reset this or outright disable it. + +## Validation + +### KAM V-1: Disable Keyboard Input + +```bash +printf "\033[1;1H" # move to top-left +printf "\033[0J" # clear screen +printf "Keyboard input is now disabled.\n" +printf "\033[2h" +sleep 5 +printf "\033[2l" +printf "Keyboard input is re-enabled.\n" +``` diff --git a/website/components/VTMode.tsx b/website/components/VTMode.tsx new file mode 100644 index 000000000..b6c690071 --- /dev/null +++ b/website/components/VTMode.tsx @@ -0,0 +1,18 @@ +export default function VTMode({ + value, + ansi = false, +}: { + value: number; + ansi: boolean; +}) { + return ( +
+
+
+ {ansi ? "?" : ""} + {value} +
+
+
+ ); +} From 89d2827910d5036ab60d4e3f5c6e24eae9f99885 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 12 Oct 2023 19:24:27 -0700 Subject: [PATCH 2/3] terminal: insert mode tests, fix insertBlanks multi-cell char split --- src/terminal/Terminal.zig | 40 +++++++++++++ website/app/vt/modes/insert/page.mdx | 89 ++++++++++++++++++++++++++++ website/app/vt/modes/kam/page.mdx | 2 + website/components/VTMode.tsx | 2 +- 4 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 website/app/vt/modes/insert/page.mdx diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index bbca660cb..9332c5479 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -1573,6 +1573,11 @@ pub fn insertBlanks(self: *Terminal, count: usize) void { // This is the index of the final copyable value that we need to copy. const copyable_end = start + copyable - 1; + // If our last cell we're shifting is wide, then we need to clear + // it to be empty so we don't split the multi-cell char. + const cell = row.getCellPtr(copyable_end); + if (cell.attrs.wide) cell.char = 0; + // Shift count cells. We have to do this backwards since we're not // allocated new space, otherwise we'll copy duplicates. var i: usize = 0; @@ -3949,6 +3954,23 @@ test "Terminal: insertBlanks shift off screen" { } } +test "Terminal: insertBlanks split multi-cell character" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 10); + defer t.deinit(alloc); + + for ("123") |c| try t.print(c); + try t.print('橋'); + t.setCursorPos(1, 1); + t.insertBlanks(1); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings(" 123", str); + } +} + test "Terminal: insertBlanks inside left/right scroll region" { const alloc = testing.allocator; var t = try init(alloc, 10, 10); @@ -4073,6 +4095,24 @@ test "Terminal: insert mode with wide characters at end" { } } +test "Terminal: insert mode pushing off wide character" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 2); + defer t.deinit(alloc); + + for ("123") |c| try t.print(c); + try t.print('😀'); // 0x1F600 + t.modes.set(.insert, true); + t.setCursorPos(1, 1); + try t.print('X'); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("X123", str); + } +} + test "Terminal: cursorIsAtPrompt" { const alloc = testing.allocator; var t = try init(alloc, 3, 2); diff --git a/website/app/vt/modes/insert/page.mdx b/website/app/vt/modes/insert/page.mdx new file mode 100644 index 000000000..1a1b6b9a0 --- /dev/null +++ b/website/app/vt/modes/insert/page.mdx @@ -0,0 +1,89 @@ +import VTMode from "@/components/VTMode"; + +# Insert + + + +When enabled, text is written to the cell under the cursor +and all existing content is shifted right. When disabled, text +overwrites existing content. + +This mode is unset as part of both [full reset (RIS)](/vt/ris) +and [soft reset (DECSTR)](/vt/decstr). + +If a multi-cell character (such as "橋") is shifted so that the cell is split +in half, the multi-cell character can either be clipped or erased. + +This mode is typically disabled on terminal startup. + +## Validation + +### INSERT V-1: Simple Usage + +```bash +printf "\033[1;1H" # move to top-left +printf "\033[0J" # clear screen +printf "123456" +printf "\033[1G" +printf "\033[4h" +printf "ABC" +``` + +``` +|ABC123456_| +``` + +### INSERT V-2: Pushing Off the Screen Edge + +```bash +cols=$(tput cols) +printf "\033[1;1H" # move to top-left +printf "\033[0J" # clear screen +printf "\033[${cols}G" +printf "\033[6D" +printf "123456" +printf "\033[6D" +printf "\033[4h" +printf "ABC" +``` + +``` +|____ABC1234| +``` + +### INSERT V-3: Writing on the Screen Edge + +```bash +cols=$(tput cols) +printf "\033[1;1H" # move to top-left +printf "\033[0J" # clear screen +printf "\033[${cols}G" +printf "\033[6D" +printf "123456" +printf "\033[1D" +printf "\033[4h" +printf "ABC" +``` + +``` +|____12345AB| +|Cc_________| +``` + +### INSERT V-3: Splitting a Multi-Cell Character + +```bash +cols=$(tput cols) +printf "\033[1;1H" # move to top-left +printf "\033[0J" # clear screen +printf "\033[${cols}G" +printf "\033[6D" +printf "1234橋" +printf "\033[6D" +printf "\033[4h" +printf "A" +``` + +``` +|_____A1234_| +``` diff --git a/website/app/vt/modes/kam/page.mdx b/website/app/vt/modes/kam/page.mdx index 3efeca79c..dfab83e9e 100644 --- a/website/app/vt/modes/kam/page.mdx +++ b/website/app/vt/modes/kam/page.mdx @@ -13,6 +13,8 @@ A poorly behaved terminal program can lock the terminal emulator using this command. Terminal emulators should provide a mechanism to reset this or outright disable it. +This mode is typically disabled on terminal startup. + ## Validation ### KAM V-1: Disable Keyboard Input diff --git a/website/components/VTMode.tsx b/website/components/VTMode.tsx index b6c690071..7287c1c87 100644 --- a/website/components/VTMode.tsx +++ b/website/components/VTMode.tsx @@ -9,7 +9,7 @@ export default function VTMode({
- {ansi ? "?" : ""} + {ansi ? "" : "?"} {value}
From 853c0427e6f8c9e75de6266276d8bdf48ed93825 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 12 Oct 2023 19:38:06 -0700 Subject: [PATCH 3/3] website: document srm mode --- src/terminal/modes.zig | 4 +++- website/app/vt/modes/srm/page.mdx | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 website/app/vt/modes/srm/page.mdx diff --git a/src/terminal/modes.zig b/src/terminal/modes.zig index 8c49941ba..0e70dd53c 100644 --- a/src/terminal/modes.zig +++ b/src/terminal/modes.zig @@ -147,6 +147,7 @@ pub fn modeFromInt(v: u16, ansi: bool) ?Mode { } fn entryForMode(comptime mode: Mode) ModeEntry { + @setEvalBranchQuota(10_000); const name = @tagName(mode); for (entries) |entry| { if (std.mem.eql(u8, entry.name, name)) return entry; @@ -172,6 +173,7 @@ const entries: []const ModeEntry = &.{ // ANSI .{ .name = "disable_keyboard", .value = 2, .ansi = true }, // KAM .{ .name = "insert", .value = 4, .ansi = true }, + .{ .name = "send_receive_mode", .value = 12, .ansi = true, .default = true }, // SRM // DEC .{ .name = "cursor_keys", .value = 1 }, // DECCKM @@ -213,7 +215,7 @@ test modeFromInt { try testing.expect(modeFromInt(4, true).? == .insert); try testing.expect(modeFromInt(9, true) == null); try testing.expect(modeFromInt(9, false).? == .mouse_event_x10); - try testing.expect(modeFromInt(12, true) == null); + try testing.expect(modeFromInt(14, true) == null); } test ModeState { diff --git a/website/app/vt/modes/srm/page.mdx b/website/app/vt/modes/srm/page.mdx new file mode 100644 index 000000000..8b2e56649 --- /dev/null +++ b/website/app/vt/modes/srm/page.mdx @@ -0,0 +1,17 @@ +import VTMode from "@/components/VTMode"; + +# Send-Receive Mode (SRM) + + + +If reset, characters entered by the keyboard are shown on the screen +as well as being sent to the running program. If set, keyboard input +is sent only to the running program and the running program can choose +whether it wants to echo it back. + +This mode is typically enabled on terminal startup. + +This mode is generally unsupported across most terminals today and +is recommended to be retired.[^1] + +[^1]: https://gitlab.gnome.org/GNOME/vte/-/issues/69