From e8ffbf7b602e3a3e148f3ba623c6a76aa6ba2dde Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Oct 2026 00:26:18 +0000 Subject: [PATCH 1/2] Initial plan From 3ee71ff9b42848583c445a8449ff50c8fd5f7258 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Oct 2026 00:38:48 +0000 Subject: [PATCH 2/2] Unwind shifted punctuation key codes to US base characters Co-authored-by: tomlm <17789481+tomlm@users.noreply.github.com> --- .../Input/KittyKeyboardStateTests.cs | 9 ++++ .../Input/KittyKeyboardTests.cs | 42 +++++++++++++++++++ src/XTerm.NET/Input/KittyKeyboard.cs | 18 ++++++++ 3 files changed, 69 insertions(+) diff --git a/src/XTerm.NET.Tests/Input/KittyKeyboardStateTests.cs b/src/XTerm.NET.Tests/Input/KittyKeyboardStateTests.cs index 1902fb5d..e04667ac 100644 --- a/src/XTerm.NET.Tests/Input/KittyKeyboardStateTests.cs +++ b/src/XTerm.NET.Tests/Input/KittyKeyboardStateTests.cs @@ -347,6 +347,15 @@ public void GenerateKittyKeyInput_honours_MacOptionIsMeta() Assert.Equal("\u001b[102;3u", t.GenerateKittyKeyInput(ev)); } + [Fact] + public void GenerateKittyKeyInput_unwinds_shifted_punctuation() + { + var t = NewTerminal(); + t.Write("\u001b[=9u"); // AllKeys | Disambiguate + var ev = new KeyEvent { Key = "+", Code = "Equal", ShiftKey = true }; + Assert.Equal("\u001b[61;2u", t.GenerateKittyKeyInput(ev)); + } + [Theory] [InlineData("Escape", "", "\u001b")] [InlineData("F13", "", "\u001b[25~")] diff --git a/src/XTerm.NET.Tests/Input/KittyKeyboardTests.cs b/src/XTerm.NET.Tests/Input/KittyKeyboardTests.cs index d0adb017..8938d249 100644 --- a/src/XTerm.NET.Tests/Input/KittyKeyboardTests.cs +++ b/src/XTerm.NET.Tests/Input/KittyKeyboardTests.cs @@ -399,6 +399,48 @@ public void Shifted_key_appears_as_a_sub_parameter() Assert.Equal("\u001b[53:37;2u", KittyKeyboard.Evaluate(Ev("%", "Digit5", shift: true), AllKeys | Alternates)); } + [Theory] + [InlineData("`", "~", "Backquote")] + [InlineData("-", "_", "Minus")] + [InlineData("=", "+", "Equal")] + [InlineData("[", "{", "BracketLeft")] + [InlineData("]", "}", "BracketRight")] + [InlineData("\\", "|", "Backslash")] + [InlineData(";", ":", "Semicolon")] + [InlineData("'", "\"", "Quote")] + [InlineData(",", "<", "Comma")] + [InlineData(".", ">", "Period")] + [InlineData("/", "?", "Slash")] + public void Shifted_punctuation_appears_as_a_sub_parameter(string unshiftedKey, string shiftedKey, string code) + { + var expected = $"\u001b[{(int)unshiftedKey[0]}:{(int)shiftedKey[0]};2u"; + Assert.Equal(expected, KittyKeyboard.Evaluate(Ev(shiftedKey, code, shift: true), AllKeys | Alternates)); + } + + [Theory] + [InlineData("+", "Equal", "\u001b[61;2u")] + [InlineData(":", "Semicolon", "\u001b[59;2u")] + [InlineData("_", "Minus", "\u001b[45;2u")] + [InlineData("?", "Slash", "\u001b[47;2u")] + public void Shifted_punctuation_unwinds_under_ReportAllKeys(string shiftedKey, string code, string expected) + { + Assert.Equal(expected, KittyKeyboard.Evaluate(Ev(shiftedKey, code, shift: true), AllKeys | Disambiguate)); + } + + [Theory] + [InlineData("+", "Equal", "\u001b[61:43;6u", "\u001b[61;6u")] + [InlineData(":", "Semicolon", "\u001b[59:58;6u", "\u001b[59;6u")] + [InlineData("_", "Minus", "\u001b[45:95;6u", "\u001b[45;6u")] + [InlineData("?", "Slash", "\u001b[47:63;6u", "\u001b[47;6u")] + public void Ctrl_Shift_punctuation_unwinds_to_base_key_code( + string shiftedKey, string code, string expectedWithAlternates, string expectedDisambiguate) + { + Assert.Equal(expectedWithAlternates, + KittyKeyboard.Evaluate(Ev(shiftedKey, code, ctrl: true, shift: true), Disambiguate | Alternates)); + Assert.Equal(expectedDisambiguate, + KittyKeyboard.Evaluate(Ev(shiftedKey, code, ctrl: true, shift: true), Disambiguate)); + } + [Fact] public void Unshifted_and_functional_keys_have_no_alternate() { diff --git a/src/XTerm.NET/Input/KittyKeyboard.cs b/src/XTerm.NET/Input/KittyKeyboard.cs index f5c96cd8..5024b0d5 100644 --- a/src/XTerm.NET/Input/KittyKeyboard.cs +++ b/src/XTerm.NET/Input/KittyKeyboard.cs @@ -240,6 +240,24 @@ private static int EncodeModifiers(KeyEvent ev) } if (ev.Code.StartsWith("Key", StringComparison.Ordinal) && ev.Code.Length == 4) return char.ToLowerInvariant(ev.Code[3]); + + if (ev.ShiftKey) + { + switch (ev.Code) + { + case "Backquote": return '`'; + case "Minus": return '-'; + case "Equal": return '='; + case "BracketLeft": return '['; + case "BracketRight": return ']'; + case "Backslash": return '\\'; + case "Semicolon": return ';'; + case "Quote": return '\''; + case "Comma": return ','; + case "Period": return '.'; + case "Slash": return '/'; + } + } } if (ev.Key.Length == 1)