From d17b5cab9d9f4f3cc964d8326cd6696ccb330b5e Mon Sep 17 00:00:00 2001 From: Heal75 <18739377+Heal75@users.noreply.github.com> Date: Fri, 2 Oct 2026 08:59:04 +0200 Subject: [PATCH] fix(parser): ignore CSI sequences with a private prefix in SGR `CSI > Ps m` (XTMODKEYS, used to enable modifyOtherKeys) and `CSI ? Ps m` reached the SGR handler with the prefix dropped, so `ESC[>4m` was executed as SGR 4 and switched underline on for everything printed afterwards. Return early from the SGR handler when the sequence has a prefix. --- lib/src/core/escape/parser.dart | 3 +++ test/src/core/escape/parser_test.dart | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/lib/src/core/escape/parser.dart b/lib/src/core/escape/parser.dart index 0c7e74a9..c9da20ac 100644 --- a/lib/src/core/escape/parser.dart +++ b/lib/src/core/escape/parser.dart @@ -408,6 +408,9 @@ class EscapeParser { /// /// https://terminalguide.namepad.de/seq/csi_sm/ void _csiHandleSgr() { + // `CSI > Ps m` (XTMODKEYS) and `CSI ? Ps m` are not SGR. + if (_csi.prefix != null) return; + final params = _csi.params; if (params.isEmpty) { diff --git a/test/src/core/escape/parser_test.dart b/test/src/core/escape/parser_test.dart index 96fa5bb1..b93f1443 100644 --- a/test/src/core/escape/parser_test.dart +++ b/test/src/core/escape/parser_test.dart @@ -13,5 +13,24 @@ void main() { parser.write('\x1b[8;24;80t'); verify(parser.handler.resize(80, 24)); }); + + test('does not treat CSI > Ps m as SGR', () { + final parser = EscapeParser(MockEscapeHandler()); + // XTMODKEYS, e.g. sent by programs enabling modifyOtherKeys. + parser.write('\x1b[>4m'); + verifyNever(parser.handler.setCursorUnderline()); + }); + + test('does not treat CSI ? Ps m as SGR', () { + final parser = EscapeParser(MockEscapeHandler()); + parser.write('\x1b[?4m'); + verifyNever(parser.handler.setCursorUnderline()); + }); + + test('still parses plain SGR', () { + final parser = EscapeParser(MockEscapeHandler()); + parser.write('\x1b[4m'); + verify(parser.handler.setCursorUnderline()).called(1); + }); }); }