add helper

Junkui Zhang created

Change summary

crates/gpui/src/platform/linux/keyboard.rs | 159 ++++++++---------------
1 file changed, 57 insertions(+), 102 deletions(-)

Detailed changes

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

@@ -1,7 +1,7 @@
 #[cfg(any(feature = "wayland", feature = "x11"))]
 use collections::{HashMap, HashSet};
 #[cfg(any(feature = "wayland", feature = "x11"))]
-use strum::EnumIter;
+use strum::{EnumIter, IntoEnumIterator as _};
 #[cfg(any(feature = "wayland", feature = "x11"))]
 use xkbcommon::xkb::{Keycode, Keysym};
 
@@ -47,16 +47,13 @@ impl LinuxKeyboardMapper {
         let shift_mask = 1 << shift_mod;
         shifted_state.update_mask(shift_mask, 0, 0, 0, 0, 0);
 
-        for &scan_code in TYPEABLE_CODES {
-            let keycode = Keycode::new(scan_code);
-            let key = xkb_state.key_get_utf8(keycode);
+        for scan_code in LinuxScanCodes::iter() {
+            let keycode = Keycode::new(scan_code as u32);
 
+            let key = xkb_state.key_get_utf8(keycode);
             if !key.is_empty() {
                 code_to_key.insert(keycode, key.clone());
                 inserted.insert(key);
-
-                let shifted_key = shifted_state.key_get_utf8(keycode);
-                code_to_shifted_key.insert(keycode, shifted_key);
             } else {
                 // keycode might be a dead key
                 let keysym = xkb_state.key_get_one_sym(keycode);
@@ -64,7 +61,13 @@ impl LinuxKeyboardMapper {
                     code_to_key.insert(keycode, key.clone());
                     inserted.insert(key);
                 }
+            }
 
+            let shifted_key = shifted_state.key_get_utf8(keycode);
+            if !shifted_key.is_empty() {
+                code_to_shifted_key.insert(keycode, shifted_key);
+            } else {
+                // keycode might be a dead key
                 let shifted_keysym = shifted_state.key_get_one_sym(keycode);
                 if let Some(shifted_key) = underlying_dead_key(shifted_keysym) {
                     code_to_shifted_key.insert(keycode, shifted_key);
@@ -186,46 +189,18 @@ pub(crate) fn underlying_dead_key(keysym: Keysym) -> Option<String> {
     }
 }
 
-#[cfg(any(feature = "wayland", feature = "x11"))]
-macro_rules! insert_letters_if_missing_internal {
-    ($inserted:expr, $code_to_key:expr, $code:expr, $key:literal) => {
-        if !$inserted.contains($key) {
-            $code_to_key.insert($code, $key.to_string());
-        }
-    };
-}
-
 #[cfg(any(feature = "wayland", feature = "x11"))]
 fn insert_letters_if_missing(
     inserted: &HashSet<String>,
     code_to_key: &mut HashMap<Keycode, String>,
 ) {
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0026), "a");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0038), "b");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0036), "c");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0028), "d");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x001a), "e");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0029), "f");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x002a), "g");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x002b), "h");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x001f), "i");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x002c), "j");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x002d), "k");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x002e), "l");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x003a), "m");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0039), "n");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0020), "o");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0021), "p");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0018), "q");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x001b), "r");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0027), "s");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x001c), "t");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x001e), "u");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0037), "v");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0019), "w");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0035), "x");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x001d), "y");
-    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0034), "z");
+    for scan_code in LinuxScanCodes::LETTERS.iter() {
+        let keycode = Keycode::new(*scan_code as u32);
+        let key = scan_code.to_str();
+        if !inserted.contains(key) {
+            code_to_key.insert(keycode, key.to_string());
+        }
+    }
 }
 
 #[cfg(any(feature = "wayland", feature = "x11"))]
@@ -278,13 +253,43 @@ enum LinuxScanCodes {
     Comma = 0x003b,
     Period = 0x003c,
     Slash = 0x003d,
+    // This key is typically located near LeftShift key, varies on international keyboards: Dan: <> Dutch: ][ Ger: <> UK: \|
     IntlBackslash = 0x005e,
+    // Used for Brazilian /? and Japanese _ 'ro'.
     IntlRo = 0x0061,
 }
 
 #[cfg(any(feature = "wayland", feature = "x11"))]
-#[cfg(test)]
 impl LinuxScanCodes {
+    const LETTERS: &'static [LinuxScanCodes] = &[
+        LinuxScanCodes::A,
+        LinuxScanCodes::B,
+        LinuxScanCodes::C,
+        LinuxScanCodes::D,
+        LinuxScanCodes::E,
+        LinuxScanCodes::F,
+        LinuxScanCodes::G,
+        LinuxScanCodes::H,
+        LinuxScanCodes::I,
+        LinuxScanCodes::J,
+        LinuxScanCodes::K,
+        LinuxScanCodes::L,
+        LinuxScanCodes::M,
+        LinuxScanCodes::N,
+        LinuxScanCodes::O,
+        LinuxScanCodes::P,
+        LinuxScanCodes::Q,
+        LinuxScanCodes::R,
+        LinuxScanCodes::S,
+        LinuxScanCodes::T,
+        LinuxScanCodes::U,
+        LinuxScanCodes::V,
+        LinuxScanCodes::W,
+        LinuxScanCodes::X,
+        LinuxScanCodes::Y,
+        LinuxScanCodes::Z,
+    ];
+
     fn to_str(&self) -> &str {
         match self {
             LinuxScanCodes::A => "a",
@@ -334,67 +339,13 @@ impl LinuxScanCodes {
             LinuxScanCodes::Comma => ",",
             LinuxScanCodes::Period => ".",
             LinuxScanCodes::Slash => "/",
-            LinuxScanCodes::IntlBackslash => "⧸",
-            LinuxScanCodes::IntlRo => "ʁ",
+            LinuxScanCodes::IntlBackslash => "unknown",
+            LinuxScanCodes::IntlRo => "unknown",
         }
     }
 }
 
-// All typeable scan codes for the standard US keyboard layout, ANSI104
-#[cfg(any(feature = "wayland", feature = "x11"))]
-const TYPEABLE_CODES: &[u32] = &[
-    0x0026, // a
-    0x0038, // b
-    0x0036, // c
-    0x0028, // d
-    0x001a, // e
-    0x0029, // f
-    0x002a, // g
-    0x002b, // h
-    0x001f, // i
-    0x002c, // j
-    0x002d, // k
-    0x002e, // l
-    0x003a, // m
-    0x0039, // n
-    0x0020, // o
-    0x0021, // p
-    0x0018, // q
-    0x001b, // r
-    0x0027, // s
-    0x001c, // t
-    0x001e, // u
-    0x0037, // v
-    0x0019, // w
-    0x0035, // x
-    0x001d, // y
-    0x0034, // z
-    0x0013, // Digit 0
-    0x000a, // Digit 1
-    0x000b, // Digit 2
-    0x000c, // Digit 3
-    0x000d, // Digit 4
-    0x000e, // Digit 5
-    0x000f, // Digit 6
-    0x0010, // Digit 7
-    0x0011, // Digit 8
-    0x0012, // Digit 9
-    0x0031, // ` Backquote
-    0x0014, // - Minus
-    0x0015, // = Equal
-    0x0022, // [ Left bracket
-    0x0023, // ] Right bracket
-    0x0033, // \ Backslash
-    0x002f, // ; Semicolon
-    0x0030, // ' Quote
-    0x003b, // , Comma
-    0x003c, // . Period
-    0x003d, // / Slash
-    0x005e, // This key is typically located near LeftShift key, varies on international keyboards: Dan: <> Dutch: ][ Ger: <> UK: \|
-    0x0061, // Used for Brazilian /? and Japanese _ 'ro'.
-];
-
-#[cfg(test)]
+#[cfg(all(test, any(feature = "wayland", feature = "x11")))]
 mod tests {
     use std::sync::LazyLock;
 
@@ -402,6 +353,8 @@ mod tests {
     use x11rb::{protocol::xkb::ConnectionExt as _, xcb_ffi::XCBConnection};
     use xkbcommon::xkb::x11::ffi::{XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION};
 
+    use crate::platform::linux::keyboard::LinuxScanCodes;
+
     use super::LinuxKeyboardMapper;
 
     static XCB_CONNECTION: LazyLock<XCBConnection> =
@@ -431,10 +384,12 @@ mod tests {
     fn test_us_layout_mapper() {
         let mapper = create_test_mapper();
         for scan_code in super::LinuxScanCodes::iter() {
+            if scan_code == LinuxScanCodes::IntlBackslash || scan_code == LinuxScanCodes::IntlRo {
+                continue;
+            }
             let keycode = xkbcommon::xkb::Keycode::new(scan_code as u32);
             let key = mapper.get_key(keycode, &mut crate::Modifiers::default());
-            // assert_eq!(key, Some(scan_code.to_str().to_string()));
-            println!("Keycode: {:?}, Key: {:?}", keycode, key);
+            assert_eq!(key, Some(scan_code.to_str().to_string()));
         }
     }
 }