1use std::collections::HashMap;
2
3use lazy_static::lazy_static;
4use rpk_common::keycodes::key_range;
5
6const DASH_USCORE: [char; 2] = ['_', '-'];
7
8const MODIFIER_BITS: [&str; 8] = ["C", "S", "A", "G", "RC", "RS", "RA", "RG"];
9lazy_static! {
10 static ref MODIFIER_BIT_MAP: HashMap<&'static str, u8> = {
11 let mut m = HashMap::new();
12 for (i, c) in MODIFIER_BITS.iter().enumerate() {
13 m.insert(*c, 1 << i);
14 }
15 m
16 };
17 static ref ACTION_NAMES: HashMap<&'static str, u16> = {
18 let mut m = HashMap::new();
19 m.insert("layer", key_range::LAYER);
20 m.insert("toggle", key_range::TOGGLE);
21 m.insert("setlayout", key_range::SET_LAYOUT);
22 m.insert("oneshot", key_range::ONESHOT);
23 m.insert("overload", key_range::MACROS_MIN);
24 m.insert("dualaction", key_range::MACROS_MIN);
25 m.insert("macro", key_range::MACROS_MIN);
26 m.insert("hold", key_range::MACROS_MIN);
27 m.insert("release", key_range::MACROS_MIN);
28 m.insert("unicode", key_range::MACROS_MIN);
29 m.insert("delay", key_range::MACROS_MIN);
30 m
31 };
32 static ref SHIFT_KEY_NAMES : HashMap<char, char> = {
33 let mut m = HashMap::new();
34 let mut n = '\0';
35 for c in r#"`~-_=+[{]}\|;:'",<.>/?1!2@3#4$5%6^7&8*9(0)"#.chars() {
36 if n == '\0' {
37 n = c;
38 } else {
39 m.insert(c, n);
40 n = '\0';
41 }
42 }
43 m
44 };
45 static ref FULL_KEY_NAMES: HashMap<&'static str, u16> = {
46 let mut m = HashMap::new();
47 m.insert("Transparent", 0);
48 m.insert("No_op", 1);
49 m.insert("Nop", 1);
50 m.insert("/", 0x38);
51 let mut ins = |a: &'static str, b: u16| {
52 for k in a.split('/') {
53 if k.is_empty() {
54 panic!("empty symbol in {a}");
55 }
56 if m.contains_key(k) {
57 panic!("key already added {a}");
58 }
59 m.insert(k, b);
60 }
61 };
62 for (i, name) in r#"
63A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
641 2 3 4 5 6 7 8 9 0
65Enter/Return/ent/⏎
66Escape/Esc/⎋
67backspace/bksp/⌫
68Tab/⇥
69Spacebar/Space/spc/␣
70Dash/minus/-
71Equals/equal/=
72LeftBrace/leftsquarebracket/[
73RightBrace/rightsquarebracket/]
74Backslash/\
75Hash/NonUsHash/#
76Semicolon/;
77LeftApos/apostrophe/'
78GraveAccent/grave/`
79Comma/,
80Period/dot/.
81Forwardslash/slash
82CapsLock/caps/⇪
83F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12
84Printscreen/print
85ScrollLock
86Pause
87Insert
88Home/⇱
89PageUp/PgUp/⇞
90Delete/del/⌦
91End/⇲
92PageDown/PgDn/⇟
93Right/→ Left/← Down/↓ Up/↑
94NumLock/KpNumLock/⇭
95KpForwardslash KpStar KpDash KpPlus KpEnter
96Kp1 Kp2 Kp3 Kp4 Kp5 Kp6 Kp7 Kp8 Kp9 Kp0
97KpPeriod
98NonUsBackslash
99Application/App
100Power
101KpEquals
102F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24
103Execute
104Help
105Menu
106Select
107Stop
108Again Undo
109Cut Copy Paste
110Find
111Mute
112VolumeUp VolumeDown
113LockingCapsLock LockingNumLock LockingScrollLock
114KpComma KpEqualSign
115International1 International2 International3 International4
116International5 International6 International7 International8 International9
117Lang1 Lang2 Lang3 Lang4 Lang5 Lang6 Lang7 Lang8 Lang9
118AlternateErase
119SysreqAttention
120Cancel
121Clear
122Prior
123KeyboardReturn
124Separator
125Out
126Oper
127ClearAgain
128CrselProps
129Exsel
130A5 A6 A7 A8 A9 AA AB AC AD AE AF
131KpDouble0 KpTriple0
132ThousandsSeparator DecimalSeparator CurrencyUnit CurrencySubUnit
133KpLeftBracket KpRightBracket KpLeftBrace KpRightBrace
134KpTab KpBackspace
135KpA KpB KpC KpD KpE KpF
136KpXor
137KpCaret
138KpPercentage
139KpLess KpGreater
140KpAmpersand KpDoubleAmpersand KpBar KpDoubleBar
141KpColon KpHash KpSpace KpAt KpBang
142KpMemoryStore KpMemoryRecall KpMemoryClear
143KpMemoryAdd KpMemorySubtract KpMemoryMultiply KpMemoryDivide KpPlusMinus
144KpClear KpClearEntry
145KpBinary KpOctal KpDecimal KpHexadecimal
146DE DF
147LeftControl/LCtrl/LCTL/⌃
148LeftShift/LShift/LSFT/⇧
149LeftAlt/LAlt
150LeftGui/LeftMeta/LGUI
151RightControl/RCtrl/RCtl
152RightShift/RShift/RSFT
153RightAlt/AltGr/RAlt
154RightGui/RightMeta/RGUI
155Media_Play_Pause
156Media_Stop_CD
157Media_Previous_Song
158Media_Next_Song
159Media_Eject_CD
160Media_Volume_Up
161Media_Volume_Down
162Media_Mute
163Media_WWW
164Media_Back
165Media_Forward
166Media_Stop
167Media_Find
168Media_Scroll_Up
169Media_Scroll_Down
170Media_Edit
171Media_Sleep
172Media_Coffee
173Media_Refresh
174Media_Calc
175"#
176 .split_whitespace().enumerate()
177 {
178 ins(name, (i + 4) as u16);
179 }
180
181 for (i, name) in r#"
182Mouse1 Mouse2 Mouse3 Mouse4 Mouse5 Mouse6 Mouse7 Mouse8
183MouseLeft MouseRight MouseUp MouseDown
184MouseScrollDown/MouseWheelDown
185MouseScrollUp/MouseWheelUp
186MouseScrollRight/MouseWheelRight
187MouseScrollLeft/MouseWheelLeft
188MouseAccel1 MouseAccel2 MouseAccel3
189"#
190 .split_whitespace()
191 .enumerate()
192 {
193 for name in name.split('/') {
194 ins(name, i as u16 + key_range::MOUSE_MIN);
195 }
196 }
197
198 let mut i = |a: &'static str, b: u16| {
199 ins(a, b+key_range::CONSUMER_MIN);
200 };
201
202 i("Snapshot" ,0x065);
204 i("Brightness_Up" ,0x06F);
205 i("Brightness_Down" ,0x070);
206 i("TC_Record" ,0x0b2);
208 i("TC_Fast_Forward" ,0x0B3);
209 i("TC_Rewind" ,0x0B4);
210 i("TC_Next_Track" ,0x0B5);
211 i("TC_Prev_Track" ,0x0B6);
212 i("TC_Stop" ,0x0B7);
213 i("TC_Eject" ,0x0B8);
214 i("TC_Random_Play" ,0x0B9);
215 i("TC_Stop_Eject" ,0x0CC);
216 i("TC_Play_Pause" ,0x0CD);
217 i("Audio_Mute" ,0x0E2);
219 i("Audio_Vol_Up" ,0x0E9);
220 i("Audio_Vol_Down" ,0x0EA);
221 i("AL_Cc_Config" ,0x183);
223 i("AL_Email" ,0x18A);
224 i("AL_Calculator" ,0x192);
225 i("AL_Local_Browser" ,0x194);
226 i("AL_Lock" ,0x19E);
227 i("AL_Control_Panel" ,0x19F);
228 i("AL_Assistant" ,0x1CB);
229 i("AL_Keyboard_Layout" ,0x1AE);
230 i("AC_New" ,0x201);
232 i("AC_Open" ,0x202);
233 i("AC_Close" ,0x203);
234 i("AC_Exit" ,0x204);
235 i("AC_Maximize" ,0x205);
236 i("AC_Minimize" ,0x206);
237 i("AC_Save" ,0x207);
238 i("AC_Print" ,0x208);
239 i("AC_Properties" ,0x209);
240 i("AC_Undo" ,0x21A);
241 i("AC_Copy" ,0x21B);
242 i("AC_Cut" ,0x21C);
243 i("AC_Paste" ,0x21D);
244 i("AC_Select_All" ,0x21E);
245 i("AC_Find" ,0x21F);
246 i("AC_Search" ,0x221);
247 i("AC_Homepage" ,0x223);
248 i("AC_Back" ,0x224);
249 i("AC_Forward" ,0x225);
250 i("AC_Cancel" ,0x226);
251 i("AC_Refresh" ,0x227);
252 i("AC_Bookmarks" ,0x22A);
253 i("AC_Next_Keyboard_Layout_Select" ,0x29D);
254 i("AC_Desktop_Show_All_Windows" ,0x29F);
255 i("AC_Soft_Key_Left" ,0x2A);
256
257 i("System_Power_Down", 0x2a1);
259 i("System_Sleep/Sleep", 0x2a2);
260 i("System_Wake_Up/Wakeup", 0x2a3);
261 i("System_Restart", 0x2af);
262 i("System_Display_Toggle_Int_Ext", 0x2d);
263
264 ins("Reset_Keyboard", key_range::FW_RESET_KEYBOARD);
267 ins("Clear_All", key_range::FW_CLEAR_ALL);
268 ins("Clear_Layers", key_range::FW_CLEAR_LAYERS);
269 ins("Stop_Active", key_range::FW_STOP_ACTIVE);
270 ins("Reset_To_Usb_Boot", key_range::FW_RESET_TO_USB_BOOT);
271m
274 };
275 static ref KEY_NAMES: HashMap<String, u16> = {
276 let mut m = HashMap::new();
277 for (r, v) in FULL_KEY_NAMES.iter() {
278 let k = if r.len() == 1 {r.to_lowercase()} else {r.replace(DASH_USCORE, "").to_lowercase()};
279 if m.contains_key(k.as_str()) {
280 panic!("duplicate key {}, {}", r, &k);
281 }
282 m.insert(k, *v);
283 }
284 m
285 };
286}
287
288pub const SHIFT_MOD: u8 = 2;
289
290pub fn unshifted_char_code(c: char) -> char {
291 match c {
292 'A'..='Z' => c.to_ascii_lowercase(),
293 c => *SHIFT_KEY_NAMES.get(&c).unwrap_or(&c),
294 }
295}
296
297pub fn char_to_code(c: char) -> u16 {
298 match c {
299 'a'..='z' => ((c as u8) - b'a' + 4) as u16,
300 '1'..='9' => ((c as u8) - b'1' + 30) as u16,
301 '0' => 39,
302 c => *KEY_NAMES.get(c.to_string().as_str()).unwrap_or(&0),
303 }
304}
305
306pub fn key_code(name: &str) -> Option<u16> {
307 let name = if name.len() > 1 && name.starts_with('\\') {
308 &name[1..]
309 } else {
310 name
311 };
312 if name.contains(DASH_USCORE) {
313 let n2 = name.replace(DASH_USCORE, "");
314
315 KEY_NAMES
316 .get(n2.as_str())
317 .or_else(|| FULL_KEY_NAMES.get(name))
318 .copied()
319 } else {
320 KEY_NAMES
321 .get(name)
322 .or_else(|| FULL_KEY_NAMES.get(name))
323 .copied()
324 }
325}
326
327pub fn action_code(name: &str) -> Option<u16> {
328 ACTION_NAMES.get(name).copied()
329}
330
331pub fn modifier_macro(_name: &str) -> Option<u16> {
332 Some(key_range::MACROS_MIN)
333}
334
335pub fn modifiers_to_bit_map(text: &str) -> Option<u8> {
336 if text.is_empty() {
337 return Some(0);
338 }
339 let mut bm = 0;
340 for s in text.split('-') {
341 match MODIFIER_BIT_MAP.get(s) {
342 Some(bit) => bm |= bit,
343 None => return None,
344 }
345 }
346
347 Some(bm)
348}
349
350pub fn modifiers_to_string(mut modifiers: u8) -> String {
351 let mut ans = String::new();
352
353 for m in MODIFIER_BITS {
354 if modifiers == 0 {
355 return ans;
356 }
357 if modifiers & 1 == 1 {
358 if !ans.is_empty() {
359 ans += "-";
360 }
361 ans += m;
362 }
363
364 modifiers >>= 1;
365 }
366 ans
367}
368
369pub struct KeycodeDescriptor {
370 pub name: &'static str,
371 pub code: u16,
372}
373
374pub fn keycodes_iter() -> impl Iterator<Item = KeycodeDescriptor> {
375 let iter = FULL_KEY_NAMES.iter();
376 iter.map(|(name, code)| KeycodeDescriptor { name, code: *code })
377}
378
379#[cfg(test)]
380#[path = "keycodes_test.rs"]
381mod test;