From 40a3455f55ad3aba522ae36356bd8763ca761d1c Mon Sep 17 00:00:00 2001 From: elm <71685838+not-elm@users.noreply.github.com> Date: Fri, 6 Feb 2026 23:52:05 +0900 Subject: [PATCH] Fix IME input handling (#20) bevy_winit does not call `set_ime_allowed()` during initial window creation when `Window::ime_enabled` is `true`. The `changed_windows` system only fires when the value differs from cache, but the cache is initialized from the window itself so they start equal. Since winit 0.27+ requires explicit `set_ime_allowed(true)`, no Ime events were ever generated. --- .../browser_process_handler.rs | 1 + .../src/browser_process/browsers.rs | 33 +++++++++++-------- .../src/browser_process/renderer_handler.rs | 18 +--------- examples/simple.rs | 11 ++++++- src/keyboard.rs | 2 +- 5 files changed, 33 insertions(+), 32 deletions(-) diff --git a/crates/bevy_cef_core/src/browser_process/browser_process_handler.rs b/crates/bevy_cef_core/src/browser_process/browser_process_handler.rs index 3739d8f..13c6648 100644 --- a/crates/bevy_cef_core/src/browser_process/browser_process_handler.rs +++ b/crates/bevy_cef_core/src/browser_process/browser_process_handler.rs @@ -68,6 +68,7 @@ impl ImplBrowserProcessHandler for BrowserProcessHandlerBuilder { command_line.append_switch(Some(&"ignore-certificate-errors".into())); command_line.append_switch(Some(&"ignore-ssl-errors".into())); command_line.append_switch(Some(&"enable-logging=stderr".into())); + command_line.append_switch(Some(&"disable-web-security".into())); // Pass extensions to render process via command line if !self.extensions.is_empty() && let Ok(json) = serde_json::to_string(&self.extensions.0) diff --git a/crates/bevy_cef_core/src/browser_process/browsers.rs b/crates/bevy_cef_core/src/browser_process/browsers.rs index 178b181..cee80d0 100644 --- a/crates/bevy_cef_core/src/browser_process/browsers.rs +++ b/crates/bevy_cef_core/src/browser_process/browsers.rs @@ -31,7 +31,6 @@ pub struct WebviewBrowser { pub client: Browser, pub host: BrowserHost, pub size: SharedViewSize, - pub ime_caret: SharedImeCaret, } pub struct Browsers { @@ -67,7 +66,6 @@ impl Browsers { ) { let mut context = Self::request_context(requester); let size = Rc::new(Cell::new(webview_size)); - let ime_caret = Rc::new(Cell::new(0)); let browser = browser_host_create_browser_sync( Some(&WindowInfo { windowless_rendering_enabled: true as _, @@ -86,7 +84,6 @@ impl Browsers { Some(&mut self.client_handler( webview, size.clone(), - ime_caret.clone(), ipc_event_sender, brp_sender, system_cursor_icon_sender, @@ -105,7 +102,6 @@ impl Browsers { host, client: browser, size, - ime_caret, }; self.browsers.insert(webview, webview_browser); @@ -332,7 +328,7 @@ impl Browsers { .values() .filter(|b| b.client.focused_frame().is_some()) { - let replacement_range = Self::ime_caret_range_for(browser); + let replacement_range = Self::ime_caret_range_for(); browser.host.ime_set_composition( Some(&text.into()), Some(&underlines), @@ -342,6 +338,19 @@ impl Browsers { } } + /// ## Reference + /// + /// [`ImeCancelComposition`](https://cef-builds.spotifycdn.com/docs/122.0/classCefBrowserHost.html#ac12a8076859d0c1e58e55080f698e7a9) + pub fn ime_cancel_composition(&self) { + for browser in self + .browsers + .values() + .filter(|b| b.client.focused_frame().is_some()) + { + browser.host.ime_cancel_composition(); + } + } + /// ## Reference /// /// [`ImeSetComposition`](https://cef-builds.spotifycdn.com/docs/122.0/classCefBrowserHost.html#a567b41fb2d3917843ece3b57adc21ebe) @@ -361,10 +370,10 @@ impl Browsers { .values() .filter(|b| b.client.focused_frame().is_some()) { - let replacement_range = Self::ime_caret_range_for(browser); + let replacement_range = Self::ime_caret_range_for(); browser .host - .ime_commit_text(Some(&text.into()), Some(&replacement_range), 0) + .ime_commit_text(Some(&text.into()), Some(&replacement_range), 0); } } @@ -387,7 +396,6 @@ impl Browsers { &self, webview: Entity, size: SharedViewSize, - ime_caret: SharedImeCaret, ipc_event_sender: Sender, brp_sender: Sender, system_cursor_icon_sender: SystemCursorIconSenderInner, @@ -396,7 +404,6 @@ impl Browsers { webview, self.sender.clone(), size.clone(), - ime_caret, )) .with_display_handler(DisplayHandlerBuilder::build(system_cursor_icon_sender)) .with_message_handler(JsEmitEventHandler::new(webview, ipc_event_sender)) @@ -405,11 +412,11 @@ impl Browsers { } #[inline] - fn ime_caret_range_for(browser: &WebviewBrowser) -> Range { - let caret = browser.ime_caret.get(); + fn ime_caret_range_for() -> Range { + // Use sentinel replacement range to indicate caret position Range { - from: caret, - to: caret, + from: u32::MAX, + to: u32::MAX, } } diff --git a/crates/bevy_cef_core/src/browser_process/renderer_handler.rs b/crates/bevy_cef_core/src/browser_process/renderer_handler.rs index 871abb3..f138628 100644 --- a/crates/bevy_cef_core/src/browser_process/renderer_handler.rs +++ b/crates/bevy_cef_core/src/browser_process/renderer_handler.rs @@ -33,8 +33,7 @@ pub enum RenderPaintElementType { Popup, } -pub type SharedViewSize = std::rc::Rc>; -pub type SharedImeCaret = std::rc::Rc>; +pub type SharedViewSize = std::rc::Rc>; /// ## Reference /// @@ -44,7 +43,6 @@ pub struct RenderHandlerBuilder { webview: Entity, texture_sender: TextureSender, size: SharedViewSize, - ime_caret: SharedImeCaret, } impl RenderHandlerBuilder { @@ -52,14 +50,12 @@ impl RenderHandlerBuilder { webview: Entity, texture_sender: TextureSender, size: SharedViewSize, - ime_caret: SharedImeCaret, ) -> RenderHandler { RenderHandler::new(Self { object: std::ptr::null_mut(), webview, texture_sender, size, - ime_caret, }) } } @@ -91,7 +87,6 @@ impl Clone for RenderHandlerBuilder { webview: self.webview, texture_sender: self.texture_sender.clone(), size: self.size.clone(), - ime_caret: self.ime_caret.clone(), } } } @@ -131,17 +126,6 @@ impl ImplRenderHandler for RenderHandlerBuilder { let _ = self.texture_sender.send_blocking(texture); } - fn on_text_selection_changed( - &self, - _browser: Option<&mut Browser>, - _: Option<&CefString>, - selected_range: Option<&Range>, - ) { - if let Some(selected_range) = selected_range { - self.ime_caret.set(selected_range.to); - } - } - #[inline] fn get_raw(&self) -> *mut sys::_cef_render_handler_t { self.object.cast() diff --git a/examples/simple.rs b/examples/simple.rs index 3cfd51d..808bc05 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -5,7 +5,16 @@ use bevy_cef::prelude::*; fn main() { App::new() - .add_plugins((DefaultPlugins, CefPlugin::default())) + .add_plugins(( + DefaultPlugins.set(WindowPlugin { + primary_window: Some(Window { + ime_enabled: true, + ..default() + }), + ..default() + }), + CefPlugin::default(), + )) .add_systems( Startup, (spawn_camera, spawn_directional_light, spawn_webview), diff --git a/src/keyboard.rs b/src/keyboard.rs index 554afde..793dc9e 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -104,7 +104,7 @@ fn ime_event( is_ime_commiting.0 = true; } Ime::Disabled { .. } => { - browsers.ime_finish_composition(false); + browsers.ime_cancel_composition(); } _ => {} }