summaryrefslogtreecommitdiff
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
parent3219978c3fe2db287cd4dd9aa3a4c67811ae80f2 (diff)
Add config file parsing and loading
-rw-r--r--Cargo.lock52
-rw-r--r--Cargo.toml3
-rw-r--r--pixelbookgo.toml23
-rw-r--r--src/main.rs36
-rw-r--r--src/mapping.rs109
5 files changed, 197 insertions, 26 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d60be47..caf74d8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -116,7 +116,10 @@ dependencies = [
"evdev-rs 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty_env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -279,6 +282,24 @@ dependencies = [
]
[[package]]
+name = "serde"
+version = "1.0.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -341,6 +362,24 @@ dependencies = [
]
[[package]]
+name = "thiserror"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "thiserror-impl 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "thread_local"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -359,6 +398,14 @@ dependencies = [
]
[[package]]
+name = "toml"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "unicode-segmentation"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -453,6 +500,8 @@ dependencies = [
"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd"
"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
"checksum rustversion 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a0538bd897e17257b0128d2fd95c2ed6df939374073a36166051a79e2eb7986"
+"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
+"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum structopt 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "884ae79d6aad1e738f4a70dff314203fd498490a63ebc4d03ea83323c40b7b72"
"checksum structopt-derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a97f829a34a0a9d5b353a881025a23b8c9fd09d46be6045df6b22920dbd7a93"
@@ -460,8 +509,11 @@ dependencies = [
"checksum syn-mid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd3937748a7eccff61ba5b90af1a20dbf610858923a9192ea0ecb0cb77db1d0"
"checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+"checksum thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6f357d1814b33bc2dc221243f8424104bfe72dbe911d5b71b3816a2dff1c977e"
+"checksum thiserror-impl 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2e25d25307eb8436894f727aba8f65d07adf02e5b35a13cebed48bd282bfef"
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
+"checksum toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf"
"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
diff --git a/Cargo.toml b/Cargo.toml
index 9185fe2..27e27a5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,3 +10,6 @@ anyhow = "1.0"
log = "0.4"
pretty_env_logger = "0.3"
structopt = "0.3"
+serde = { version="1.0", features=["derive"]}
+thiserror = "1.0"
+toml = "0.5"
diff --git a/pixelbookgo.toml b/pixelbookgo.toml
new file mode 100644
index 0000000..b77d19e
--- /dev/null
+++ b/pixelbookgo.toml
@@ -0,0 +1,23 @@
+device_name = "AT Translated Set 2 keyboard"
+
+[[dual_role]]
+input = "KEY_CAPSLOCK"
+hold = ["KEY_LEFTCTRL"]
+tap = ["KEY_ESC"]
+
+[[remap]]
+input = ["KEY_F1"]
+output = ["KEY_BACK"]
+
+[[remap]]
+input = ["KEY_F5"]
+output = ["KEY_BRIGHTNESSDOWN"]
+
+[[remap]]
+input = ["KEY_F6"]
+output = ["KEY_BRIGHTNESSUP"]
+
+[[remap]]
+input = ["KEY_F8"]
+output = ["KEY_MUTE"]
+
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>,
+}