keyboard.rs

  1#[cfg(any(feature = "wayland", feature = "x11"))]
  2use collections::HashMap;
  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
 41        let keymap = xkb_state.get_keymap();
 42        let mut shifted_state = xkbcommon::xkb::State::new(&keymap);
 43        let shift_mod = keymap.mod_get_index(xkbcommon::xkb::MOD_NAME_SHIFT);
 44        let shift_mask = 1 << shift_mod;
 45        shifted_state.update_mask(shift_mask, 0, 0, 0, 0, 0);
 46
 47        for &scan_code in TYPEABLE_CODES {
 48            let keycode = Keycode::new(scan_code);
 49            let key = xkb_state.key_get_utf8(keycode);
 50            code_to_key.insert(keycode, key.clone());
 51
 52            let shifted_key = shifted_state.key_get_utf8(keycode);
 53            code_to_shifted_key.insert(keycode, shifted_key);
 54        }
 55
 56        Self {
 57            code_to_key,
 58            code_to_shifted_key,
 59        }
 60    }
 61
 62    pub(crate) fn get_key(
 63        &self,
 64        keycode: Keycode,
 65        modifiers: &mut crate::Modifiers,
 66    ) -> Option<String> {
 67        if is_alphabetic_key(keycode) || !modifiers.shift {
 68            self.code_to_key.get(&keycode).cloned()
 69        } else {
 70            modifiers.shift = false;
 71            self.code_to_shifted_key.get(&keycode).cloned()
 72        }
 73    }
 74}
 75
 76#[cfg(any(feature = "wayland", feature = "x11"))]
 77fn is_alphabetic_key(keycode: Keycode) -> bool {
 78    matches!(
 79        keycode.raw(),
 80        0x0026 // a
 81        | 0x0038 // b
 82        | 0x0036 // c
 83        | 0x0028 // d
 84        | 0x001a // e
 85        | 0x0029 // f
 86        | 0x002a // g
 87        | 0x002b // h
 88        | 0x001f // i
 89        | 0x002c // j
 90        | 0x002d // k
 91        | 0x002e // l
 92        | 0x003a // m
 93        | 0x0039 // n
 94        | 0x0020 // o
 95        | 0x0021 // p
 96        | 0x0018 // q
 97        | 0x001b // r
 98        | 0x0027 // s
 99        | 0x001c // t
100        | 0x001e // u
101        | 0x0037 // v
102        | 0x0019 // w
103        | 0x0035 // x
104        | 0x001d // y
105        | 0x0034 // z
106    )
107}
108
109// All typeable scan codes for the standard US keyboard layout, ANSI104
110#[cfg(any(feature = "wayland", feature = "x11"))]
111const TYPEABLE_CODES: &[u32] = &[
112    0x0026, // a
113    0x0038, // b
114    0x0036, // c
115    0x0028, // d
116    0x001a, // e
117    0x0029, // f
118    0x002a, // g
119    0x002b, // h
120    0x001f, // i
121    0x002c, // j
122    0x002d, // k
123    0x002e, // l
124    0x003a, // m
125    0x0039, // n
126    0x0020, // o
127    0x0021, // p
128    0x0018, // q
129    0x001b, // r
130    0x0027, // s
131    0x001c, // t
132    0x001e, // u
133    0x0037, // v
134    0x0019, // w
135    0x0035, // x
136    0x001d, // y
137    0x0034, // z
138    0x0013, // Digit 0
139    0x000a, // Digit 1
140    0x000b, // Digit 2
141    0x000c, // Digit 3
142    0x000d, // Digit 4
143    0x000e, // Digit 5
144    0x000f, // Digit 6
145    0x0010, // Digit 7
146    0x0011, // Digit 8
147    0x0012, // Digit 9
148    0x0031, // ` Backquote
149    0x0014, // - Minus
150    0x0015, // = Equal
151    0x0022, // [ Left bracket
152    0x0023, // ] Right bracket
153    0x0033, // \ Backslash
154    0x002f, // ; Semicolon
155    0x0030, // ' Quote
156    0x003b, // , Comma
157    0x003c, // . Period
158    0x003d, // / Slash
159];