rmk_config/
lib.rs

1use std::collections::HashMap;
2use std::path::Path;
3
4use config::{Config, File, FileFormat};
5use serde::de;
6use serde::Deserialize as SerdeDeserialize;
7use serde_derive::Deserialize;
8use serde_inline_default::serde_inline_default;
9
10pub mod chip;
11pub mod communication;
12pub mod keyboard;
13#[rustfmt::skip]
14pub mod usb_interrupt_map;
15pub mod behavior;
16pub mod board;
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/// Keyboard constants configuration for performance and hardware limits
29#[serde_inline_default]
30#[derive(Clone, Debug, Deserialize)]
31#[serde(deny_unknown_fields)]
32pub struct RmkConstantsConfig {
33    /// Mouse key interval (ms) - controls mouse movement speed
34    #[serde_inline_default(20)]
35    pub mouse_key_interval: u32,
36    /// Mouse wheel interval (ms) - controls scrolling speed
37    #[serde_inline_default(80)]
38    pub mouse_wheel_interval: u32,
39    /// Maximum number of combos keyboard can store
40    #[serde_inline_default(8)]
41    #[serde(deserialize_with = "check_combo_max_num")]
42    pub combo_max_num: usize,
43    /// Maximum number of keys pressed simultaneously in a combo
44    #[serde_inline_default(4)]
45    pub combo_max_length: usize,
46    /// Maximum number of forks for conditional key actions
47    #[serde_inline_default(8)]
48    #[serde(deserialize_with = "check_fork_max_num")]
49    pub fork_max_num: usize,
50    /// Macro space size in bytes for storing sequences
51    #[serde_inline_default(256)]
52    pub macro_space_size: usize,
53    /// Default debounce time in ms
54    #[serde_inline_default(20)]
55    pub debounce_time: u16,
56    /// Event channel size
57    #[serde_inline_default(16)]
58    pub event_channel_size: usize,
59    /// Controller event channel size
60    #[serde_inline_default(16)]
61    pub controller_channel_size: usize,
62    /// Number of publishers to controllers
63    #[serde_inline_default(8)]
64    pub controller_channel_pubs: usize,
65    /// Number of controllers
66    #[serde_inline_default(8)]
67    pub controller_channel_subs: usize,
68    /// Report channel size
69    #[serde_inline_default(16)]
70    pub report_channel_size: usize,
71    /// Vial channel size
72    #[serde_inline_default(4)]
73    pub vial_channel_size: usize,
74    /// Flash channel size
75    #[serde_inline_default(4)]
76    pub flash_channel_size: usize,
77    /// The number of the split peripherals
78    #[serde_inline_default(1)]
79    pub split_peripherals_num: usize,
80    /// The size of the split message
81    #[serde_inline_default(4)]
82    pub split_message_channel_size: usize,
83    /// The number of available BLE profiles
84    #[serde_inline_default(3)]
85    pub ble_profiles_num: usize,
86}
87
88fn check_combo_max_num<'de, D>(deserializer: D) -> Result<usize, D::Error>
89where
90    D: de::Deserializer<'de>,
91{
92    let value = SerdeDeserialize::deserialize(deserializer)?;
93    if value > 256 {
94        panic!("❌ Parse `keyboard.toml` error: combo_max_num must be between 0 and 256, got {value}");
95    }
96    Ok(value)
97}
98
99fn check_fork_max_num<'de, D>(deserializer: D) -> Result<usize, D::Error>
100where
101    D: de::Deserializer<'de>,
102{
103    let value = SerdeDeserialize::deserialize(deserializer)?;
104    if value > 256 {
105        panic!("❌ Parse `keyboard.toml` error: fork_max_num must be between 0 and 256, got {value}");
106    }
107    Ok(value)
108}
109
110/// This separate Default impl is needed when `[rmk]` section is not set in keyboard.toml
111impl Default for RmkConstantsConfig {
112    fn default() -> Self {
113        Self {
114            mouse_key_interval: 20,
115            mouse_wheel_interval: 80,
116            combo_max_num: 8,
117            combo_max_length: 4,
118            fork_max_num: 8,
119            macro_space_size: 256,
120            debounce_time: 20,
121            event_channel_size: 16,
122            controller_channel_size: 16,
123            controller_channel_pubs: 8,
124            controller_channel_subs: 8,
125            report_channel_size: 16,
126            vial_channel_size: 4,
127            flash_channel_size: 4,
128            split_peripherals_num: 1,
129            split_message_channel_size: 4,
130            ble_profiles_num: 3,
131        }
132    }
133}
134
135/// Configurations for RMK keyboard.
136#[derive(Clone, Debug, Deserialize)]
137#[allow(unused)]
138pub struct KeyboardTomlConfig {
139    /// Basic keyboard info
140    keyboard: Option<KeyboardInfo>,
141    /// Matrix of the keyboard, only for non-split keyboards
142    matrix: Option<MatrixConfig>,
143    // Aliases for key maps
144    aliases: Option<HashMap<String, String>>,
145    // Layers of key maps
146    layer: Option<Vec<LayerTomlConfig>>,
147    /// Layout config.
148    /// For split keyboard, the total row/col should be defined in this section
149    layout: Option<LayoutTomlConfig>,
150    /// Behavior config
151    behavior: Option<BehaviorConfig>,
152    /// Light config
153    light: Option<LightConfig>,
154    /// Storage config
155    storage: Option<StorageConfig>,
156    /// Ble config
157    ble: Option<BleConfig>,
158    /// Dependency config
159    dependency: Option<DependencyConfig>,
160    /// Split config
161    split: Option<SplitConfig>,
162    /// Input device config
163    input_device: Option<InputDeviceConfig>,
164    /// RMK config constants
165    #[serde(default)]
166    pub rmk: RmkConstantsConfig,
167}
168
169impl KeyboardTomlConfig {
170    pub fn new_from_toml_str<P: AsRef<Path>>(config_toml_path: P) -> Self {
171        // The first run, load chip model only
172        let user_config = match std::fs::read_to_string(config_toml_path.as_ref()) {
173            Ok(s) => match toml::from_str::<KeyboardTomlConfig>(&s) {
174                Ok(c) => c,
175                Err(e) => panic!("Parse {:?} error: {}", config_toml_path.as_ref(), e.message()),
176            },
177            Err(e) => panic!("Read keyboard config file {:?} error: {}", config_toml_path.as_ref(), e),
178        };
179        let default_config_str = user_config.get_chip_model().unwrap().get_default_config_str().unwrap();
180
181        // The second run, load the user config and merge with the default config
182        Config::builder()
183            .add_source(File::from_str(default_config_str, FileFormat::Toml))
184            .add_source(File::with_name(config_toml_path.as_ref().to_str().unwrap()))
185            .build()
186            .unwrap()
187            .try_deserialize()
188            .unwrap()
189    }
190}
191
192/// Configurations for keyboard layout
193#[derive(Clone, Debug, Deserialize)]
194#[allow(unused)]
195pub struct LayoutTomlConfig {
196    pub rows: u8,
197    pub cols: u8,
198    pub layers: u8,
199    pub keymap: Option<Vec<Vec<Vec<String>>>>, // will be deprecated in the future
200    pub matrix_map: Option<String>,            //temporarily allow both matrix_map and keymap to be set
201}
202
203#[derive(Clone, Debug, Deserialize)]
204#[allow(unused)]
205pub struct LayerTomlConfig {
206    pub name: Option<String>,
207    pub keys: String,
208}
209
210/// Configurations for keyboard info
211#[derive(Clone, Debug, Default, Deserialize)]
212pub struct KeyboardInfo {
213    /// Keyboard name
214    pub name: String,
215    /// Vender id
216    pub vendor_id: u16,
217    /// Product id
218    pub product_id: u16,
219    /// Manufacturer
220    pub manufacturer: Option<String>,
221    /// Product name, if not set, it will use `name` as default
222    pub product_name: Option<String>,
223    /// Serial number
224    pub serial_number: Option<String>,
225    /// Board name(if a supported board is used)
226    pub board: Option<String>,
227    /// Chip model
228    pub chip: Option<String>,
229    /// enable usb
230    pub usb_enable: Option<bool>,
231}
232
233#[derive(Clone, Debug, Default, Deserialize)]
234#[allow(non_camel_case_types)]
235pub enum MatrixType {
236    #[default]
237    normal,
238    direct_pin,
239}
240
241#[derive(Clone, Debug, Default, Deserialize)]
242pub struct MatrixConfig {
243    #[serde(default)]
244    pub matrix_type: MatrixType,
245    pub input_pins: Option<Vec<String>>,
246    pub output_pins: Option<Vec<String>>,
247    pub direct_pins: Option<Vec<Vec<String>>>,
248    #[serde(default = "default_true")]
249    pub direct_pin_low_active: bool,
250    #[serde(default = "default_false")]
251    pub row2col: bool,
252}
253
254/// Config for storage
255#[derive(Clone, Copy, Debug, Default, Deserialize)]
256#[serde(deny_unknown_fields)]
257pub struct StorageConfig {
258    /// Start address of local storage, MUST BE start of a sector.
259    /// If start_addr is set to 0(this is the default value), the last `num_sectors` sectors will be used.
260    pub start_addr: Option<usize>,
261    // Number of sectors used for storage, >= 2.
262    pub num_sectors: Option<u8>,
263    #[serde(default = "default_true")]
264    pub enabled: bool,
265    // Clear on the storage at reboot, set this to true if you want to reset the keymap
266    pub clear_storage: Option<bool>,
267}
268
269#[derive(Clone, Default, Debug, Deserialize)]
270#[serde(deny_unknown_fields)]
271pub struct BleConfig {
272    pub enabled: bool,
273    pub battery_adc_pin: Option<String>,
274    pub charge_state: Option<PinConfig>,
275    pub charge_led: Option<PinConfig>,
276    pub adc_divider_measured: Option<u32>,
277    pub adc_divider_total: Option<u32>,
278}
279
280/// Config for lights
281#[derive(Clone, Default, Debug, Deserialize)]
282#[serde(deny_unknown_fields)]
283pub struct LightConfig {
284    pub capslock: Option<PinConfig>,
285    pub scrolllock: Option<PinConfig>,
286    pub numslock: Option<PinConfig>,
287}
288
289/// Config for a single pin
290#[derive(Clone, Default, Debug, Deserialize)]
291#[serde(deny_unknown_fields)]
292pub struct PinConfig {
293    pub pin: String,
294    pub low_active: bool,
295}
296
297/// Configurations for dependencies
298#[derive(Clone, Debug, Deserialize)]
299#[serde(deny_unknown_fields)]
300pub struct DependencyConfig {
301    /// Enable defmt log or not
302    #[serde(default = "default_true")]
303    pub defmt_log: bool,
304}
305
306impl Default for DependencyConfig {
307    fn default() -> Self {
308        Self { defmt_log: true }
309    }
310}
311
312/// Configurations for keyboard layout
313#[derive(Clone, Debug, Default, Deserialize)]
314#[serde(deny_unknown_fields)]
315pub struct LayoutConfig {
316    pub rows: u8,
317    pub cols: u8,
318    pub layers: u8,
319    pub keymap: Vec<Vec<Vec<String>>>,
320}
321
322/// Configurations for actions behavior
323#[derive(Clone, Debug, Default, Deserialize)]
324#[serde(deny_unknown_fields)]
325pub struct BehaviorConfig {
326    pub tri_layer: Option<TriLayerConfig>,
327    pub tap_hold: Option<TapHoldConfig>,
328    pub one_shot: Option<OneShotConfig>,
329    pub combo: Option<CombosConfig>,
330    pub fork: Option<ForksConfig>,
331}
332
333/// Configurations for tap hold
334#[derive(Clone, Debug, Deserialize)]
335#[serde(deny_unknown_fields)]
336pub struct TapHoldConfig {
337    pub enable_hrm: Option<bool>,
338    pub prior_idle_time: Option<DurationMillis>,
339    pub post_wait_time: Option<DurationMillis>,
340    pub hold_timeout: Option<DurationMillis>,
341}
342
343/// Configurations for tri layer
344#[derive(Clone, Debug, Deserialize)]
345#[serde(deny_unknown_fields)]
346pub struct TriLayerConfig {
347    pub upper: u8,
348    pub lower: u8,
349    pub adjust: u8,
350}
351
352/// Configurations for one shot
353#[derive(Clone, Debug, Deserialize)]
354#[serde(deny_unknown_fields)]
355pub struct OneShotConfig {
356    pub timeout: Option<DurationMillis>,
357}
358
359/// Configurations for combos
360#[derive(Clone, Debug, Deserialize)]
361#[serde(deny_unknown_fields)]
362pub struct CombosConfig {
363    pub combos: Vec<ComboConfig>,
364    pub timeout: Option<DurationMillis>,
365}
366
367/// Configurations for combo
368#[derive(Clone, Debug, Deserialize)]
369pub struct ComboConfig {
370    pub actions: Vec<String>,
371    pub output: String,
372    pub layer: Option<u8>,
373}
374
375/// Configurations for forks
376#[derive(Clone, Debug, Deserialize)]
377#[serde(deny_unknown_fields)]
378pub struct ForksConfig {
379    pub forks: Vec<ForkConfig>,
380}
381
382/// Configurations for fork
383#[derive(Clone, Debug, Deserialize)]
384pub struct ForkConfig {
385    pub trigger: String,
386    pub negative_output: String,
387    pub positive_output: String,
388    pub match_any: Option<String>,
389    pub match_none: Option<String>,
390    pub kept_modifiers: Option<String>,
391    pub bindable: Option<bool>,
392}
393
394/// Configurations for split keyboards
395#[derive(Clone, Debug, Default, Deserialize)]
396#[serde(deny_unknown_fields)]
397pub struct SplitConfig {
398    pub connection: String,
399    pub central: SplitBoardConfig,
400    pub peripheral: Vec<SplitBoardConfig>,
401}
402
403/// Configurations for each split board
404///
405/// Either ble_addr or serial must be set, but not both.
406#[allow(unused)]
407#[derive(Clone, Debug, Default, Deserialize)]
408#[serde(deny_unknown_fields)]
409pub struct SplitBoardConfig {
410    /// Row number of the split board
411    pub rows: usize,
412    /// Col number of the split board
413    pub cols: usize,
414    /// Row offset of the split board
415    pub row_offset: usize,
416    /// Col offset of the split board
417    pub col_offset: usize,
418    /// Ble address
419    pub ble_addr: Option<[u8; 6]>,
420    /// Serial config, the vector length should be 1 for peripheral
421    pub serial: Option<Vec<SerialConfig>>,
422    /// Matrix config for the split
423    pub matrix: MatrixConfig,
424    /// Input device config for the split
425    pub input_device: Option<InputDeviceConfig>,
426}
427
428/// Serial port config
429#[derive(Clone, Debug, Default, Deserialize)]
430pub struct SerialConfig {
431    pub instance: String,
432    pub tx_pin: String,
433    pub rx_pin: String,
434}
435
436/// Duration in milliseconds
437#[derive(Clone, Debug, Deserialize)]
438pub struct DurationMillis(#[serde(deserialize_with = "parse_duration_millis")] pub u64);
439
440const fn default_true() -> bool {
441    true
442}
443
444const fn default_false() -> bool {
445    false
446}
447
448fn parse_duration_millis<'de, D: de::Deserializer<'de>>(deserializer: D) -> Result<u64, D::Error> {
449    let input: String = de::Deserialize::deserialize(deserializer)?;
450    let num = input.trim_end_matches(|c: char| !c.is_numeric());
451    let unit = &input[num.len()..];
452    let num: u64 = num.parse().map_err(|_| {
453        de::Error::custom(format!(
454            "Invalid number \"{num}\" in duration: number part must be a u64"
455        ))
456    })?;
457
458    match unit {
459        "s" => Ok(num * 1000),
460        "ms" => Ok(num),
461        other => Err(de::Error::custom(format!(
462            "Invalid duration unit \"{other}\": unit part must be either \"s\" or \"ms\""
463        ))),
464    }
465}
466
467/// Configurations for input devices
468///
469#[derive(Clone, Debug, Default, Deserialize)]
470#[allow(unused)]
471#[serde(deny_unknown_fields)]
472pub struct InputDeviceConfig {
473    pub encoder: Option<Vec<EncoderConfig>>,
474    pub pointing: Option<Vec<PointingDeviceConfig>>,
475    pub joystick: Option<Vec<JoystickConfig>>,
476}
477
478#[derive(Clone, Debug, Default, Deserialize)]
479#[allow(unused)]
480#[serde(deny_unknown_fields)]
481pub struct JoystickConfig {
482    // Name of the joystick
483    pub name: String,
484    // Pin a of the joystick
485    pub pin_x: String,
486    // Pin b of the joystick
487    pub pin_y: String,
488    // Pin z of the joystick
489    pub pin_z: String,
490    pub transform: Vec<Vec<i16>>,
491    pub bias: Vec<i16>,
492    pub resolution: u16,
493}
494
495#[derive(Clone, Debug, Default, Deserialize)]
496#[allow(unused)]
497#[serde(deny_unknown_fields)]
498pub struct EncoderConfig {
499    // Pin a of the encoder
500    pub pin_a: String,
501    // Pin b of the encoder
502    pub pin_b: String,
503    // Phase is the working mode of the rotary encoders.
504    // Available mode:
505    // - default: EC11 compatible, resolution = 1
506    // - e8h7: resolution = 2, reverse = true
507    // - resolution: customized resolution, the resolution value and reverse should be specified
508    pub phase: Option<String>,
509    // Resolution
510    pub resolution: Option<u8>,
511    // Whether the direction of the rotary encoder is reversed.
512    pub reverse: Option<bool>,
513    // Use MCU's internal pull-up resistor or not
514    #[serde(default = "default_false")]
515    pub internal_pullup: bool,
516}
517
518/// Pointing device config
519#[derive(Clone, Debug, Default, Deserialize)]
520#[allow(unused)]
521#[serde(deny_unknown_fields)]
522pub struct PointingDeviceConfig {
523    pub interface: Option<CommunicationProtocol>,
524}
525
526#[derive(Clone, Debug, Deserialize)]
527#[allow(unused)]
528pub enum CommunicationProtocol {
529    I2c(I2cConfig),
530    Spi(SpiConfig),
531}
532
533/// SPI config
534#[derive(Clone, Debug, Default, Deserialize)]
535#[allow(unused)]
536pub struct SpiConfig {
537    pub instance: String,
538    pub sck: String,
539    pub mosi: String,
540    pub miso: String,
541    pub cs: Option<String>,
542    pub cpi: Option<u32>,
543}
544
545/// I2C config
546#[derive(Clone, Debug, Default, Deserialize)]
547#[allow(unused)]
548pub struct I2cConfig {
549    pub instance: String,
550    pub sda: String,
551    pub scl: String,
552    pub address: u8,
553}