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];