use std::collections::HashMap;
use std::path::Path;
use config::{Config, File, FileFormat};
use serde::de;
use serde::Deserialize as SerdeDeserialize;
use serde_derive::Deserialize;
use serde_inline_default::serde_inline_default;
pub mod chip;
pub mod communication;
pub mod keyboard;
#[rustfmt::skip]
pub mod usb_interrupt_map;
pub mod behavior;
pub mod board;
pub mod keycode_alias;
pub mod layout;
pub mod light;
pub mod storage;
pub use board::{BoardConfig, UniBodyConfig};
pub use chip::{ChipModel, ChipSeries};
pub use communication::{CommunicationConfig, UsbInfo};
pub use keyboard::Basic;
pub use keycode_alias::KEYCODE_ALIAS;
#[serde_inline_default]
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RmkConstantsConfig {
#[serde_inline_default(20)]
pub mouse_key_interval: u32,
#[serde_inline_default(80)]
pub mouse_wheel_interval: u32,
#[serde_inline_default(8)]
#[serde(deserialize_with = "check_combo_max_num")]
pub combo_max_num: usize,
#[serde_inline_default(4)]
pub combo_max_length: usize,
#[serde_inline_default(8)]
#[serde(deserialize_with = "check_fork_max_num")]
pub fork_max_num: usize,
#[serde_inline_default(256)]
pub macro_space_size: usize,
#[serde_inline_default(20)]
pub debounce_time: u16,
#[serde_inline_default(16)]
pub event_channel_size: usize,
#[serde_inline_default(16)]
pub controller_channel_size: usize,
#[serde_inline_default(4)]
pub controller_channel_pubs: usize,
#[serde_inline_default(4)]
pub controller_channel_subs: usize,
#[serde_inline_default(16)]
pub report_channel_size: usize,
#[serde_inline_default(4)]
pub vial_channel_size: usize,
#[serde_inline_default(4)]
pub flash_channel_size: usize,
#[serde_inline_default(1)]
pub split_peripherals_num: usize,
#[serde_inline_default(4)]
pub split_message_channel_size: usize,
#[serde_inline_default(3)]
pub ble_profiles_num: usize,
}
fn check_combo_max_num<'de, D>(deserializer: D) -> Result<usize, D::Error>
where
D: de::Deserializer<'de>,
{
let value = SerdeDeserialize::deserialize(deserializer)?;
if value > 256 {
panic!("❌ Parse `keyboard.toml` error: combo_max_num must be between 0 and 256, got {value}");
}
Ok(value)
}
fn check_fork_max_num<'de, D>(deserializer: D) -> Result<usize, D::Error>
where
D: de::Deserializer<'de>,
{
let value = SerdeDeserialize::deserialize(deserializer)?;
if value > 256 {
panic!("❌ Parse `keyboard.toml` error: fork_max_num must be between 0 and 256, got {value}");
}
Ok(value)
}
impl Default for RmkConstantsConfig {
fn default() -> Self {
Self {
mouse_key_interval: 20,
mouse_wheel_interval: 80,
combo_max_num: 8,
combo_max_length: 4,
fork_max_num: 8,
macro_space_size: 256,
debounce_time: 20,
event_channel_size: 16,
controller_channel_size: 16,
controller_channel_pubs: 4,
controller_channel_subs: 4,
report_channel_size: 16,
vial_channel_size: 4,
flash_channel_size: 4,
split_peripherals_num: 1,
split_message_channel_size: 4,
ble_profiles_num: 3,
}
}
}
#[derive(Clone, Debug, Deserialize)]
#[allow(unused)]
pub struct KeyboardTomlConfig {
pub keyboard: Option<KeyboardInfo>,
pub matrix: Option<MatrixConfig>,
pub aliases: Option<HashMap<String, String>>,
pub layer: Option<Vec<LayerTomlConfig>>,
pub layout: Option<LayoutTomlConfig>,
pub behavior: Option<BehaviorConfig>,
pub light: Option<LightConfig>,
pub storage: Option<StorageConfig>,
pub ble: Option<BleConfig>,
pub dependency: Option<DependencyConfig>,
pub split: Option<SplitConfig>,
pub input_device: Option<InputDeviceConfig>,
#[serde(default)]
pub rmk: RmkConstantsConfig,
}
impl KeyboardTomlConfig {
pub fn new_from_toml_str<P: AsRef<Path>>(config_toml_path: P) -> Self {
let user_config = match std::fs::read_to_string(config_toml_path.as_ref()) {
Ok(s) => match toml::from_str::<KeyboardTomlConfig>(&s) {
Ok(c) => c,
Err(e) => panic!("Parse {:?} error: {}", config_toml_path.as_ref(), e.message()),
},
Err(e) => panic!("Read keyboard config file {:?} error: {}", config_toml_path.as_ref(), e),
};
let default_config_str = user_config.get_chip_model().unwrap().get_default_config_str().unwrap();
Config::builder()
.add_source(File::from_str(default_config_str, FileFormat::Toml))
.add_source(File::with_name(config_toml_path.as_ref().to_str().unwrap()))
.build()
.unwrap()
.try_deserialize()
.unwrap()
}
}
#[derive(Clone, Debug, Deserialize)]
#[allow(unused)]
pub struct LayoutTomlConfig {
pub rows: u8,
pub cols: u8,
pub layers: u8,
pub keymap: Option<Vec<Vec<Vec<String>>>>, pub matrix_map: Option<String>, }
#[derive(Clone, Debug, Deserialize)]
#[allow(unused)]
pub struct LayerTomlConfig {
pub name: Option<String>,
pub keys: String,
}
#[derive(Clone, Debug, Default, Deserialize)]
pub struct KeyboardInfo {
pub name: String,
pub vendor_id: u16,
pub product_id: u16,
pub manufacturer: Option<String>,
pub product_name: Option<String>,
pub serial_number: Option<String>,
pub board: Option<String>,
pub chip: Option<String>,
pub usb_enable: Option<bool>,
}
#[derive(Clone, Debug, Default, Deserialize)]
#[allow(non_camel_case_types)]
pub enum MatrixType {
#[default]
normal,
direct_pin,
}
#[derive(Clone, Debug, Default, Deserialize)]
pub struct MatrixConfig {
#[serde(default)]
pub matrix_type: MatrixType,
pub input_pins: Option<Vec<String>>,
pub output_pins: Option<Vec<String>>,
pub direct_pins: Option<Vec<Vec<String>>>,
#[serde(default = "default_true")]
pub direct_pin_low_active: bool,
#[serde(default = "default_false")]
pub row2col: bool,
}
#[derive(Clone, Copy, Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct StorageConfig {
pub start_addr: Option<usize>,
pub num_sectors: Option<u8>,
#[serde(default = "default_true")]
pub enabled: bool,
pub clear_storage: Option<bool>,
}
#[derive(Clone, Default, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BleConfig {
pub enabled: bool,
pub battery_adc_pin: Option<String>,
pub charge_state: Option<PinConfig>,
pub charge_led: Option<PinConfig>,
pub adc_divider_measured: Option<u32>,
pub adc_divider_total: Option<u32>,
}
#[derive(Clone, Default, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct LightConfig {
pub capslock: Option<PinConfig>,
pub scrolllock: Option<PinConfig>,
pub numslock: Option<PinConfig>,
}
#[derive(Clone, Default, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PinConfig {
pub pin: String,
pub low_active: bool,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DependencyConfig {
#[serde(default = "default_true")]
pub defmt_log: bool,
}
impl Default for DependencyConfig {
fn default() -> Self {
Self { defmt_log: true }
}
}
#[derive(Clone, Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct LayoutConfig {
pub rows: u8,
pub cols: u8,
pub layers: u8,
pub keymap: Vec<Vec<Vec<String>>>,
}
#[derive(Clone, Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BehaviorConfig {
pub tri_layer: Option<TriLayerConfig>,
pub tap_hold: Option<TapHoldConfig>,
pub one_shot: Option<OneShotConfig>,
pub combo: Option<CombosConfig>,
pub fork: Option<ForksConfig>,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct TapHoldConfig {
pub enable_hrm: Option<bool>,
pub prior_idle_time: Option<DurationMillis>,
pub post_wait_time: Option<DurationMillis>,
pub hold_timeout: Option<DurationMillis>,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct TriLayerConfig {
pub upper: u8,
pub lower: u8,
pub adjust: u8,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct OneShotConfig {
pub timeout: Option<DurationMillis>,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CombosConfig {
pub combos: Vec<ComboConfig>,
pub timeout: Option<DurationMillis>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ComboConfig {
pub actions: Vec<String>,
pub output: String,
pub layer: Option<u8>,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ForksConfig {
pub forks: Vec<ForkConfig>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ForkConfig {
pub trigger: String,
pub negative_output: String,
pub positive_output: String,
pub match_any: Option<String>,
pub match_none: Option<String>,
pub kept_modifiers: Option<String>,
pub bindable: Option<bool>,
}
#[derive(Clone, Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SplitConfig {
pub connection: String,
pub central: SplitBoardConfig,
pub peripheral: Vec<SplitBoardConfig>,
}
#[allow(unused)]
#[derive(Clone, Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SplitBoardConfig {
pub rows: usize,
pub cols: usize,
pub row_offset: usize,
pub col_offset: usize,
pub ble_addr: Option<[u8; 6]>,
pub serial: Option<Vec<SerialConfig>>,
pub matrix: MatrixConfig,
pub input_device: Option<InputDeviceConfig>,
}
#[derive(Clone, Debug, Default, Deserialize)]
pub struct SerialConfig {
pub instance: String,
pub tx_pin: String,
pub rx_pin: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct DurationMillis(#[serde(deserialize_with = "parse_duration_millis")] pub u64);
const fn default_true() -> bool {
true
}
const fn default_false() -> bool {
false
}
fn parse_duration_millis<'de, D: de::Deserializer<'de>>(deserializer: D) -> Result<u64, D::Error> {
let input: String = de::Deserialize::deserialize(deserializer)?;
let num = input.trim_end_matches(|c: char| !c.is_numeric());
let unit = &input[num.len()..];
let num: u64 = num.parse().map_err(|_| {
de::Error::custom(format!(
"Invalid number \"{num}\" in duration: number part must be a u64"
))
})?;
match unit {
"s" => Ok(num * 1000),
"ms" => Ok(num),
other => Err(de::Error::custom(format!(
"Invalid duration unit \"{other}\": unit part must be either \"s\" or \"ms\""
))),
}
}
#[derive(Clone, Debug, Default, Deserialize)]
#[allow(unused)]
#[serde(deny_unknown_fields)]
pub struct InputDeviceConfig {
pub encoder: Option<Vec<EncoderConfig>>,
pub pointing: Option<Vec<PointingDeviceConfig>>,
pub joystick: Option<Vec<JoystickConfig>>,
}
#[derive(Clone, Debug, Default, Deserialize)]
#[allow(unused)]
#[serde(deny_unknown_fields)]
pub struct JoystickConfig {
pub name: String,
pub pin_x: String,
pub pin_y: String,
pub pin_z: String,
pub transform: Vec<Vec<i16>>,
pub bias: Vec<i16>,
pub resolution: u16,
}
#[derive(Clone, Debug, Default, Deserialize)]
#[allow(unused)]
#[serde(deny_unknown_fields)]
pub struct EncoderConfig {
pub pin_a: String,
pub pin_b: String,
pub phase: Option<String>,
pub resolution: Option<u8>,
pub reverse: Option<bool>,
#[serde(default = "default_false")]
pub internal_pullup: bool,
}
#[derive(Clone, Debug, Default, Deserialize)]
#[allow(unused)]
#[serde(deny_unknown_fields)]
pub struct PointingDeviceConfig {
pub interface: Option<CommunicationProtocol>,
}
#[derive(Clone, Debug, Deserialize)]
#[allow(unused)]
pub enum CommunicationProtocol {
I2c(I2cConfig),
Spi(SpiConfig),
}
#[derive(Clone, Debug, Default, Deserialize)]
#[allow(unused)]
pub struct SpiConfig {
pub instance: String,
pub sck: String,
pub mosi: String,
pub miso: String,
pub cs: Option<String>,
pub cpi: Option<u32>,
}
#[derive(Clone, Debug, Default, Deserialize)]
#[allow(unused)]
pub struct I2cConfig {
pub instance: String,
pub sda: String,
pub scl: String,
pub address: u8,
}