From 183a426efa93e7a1b25730e85e194246523ef1c8 Mon Sep 17 00:00:00 2001 From: 739C1AE2 <134778074+739C1AE2@users.noreply.github.com> Date: Sat, 6 Dec 2025 17:59:43 +0800 Subject: [PATCH] Fix UTF-16 surrogate pair handling on Windows input (#1160) Fix(Windows): Correctly handle UTF-16 surrogate pairs for non-BMP input. Co-authored-by: ArthurSonzogni --- CHANGELOG.md | 3 +++ src/ftxui/component/screen_interactive.cpp | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e2c4533a..c9d86f6df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,9 @@ Next output. Thanks @zozowell in #1064. - Fix vertical `ftxui::Slider`. The "up" key was previously decreasing the value. Thanks @its-pablo in #1093 for reporting the issue. +- Fix Windows UTF-16 key input handling. Emoji and other code points outside the + Basic Multilingual Plane (BMP) are now correctly processed. Thanks @739C1AE2 + in #1160 for fixing the issue. ### Dom - Fix integer overflow in `ComputeShrinkHard`. Thanks @its-pablo in #1137 for diff --git a/src/ftxui/component/screen_interactive.cpp b/src/ftxui/component/screen_interactive.cpp index 6dc982f0c..e5207a141 100644 --- a/src/ftxui/component/screen_interactive.cpp +++ b/src/ftxui/component/screen_interactive.cpp @@ -1098,6 +1098,7 @@ void ScreenInteractive::FetchTerminalEvents() { // Convert the input events to FTXUI events. // For each event, we call the terminal input parser to convert it to // Event. + std::wstring wstring; for (const auto& r : records) { switch (r.EventType) { case KEY_EVENT: { @@ -1106,11 +1107,16 @@ void ScreenInteractive::FetchTerminalEvents() { if (key_event.bKeyDown == FALSE) { continue; } - std::wstring wstring; - wstring += key_event.uChar.UnicodeChar; + const wchar_t wc = key_event.uChar.UnicodeChar; + wstring += wc; + if (wc >= 0xd800 && wc <= 0xdbff) { + // Wait for the Low Surrogate to arrive in the next record. + continue; + } for (auto it : to_string(wstring)) { internal_->terminal_input_parser.Add(it); } + wstring.clear(); } break; case WINDOW_BUFFER_SIZE_EVENT: Post(Event::Special({0}));