linux: Fix keyboard events not working on first start in X11 (#36224)

smit created

Closes #29083

On X11, `ibus-x11` crashes on some distros after Zed interacts with it.
This is not unique to Zed, `xim-rs` shows the same behavior, and there
are similar upstream `ibus` reports with apps like Blender:

- https://github.com/ibus/ibus/issues/2697

I opened an upstream issue to track this:

- https://github.com/ibus/ibus/issues/2789

When this crash happens, we don’t get a disconnect event, so Zed keeps
sending events to the IM server and waits for a response. It works on
subsequent starts because IM server doesn't exist now and we default to
non-XIM path.

This PR detects the crash via X11 events and falls back to the non-XIM
path so typing keeps working. We still need to investigate whether the
root cause is in `xim-rs` or `ibus-x11`.

Release Notes:

- Fixed an issue on X11 where keyboard input sometimes didn’t work on
first start.

Change summary

Cargo.lock                                   |  6 +-
crates/gpui/Cargo.toml                       |  2 
crates/gpui/src/platform/linux/x11/client.rs | 38 +++++++++++++--------
3 files changed, 27 insertions(+), 19 deletions(-)

Detailed changes

Cargo.lock πŸ”—

@@ -20277,7 +20277,7 @@ dependencies = [
 [[package]]
 name = "xim"
 version = "0.4.0"
-source = "git+https://github.com/XDeme1/xim-rs?rev=d50d461764c2213655cd9cf65a0ea94c70d3c4fd#d50d461764c2213655cd9cf65a0ea94c70d3c4fd"
+source = "git+https://github.com/zed-industries/xim-rs?rev=c0a70c1bd2ce197364216e5e818a2cb3adb99a8d#c0a70c1bd2ce197364216e5e818a2cb3adb99a8d"
 dependencies = [
  "ahash 0.8.11",
  "hashbrown 0.14.5",
@@ -20290,7 +20290,7 @@ dependencies = [
 [[package]]
 name = "xim-ctext"
 version = "0.3.0"
-source = "git+https://github.com/XDeme1/xim-rs?rev=d50d461764c2213655cd9cf65a0ea94c70d3c4fd#d50d461764c2213655cd9cf65a0ea94c70d3c4fd"
+source = "git+https://github.com/zed-industries/xim-rs?rev=c0a70c1bd2ce197364216e5e818a2cb3adb99a8d#c0a70c1bd2ce197364216e5e818a2cb3adb99a8d"
 dependencies = [
  "encoding_rs",
 ]
@@ -20298,7 +20298,7 @@ dependencies = [
 [[package]]
 name = "xim-parser"
 version = "0.2.1"
-source = "git+https://github.com/XDeme1/xim-rs?rev=d50d461764c2213655cd9cf65a0ea94c70d3c4fd#d50d461764c2213655cd9cf65a0ea94c70d3c4fd"
+source = "git+https://github.com/zed-industries/xim-rs?rev=c0a70c1bd2ce197364216e5e818a2cb3adb99a8d#c0a70c1bd2ce197364216e5e818a2cb3adb99a8d"
 dependencies = [
  "bitflags 2.9.0",
 ]

crates/gpui/Cargo.toml πŸ”—

@@ -209,7 +209,7 @@ xkbcommon = { version = "0.8.0", features = [
     "wayland",
     "x11",
 ], optional = true }
-xim = { git = "https://github.com/XDeme1/xim-rs", rev = "d50d461764c2213655cd9cf65a0ea94c70d3c4fd", features = [
+xim = { git = "https://github.com/zed-industries/xim-rs", rev = "c0a70c1bd2ce197364216e5e818a2cb3adb99a8d" , features = [
     "x11rb-xcb",
     "x11rb-client",
 ], optional = true }

crates/gpui/src/platform/linux/x11/client.rs πŸ”—

@@ -642,13 +642,7 @@ impl X11Client {
                 let xim_connected = xim_handler.connected;
                 drop(state);
 
-                let xim_filtered = match ximc.filter_event(&event, &mut xim_handler) {
-                    Ok(handled) => handled,
-                    Err(err) => {
-                        log::error!("XIMClientError: {}", err);
-                        false
-                    }
-                };
+                let xim_filtered = ximc.filter_event(&event, &mut xim_handler);
                 let xim_callback_event = xim_handler.last_callback_event.take();
 
                 let mut state = self.0.borrow_mut();
@@ -659,14 +653,28 @@ impl X11Client {
                     self.handle_xim_callback_event(event);
                 }
 
-                if xim_filtered {
-                    continue;
-                }
-
-                if xim_connected {
-                    self.xim_handle_event(event);
-                } else {
-                    self.handle_event(event);
+                match xim_filtered {
+                    Ok(handled) => {
+                        if handled {
+                            continue;
+                        }
+                        if xim_connected {
+                            self.xim_handle_event(event);
+                        } else {
+                            self.handle_event(event);
+                        }
+                    }
+                    Err(err) => {
+                        // this might happen when xim server crashes on one of the events
+                        // we do lose 1-2 keys when crash happens since there is no reliable way to get that info
+                        // luckily, x11 sends us window not found error when xim server crashes upon further key press
+                        // hence we fall back to handle_event
+                        log::error!("XIMClientError: {}", err);
+                        let mut state = self.0.borrow_mut();
+                        state.take_xim();
+                        drop(state);
+                        self.handle_event(event);
+                    }
                 }
             }
         }