summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorWez Furlong <wez@wezfurlong.org>2019-12-31 08:25:40 -0800
committerWez Furlong <wez@wezfurlong.org>2019-12-31 08:27:33 -0800
commit7f2cb52d535ece13ff7d50012774d43936d69d89 (patch)
treed432529bb842d8a3317caac928e6f7cfedd983b1 /src
parent3219978c3fe2db287cd4dd9aa3a4c67811ae80f2 (diff)
Add config file parsing and loading
Diffstat (limited to 'src')
-rw-r--r--src/main.rs36
-rw-r--r--src/mapping.rs109
2 files changed, 119 insertions, 26 deletions
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<Mapping>,
+}
+
+impl MappingConfig {
+ pub fn from_file<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
+ 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<KeyCode>,
},
}
+
+#[derive(Debug, Deserialize)]
+#[serde(try_from = "String")]
+struct KeyCodeWrapper {
+ pub code: KeyCode,
+}
+
+impl Into<KeyCode> 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<String> for KeyCodeWrapper {
+ type Error = ConfigError;
+ fn try_from(s: String) -> Result<KeyCodeWrapper, Self::Error> {
+ 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<KeyCodeWrapper>,
+ tap: Vec<KeyCodeWrapper>,
+}
+
+impl Into<Mapping> 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<KeyCodeWrapper>,
+ output: Vec<KeyCodeWrapper>,
+}
+
+impl Into<Mapping> 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<DualRoleConfig>,
+
+ #[serde(default)]
+ remap: Vec<RemapConfig>,
+}