1#[cfg(windows)]
2mod windows;
3
4use std::collections::BTreeMap;
5
6use crate::device::Key;
7
8#[cfg(windows)]
9pub use windows::WindowsLayout;
10
11pub trait LayoutSource {
12 fn layout(&mut self) -> u64;
13
14 fn label(&mut self, key: Key) -> Option<String>;
15}
16
17#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
18pub struct Us;
19
20impl LayoutSource for Us {
21 fn layout(&mut self) -> u64 {
22 0
23 }
24
25 fn label(&mut self, _key: Key) -> Option<String> {
26 None
27 }
28}
29
30#[derive(Clone, Debug, Default, PartialEq, Eq)]
31pub struct KeyLabels {
32 layout: Option<u64>,
33 labels: BTreeMap<Key, String>,
34 names: BTreeMap<Key, String>,
35}
36
37impl KeyLabels {
38 pub fn new() -> Self {
39 Self::default()
40 }
41
42 pub fn label(&self, key: Key) -> &str {
43 self.names
44 .get(&key)
45 .or_else(|| self.labels.get(&key))
46 .map_or_else(|| key.label(), String::as_str)
47 }
48
49 pub fn layout(&self) -> Option<u64> {
50 self.layout
51 }
52
53 pub fn refresh(&mut self, source: &mut (impl LayoutSource + ?Sized)) -> bool {
54 let layout = source.layout();
55 if self.layout == Some(layout) {
56 return false;
57 }
58 self.layout = Some(layout);
59 let labels: BTreeMap<Key, String> = Key::ALL
60 .into_iter()
61 .filter(|key| key.follows_layout())
62 .filter_map(|key| {
63 let label = cap_label(&source.label(key)?)?;
64 (label != key.label()).then_some((key, label))
65 })
66 .collect();
67 let changed = labels != self.labels;
68 self.labels = labels;
69 changed
70 }
71
72 pub fn learn(&mut self, key: Key, text: &str) -> bool {
73 if !key.follows_layout() {
74 return false;
75 }
76 let Some(label) = cap_label(text) else {
77 return false;
78 };
79 let known = self.labels.get(&key).map(String::as_str);
80 if known.unwrap_or(key.label()) == label {
81 return false;
82 }
83 if known.is_some() {
84 self.labels.clear();
85 self.layout = None;
86 }
87 if label != key.label() {
88 self.labels.insert(key, label);
89 }
90 true
91 }
92
93 pub fn rename(&mut self, key: Key, name: &str) {
94 match cap_label(name) {
95 Some(name) => self.names.insert(key, name),
96 None => self.names.remove(&key),
97 };
98 }
99
100 pub fn forget(&mut self) {
101 self.layout = None;
102 self.labels.clear();
103 }
104}
105
106pub fn cap_label(text: &str) -> Option<String> {
107 let label: String = text
108 .trim()
109 .chars()
110 .filter(|c| !c.is_control())
111 .map(|c| {
112 let mut upper = c.to_uppercase();
113 match (upper.next(), upper.next()) {
114 (Some(single), None) => single,
115 _ => c,
116 }
117 })
118 .collect();
119 (!label.is_empty()).then_some(label)
120}
121
122pub fn scancode(key: Key) -> Option<u16> {
123 Some(match key {
124 Key::Escape => 0x01,
125 Key::Digit1 => 0x02,
126 Key::Digit2 => 0x03,
127 Key::Digit3 => 0x04,
128 Key::Digit4 => 0x05,
129 Key::Digit5 => 0x06,
130 Key::Digit6 => 0x07,
131 Key::Digit7 => 0x08,
132 Key::Digit8 => 0x09,
133 Key::Digit9 => 0x0a,
134 Key::Digit0 => 0x0b,
135 Key::Minus => 0x0c,
136 Key::Equal => 0x0d,
137 Key::Backspace => 0x0e,
138 Key::Tab => 0x0f,
139 Key::Q => 0x10,
140 Key::W => 0x11,
141 Key::E => 0x12,
142 Key::R => 0x13,
143 Key::T => 0x14,
144 Key::Y => 0x15,
145 Key::U => 0x16,
146 Key::I => 0x17,
147 Key::O => 0x18,
148 Key::P => 0x19,
149 Key::BracketLeft => 0x1a,
150 Key::BracketRight => 0x1b,
151 Key::Enter => 0x1c,
152 Key::ControlLeft => 0x1d,
153 Key::A => 0x1e,
154 Key::S => 0x1f,
155 Key::D => 0x20,
156 Key::F => 0x21,
157 Key::G => 0x22,
158 Key::H => 0x23,
159 Key::J => 0x24,
160 Key::K => 0x25,
161 Key::L => 0x26,
162 Key::Semicolon => 0x27,
163 Key::Quote => 0x28,
164 Key::Backquote => 0x29,
165 Key::ShiftLeft => 0x2a,
166 Key::Backslash => 0x2b,
167 Key::Z => 0x2c,
168 Key::X => 0x2d,
169 Key::C => 0x2e,
170 Key::V => 0x2f,
171 Key::B => 0x30,
172 Key::N => 0x31,
173 Key::M => 0x32,
174 Key::Comma => 0x33,
175 Key::Period => 0x34,
176 Key::Slash => 0x35,
177 Key::ShiftRight => 0x36,
178 Key::AltLeft => 0x38,
179 Key::Space => 0x39,
180 Key::CapsLock => 0x3a,
181 _ => return None,
182 })
183}