From 7f2cb52d535ece13ff7d50012774d43936d69d89 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Tue, 31 Dec 2019 08:25:40 -0800 Subject: Add config file parsing and loading --- src/main.rs | 36 ++++++------------- src/mapping.rs | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 119 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/main.rs b/src/main.rs index 3473bf7..e71ff25 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use crate::mapping::*; use crate::remapper::*; use anyhow::*; +use std::path::PathBuf; use std::time::Duration; use structopt::StructOpt; @@ -20,6 +21,10 @@ struct Opt { /// configuration #[structopt(name = "list-devices", long)] list_devices: bool, + + /// Specify the configuration file to be loaded + #[structopt(name = "config-file", long)] + config_file: PathBuf, } fn main() -> Result<()> { @@ -30,36 +35,17 @@ fn main() -> Result<()> { return deviceinfo::list_devices(); } - let mappings = vec![ - Mapping::DualRole { - input: KeyCode::KEY_CAPSLOCK, - hold: vec![KeyCode::KEY_LEFTCTRL], - tap: vec![KeyCode::KEY_ESC], - }, - Mapping::Remap { - input: [KeyCode::KEY_F1].into_iter().cloned().collect(), - output: [KeyCode::KEY_BACK].into_iter().cloned().collect(), - }, - Mapping::Remap { - input: [KeyCode::KEY_F8].into_iter().cloned().collect(), - output: [KeyCode::KEY_MUTE].into_iter().cloned().collect(), - }, - Mapping::Remap { - input: [KeyCode::KEY_F5].into_iter().cloned().collect(), - output: [KeyCode::KEY_BRIGHTNESSDOWN].into_iter().cloned().collect(), - }, - Mapping::Remap { - input: [KeyCode::KEY_F6].into_iter().cloned().collect(), - output: [KeyCode::KEY_BRIGHTNESSUP].into_iter().cloned().collect(), - }, - ]; + let mapping_config = MappingConfig::from_file(&opt.config_file).context(format!( + "loading --config-file={}", + opt.config_file.display() + ))?; log::error!("Short delay: release any keys now!"); std::thread::sleep(Duration::new(2, 0)); - let device_info = deviceinfo::DeviceInfo::with_name("AT Translated Set 2 keyboard")?; + let device_info = deviceinfo::DeviceInfo::with_name(&mapping_config.device_name)?; - let mut mapper = InputMapper::create_mapper(device_info.path, mappings)?; + let mut mapper = InputMapper::create_mapper(device_info.path, mapping_config.mappings)?; mapper.run_mapper()?; Ok(()) } diff --git a/src/mapping.rs b/src/mapping.rs index 6fa655e..34a9615 100644 --- a/src/mapping.rs +++ b/src/mapping.rs @@ -1,5 +1,36 @@ -pub use evdev_rs::enums::{EventCode, EV_KEY as KeyCode}; +use anyhow::Context; +pub use evdev_rs::enums::{EventCode, EventType, EV_KEY as KeyCode}; +use serde::Deserialize; use std::collections::HashSet; +use std::path::Path; +use thiserror::Error; + +#[derive(Debug, Clone)] +pub struct MappingConfig { + pub device_name: String, + pub mappings: Vec, +} + +impl MappingConfig { + pub fn from_file>(path: P) -> anyhow::Result { + let path = path.as_ref(); + let toml_data = std::fs::read_to_string(path) + .context(format!("reading toml from {}", path.display()))?; + let config_file: ConfigFile = + toml::from_str(&toml_data).context(format!("parsing toml from {}", path.display()))?; + let mut mappings = vec![]; + for dual in config_file.dual_role { + mappings.push(dual.into()); + } + for remap in config_file.remap { + mappings.push(remap.into()); + } + Ok(Self { + device_name: config_file.device_name, + mappings, + }) + } +} #[derive(Debug, Clone, Eq, PartialEq)] pub enum Mapping { @@ -13,3 +44,79 @@ pub enum Mapping { output: HashSet, }, } + +#[derive(Debug, Deserialize)] +#[serde(try_from = "String")] +struct KeyCodeWrapper { + pub code: KeyCode, +} + +impl Into for KeyCodeWrapper { + fn into(self) -> KeyCode { + self.code + } +} + +#[derive(Error, Debug)] +pub enum ConfigError { + #[error("Invalid key `{0}`")] + InvalidKey(String), + #[error("Impossible: parsed KEY_XXX but not into an EV_KEY")] + ImpossibleParseKey, +} + +impl std::convert::TryFrom for KeyCodeWrapper { + type Error = ConfigError; + fn try_from(s: String) -> Result { + match EventCode::from_str(&EventType::EV_KEY, &s) { + Some(code) => match code { + EventCode::EV_KEY(code) => Ok(KeyCodeWrapper { code }), + _ => Err(ConfigError::ImpossibleParseKey), + }, + None => Err(ConfigError::InvalidKey(s)), + } + } +} + +#[derive(Debug, Deserialize)] +struct DualRoleConfig { + input: KeyCodeWrapper, + hold: Vec, + tap: Vec, +} + +impl Into for DualRoleConfig { + fn into(self) -> Mapping { + Mapping::DualRole { + input: self.input.into(), + hold: self.hold.into_iter().map(Into::into).collect(), + tap: self.tap.into_iter().map(Into::into).collect(), + } + } +} + +#[derive(Debug, Deserialize)] +struct RemapConfig { + input: Vec, + output: Vec, +} + +impl Into for RemapConfig { + fn into(self) -> Mapping { + Mapping::Remap { + input: self.input.into_iter().map(Into::into).collect(), + output: self.output.into_iter().map(Into::into).collect(), + } + } +} + +#[derive(Debug, Deserialize)] +struct ConfigFile { + device_name: String, + + #[serde(default)] + dual_role: Vec, + + #[serde(default)] + remap: Vec, +} -- cgit v1.2.3