rmk_config/
lib.rs

1use std::collections::HashMap;
2use std::path::Path;
3
4use config::{Config, File, FileFormat};
5use serde::{Deserialize as SerdeDeserialize, de};
6use serde_derive::Deserialize;
7use serde_inline_default::serde_inline_default;
8
9pub mod chip;
10pub mod communication;
11pub mod keyboard;
12#[rustfmt::skip]
13pub mod usb_interrupt_map;
14pub mod behavior;
15pub mod board;
16pub mod host;
17pub mod keycode_alias;
18pub mod layout;
19pub mod light;
20pub mod storage;
21
22pub use board::{BoardConfig, UniBodyConfig};
23pub use chip::{ChipModel, ChipSeries};
24pub use communication::{CommunicationConfig, UsbInfo};
25pub use keyboard::Basic;
26pub use keycode_alias::KEYCODE_ALIAS;
27
28/// Configurations for RMK keyboard.
29#[derive(Clone, Debug, Deserialize)]
30#[allow(unused)]
31pub struct KeyboardTomlConfig {
32    /// Basic keyboard info
33    keyboard: Option<KeyboardInfo>,
34    /// Matrix of the keyboard, only for non-split keyboards
35    matrix: Option<MatrixConfig>,
36    // Aliases for key maps
37    aliases: Option<HashMap<String, String>>,
38    // Layers of key maps
39    layer: Option<Vec<LayerTomlConfig>>,
40    /// Layout config.
41    /// For split keyboard, the total row/col should be defined in this section
42    layout: Option<LayoutTomlConfig>,
43    /// Behavior config
44    behavior: Option<BehaviorConfig>,
45    /// Light config
46    light: Option<LightConfig>,
47    /// Storage config
48    storage: Option<StorageConfig>,
49    /// Ble config
50    ble: Option<BleConfig>,
51    /// Chip-specific configs (e.g., [chip.nrf52840])
52    chip: Option<HashMap<String, ChipConfig>>,
53    /// Dependency config
54    dependency: Option<DependencyConfig>,
55    /// Split config
56    split: Option<SplitConfig>,
57    /// Input device config
58    input_device: Option<InputDeviceConfig>,
59    /// Output Pin config
60    output: Option<Vec<OutputConfig>>,
61    /// Set host configurations
62    pub host: Option<HostConfig>,
63    /// RMK config constants
64    #[serde(default)]
65    pub rmk: RmkConstantsConfig,
66}
67
68impl KeyboardTomlConfig {
69    pub fn new_from_toml_path<P: AsRef<Path>>(config_toml_path: P) -> Self {
70        // The first run, load chip model only
71        let user_config = match std::fs::read_to_string(config_toml_path.as_ref()) {
72            Ok(s) => match toml::from_str::<KeyboardTomlConfig>(&s) {
73                Ok(c) => c,
74                Err(e) => panic!("Parse {:?} error: {}", config_toml_path.as_ref(), e.message()),
75            },
76            Err(e) => panic!("Read keyboard config file {:?} error: {}", config_toml_path.as_ref(), e),
77        };
78        let default_config_str = user_config.get_chip_model().unwrap().get_default_config_str().unwrap();
79
80        // The second run, load the user config and merge with the default config
81        let mut config: KeyboardTomlConfig = Config::builder()
82            .add_source(File::from_str(default_config_str, FileFormat::Toml))
83            .add_source(File::with_name(config_toml_path.as_ref().to_str().unwrap()))
84            .build()
85            .unwrap()
86            .try_deserialize()
87            .unwrap();
88
89        config.auto_calculate_parameters();
90
91        config
92    }
93
94    /// Auto calculate some parameters in toml:
95    /// - Update morse_max_num to fit all configured morses
96    /// - Update max_patterns_per_key to fit the max number of configured (pattern, action) pairs per morse key
97    /// - Update peripheral number based on the number of split boards
98    /// - TODO: Update controller number based on the number of split boards
99    pub fn auto_calculate_parameters(&mut self) {
100        // Update the number of peripherals
101        if let Some(split) = &self.split
102            && split.peripheral.len() > self.rmk.split_peripherals_num
103        {
104            // eprintln!(
105            //     "The number of split peripherals is updated to {} from {}",
106            //     split.peripheral.len(),
107            //     self.rmk.split_peripherals_num
108            // );
109            self.rmk.split_peripherals_num = split.peripheral.len();
110        }
111
112        if let Some(behavior) = &self.behavior {
113            // Update the max_patterns_per_key
114            if let Some(morse) = &behavior.morse
115                && let Some(morses) = &morse.morses
116            {
117                let mut max_required_patterns = self.rmk.max_patterns_per_key;
118
119                for morse in morses {
120                    let tap_actions_len = morse.tap_actions.as_ref().map(|v| v.len()).unwrap_or(0);
121                    let hold_actions_len = morse.hold_actions.as_ref().map(|v| v.len()).unwrap_or(0);
122
123                    let n = tap_actions_len.max(hold_actions_len);
124                    if n > 15 {
125                        panic!("The number of taps per morse is too large, the max number of taps is 15, got {n}");
126                    }
127
128                    let morse_actions_len = morse.morse_actions.as_ref().map(|v| v.len()).unwrap_or(0);
129
130                    max_required_patterns =
131                        max_required_patterns.max(tap_actions_len + hold_actions_len + morse_actions_len);
132                }
133                self.rmk.max_patterns_per_key = max_required_patterns;
134
135                // Update the morse_max_num
136                self.rmk.morse_max_num = self.rmk.morse_max_num.max(morses.len());
137            }
138        }
139    }
140}
141
142/// Keyboard constants configuration for performance and hardware limits
143#[serde_inline_default]
144#[derive(Clone, Debug, Deserialize)]
145#[serde(deny_unknown_fields)]
146pub struct RmkConstantsConfig {
147    /// Mouse key interval (ms) - controls mouse movement speed
148    #[serde_inline_default(20)]
149    pub mouse_key_interval: u32,
150    /// Mouse wheel interval (ms) - controls scrolling speed
151    #[serde_inline_default(80)]
152    pub mouse_wheel_interval: u32,
153    /// Maximum number of combos keyboard can store
154    #[serde_inline_default(8)]
155    #[serde(deserialize_with = "check_combo_max_num")]
156    pub combo_max_num: usize,
157    /// Maximum number of keys pressed simultaneously in a combo
158    #[serde_inline_default(4)]
159    pub combo_max_length: usize,
160    /// Maximum number of forks for conditional key actions
161    #[serde_inline_default(8)]
162    #[serde(deserialize_with = "check_fork_max_num")]
163    pub fork_max_num: usize,
164    /// Maximum number of morses keyboard can store
165    #[serde_inline_default(8)]
166    #[serde(deserialize_with = "check_morse_max_num")]
167    pub morse_max_num: usize,
168    /// Maximum number of patterns a morse key can handle
169    #[serde_inline_default(8)]
170    #[serde(deserialize_with = "check_max_patterns_per_key")]
171    pub max_patterns_per_key: usize,
172    /// Macro space size in bytes for storing sequences
173    #[serde_inline_default(256)]
174    pub macro_space_size: usize,
175    /// Default debounce time in ms
176    #[serde_inline_default(20)]
177    pub debounce_time: u16,
178    /// Event channel size
179    #[serde_inline_default(16)]
180    pub event_channel_size: usize,
181    /// Controller event channel size
182    #[serde_inline_default(16)]
183    pub controller_channel_size: usize,
184    /// Number of publishers to controllers
185    #[serde_inline_default(12)]
186    pub controller_channel_pubs: usize,
187    /// Number of controllers
188    #[serde_inline_default(8)]
189    pub controller_channel_subs: usize,
190    /// Report channel size
191    #[serde_inline_default(16)]
192    pub report_channel_size: usize,
193    /// Vial channel size
194    #[serde_inline_default(4)]
195    pub vial_channel_size: usize,
196    /// Flash channel size
197    #[serde_inline_default(4)]
198    pub flash_channel_size: usize,
199    /// The number of the split peripherals
200    #[serde_inline_default(1)]
201    pub split_peripherals_num: usize,
202    /// The number of available BLE profiles
203    #[serde_inline_default(3)]
204    pub ble_profiles_num: usize,
205    /// BLE Split Central sleep timeout in minutes (0 = disabled)
206    #[serde_inline_default(0)]
207    pub split_central_sleep_timeout_seconds: u32,
208}
209
210fn check_combo_max_num<'de, D>(deserializer: D) -> Result<usize, D::Error>
211where
212    D: de::Deserializer<'de>,
213{
214    let value = SerdeDeserialize::deserialize(deserializer)?;
215    if value > 256 {
216        panic!("❌ Parse `keyboard.toml` error: combo_max_num must be between 0 and 256, got {value}");
217    }
218    Ok(value)
219}
220
221fn check_morse_max_num<'de, D>(deserializer: D) -> Result<usize, D::Error>
222where
223    D: de::Deserializer<'de>,
224{
225    let value = SerdeDeserialize::deserialize(deserializer)?;
226    if value > 256 {
227        panic!("❌ Parse `keyboard.toml` error: morse_max_num must be between 0 and 256, got {value}");
228    }
229    Ok(value)
230}
231
232fn check_max_patterns_per_key<'de, D>(deserializer: D) -> Result<usize, D::Error>
233where
234    D: de::Deserializer<'de>,
235{
236    let value = SerdeDeserialize::deserialize(deserializer)?;
237    if !(4..=65536).contains(&value) {
238        panic!("❌ Parse `keyboard.toml` error: max_patterns_per_key must be between 4 and 65566, got {value}");
239    }
240    Ok(value)
241}
242
243fn check_fork_max_num<'de, D>(deserializer: D) -> Result<usize, D::Error>
244where
245    D: de::Deserializer<'de>,
246{
247    let value = SerdeDeserialize::deserialize(deserializer)?;
248    if value > 256 {
249        panic!("❌ Parse `keyboard.toml` error: fork_max_num must be between 0 and 256, got {value}");
250    }
251    Ok(value)
252}
253
254/// This separate Default impl is needed when `[rmk]` section is not set in keyboard.toml
255impl Default for RmkConstantsConfig {
256    fn default() -> Self {
257        Self {
258            mouse_key_interval: 20,
259            mouse_wheel_interval: 80,
260            combo_max_num: 8,
261            combo_max_length: 4,
262            fork_max_num: 8,
263            morse_max_num: 8,
264            max_patterns_per_key: 8,
265            macro_space_size: 256,
266            debounce_time: 20,
267            event_channel_size: 16,
268            controller_channel_size: 16,
269            controller_channel_pubs: 12,
270            controller_channel_subs: 8,
271            report_channel_size: 16,
272            vial_channel_size: 4,
273            flash_channel_size: 4,
274            split_peripherals_num: 0,
275            ble_profiles_num: 3,
276            split_central_sleep_timeout_seconds: 0,
277        }
278    }
279}
280
281/// Configurations for keyboard layout
282#[derive(Clone, Debug, Deserialize)]
283#[allow(unused)]
284pub struct LayoutTomlConfig {
285    pub rows: u8,
286    pub cols: u8,
287    pub layers: u8,
288    pub keymap: Option<Vec<Vec<Vec<String>>>>, // Will be deprecated in the future
289    pub matrix_map: Option<String>,            // Temporarily allow both matrix_map and keymap to be set
290    pub encoder_map: Option<Vec<Vec<[String; 2]>>>, // Will be deprecated together with keymap
291}
292
293#[derive(Clone, Debug, Deserialize)]
294#[allow(unused)]
295pub struct LayerTomlConfig {
296    pub name: Option<String>,
297    pub keys: String,
298    pub encoders: Option<Vec<[String; 2]>>,
299}
300
301/// Configurations for keyboard info
302#[derive(Clone, Debug, Default, Deserialize)]
303pub struct KeyboardInfo {
304    /// Keyboard name
305    pub name: String,
306    /// Vender id
307    pub vendor_id: u16,
308    /// Product id
309    pub product_id: u16,
310    /// Manufacturer
311    pub manufacturer: Option<String>,
312    /// Product name, if not set, it will use `name` as default
313    pub product_name: Option<String>,
314    /// Serial number
315    pub serial_number: Option<String>,
316    /// Board name(if a supported board is used)
317    pub board: Option<String>,
318    /// Chip model
319    pub chip: Option<String>,
320    /// enable usb
321    pub usb_enable: Option<bool>,
322}
323
324#[derive(Clone, Debug, Default, Deserialize)]
325#[allow(non_camel_case_types)]
326pub enum MatrixType {
327    #[default]
328    normal,
329    direct_pin,
330}
331
332#[derive(Clone, Debug, Default, Deserialize)]
333pub struct MatrixConfig {
334    #[serde(default)]
335    pub matrix_type: MatrixType,
336    pub row_pins: Option<Vec<String>>,
337    pub col_pins: Option<Vec<String>>,
338    pub direct_pins: Option<Vec<Vec<String>>>,
339    #[serde(default = "default_true")]
340    pub direct_pin_low_active: bool,
341    #[serde(default = "default_false")]
342    pub row2col: bool,
343    pub debouncer: Option<String>,
344}
345
346/// Config for storage
347#[derive(Clone, Copy, Debug, Default, Deserialize)]
348#[serde(deny_unknown_fields)]
349pub struct StorageConfig {
350    /// Start address of local storage, MUST BE start of a sector.
351    /// If start_addr is set to 0(this is the default value), the last `num_sectors` sectors will be used.
352    pub start_addr: Option<usize>,
353    // Number of sectors used for storage, >= 2.
354    pub num_sectors: Option<u8>,
355    #[serde(default = "default_true")]
356    pub enabled: bool,
357    // Clear on the storage at reboot, set this to true if you want to reset the keymap
358    pub clear_storage: Option<bool>,
359    // Clear on the layout at reboot, set this to true if you want to reset the layout
360    pub clear_layout: Option<bool>,
361}
362
363#[derive(Clone, Default, Debug, Deserialize)]
364#[serde(deny_unknown_fields)]
365pub struct BleConfig {
366    pub enabled: bool,
367    pub battery_adc_pin: Option<String>,
368    pub charge_state: Option<PinConfig>,
369    pub charge_led: Option<PinConfig>,
370    pub adc_divider_measured: Option<u32>,
371    pub adc_divider_total: Option<u32>,
372    pub default_tx_power: Option<i8>,
373    pub ble_use_2m_phy: Option<bool>,
374}
375
376/// Config for chip-specific settings
377#[derive(Clone, Default, Debug, Deserialize)]
378#[serde(deny_unknown_fields)]
379pub struct ChipConfig {
380    /// DCDC regulator 0 enabled (for nrf52840)
381    pub dcdc_reg0: Option<bool>,
382    /// DCDC regulator 1 enabled (for nrf52840, nrf52833)
383    pub dcdc_reg1: Option<bool>,
384    /// DCDC regulator 0 voltage (for nrf52840)
385    /// Values: "3V3" or "1V8"
386    pub dcdc_reg0_voltage: Option<String>,
387}
388
389/// Config for lights
390#[derive(Clone, Default, Debug, Deserialize)]
391#[serde(deny_unknown_fields)]
392pub struct LightConfig {
393    pub capslock: Option<PinConfig>,
394    pub scrolllock: Option<PinConfig>,
395    pub numslock: Option<PinConfig>,
396}
397
398/// Config for a single pin
399#[derive(Clone, Default, Debug, Deserialize)]
400#[serde(deny_unknown_fields)]
401pub struct PinConfig {
402    pub pin: String,
403    pub low_active: bool,
404}
405
406/// Configurations for dependencies
407#[derive(Clone, Debug, Deserialize)]
408#[serde(deny_unknown_fields)]
409pub struct DependencyConfig {
410    /// Enable defmt log or not
411    #[serde(default = "default_true")]
412    pub defmt_log: bool,
413}
414
415impl Default for DependencyConfig {
416    fn default() -> Self {
417        Self { defmt_log: true }
418    }
419}
420
421/// Configurations for keyboard layout
422#[derive(Clone, Debug, Default, Deserialize)]
423#[serde(deny_unknown_fields)]
424pub struct LayoutConfig {
425    pub rows: u8,
426    pub cols: u8,
427    pub layers: u8,
428    pub keymap: Vec<Vec<Vec<String>>>,
429    pub encoder_map: Vec<Vec<[String; 2]>>, // Empty if there are no encoders or not configured
430}
431
432#[derive(Clone, Debug, Default, Deserialize)]
433#[serde(deny_unknown_fields)]
434pub struct KeyInfo {
435    pub hand: char, // 'L' or 'R' or other chars
436}
437
438/// Configurations for actions behavior
439#[derive(Clone, Debug, Default, Deserialize)]
440#[serde(deny_unknown_fields)]
441pub struct BehaviorConfig {
442    pub tri_layer: Option<TriLayerConfig>,
443    pub one_shot: Option<OneShotConfig>,
444    pub combo: Option<CombosConfig>,
445    #[serde(alias = "macro")]
446    pub macros: Option<MacrosConfig>,
447    pub fork: Option<ForksConfig>,
448    pub morse: Option<MorsesConfig>,
449}
450
451/// Per Key configurations profiles for morse, tap-hold, etc.
452/// overrides the defaults given in TapHoldConfig
453#[derive(Clone, Debug, Deserialize, Default)]
454pub struct MorseProfile {
455    /// if true, tap-hold key will always send tap action when tapped with the same hand only
456    pub unilateral_tap: Option<bool>,
457
458    /// The decision mode of the morse/tap-hold key (only one of permissive_hold, hold_on_other_press and normal_mode can be true)
459    /// /// if none of them is given, normal mode will be the default
460    pub permissive_hold: Option<bool>,
461    pub hold_on_other_press: Option<bool>,
462    pub normal_mode: Option<bool>,
463
464    /// If the key is pressed longer than this, it is accepted as `hold` (in milliseconds)
465    pub hold_timeout: Option<DurationMillis>,
466
467    /// The time elapsed from the last release of a key is longer than this, it will break the morse pattern (in milliseconds)
468    pub gap_timeout: Option<DurationMillis>,
469}
470
471/// Configurations for tri layer
472#[derive(Clone, Debug, Deserialize)]
473#[serde(deny_unknown_fields)]
474pub struct TriLayerConfig {
475    pub upper: u8,
476    pub lower: u8,
477    pub adjust: u8,
478}
479
480/// Configurations for one shot
481#[derive(Clone, Debug, Deserialize)]
482#[serde(deny_unknown_fields)]
483pub struct OneShotConfig {
484    pub timeout: Option<DurationMillis>,
485}
486
487/// Configurations for combos
488#[derive(Clone, Debug, Deserialize)]
489#[serde(deny_unknown_fields)]
490pub struct CombosConfig {
491    pub combos: Vec<ComboConfig>,
492    pub timeout: Option<DurationMillis>,
493}
494
495/// Configurations for combo
496#[derive(Clone, Debug, Deserialize)]
497pub struct ComboConfig {
498    pub actions: Vec<String>,
499    pub output: String,
500    pub layer: Option<u8>,
501}
502
503/// Configurations for macros
504#[derive(Clone, Debug, Deserialize)]
505#[serde(deny_unknown_fields)]
506pub struct MacrosConfig {
507    pub macros: Vec<MacroConfig>,
508}
509
510/// Configurations for macro
511#[derive(Clone, Debug, Deserialize)]
512pub struct MacroConfig {
513    pub operations: Vec<MacroOperation>,
514}
515
516/// Macro operations
517#[derive(Clone, Debug, Deserialize)]
518#[serde(tag = "operation", rename_all = "lowercase")]
519pub enum MacroOperation {
520    Tap { keycode: String },
521    Down { keycode: String },
522    Up { keycode: String },
523    Delay { duration: DurationMillis },
524    Text { text: String },
525}
526
527/// Configurations for forks
528#[derive(Clone, Debug, Deserialize)]
529#[serde(deny_unknown_fields)]
530pub struct ForksConfig {
531    pub forks: Vec<ForkConfig>,
532}
533
534/// Configurations for fork
535#[derive(Clone, Debug, Deserialize)]
536#[serde(deny_unknown_fields)]
537pub struct ForkConfig {
538    pub trigger: String,
539    pub negative_output: String,
540    pub positive_output: String,
541    pub match_any: Option<String>,
542    pub match_none: Option<String>,
543    pub kept_modifiers: Option<String>,
544    pub bindable: Option<bool>,
545}
546
547/// Configurations for morse keys
548#[derive(Clone, Debug, Deserialize)]
549#[serde(deny_unknown_fields)]
550pub struct MorsesConfig {
551    pub enable_flow_tap: Option<bool>, //default: false
552    /// used in permissive_hold mode
553    pub prior_idle_time: Option<DurationMillis>,
554
555    /// if true, tap-hold key will always send tap action when tapped with the same hand only
556    pub unilateral_tap: Option<bool>,
557
558    /// The decision mode of the morse/tap-hold key (only one of permissive_hold, hold_on_other_press and normal_mode can be true)
559    /// if none of them is given, normal mode will be the default
560    pub permissive_hold: Option<bool>,
561    pub hold_on_other_press: Option<bool>,
562    pub normal_mode: Option<bool>,
563
564    /// If the key is pressed longer than this, it is accepted as `hold` (in milliseconds)
565    pub hold_timeout: Option<DurationMillis>,
566
567    /// The time elapsed from the last release of a key is longer than this, it will break the morse pattern (in milliseconds)
568    pub gap_timeout: Option<DurationMillis>,
569
570    /// these can be used to overrides the defaults given above
571    pub profiles: Option<HashMap<String, MorseProfile>>,
572
573    /// the definition of morse / tap dance keys
574    pub morses: Option<Vec<MorseConfig>>,
575}
576
577/// Configurations for morse
578#[derive(Clone, Debug, Deserialize)]
579#[serde(deny_unknown_fields)]
580pub struct MorseConfig {
581    // name of morse profile (to address BehaviorConfig::morse.profiles[self.profile])
582    pub profile: Option<String>,
583
584    pub tap: Option<String>,
585    pub hold: Option<String>,
586    pub hold_after_tap: Option<String>,
587    pub double_tap: Option<String>,
588    /// Array of tap actions for each tap count (0-indexed)
589    pub tap_actions: Option<Vec<String>>,
590    /// Array of hold actions for each tap count (0-indexed)
591    pub hold_actions: Option<Vec<String>>,
592    /// Array of morse patter->action pairs  count (0-indexed)
593    pub morse_actions: Option<Vec<MorseActionPair>>,
594}
595
596/// Configurations for morse action pairs
597#[derive(Clone, Debug, Deserialize)]
598#[serde(deny_unknown_fields)]
599pub struct MorseActionPair {
600    pub pattern: String, // for example morse code of "B": "-..." or "_..." or "1000"
601    pub action: String,  // "B"
602}
603
604/// Configurations for split keyboards
605#[derive(Clone, Debug, Default, Deserialize)]
606#[serde(deny_unknown_fields)]
607pub struct SplitConfig {
608    pub connection: String,
609    pub central: SplitBoardConfig,
610    pub peripheral: Vec<SplitBoardConfig>,
611}
612
613/// Configurations for each split board
614///
615/// Either ble_addr or serial must be set, but not both.
616#[allow(unused)]
617#[derive(Clone, Debug, Default, Deserialize)]
618#[serde(deny_unknown_fields)]
619pub struct SplitBoardConfig {
620    /// Row number of the split board
621    pub rows: usize,
622    /// Col number of the split board
623    pub cols: usize,
624    /// Row offset of the split board
625    pub row_offset: usize,
626    /// Col offset of the split board
627    pub col_offset: usize,
628    /// Ble address
629    pub ble_addr: Option<[u8; 6]>,
630    /// Serial config, the vector length should be 1 for peripheral
631    pub serial: Option<Vec<SerialConfig>>,
632    /// Matrix config for the split
633    pub matrix: MatrixConfig,
634    /// Input device config for the split
635    pub input_device: Option<InputDeviceConfig>,
636    /// Output Pin config for the split
637    pub output: Option<Vec<OutputConfig>>,
638}
639
640/// Serial port config
641#[derive(Clone, Debug, Default, Deserialize)]
642pub struct SerialConfig {
643    pub instance: String,
644    pub tx_pin: String,
645    pub rx_pin: String,
646}
647
648/// Duration in milliseconds
649#[derive(Clone, Debug, Deserialize)]
650pub struct DurationMillis(#[serde(deserialize_with = "parse_duration_millis")] pub u64);
651
652const fn default_true() -> bool {
653    true
654}
655
656const fn default_false() -> bool {
657    false
658}
659
660fn parse_duration_millis<'de, D: de::Deserializer<'de>>(deserializer: D) -> Result<u64, D::Error> {
661    let input: String = de::Deserialize::deserialize(deserializer)?;
662    let num = input.trim_end_matches(|c: char| !c.is_numeric());
663    let unit = &input[num.len()..];
664    let num: u64 = num.parse().map_err(|_| {
665        de::Error::custom(format!(
666            "Invalid number \"{num}\" in duration: number part must be a u64"
667        ))
668    })?;
669
670    match unit {
671        "s" => Ok(num * 1000),
672        "ms" => Ok(num),
673        other => Err(de::Error::custom(format!(
674            "Invalid duration unit \"{other}\": unit part must be either \"s\" or \"ms\""
675        ))),
676    }
677}
678
679/// Configuration for host tools
680#[serde_inline_default]
681#[derive(Clone, Debug, Deserialize)]
682#[serde(deny_unknown_fields)]
683pub struct HostConfig {
684    /// Whether Vial is enabled
685    #[serde_inline_default(true)]
686    pub vial_enabled: bool,
687    /// Unlock keys for Vial (optional)
688    pub unlock_keys: Option<Vec<[u8; 2]>>,
689}
690
691impl Default for HostConfig {
692    fn default() -> Self {
693        Self {
694            vial_enabled: true,
695            unlock_keys: None,
696        }
697    }
698}
699
700/// Configurations for input devices
701///
702#[derive(Clone, Debug, Default, Deserialize)]
703#[allow(unused)]
704#[serde(deny_unknown_fields)]
705pub struct InputDeviceConfig {
706    pub encoder: Option<Vec<EncoderConfig>>,
707    pub pointing: Option<Vec<PointingDeviceConfig>>,
708    pub joystick: Option<Vec<JoystickConfig>>,
709    pub pmw3610: Option<Vec<Pmw3610Config>>,
710}
711
712#[derive(Clone, Debug, Default, Deserialize)]
713#[allow(unused)]
714#[serde(deny_unknown_fields)]
715pub struct JoystickConfig {
716    // Name of the joystick
717    pub name: String,
718    // Pin a of the joystick
719    pub pin_x: String,
720    // Pin b of the joystick
721    pub pin_y: String,
722    // Pin z of the joystick
723    pub pin_z: String,
724    pub transform: Vec<Vec<i16>>,
725    pub bias: Vec<i16>,
726    pub resolution: u16,
727}
728
729/// PMW3610 optical mouse sensor configuration
730#[derive(Clone, Debug, Default, Deserialize)]
731#[allow(unused)]
732#[serde(deny_unknown_fields)]
733pub struct Pmw3610Config {
734    /// Name of the sensor (used for variable naming)
735    pub name: String,
736    /// SPI pins
737    pub spi: SpiConfig,
738    /// Optional motion interrupt pin
739    pub motion: Option<String>,
740    /// CPI resolution (200-3200, step 200). Optional, uses sensor default if not set.
741    pub cpi: Option<u16>,
742    /// Invert X axis
743    #[serde(default)]
744    pub invert_x: bool,
745    /// Invert Y axis
746    #[serde(default)]
747    pub invert_y: bool,
748    /// Swap X and Y axes
749    #[serde(default)]
750    pub swap_xy: bool,
751    /// Force awake mode (disable power saving)
752    #[serde(default)]
753    pub force_awake: bool,
754    /// Enable smart mode for better tracking on shiny surfaces
755    #[serde(default)]
756    pub smart_mode: bool,
757}
758
759#[derive(Clone, Debug, Default, Deserialize)]
760#[allow(unused)]
761#[serde(deny_unknown_fields)]
762pub struct EncoderConfig {
763    // Pin a of the encoder
764    pub pin_a: String,
765    // Pin b of the encoder
766    pub pin_b: String,
767    // Phase is the working mode of the rotary encoders.
768    // Available mode:
769    // - default: resolution = 1
770    // - resolution: customized resolution, the resolution value and reverse should be specified
771    //   A typical [EC11 encoder](https://tech.alpsalpine.com/cms.media/product_catalog_ec_01_ec11e_en_611f078659.pdf)'s resolution is 2
772    //   In resolution mode, you can also specify the number of detent and pulses, the resolution will be calculated by `pulse * 4 / detent`
773    pub phase: Option<String>,
774    // Resolution
775    pub resolution: Option<EncoderResolution>,
776    // The number of detent
777    pub detent: Option<u8>,
778    // The number of pulse
779    pub pulse: Option<u8>,
780    // Whether the direction of the rotary encoder is reversed.
781    pub reverse: Option<bool>,
782    // Use MCU's internal pull-up resistor or not, defaults to false, the external pull-up resistor is needed
783    #[serde(default = "default_false")]
784    pub internal_pullup: bool,
785}
786
787#[derive(Clone, Debug, Deserialize)]
788#[allow(unused)]
789#[serde(deny_unknown_fields, untagged)]
790pub enum EncoderResolution {
791    Value(u8),
792    Derived { detent: u8, pulse: u8 },
793}
794
795impl Default for EncoderResolution {
796    fn default() -> Self {
797        Self::Value(4)
798    }
799}
800
801/// Pointing device config
802#[derive(Clone, Debug, Default, Deserialize)]
803#[allow(unused)]
804#[serde(deny_unknown_fields)]
805pub struct PointingDeviceConfig {
806    pub interface: Option<CommunicationProtocol>,
807}
808
809#[derive(Clone, Debug, Deserialize)]
810#[allow(unused)]
811pub enum CommunicationProtocol {
812    I2c(I2cConfig),
813    Spi(SpiConfig),
814}
815
816/// SPI config
817#[derive(Clone, Debug, Default, Deserialize)]
818#[allow(unused)]
819pub struct SpiConfig {
820    pub instance: String,
821    pub sck: String,
822    pub mosi: String,
823    pub miso: String,
824    pub cs: Option<String>,
825    pub cpi: Option<u32>,
826}
827
828/// I2C config
829#[derive(Clone, Debug, Default, Deserialize)]
830#[allow(unused)]
831pub struct I2cConfig {
832    pub instance: String,
833    pub sda: String,
834    pub scl: String,
835    pub address: u8,
836}
837
838/// Configuration for an output pin
839#[allow(unused)]
840#[derive(Clone, Debug, Default, Deserialize)]
841#[serde(deny_unknown_fields)]
842pub struct OutputConfig {
843    pub pin: String,
844    #[serde(default)]
845    pub low_active: bool,
846    #[serde(default)]
847    pub initial_state_active: bool,
848}
849
850impl KeyboardTomlConfig {
851    pub fn get_output_config(&self) -> Result<Vec<OutputConfig>, String> {
852        let output_config = self.output.clone();
853        let split = self.split.clone();
854        match (output_config, split) {
855            (None, Some(s)) => Ok(s.central.output.unwrap_or_default()),
856            (Some(c), None) => Ok(c),
857            (None, None) => Ok(Default::default()),
858            _ => Err("Use [[split.output]] to define outputs for split in your keyboard.toml!".to_string()),
859        }
860    }
861}