1use rpk_common::{
2 globals,
3 keycodes::key_range::{self, LAYER_MAX, LAYER_MIN, MACROS_MAX, MACROS_MIN},
4 mouse::{MouseAnalogSetting, MouseConfig},
5 PROTOCOL_VERSION,
6};
7
8use crate::mapper::{macros::Macro, KeyPlusMod};
9
10pub const MAIN_BASE: u16 = 5;
11
12pub struct Manager<const ROWS: usize, const COLS: usize, const CODE_SIZE: usize> {
13 mapping: [u16; CODE_SIZE],
14 globals: Globals,
15 mouse_profiles: [MouseConfig; 3],
16 layout_bottom: usize,
17 layout_top: usize,
18 macro_dir_base: usize,
19 memo_bottom: usize,
20 memo_top: usize,
21 macro_stack: usize,
22}
23
24#[derive(Debug)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub enum LoadError {
27 OutOfSpace,
28 VersionMismatch,
29 RowColMismatch,
30 Corrupt,
31}
32
33pub(crate) struct Globals {
34 pub(crate) values: [u16; 2],
35}
36impl Default for Globals {
37 fn default() -> Self {
38 Self { values: [180, 20] }
39 }
40}
41
42#[derive(Debug)]
43pub struct Layer<'l, const ROWS: usize, const COLS: usize>(&'l [u16]);
44
45impl<const ROWS: usize, const COLS: usize> Layer<'_, ROWS, COLS> {
46 pub fn get(&self, row: usize, column: usize) -> u16 {
47 let slice = &self.0[1..];
48 if slice.len() == ROWS * COLS {
49 *slice.get(row * COLS + column).unwrap_or(&0u16)
50 } else {
51 search_code(slice, row, column)
52 }
53 }
54
55 pub fn modifiers(&self) -> u8 {
56 self.0[0] as u8
57 }
58}
59
60impl<const ROWS: usize, const COLS: usize, const LAYOUT_MAX: usize> Default
61 for Manager<ROWS, COLS, LAYOUT_MAX>
62{
63 fn default() -> Self {
64 Self {
65 mapping: [0; LAYOUT_MAX],
66 globals: Default::default(),
67 mouse_profiles: [
68 MouseConfig::slow(),
69 MouseConfig::normal(),
70 MouseConfig::fast(),
71 ],
72 layout_bottom: 0,
73 layout_top: 0,
74 macro_dir_base: 0,
75 memo_bottom: 0,
76 memo_top: 0,
77 macro_stack: 0,
78 }
79 }
80}
81
82impl<const ROWS: usize, const COLS: usize, const LAYOUT_MAX: usize>
83 Manager<ROWS, COLS, LAYOUT_MAX>
84{
85 pub fn load(&mut self, iter: impl IntoIterator<Item = u16>) -> Result<(), LoadError> {
96 let mut iter = iter.into_iter();
97 if iter.next().ok_or(LoadError::Corrupt)? != PROTOCOL_VERSION {
98 return Err(LoadError::VersionMismatch);
99 }
100 {
101 let n = u16::from_le(iter.next().ok_or(LoadError::Corrupt)?);
102
103 if (n >> 8) as usize != ROWS || (n & 0xff) as usize != COLS {
104 return Err(LoadError::RowColMismatch);
105 }
106 }
107
108 let layer_count = iter.next().ok_or(LoadError::Corrupt)?;
109 if layer_count < 6 {
110 return Err(LoadError::Corrupt);
111 }
112 let macros_count = iter.next().ok_or(LoadError::Corrupt)?;
113 let mut globals_count = iter.next().ok_or(LoadError::Corrupt)?;
114
115 while globals_count != 0 {
116 if globals_count < 2 {
117 crate::error!("corrupt layout: globals_count is wrong");
118 return Err(LoadError::Corrupt);
119 }
120 let i = iter.next().ok_or(LoadError::Corrupt)?;
121 match i {
122 globals::MOUSE_PROFILE1 | globals::MOUSE_PROFILE2 | globals::MOUSE_PROFILE3 => {
123 let mp = self
124 .mouse_profiles
125 .get_mut((i - globals::MOUSE_PROFILE1) as usize)
126 .ok_or(LoadError::Corrupt)?;
127 mp.movement =
128 MouseAnalogSetting::deserialize(&mut iter).ok_or(LoadError::Corrupt)?;
129 mp.scroll =
130 MouseAnalogSetting::deserialize(&mut iter).ok_or(LoadError::Corrupt)?;
131 globals_count -= 21;
132 }
133 globals::DUAL_ACTION_TIMEOUT | globals::DUAL_ACTION_TIMEOUT2 => {
134 globals_count -= 2;
135 let v = iter.next().ok_or(LoadError::Corrupt)?;
136 *self
137 .globals
138 .values
139 .get_mut(i as usize)
140 .ok_or(LoadError::Corrupt)? = v;
141 }
142 _ => return Err(LoadError::Corrupt),
143 }
144 }
145
146 if layer_count >= (LAYER_MAX - LAYER_MIN) || macros_count >= (MACROS_MAX - MACROS_MIN) {
147 crate::error!(
148 "corrupt layout: layer_count {} or macros_count {} is out-of-range",
149 layer_count,
150 macros_count
151 );
152 return Err(LoadError::Corrupt);
153 }
154
155 let layer_start = (layer_count + macros_count) as usize;
156
157 let mut i = 0;
158 let mut p = 0;
159 for (f, t) in iter.take(self.mapping.len()).zip(self.mapping.iter_mut()) {
160 let n = u16::from_le(f);
161 if i <= layer_start {
162 if n > LAYOUT_MAX as u16 {
163 crate::error!("corrupt layout: layer/macro {} index is invalid", i);
164 return Err(LoadError::Corrupt);
165 }
166 if n <= p {
167 crate::error!("corrupt layout: layer/macro {} index is invalid", i);
168 return Err(LoadError::Corrupt);
169 }
170 p = n;
171 }
172 i += 1;
173 *t = n;
174 }
175
176 if i >= LAYOUT_MAX {
177 crate::error!("layout too big: LAYOUT_MAX is {}", LAYOUT_MAX);
178 return Err(LoadError::Corrupt);
179 }
180
181 self.macro_dir_base = layer_count as usize;
182
183 self.layout_bottom = i;
184 self.clear_all();
185
186 Ok(())
187 }
188
189 pub(super) fn clear_all(&mut self) {
190 self.macro_stack = LAYOUT_MAX;
191 self.memo_bottom = LAYOUT_MAX;
192 self.memo_top = LAYOUT_MAX;
193 self.clear_layers();
194 self.set_layout(MAIN_BASE);
195 }
196
197 pub(super) fn clear_layers(&mut self) {
198 self.layout_top = self.layout_bottom + 1;
199 }
200
201 pub(super) fn clear_modifier_layers(&mut self) {
202 let layers = &mut self.mapping[self.layout_bottom + 1..=self.layout_top];
203 let mut j = 0;
204
205 for i in 0..layers.len() {
206 unsafe {
207 if layers[i] >= 5 {
208 *layers.get_unchecked_mut(j) = layers[i];
209 j += 1;
210 }
211 }
212 }
213
214 self.layout_top = self.layout_bottom + 1 + j;
215 }
216
217 pub fn find_code(&self, row: usize, column: usize) -> Option<KeyPlusMod> {
218 for &layer_idx in self.mapping[self.layout_bottom..self.layout_top]
219 .iter()
220 .rev()
221 {
222 if let Some(layer) = self.get_layer(layer_idx) {
223 let code = layer.get(row, column);
224 if code != 0 {
225 if code < key_range::BASIC_MIN {
226 return None;
227 }
228 return Some(KeyPlusMod::new(code, layer.modifiers()));
229 }
230 }
231 }
232 None
233 }
234
235 pub fn get_macro(&self, id: u16) -> Macro {
236 let idx = id as usize + self.macro_dir_base;
237 if idx + 1 >= self.mapping.len() {
238 return Macro::Noop;
239 }
240
241 let s = self.mapping[idx] as usize;
242 let e = self.mapping[idx + 1] as usize;
243 if e < s || e > self.mapping.len() {
244 return Macro::Noop;
245 }
246
247 Macro::decode(s, self.mapping.get(s..e))
248 }
249
250 pub fn get_layer(&self, layer_num: u16) -> Option<Layer<'_, ROWS, COLS>> {
251 let idx = layer_num as usize;
252 if idx + 1 >= self.mapping.len() || idx >= self.macro_dir_base {
253 crate::error!("corrupt layout: layer index out of range {}", layer_num);
254 return None;
255 }
256
257 let s = self.mapping[idx] as usize;
258 let e = self.mapping[idx + 1] as usize;
259 if e < s || e > self.mapping.len() {
261 crate::error!("corrupt layout: layer address out of range {}..{}", s, e);
262 return None;
263 }
264
265 self.mapping
266 .get(s..(self.mapping[idx + 1] as usize))
267 .map(Layer)
268 }
269
270 pub fn macro_stack(&self) -> usize {
271 self.macro_stack
272 }
273
274 pub fn set_layout(&mut self, n: u16) {
275 self.mapping[self.layout_bottom] = n;
276 }
277
278 pub fn push_layer(&mut self, n: u16) -> bool {
279 if self.layout_top + 1 >= self.macro_stack {
280 return false;
281 }
282 self.mapping[self.layout_top] = n;
283 self.layout_top += 1;
284 true
285 }
286
287 pub fn pop_layer(&mut self, n: u16) -> bool {
288 if let Some((i, _)) = self.mapping[self.layout_bottom..self.layout_top]
289 .iter()
290 .copied()
291 .enumerate()
292 .rfind(|(_, v)| *v == n)
293 {
294 self.mapping.copy_within(
295 self.layout_bottom + i + 1..self.layout_top,
296 self.layout_bottom + i,
297 );
298
299 self.layout_top -= 1;
300 return true;
301 }
302 false
303 }
304
305 pub(crate) fn macro_code(&self, location: usize) -> u16 {
306 self.mapping[location]
307 }
308
309 pub(crate) fn update_macro(&mut self, mac: &Macro) {
310 mac.update(&mut self.mapping[self.macro_stack..]);
311 }
312
313 pub(crate) fn push_macro(&mut self, mac: Macro) -> Macro {
314 self.defrag_stack();
315 let (mac, len) = mac.push(&mut self.mapping[self.layout_top..self.macro_stack]);
316 self.macro_stack -= len;
317 mac
318 }
319
320 pub(crate) fn pop_macro(&mut self) -> Macro {
321 let (mac, len) = Macro::pop(&self.mapping[self.macro_stack..self.memo_bottom]);
322 self.macro_stack += len;
323 mac
324 }
325
326 pub(crate) fn global(&self, index: usize) -> u16 {
327 self.globals.values[index]
328 }
329
330 pub(crate) fn get_mouse_profile(&self, index: usize) -> Option<&MouseConfig> {
331 self.mouse_profiles.get(index)
332 }
333
334 pub(crate) fn push_memo(&mut self, memo: &[u16]) -> bool {
335 self.defrag_stack();
336 if self.macro_stack != self.memo_bottom
337 || self.memo_bottom <= memo.len() + 1 + self.layout_top
338 {
339 false
340 } else {
341 self.macro_stack -= memo.len() + 1;
342 self.mapping[self.macro_stack..self.memo_bottom - 1].copy_from_slice(memo);
343 self.mapping[self.memo_bottom - 1] = memo.len() as u16;
344 self.memo_bottom = self.macro_stack;
345 true
346 }
347 }
348
349 pub(crate) fn pop_memo(&mut self, receiver: impl FnOnce(&[u16])) -> bool {
350 if self.memo_bottom == self.memo_top {
351 false
352 } else {
353 let end = self.memo_top - 1;
354 let start = end - self.mapping[end] as usize;
355 self.memo_top = start;
356 receiver(&self.mapping[start..end]);
357 true
358 }
359 }
360
361 fn defrag_stack(&mut self) {
362 let diff = LAYOUT_MAX - self.memo_top;
363 if diff == 0 {
364 return;
365 }
366 self.mapping
367 .copy_within(self.macro_stack..self.memo_top, self.macro_stack + diff);
368
369 self.macro_stack += diff;
370 self.memo_bottom += diff;
371 self.memo_top += diff;
372 }
373}
374
375fn search_code(mut codes: &[u16], row: usize, column: usize) -> u16 {
376 let cmp = (row as u16) << 8 | (column as u16);
377
378 let mut s = codes.len();
379
380 loop {
381 s = (s >> 1) & !1;
382 if codes.len() <= s {
384 if codes.len() == 2 && cmp == codes[0] {
385 return codes[1];
386 }
387 return 0;
388 }
389 let v = codes[s];
390
391 #[allow(clippy::comparison_chain)]
392 if cmp < v {
393 codes = &codes[..s];
394 } else if cmp > v {
395 if codes.len() > s + 2 {
396 codes = &codes[s + 2..]
397 } else {
398 return 0;
399 }
400 } else {
401 return if codes.len() > s + 1 { codes[s + 1] } else { 0 };
402 }
403 }
404}
405
406#[cfg(test)]
407#[path = "layout_test.rs"]
408mod test;