ime

Smit Barmase created

Change summary

crates/gpui/src/platform/linux/x11/client.rs      | 47 +++++++++++++---
crates/gpui/src/platform/linux/x11/xim_handler.rs | 17 ++---
2 files changed, 44 insertions(+), 20 deletions(-)

Detailed changes

crates/gpui/src/platform/linux/x11/client.rs 🔗

@@ -655,6 +655,20 @@ impl X11Client {
                 state.restore_xim(ximc, xim_handler);
                 drop(state);
 
+                // Create IC when IME becomes ready while we already have focus
+                {
+                    let s = self.0.borrow();
+                    let need_enable = s
+                        .xim_handler
+                        .as_ref()
+                        .map(|h| h.ready && !h.connected && s.keyboard_focused_window.is_some())
+                        .unwrap_or(false);
+                    drop(s);
+                    if need_enable {
+                        self.enable_ime();
+                    }
+                }
+
                 if let Some(event) = xim_callback_event {
                     self.handle_xim_callback_event(event);
                 }
@@ -682,22 +696,25 @@ impl X11Client {
         let Some((mut ximc, mut xim_handler)) = state.take_xim() else {
             return;
         };
+
+        let window_id = state.keyboard_focused_window;
+        if window_id.is_none() {
+            // no focus -> nothing to bind to; put XIM back and bail
+            state.restore_xim(ximc, xim_handler);
+            return;
+        }
+        let window_id = window_id.unwrap();
+        // ensure handler.window is the actual focused window
+        xim_handler.window = window_id;
+
         let mut ic_attributes = ximc
             .build_ic_attributes()
             .push(AttributeName::InputStyle, InputStyle::PREEDIT_CALLBACKS)
             .push(AttributeName::ClientWindow, xim_handler.window)
             .push(AttributeName::FocusWindow, xim_handler.window);
 
-        let window_id = state.keyboard_focused_window;
         drop(state);
-        if let Some(window_id) = window_id {
-            let Some(window) = self.get_window(window_id) else {
-                log::error!("Failed to get window for IME positioning");
-                let mut state = self.0.borrow_mut();
-                state.ximc = Some(ximc);
-                state.xim_handler = Some(xim_handler);
-                return;
-            };
+        if let Some(window) = self.get_window(window_id) {
             if let Some(area) = window.get_ime_area() {
                 ic_attributes =
                     ic_attributes.nested_list(xim::AttributeName::PreeditAttributes, |b| {
@@ -710,9 +727,17 @@ impl X11Client {
                         );
                     });
             }
+        } else {
+            log::error!("Failed to get window for IME positioning");
         }
-        ximc.create_ic(xim_handler.im_id, ic_attributes.build())
-            .ok();
+
+        // if we already have an IC, just rebind its window/spot; else create it
+        if xim_handler.connected {
+            let _ = ximc.set_ic_values(xim_handler.im_id, xim_handler.ic_id, ic_attributes.build());
+        } else {
+            let _ = ximc.create_ic(xim_handler.im_id, ic_attributes.build());
+        }
+
         let mut state = self.0.borrow_mut();
         state.restore_xim(ximc, xim_handler);
     }

crates/gpui/src/platform/linux/x11/xim_handler.rs 🔗

@@ -13,6 +13,7 @@ pub struct XimHandler {
     pub im_id: u16,
     pub ic_id: u16,
     pub connected: bool,
+    pub ready: bool,
     pub window: xproto::Window,
     pub last_callback_event: Option<XimCallbackEvent>,
 }
@@ -23,6 +24,7 @@ impl XimHandler {
             im_id: Default::default(),
             ic_id: Default::default(),
             connected: false,
+            ready: false,
             window: Default::default(),
             last_callback_event: None,
         }
@@ -42,17 +44,14 @@ impl<C: Client<XEvent = xproto::KeyPressEvent>> ClientHandler<C> for XimHandler
 
     fn handle_get_im_values(
         &mut self,
-        client: &mut C,
-        input_method_id: u16,
+        _client: &mut C,
+        _input_method_id: u16,
         _attributes: AHashMap<AttributeName, Vec<u8>>,
     ) -> Result<(), ClientError> {
-        let ic_attributes = client
-            .build_ic_attributes()
-            .push(AttributeName::InputStyle, InputStyle::PREEDIT_CALLBACKS)
-            .push(AttributeName::ClientWindow, self.window)
-            .push(AttributeName::FocusWindow, self.window)
-            .build();
-        client.create_ic(input_method_id, ic_attributes)
+        // IM is ready, but we don't know the real window yet.
+        // Defer IC creation to client.enable_ime().
+        self.ready = true;
+        Ok(())
     }
 
     fn handle_create_ic(