From eca211e0f128dd2d974225511b0a1cb535787e0c Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 21 May 2025 18:27:04 +0800 Subject: [PATCH] add helper --- crates/gpui/src/platform/linux/keyboard.rs | 159 ++++++++------------- 1 file changed, 57 insertions(+), 102 deletions(-) diff --git a/crates/gpui/src/platform/linux/keyboard.rs b/crates/gpui/src/platform/linux/keyboard.rs index b32b95f101d1982f033d4a138b7ae9718bfba671..ca4778b2aa74fdc05cf044393329e6eaf3748323 100644 --- a/crates/gpui/src/platform/linux/keyboard.rs +++ b/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 { } } -#[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, code_to_key: &mut HashMap, ) { - 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 = @@ -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())); } } }