From 636a2f35b4a37fbb1b58dd801ae14513d355e95e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 4 Sep 2026 09:38:21 -0700 Subject: [PATCH 1/4] input: preserve numeric keypad output with MOK2 --- src/input/function_keys.zig | 38 +++++++++++++++++++------------------ src/input/key_encode.zig | 29 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/input/function_keys.zig b/src/input/function_keys.zig index 66ab4bc4d..ac7f05d65 100644 --- a/src/input/function_keys.zig +++ b/src/input/function_keys.zig @@ -104,22 +104,22 @@ pub const keys = keys: { result.set(.f12, pcStyle("\x1b[24;{}~") ++ .{Entry{ .sequence = "\x1B[24~" }}); // Keypad keys - result.set(.numpad_0, kpKeys("p")); - result.set(.numpad_1, kpKeys("q")); - result.set(.numpad_2, kpKeys("r")); - result.set(.numpad_3, kpKeys("s")); - result.set(.numpad_4, kpKeys("t")); - result.set(.numpad_5, kpKeys("u")); - result.set(.numpad_6, kpKeys("v")); - result.set(.numpad_7, kpKeys("w")); - result.set(.numpad_8, kpKeys("x")); - result.set(.numpad_9, kpKeys("y")); - result.set(.numpad_decimal, kpKeys("n")); - result.set(.numpad_divide, kpKeys("o")); - result.set(.numpad_multiply, kpKeys("j")); - result.set(.numpad_subtract, kpKeys("m")); - result.set(.numpad_add, kpKeys("k")); - result.set(.numpad_enter, kpKeys("M") ++ .{Entry{ .sequence = "\r" }}); + result.set(.numpad_0, kpKeys("p", "0")); + result.set(.numpad_1, kpKeys("q", "1")); + result.set(.numpad_2, kpKeys("r", "2")); + result.set(.numpad_3, kpKeys("s", "3")); + result.set(.numpad_4, kpKeys("t", "4")); + result.set(.numpad_5, kpKeys("u", "5")); + result.set(.numpad_6, kpKeys("v", "6")); + result.set(.numpad_7, kpKeys("w", "7")); + result.set(.numpad_8, kpKeys("x", "8")); + result.set(.numpad_9, kpKeys("y", "9")); + result.set(.numpad_decimal, kpKeys("n", ".")); + result.set(.numpad_divide, kpKeys("o", "/")); + result.set(.numpad_multiply, kpKeys("j", "*")); + result.set(.numpad_subtract, kpKeys("m", "-")); + result.set(.numpad_add, kpKeys("k", "+")); + result.set(.numpad_enter, kpKeys("M", "\r")); result.set(.numpad_up, pcStyle("\x1b[1;{}A") ++ cursorKey("\x1b[A", "\x1bOA")); result.set(.numpad_down, pcStyle("\x1b[1;{}B") ++ cursorKey("\x1b[B", "\x1bOB")); result.set(.numpad_right, pcStyle("\x1b[1;{}C") ++ cursorKey("\x1b[C", "\x1bOC")); @@ -257,10 +257,12 @@ fn kpDefault(comptime suffix: []const u8) []const Entry { /// Returns the entries for a keypad key. The suffix is the final character /// of the sent sequence, such as "r" for kp_2. -fn kpKeys(comptime suffix: []const u8) []const Entry { +fn kpKeys(comptime suffix: []const u8, comptime normal: []const u8) []const Entry { const pc = pcStyle("\x1bO{}" ++ suffix); for (pc) |*entry| entry.keypad = .application; - return kpDefault(suffix) ++ pc; + return kpDefault(suffix) ++ pc ++ .{ + Entry{ .keypad = .normal, .sequence = normal }, + }; } /// Returns entries that are dependent on cursor key settings. diff --git a/src/input/key_encode.zig b/src/input/key_encode.zig index d756de3c6..0f45c8184 100644 --- a/src/input/key_encode.zig +++ b/src/input/key_encode.zig @@ -2379,6 +2379,35 @@ test "legacy: keypad 1" { try testing.expectEqualStrings("1", writer.buffered()); } +test "legacy: keypad 1 with modify other state 2" { + var buf: [128]u8 = undefined; + + { + var writer: std.Io.Writer = .fixed(&buf); + try legacy(&writer, .{ + .key = .numpad_1, + .mods = .{ .ctrl = true }, + .utf8 = "1", + }, .{ + .modify_other_keys_state_2 = true, + }); + try testing.expectEqualStrings("1", writer.buffered()); + } + + { + var writer: std.Io.Writer = .fixed(&buf); + try legacy(&writer, .{ + .key = .numpad_1, + .mods = .{ .ctrl = true }, + .utf8 = "1", + }, .{ + .keypad_key_application = true, + .modify_other_keys_state_2 = true, + }); + try testing.expectEqualStrings("\x1bO5q", writer.buffered()); + } +} + test "legacy: keypad 1 with application keypad" { var buf: [128]u8 = undefined; var writer: std.Io.Writer = .fixed(&buf); From e7bdda9918a87f74178e15132c2894e29a8bf271 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 4 Sep 2026 09:39:56 -0700 Subject: [PATCH 2/4] input: encode F13 through F25 --- src/input/function_keys.zig | 15 +++++++++++- src/input/key_encode.zig | 48 +++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/input/function_keys.zig b/src/input/function_keys.zig index ac7f05d65..5390e2f63 100644 --- a/src/input/function_keys.zig +++ b/src/input/function_keys.zig @@ -89,7 +89,7 @@ pub const keys = keys: { result.set(.page_up, pcStyle("\x1b[5;{}~") ++ .{Entry{ .sequence = "\x1B[5~" }}); result.set(.page_down, pcStyle("\x1b[6;{}~") ++ .{Entry{ .sequence = "\x1B[6~" }}); - // Function Keys. todo: f13-f35 but we need to add to input.Key + // Function Keys. todo: f26-f35 but we need to add to input.Key result.set(.f1, pcStyle("\x1b[1;{}P") ++ .{Entry{ .sequence = "\x1BOP" }}); result.set(.f2, pcStyle("\x1b[1;{}Q") ++ .{Entry{ .sequence = "\x1BOQ" }}); result.set(.f3, pcStyle("\x1b[13;{}~") ++ .{Entry{ .sequence = "\x1BOR" }}); @@ -102,6 +102,19 @@ pub const keys = keys: { result.set(.f10, pcStyle("\x1b[21;{}~") ++ .{Entry{ .sequence = "\x1B[21~" }}); result.set(.f11, pcStyle("\x1b[23;{}~") ++ .{Entry{ .sequence = "\x1B[23~" }}); result.set(.f12, pcStyle("\x1b[24;{}~") ++ .{Entry{ .sequence = "\x1B[24~" }}); + result.set(.f13, pcStyle("\x1b[25;{}~") ++ .{Entry{ .sequence = "\x1B[25~" }}); + result.set(.f14, pcStyle("\x1b[26;{}~") ++ .{Entry{ .sequence = "\x1B[26~" }}); + result.set(.f15, pcStyle("\x1b[28;{}~") ++ .{Entry{ .sequence = "\x1B[28~" }}); + result.set(.f16, pcStyle("\x1b[29;{}~") ++ .{Entry{ .sequence = "\x1B[29~" }}); + result.set(.f17, pcStyle("\x1b[31;{}~") ++ .{Entry{ .sequence = "\x1B[31~" }}); + result.set(.f18, pcStyle("\x1b[32;{}~") ++ .{Entry{ .sequence = "\x1B[32~" }}); + result.set(.f19, pcStyle("\x1b[33;{}~") ++ .{Entry{ .sequence = "\x1B[33~" }}); + result.set(.f20, pcStyle("\x1b[34;{}~") ++ .{Entry{ .sequence = "\x1B[34~" }}); + result.set(.f21, pcStyle("\x1b[42;{}~") ++ .{Entry{ .sequence = "\x1B[42~" }}); + result.set(.f22, pcStyle("\x1b[43;{}~") ++ .{Entry{ .sequence = "\x1B[43~" }}); + result.set(.f23, pcStyle("\x1b[44;{}~") ++ .{Entry{ .sequence = "\x1B[44~" }}); + result.set(.f24, pcStyle("\x1b[45;{}~") ++ .{Entry{ .sequence = "\x1B[45~" }}); + result.set(.f25, pcStyle("\x1b[46;{}~") ++ .{Entry{ .sequence = "\x1B[46~" }}); // Keypad keys result.set(.numpad_0, kpKeys("p", "0")); diff --git a/src/input/key_encode.zig b/src/input/key_encode.zig index 0f45c8184..96eb6dec2 100644 --- a/src/input/key_encode.zig +++ b/src/input/key_encode.zig @@ -2510,6 +2510,54 @@ test "legacy: f1" { } } +test "legacy: f13 through f25" { + const Case = struct { key: key.Key, code: u8 }; + const cases = [_]Case{ + .{ .key = .f13, .code = 25 }, + .{ .key = .f14, .code = 26 }, + .{ .key = .f15, .code = 28 }, + .{ .key = .f16, .code = 29 }, + .{ .key = .f17, .code = 31 }, + .{ .key = .f18, .code = 32 }, + .{ .key = .f19, .code = 33 }, + .{ .key = .f20, .code = 34 }, + .{ .key = .f21, .code = 42 }, + .{ .key = .f22, .code = 43 }, + .{ .key = .f23, .code = 44 }, + .{ .key = .f24, .code = 45 }, + .{ .key = .f25, .code = 46 }, + }; + + var buf: [128]u8 = undefined; + var expected_buf: [32]u8 = undefined; + for (cases) |case| { + { + var writer: std.Io.Writer = .fixed(&buf); + try legacy(&writer, .{ .key = case.key }, .{}); + const expected = try std.fmt.bufPrint( + &expected_buf, + "\x1b[{}~", + .{case.code}, + ); + try testing.expectEqualStrings(expected, writer.buffered()); + } + + { + var writer: std.Io.Writer = .fixed(&buf); + try legacy(&writer, .{ + .key = case.key, + .mods = .{ .ctrl = true }, + }, .{ .modify_other_keys_state_2 = true }); + const expected = try std.fmt.bufPrint( + &expected_buf, + "\x1b[{};5~", + .{case.code}, + ); + try testing.expectEqualStrings(expected, writer.buffered()); + } + } +} + test "legacy: left_shift+tab" { var buf: [128]u8 = undefined; var writer: std.Io.Writer = .fixed(&buf); From 37e3cdd2d281feb2bb403600974289406f891461 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 4 Sep 2026 09:41:08 -0700 Subject: [PATCH 3/4] input: encode alt+escape with MOK2 --- src/input/function_keys.zig | 3 ++- src/input/key_encode.zig | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/input/function_keys.zig b/src/input/function_keys.zig index 5390e2f63..b68b26362 100644 --- a/src/input/function_keys.zig +++ b/src/input/function_keys.zig @@ -237,7 +237,8 @@ pub const keys = keys: { result.set(.escape, &.{ .{ .mods = .{ .shift = true }, .sequence = "\x1b[27;2;27~" }, - .{ .mods = .{ .alt = true }, .sequence = "\x1b\x1b" }, + .{ .mods = .{ .alt = true }, .modify_other_keys = .set, .sequence = "\x1b\x1b" }, + .{ .mods = .{ .alt = true }, .modify_other_keys = .set_other, .sequence = "\x1b[27;3;27~" }, .{ .mods = .{ .alt = true, .shift = true }, .sequence = "\x1b[27;4;27~" }, .{ .mods = .{ .ctrl = true }, .sequence = "\x1b[27;5;27~" }, .{ .mods = .{ .ctrl = true, .shift = true }, .sequence = "\x1b[27;6;27~" }, diff --git a/src/input/key_encode.zig b/src/input/key_encode.zig index 96eb6dec2..2b0a668ee 100644 --- a/src/input/key_encode.zig +++ b/src/input/key_encode.zig @@ -2033,6 +2033,28 @@ test "legacy: esc with utf8 (dead key state)" { try testing.expectEqualStrings("A", writer.buffered()); } +test "legacy: alt+escape with modify other state 2" { + var buf: [128]u8 = undefined; + + { + var writer: std.Io.Writer = .fixed(&buf); + try legacy(&writer, .{ + .key = .escape, + .mods = .{ .alt = true }, + }, .{}); + try testing.expectEqualStrings("\x1b\x1b", writer.buffered()); + } + + { + var writer: std.Io.Writer = .fixed(&buf); + try legacy(&writer, .{ + .key = .escape, + .mods = .{ .alt = true }, + }, .{ .modify_other_keys_state_2 = true }); + try testing.expectEqualStrings("\x1b[27;3;27~", writer.buffered()); + } +} + test "legacy: ctrl+shift+minus (underscore on US)" { var buf: [128]u8 = undefined; var writer: std.Io.Writer = .fixed(&buf); From cc3fd8a773300371e2d1f41265128a7bf10adfa2 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 4 Sep 2026 09:42:50 -0700 Subject: [PATCH 4/4] input: encode help and context menu keys --- src/input/function_keys.zig | 2 ++ src/input/key_encode.zig | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/input/function_keys.zig b/src/input/function_keys.zig index b68b26362..995e576fb 100644 --- a/src/input/function_keys.zig +++ b/src/input/function_keys.zig @@ -88,6 +88,8 @@ pub const keys = keys: { result.set(.delete, pcStyle("\x1b[3;{}~") ++ .{Entry{ .sequence = "\x1B[3~" }}); result.set(.page_up, pcStyle("\x1b[5;{}~") ++ .{Entry{ .sequence = "\x1B[5~" }}); result.set(.page_down, pcStyle("\x1b[6;{}~") ++ .{Entry{ .sequence = "\x1B[6~" }}); + result.set(.help, pcStyle("\x1b[28;{}~") ++ .{Entry{ .sequence = "\x1B[28~" }}); + result.set(.context_menu, pcStyle("\x1b[29;{}~") ++ .{Entry{ .sequence = "\x1B[29~" }}); // Function Keys. todo: f26-f35 but we need to add to input.Key result.set(.f1, pcStyle("\x1b[1;{}P") ++ .{Entry{ .sequence = "\x1BOP" }}); diff --git a/src/input/key_encode.zig b/src/input/key_encode.zig index 2b0a668ee..75dfa2174 100644 --- a/src/input/key_encode.zig +++ b/src/input/key_encode.zig @@ -2580,6 +2580,43 @@ test "legacy: f13 through f25" { } } +test "legacy: help and context menu" { + const Case = struct { key: key.Key, code: u8 }; + const cases = [_]Case{ + .{ .key = .help, .code = 28 }, + .{ .key = .context_menu, .code = 29 }, + }; + + var buf: [128]u8 = undefined; + var expected_buf: [32]u8 = undefined; + for (cases) |case| { + { + var writer: std.Io.Writer = .fixed(&buf); + try legacy(&writer, .{ .key = case.key }, .{}); + const expected = try std.fmt.bufPrint( + &expected_buf, + "\x1b[{}~", + .{case.code}, + ); + try testing.expectEqualStrings(expected, writer.buffered()); + } + + { + var writer: std.Io.Writer = .fixed(&buf); + try legacy(&writer, .{ + .key = case.key, + .mods = .{ .ctrl = true }, + }, .{ .modify_other_keys_state_2 = true }); + const expected = try std.fmt.bufPrint( + &expected_buf, + "\x1b[{};5~", + .{case.code}, + ); + try testing.expectEqualStrings(expected, writer.buffered()); + } + } +} + test "legacy: left_shift+tab" { var buf: [128]u8 = undefined; var writer: std.Io.Writer = .fixed(&buf);