From e8dfc303147202b601495a0f946b0083fab90341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=B0=8F=E7=99=BD?= <364772080@qq.com> Date: Thu, 29 Aug 2024 10:29:53 +0800 Subject: [PATCH] windows: Fix IME window position on Win10 (#15471) On Windows, different input methods use different APIs to set their window positions: - The Japanese input method on Windows 11 uses `ImmSetCandidateWindow`. - The Chinese input method on Windows 10 uses `ImmSetCompositionWindow`. - The Chinese input method on Windows 11 can use either. Therefore, this PR calls both functions to cover the various scenarios. Additionally, introduced a helper function `with_input_handler` to improve code readability. Release Notes: - N/A --- crates/gpui/src/platform/windows/events.rs | 171 ++++++++++++--------- 1 file changed, 101 insertions(+), 70 deletions(-) diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index c831b89dffb5b2a91e3014c5a5fabd9012cd7e72..cc556019636f379c204183963c6a6004f6316c85 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -386,22 +386,18 @@ fn handle_char_msg( keystroke, is_held: lparam.0 & (0x1 << 30) > 0, }; - let dispatch_event_result = func(PlatformInput::KeyDown(event)); - let mut lock = state_ptr.state.borrow_mut(); - lock.callbacks.input = Some(func); + state_ptr.state.borrow_mut().callbacks.input = Some(func); + if dispatch_event_result.default_prevented || !dispatch_event_result.propagate { return Some(0); } let Some(ime_char) = ime_key else { return Some(1); }; - let Some(mut input_handler) = lock.input_handler.take() else { - return Some(1); - }; - drop(lock); - input_handler.replace_text_in_range(None, &ime_char); - state_ptr.state.borrow_mut().input_handler = Some(input_handler); + with_input_handler(&state_ptr, |input_handler| { + input_handler.replace_text_in_range(None, &ime_char); + }); Some(0) } @@ -581,33 +577,41 @@ fn handle_mouse_horizontal_wheel_msg( } } +fn retrieve_caret_position(state_ptr: &Rc) -> Option { + with_input_handler_and_scale_factor(state_ptr, |input_handler, scale_factor| { + let caret_range = input_handler.selected_text_range()?; + let caret_position = input_handler.bounds_for_range(caret_range)?; + Some(POINT { + // logical to physical + x: (caret_position.origin.x.0 * scale_factor) as i32, + y: (caret_position.origin.y.0 * scale_factor) as i32 + + ((caret_position.size.height.0 * scale_factor) as i32 / 2), + }) + }) +} + fn handle_ime_position(handle: HWND, state_ptr: Rc) -> Option { unsafe { - let mut lock = state_ptr.state.borrow_mut(); let ctx = ImmGetContext(handle); - let Some(mut input_handler) = lock.input_handler.take() else { - return Some(1); - }; - let scale_factor = lock.scale_factor; - drop(lock); - - let Some(caret_range) = input_handler.selected_text_range() else { - state_ptr.state.borrow_mut().input_handler = Some(input_handler); + let Some(caret_position) = retrieve_caret_position(&state_ptr) else { return Some(0); }; - let caret_position = input_handler.bounds_for_range(caret_range).unwrap(); - state_ptr.state.borrow_mut().input_handler = Some(input_handler); - let config = CANDIDATEFORM { - dwStyle: CFS_CANDIDATEPOS, - // logical to physical - ptCurrentPos: POINT { - x: (caret_position.origin.x.0 * scale_factor) as i32, - y: (caret_position.origin.y.0 * scale_factor) as i32 - + ((caret_position.size.height.0 * scale_factor) as i32 / 2), - }, - ..Default::default() - }; - ImmSetCandidateWindow(ctx, &config as _).ok().log_err(); + { + let config = COMPOSITIONFORM { + dwStyle: CFS_POINT, + ptCurrentPos: caret_position, + ..Default::default() + }; + ImmSetCompositionWindow(ctx, &config as _).ok().log_err(); + } + { + let config = CANDIDATEFORM { + dwStyle: CFS_CANDIDATEPOS, + ptCurrentPos: caret_position, + ..Default::default() + }; + ImmSetCandidateWindow(ctx, &config as _).ok().log_err(); + } ImmReleaseContext(handle, ctx).ok().log_err(); Some(0) } @@ -617,35 +621,46 @@ fn handle_ime_composition( handle: HWND, lparam: LPARAM, state_ptr: Rc, +) -> Option { + let ctx = unsafe { ImmGetContext(handle) }; + let result = handle_ime_composition_inner(ctx, lparam, state_ptr); + unsafe { ImmReleaseContext(handle, ctx).ok().log_err() }; + result +} + +fn handle_ime_composition_inner( + ctx: HIMC, + lparam: LPARAM, + state_ptr: Rc, ) -> Option { let mut ime_input = None; if lparam.0 as u32 & GCS_COMPSTR.0 > 0 { - let (comp_string, string_len) = parse_ime_compostion_string(handle)?; - let mut input_handler = state_ptr.state.borrow_mut().input_handler.take()?; - input_handler.replace_and_mark_text_in_range( - None, - &comp_string, - Some(string_len..string_len), - ); - state_ptr.state.borrow_mut().input_handler = Some(input_handler); + let (comp_string, string_len) = parse_ime_compostion_string(ctx)?; + with_input_handler(&state_ptr, |input_handler| { + input_handler.replace_and_mark_text_in_range( + None, + &comp_string, + Some(string_len..string_len), + ); + })?; ime_input = Some(comp_string); } if lparam.0 as u32 & GCS_CURSORPOS.0 > 0 { let comp_string = &ime_input?; - let caret_pos = retrieve_composition_cursor_position(handle); - let mut input_handler = state_ptr.state.borrow_mut().input_handler.take()?; - input_handler.replace_and_mark_text_in_range(None, comp_string, Some(caret_pos..caret_pos)); - state_ptr.state.borrow_mut().input_handler = Some(input_handler); + let caret_pos = retrieve_composition_cursor_position(ctx); + with_input_handler(&state_ptr, |input_handler| { + input_handler.replace_and_mark_text_in_range( + None, + comp_string, + Some(caret_pos..caret_pos), + ); + })?; } if lparam.0 as u32 & GCS_RESULTSTR.0 > 0 { - let comp_result = parse_ime_compostion_result(handle)?; - let mut lock = state_ptr.state.borrow_mut(); - let Some(mut input_handler) = lock.input_handler.take() else { - return Some(1); - }; - drop(lock); - input_handler.replace_text_in_range(None, &comp_result); - state_ptr.state.borrow_mut().input_handler = Some(input_handler); + let comp_result = parse_ime_compostion_result(ctx)?; + with_input_handler(&state_ptr, |input_handler| { + input_handler.replace_text_in_range(None, &comp_result); + })?; return Some(0); } // currently, we don't care other stuff @@ -1218,11 +1233,10 @@ fn parse_char_msg_keystroke(wparam: WPARAM) -> Option { } } -fn parse_ime_compostion_string(handle: HWND) -> Option<(String, usize)> { +fn parse_ime_compostion_string(ctx: HIMC) -> Option<(String, usize)> { unsafe { - let ctx = ImmGetContext(handle); let string_len = ImmGetCompositionStringW(ctx, GCS_COMPSTR, None, 0); - let result = if string_len >= 0 { + if string_len >= 0 { let mut buffer = vec![0u8; string_len as usize + 2]; ImmGetCompositionStringW( ctx, @@ -1238,26 +1252,19 @@ fn parse_ime_compostion_string(handle: HWND) -> Option<(String, usize)> { Some((string, string_len as usize / 2)) } else { None - }; - ImmReleaseContext(handle, ctx).ok().log_err(); - result + } } } -fn retrieve_composition_cursor_position(handle: HWND) -> usize { - unsafe { - let ctx = ImmGetContext(handle); - let ret = ImmGetCompositionStringW(ctx, GCS_CURSORPOS, None, 0); - ImmReleaseContext(handle, ctx).ok().log_err(); - ret as usize - } +#[inline] +fn retrieve_composition_cursor_position(ctx: HIMC) -> usize { + unsafe { ImmGetCompositionStringW(ctx, GCS_CURSORPOS, None, 0) as usize } } -fn parse_ime_compostion_result(handle: HWND) -> Option { +fn parse_ime_compostion_result(ctx: HIMC) -> Option { unsafe { - let ctx = ImmGetContext(handle); let string_len = ImmGetCompositionStringW(ctx, GCS_RESULTSTR, None, 0); - let result = if string_len >= 0 { + if string_len >= 0 { let mut buffer = vec![0u8; string_len as usize + 2]; ImmGetCompositionStringW( ctx, @@ -1273,9 +1280,7 @@ fn parse_ime_compostion_result(handle: HWND) -> Option { Some(string) } else { None - }; - ImmReleaseContext(handle, ctx).ok().log_err(); - result + } } } @@ -1323,3 +1328,29 @@ pub(crate) fn current_modifiers() -> Modifiers { function: false, } } + +fn with_input_handler(state_ptr: &Rc, f: F) -> Option +where + F: FnOnce(&mut PlatformInputHandler) -> R, +{ + let mut input_handler = state_ptr.state.borrow_mut().input_handler.take()?; + let result = f(&mut input_handler); + state_ptr.state.borrow_mut().input_handler = Some(input_handler); + Some(result) +} + +fn with_input_handler_and_scale_factor( + state_ptr: &Rc, + f: F, +) -> Option +where + F: FnOnce(&mut PlatformInputHandler, f32) -> Option, +{ + let mut lock = state_ptr.state.borrow_mut(); + let mut input_handler = lock.input_handler.take()?; + let scale_factor = lock.scale_factor; + drop(lock); + let result = f(&mut input_handler, scale_factor); + state_ptr.state.borrow_mut().input_handler = Some(input_handler); + result +}