keyboard.rs

  1#[cfg(any(feature = "wayland", feature = "x11"))]
  2use collections::{HashMap, HashSet};
  3#[cfg(any(feature = "wayland", feature = "x11"))]
  4use xkbcommon::xkb::Keycode;
  5
  6use crate::{PlatformKeyboardLayout, SharedString};
  7
  8#[derive(Clone)]
  9pub(crate) struct LinuxKeyboardLayout {
 10    name: SharedString,
 11}
 12
 13impl PlatformKeyboardLayout for LinuxKeyboardLayout {
 14    fn id(&self) -> &str {
 15        &self.name
 16    }
 17
 18    fn name(&self) -> &str {
 19        &self.name
 20    }
 21}
 22
 23impl LinuxKeyboardLayout {
 24    pub(crate) fn new(name: SharedString) -> Self {
 25        Self { name }
 26    }
 27}
 28
 29#[cfg(any(feature = "wayland", feature = "x11"))]
 30pub(crate) struct LinuxKeyboardMapper {
 31    code_to_key: HashMap<Keycode, String>,
 32    code_to_shifted_key: HashMap<Keycode, String>,
 33}
 34
 35#[cfg(any(feature = "wayland", feature = "x11"))]
 36impl LinuxKeyboardMapper {
 37    pub(crate) fn new(xkb_state: &xkbcommon::xkb::State) -> Self {
 38        let mut code_to_key = HashMap::default();
 39        let mut code_to_shifted_key = HashMap::default();
 40        let mut inserted = HashSet::default();
 41
 42        let keymap = xkb_state.get_keymap();
 43        let mut shifted_state = xkbcommon::xkb::State::new(&keymap);
 44        let shift_mod = keymap.mod_get_index(xkbcommon::xkb::MOD_NAME_SHIFT);
 45        let shift_mask = 1 << shift_mod;
 46        shifted_state.update_mask(shift_mask, 0, 0, 0, 0, 0);
 47
 48        for &scan_code in TYPEABLE_CODES {
 49            let keycode = Keycode::new(scan_code);
 50            let key = xkb_state.key_get_utf8(keycode);
 51            code_to_key.insert(keycode, key.clone());
 52            inserted.insert(key);
 53
 54            let shifted_key = shifted_state.key_get_utf8(keycode);
 55            code_to_shifted_key.insert(keycode, shifted_key);
 56        }
 57        insert_letters_if_missing(&inserted, &mut code_to_key);
 58
 59        Self {
 60            code_to_key,
 61            code_to_shifted_key,
 62        }
 63    }
 64
 65    pub(crate) fn get_key(
 66        &self,
 67        keycode: Keycode,
 68        modifiers: &mut crate::Modifiers,
 69    ) -> Option<String> {
 70        if is_alphabetic_key(keycode) || !modifiers.shift {
 71            self.code_to_key.get(&keycode).cloned()
 72        } else {
 73            modifiers.shift = false;
 74            self.code_to_shifted_key.get(&keycode).cloned()
 75        }
 76    }
 77}
 78
 79#[cfg(any(feature = "wayland", feature = "x11"))]
 80fn is_alphabetic_key(keycode: Keycode) -> bool {
 81    matches!(
 82        keycode.raw(),
 83        0x0026 // a
 84        | 0x0038 // b
 85        | 0x0036 // c
 86        | 0x0028 // d
 87        | 0x001a // e
 88        | 0x0029 // f
 89        | 0x002a // g
 90        | 0x002b // h
 91        | 0x001f // i
 92        | 0x002c // j
 93        | 0x002d // k
 94        | 0x002e // l
 95        | 0x003a // m
 96        | 0x0039 // n
 97        | 0x0020 // o
 98        | 0x0021 // p
 99        | 0x0018 // q
100        | 0x001b // r
101        | 0x0027 // s
102        | 0x001c // t
103        | 0x001e // u
104        | 0x0037 // v
105        | 0x0019 // w
106        | 0x0035 // x
107        | 0x001d // y
108        | 0x0034 // z
109    )
110}
111
112#[cfg(any(feature = "wayland", feature = "x11"))]
113macro_rules! insert_letters_if_missing_internal {
114    ($inserted:expr, $code_to_key:expr, $code:expr, $key:literal) => {
115        if !$inserted.contains($key) {
116            $code_to_key.insert($code, $key.to_string());
117        }
118    };
119}
120
121#[cfg(any(feature = "wayland", feature = "x11"))]
122fn insert_letters_if_missing(
123    inserted: &HashSet<String>,
124    code_to_key: &mut HashMap<Keycode, String>,
125) {
126    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0026), "a");
127    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0038), "b");
128    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0036), "c");
129    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0028), "d");
130    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x001a), "e");
131    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0029), "f");
132    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x002a), "g");
133    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x002b), "h");
134    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x001f), "i");
135    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x002c), "j");
136    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x002d), "k");
137    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x002e), "l");
138    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x003a), "m");
139    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0039), "n");
140    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0020), "o");
141    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0021), "p");
142    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0018), "q");
143    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x001b), "r");
144    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0027), "s");
145    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x001c), "t");
146    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x001e), "u");
147    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0037), "v");
148    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0019), "w");
149    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0035), "x");
150    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x001d), "y");
151    insert_letters_if_missing_internal!(inserted, code_to_key, Keycode::new(0x0034), "z");
152}
153
154// All typeable scan codes for the standard US keyboard layout, ANSI104
155#[cfg(any(feature = "wayland", feature = "x11"))]
156const TYPEABLE_CODES: &[u32] = &[
157    0x0026, // a
158    0x0038, // b
159    0x0036, // c
160    0x0028, // d
161    0x001a, // e
162    0x0029, // f
163    0x002a, // g
164    0x002b, // h
165    0x001f, // i
166    0x002c, // j
167    0x002d, // k
168    0x002e, // l
169    0x003a, // m
170    0x0039, // n
171    0x0020, // o
172    0x0021, // p
173    0x0018, // q
174    0x001b, // r
175    0x0027, // s
176    0x001c, // t
177    0x001e, // u
178    0x0037, // v
179    0x0019, // w
180    0x0035, // x
181    0x001d, // y
182    0x0034, // z
183    0x0013, // Digit 0
184    0x000a, // Digit 1
185    0x000b, // Digit 2
186    0x000c, // Digit 3
187    0x000d, // Digit 4
188    0x000e, // Digit 5
189    0x000f, // Digit 6
190    0x0010, // Digit 7
191    0x0011, // Digit 8
192    0x0012, // Digit 9
193    0x0031, // ` Backquote
194    0x0014, // - Minus
195    0x0015, // = Equal
196    0x0022, // [ Left bracket
197    0x0023, // ] Right bracket
198    0x0033, // \ Backslash
199    0x002f, // ; Semicolon
200    0x0030, // ' Quote
201    0x003b, // , Comma
202    0x003c, // . Period
203    0x003d, // / Slash
204];