fix deadkey

Junkui Zhang created

Change summary

crates/gpui/src/platform/linux/keyboard.rs | 85 ++++++++++++++++++++++-
1 file changed, 80 insertions(+), 5 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 xkbcommon::xkb::Keycode;
+use xkbcommon::xkb::{Keycode, Keysym};
 
 use crate::{PlatformKeyboardLayout, SharedString};
 
@@ -48,11 +48,26 @@ impl LinuxKeyboardMapper {
         for &scan_code in TYPEABLE_CODES {
             let keycode = Keycode::new(scan_code);
             let key = xkb_state.key_get_utf8(keycode);
-            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);
+            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);
+                if let Some(key) = underlying_dead_key(keysym) {
+                    code_to_key.insert(keycode, key.clone());
+                    inserted.insert(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);
+                }
+            }
         }
         insert_letters_if_missing(&inserted, &mut code_to_key);
 
@@ -109,6 +124,66 @@ fn is_alphabetic_key(keycode: Keycode) -> bool {
     )
 }
 
+/**
+ * Returns which symbol the dead key represents
+ * <https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values#dead_keycodes_for_linux>
+ */
+#[cfg(any(feature = "wayland", feature = "x11"))]
+pub(crate) fn underlying_dead_key(keysym: Keysym) -> Option<String> {
+    match keysym {
+        Keysym::dead_grave => Some("`".to_owned()),
+        Keysym::dead_acute => Some("´".to_owned()),
+        Keysym::dead_circumflex => Some("^".to_owned()),
+        Keysym::dead_tilde => Some("~".to_owned()),
+        Keysym::dead_macron => Some("¯".to_owned()),
+        Keysym::dead_breve => Some("˘".to_owned()),
+        Keysym::dead_abovedot => Some("˙".to_owned()),
+        Keysym::dead_diaeresis => Some("¨".to_owned()),
+        Keysym::dead_abovering => Some("˚".to_owned()),
+        Keysym::dead_doubleacute => Some("˝".to_owned()),
+        Keysym::dead_caron => Some("ˇ".to_owned()),
+        Keysym::dead_cedilla => Some("¸".to_owned()),
+        Keysym::dead_ogonek => Some("˛".to_owned()),
+        Keysym::dead_iota => Some("ͅ".to_owned()),
+        Keysym::dead_voiced_sound => Some("゙".to_owned()),
+        Keysym::dead_semivoiced_sound => Some("゚".to_owned()),
+        Keysym::dead_belowdot => Some("̣̣".to_owned()),
+        Keysym::dead_hook => Some("̡".to_owned()),
+        Keysym::dead_horn => Some("̛".to_owned()),
+        Keysym::dead_stroke => Some("̶̶".to_owned()),
+        Keysym::dead_abovecomma => Some("̓̓".to_owned()),
+        Keysym::dead_abovereversedcomma => Some("ʽ".to_owned()),
+        Keysym::dead_doublegrave => Some("̏".to_owned()),
+        Keysym::dead_belowring => Some("˳".to_owned()),
+        Keysym::dead_belowmacron => Some("̱".to_owned()),
+        Keysym::dead_belowcircumflex => Some("ꞈ".to_owned()),
+        Keysym::dead_belowtilde => Some("̰".to_owned()),
+        Keysym::dead_belowbreve => Some("̮".to_owned()),
+        Keysym::dead_belowdiaeresis => Some("̤".to_owned()),
+        Keysym::dead_invertedbreve => Some("̯".to_owned()),
+        Keysym::dead_belowcomma => Some("̦".to_owned()),
+        Keysym::dead_currency => None,
+        Keysym::dead_lowline => None,
+        Keysym::dead_aboveverticalline => None,
+        Keysym::dead_belowverticalline => None,
+        Keysym::dead_longsolidusoverlay => None,
+        Keysym::dead_a => None,
+        Keysym::dead_A => None,
+        Keysym::dead_e => None,
+        Keysym::dead_E => None,
+        Keysym::dead_i => None,
+        Keysym::dead_I => None,
+        Keysym::dead_o => None,
+        Keysym::dead_O => None,
+        Keysym::dead_u => None,
+        Keysym::dead_U => None,
+        Keysym::dead_small_schwa => Some("ə".to_owned()),
+        Keysym::dead_capital_schwa => Some("Ə".to_owned()),
+        Keysym::dead_greek => None,
+        _ => None,
+    }
+}
+
 #[cfg(any(feature = "wayland", feature = "x11"))]
 macro_rules! insert_letters_if_missing_internal {
     ($inserted:expr, $code_to_key:expr, $code:expr, $key:literal) => {