Improve key handling on x11, sharing wayland implementation (#8094)

gmorenz created

Makes keyboard shortcuts work on x11.

Release Notes:

- N/A

Change summary

crates/gpui/src/platform/linux.rs                |  1 
crates/gpui/src/platform/linux/util.rs           | 28 +++++++++++++
crates/gpui/src/platform/linux/wayland/client.rs | 37 +++++------------
crates/gpui/src/platform/linux/x11/client.rs     | 28 ++++--------
crates/gpui/src/platform/linux/x11/window.rs     |  4 +
5 files changed, 53 insertions(+), 45 deletions(-)

Detailed changes

crates/gpui/src/platform/linux/util.rs 🔗

@@ -0,0 +1,28 @@
+use xkbcommon::xkb::{self, Keycode, Keysym, State};
+
+use crate::{Keystroke, Modifiers};
+
+impl Keystroke {
+    pub(super) fn from_xkb(state: &State, modifiers: Modifiers, keycode: Keycode) -> Self {
+        let key_utf32 = state.key_get_utf32(keycode);
+        let key_utf8 = state.key_get_utf8(keycode);
+        let key_sym = state.key_get_one_sym(keycode);
+
+        let key = match key_sym {
+            Keysym::Return => "enter".to_owned(),
+            Keysym::Prior => "pageup".to_owned(),
+            Keysym::Next => "pagedown".to_owned(),
+            _ => xkb::keysym_get_name(key_sym).to_lowercase(),
+        };
+
+        // Ignore control characters (and DEL) for the purposes of ime_key,
+        // but if key_utf32 is 0 then assume it isn't one
+        let ime_key = (key_utf32 == 0 || (key_utf32 >= 32 && key_utf32 != 127)).then_some(key_utf8);
+
+        Keystroke {
+            modifiers,
+            key,
+            ime_key,
+        }
+    }
+}

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

@@ -24,8 +24,7 @@ use wayland_protocols::xdg::decoration::zv1::client::{
 };
 use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};
 use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;
-use xkbcommon::xkb::{self, Keysym};
-use xkbcommon::xkb::{Keycode, KEYMAP_COMPILE_NO_FLAGS};
+use xkbcommon::xkb::{self, Keycode, KEYMAP_COMPILE_NO_FLAGS};
 
 use crate::platform::linux::client::Client;
 use crate::platform::linux::wayland::window::{WaylandDecorationState, WaylandWindow};
@@ -453,41 +452,27 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientState {
             } => {
                 let keymap_state = state.keymap_state.as_ref().unwrap();
                 let keycode = Keycode::from(key + MIN_KEYCODE);
-                let key_utf32 = keymap_state.key_get_utf32(keycode);
-                let key_utf8 = keymap_state.key_get_utf8(keycode);
-                let key_sym = keymap_state.key_get_one_sym(keycode);
-                let key = match key_sym {
-                    Keysym::Return => "enter".to_owned(),
-                    Keysym::Prior => "pageup".to_owned(),
-                    Keysym::Next => "pagedown".to_owned(),
-                    _ => xkb::keysym_get_name(key_sym).to_lowercase(),
-                };
-
-                // Ignore control characters (and DEL) for the purposes of ime_key,
-                // but if key_utf32 is 0 then assume it isn't one
-                let ime_key =
-                    (key_utf32 == 0 || (key_utf32 >= 32 && key_utf32 != 127)).then_some(key_utf8);
 
                 let focused_window = &state.keyboard_focused_window;
                 if let Some(focused_window) = focused_window {
                     match key_state {
                         wl_keyboard::KeyState::Pressed => {
                             focused_window.handle_input(PlatformInput::KeyDown(KeyDownEvent {
-                                keystroke: Keystroke {
-                                    modifiers: state.modifiers,
-                                    key,
-                                    ime_key,
-                                },
+                                keystroke: Keystroke::from_xkb(
+                                    keymap_state,
+                                    state.modifiers,
+                                    keycode,
+                                ),
                                 is_held: false, // todo!(linux)
                             }));
                         }
                         wl_keyboard::KeyState::Released => {
                             focused_window.handle_input(PlatformInput::KeyUp(KeyUpEvent {
-                                keystroke: Keystroke {
-                                    modifiers: state.modifiers,
-                                    key,
-                                    ime_key,
-                                },
+                                keystroke: Keystroke::from_xkb(
+                                    keymap_state,
+                                    state.modifiers,
+                                    keycode,
+                                ),
                             }));
                         }
                         _ => {}

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

@@ -118,39 +118,31 @@ impl Client for X11Client {
                 xcb::Event::X(x::Event::KeyPress(ev)) => {
                     let window = self.get_window(ev.event());
                     let modifiers = super::modifiers_from_state(ev.state());
-                    let key = {
+                    let keystroke = {
                         let code = ev.detail().into();
                         let mut state = self.state.lock();
-                        let key = state.xkb.key_get_utf8(code);
+                        let keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
                         state.xkb.update_key(code, xkb::KeyDirection::Down);
-                        key
+                        keystroke
                     };
+
                     window.handle_input(PlatformInput::KeyDown(crate::KeyDownEvent {
-                        keystroke: crate::Keystroke {
-                            modifiers,
-                            key,
-                            ime_key: None,
-                        },
+                        keystroke,
                         is_held: false,
                     }));
                 }
                 xcb::Event::X(x::Event::KeyRelease(ev)) => {
                     let window = self.get_window(ev.event());
                     let modifiers = super::modifiers_from_state(ev.state());
-                    let key = {
+                    let keystroke = {
                         let code = ev.detail().into();
                         let mut state = self.state.lock();
-                        let key = state.xkb.key_get_utf8(code);
+                        let keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
                         state.xkb.update_key(code, xkb::KeyDirection::Up);
-                        key
+                        keystroke
                     };
-                    window.handle_input(PlatformInput::KeyUp(crate::KeyUpEvent {
-                        keystroke: crate::Keystroke {
-                            modifiers,
-                            key,
-                            ime_key: None,
-                        },
-                    }));
+
+                    window.handle_input(PlatformInput::KeyUp(crate::KeyUpEvent { keystroke }));
                 }
                 xcb::Event::X(x::Event::ButtonPress(ev)) => {
                     let window = self.get_window(ev.event());

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

@@ -338,7 +338,9 @@ impl X11WindowState {
         if let PlatformInput::KeyDown(event) = input {
             let mut inner = self.inner.lock();
             if let Some(ref mut input_handler) = inner.input_handler {
-                input_handler.replace_text_in_range(None, &event.keystroke.key);
+                if let Some(ime_key) = &event.keystroke.ime_key {
+                    input_handler.replace_text_in_range(None, ime_key);
+                }
             }
         }
     }