From c99e42a3d6d57b9f6155a17e99ef9ca5aa5e694d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=B0=8F=E7=99=BD?= <364772080@qq.com> Date: Mon, 7 Jul 2025 20:21:48 +0800 Subject: [PATCH] windows: Properly handle surrogates (#34006) Closes #33791 Surrogate pairs are now handled correctly, so input from tools like `WinCompose` is properly received. Release Notes: - N/A --- crates/gpui/src/platform/windows/events.rs | 41 ++++++++++++++++++---- crates/gpui/src/platform/windows/window.rs | 3 ++ 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index a43fdc097f..8b8964b2df 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -466,12 +466,7 @@ fn handle_keyup_msg( } fn handle_char_msg(wparam: WPARAM, state_ptr: Rc) -> Option { - let Some(input) = char::from_u32(wparam.0 as u32) - .filter(|c| !c.is_control()) - .map(String::from) - else { - return Some(1); - }; + let input = parse_char_message(wparam, &state_ptr)?; with_input_handler(&state_ptr, |input_handler| { input_handler.replace_text_in_range(None, &input); }); @@ -1228,6 +1223,36 @@ fn handle_input_language_changed( Some(0) } +#[inline] +fn parse_char_message(wparam: WPARAM, state_ptr: &Rc) -> Option { + let code_point = wparam.loword(); + let mut lock = state_ptr.state.borrow_mut(); + // https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-3/#G2630 + match code_point { + 0xD800..=0xDBFF => { + // High surrogate, wait for low surrogate + lock.pending_surrogate = Some(code_point); + None + } + 0xDC00..=0xDFFF => { + if let Some(high_surrogate) = lock.pending_surrogate.take() { + // Low surrogate, combine with pending high surrogate + String::from_utf16(&[high_surrogate, code_point]).ok() + } else { + // Invalid low surrogate without a preceding high surrogate + log::warn!( + "Received low surrogate without a preceding high surrogate: {code_point:x}" + ); + None + } + } + _ => { + lock.pending_surrogate = None; + String::from_utf16(&[code_point]).ok() + } + } +} + #[inline] fn translate_message(handle: HWND, wparam: WPARAM, lparam: LPARAM) { let msg = MSG { @@ -1270,6 +1295,10 @@ where capslock: current_capslock(), })) } + VK_PACKET => { + translate_message(handle, wparam, lparam); + None + } VK_CAPITAL => { let capslock = current_capslock(); if state diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 5c7dd07c48..5703a82815 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -43,6 +43,7 @@ pub struct WindowsWindowState { pub callbacks: Callbacks, pub input_handler: Option, + pub pending_surrogate: Option, pub last_reported_modifiers: Option, pub last_reported_capslock: Option, pub system_key_handled: bool, @@ -105,6 +106,7 @@ impl WindowsWindowState { let renderer = windows_renderer::init(gpu_context, hwnd, transparent)?; let callbacks = Callbacks::default(); let input_handler = None; + let pending_surrogate = None; let last_reported_modifiers = None; let last_reported_capslock = None; let system_key_handled = false; @@ -126,6 +128,7 @@ impl WindowsWindowState { min_size, callbacks, input_handler, + pending_surrogate, last_reported_modifiers, last_reported_capslock, system_key_handled,