rpk_firmware/
layout.rs

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    /// Load mapping config into `Manager`. The format of mapping is as follows:
86    ///
87    /// protocol version
88    /// layer count
89    /// row_count (high byte), column_count (low byte) should match `ROWS` and `COLS`
90    /// layer positions, layers.
91    /// Layer positions mark the index in codes where the layers start.
92    /// Layers are dense if every entry has a value; size == ROWS * COLS
93    /// Layers are sparse if size < ROWS * COLS in which case it is a list of ordered tuples where
94    /// the first byte is the row, second is the column and the next word is the value
95    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        // todo use first macro rather than mapping.len
260        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        // len < 2 would work but s avoids the bounds checker
383        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;