diff options
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | src/deviceinfo.rs | 69 | ||||
-rw-r--r-- | src/main.rs | 17 | ||||
-rw-r--r-- | src/mapping.rs | 4 |
4 files changed, 84 insertions, 10 deletions
@@ -29,6 +29,10 @@ Here's an example configuration that makes capslock useful: # on your system. device_name = "AT Translated Set 2 keyboard" +# If you have multiple devices with the same name, you can optionally +# specify the `phys` value that is printed by the `list-devices` subcommand +# phys = "usb-0000:07:00.3-2.1.1/input0" + # Configure CAPSLOCK as a Dual Role key. # Holding it produces LEFTCTRL, but tapping it # will produce ESC. diff --git a/src/deviceinfo.rs b/src/deviceinfo.rs index cc05e47..9097433 100644 --- a/src/deviceinfo.rs +++ b/src/deviceinfo.rs @@ -1,11 +1,13 @@ use anyhow::*; use evdev_rs::Device; +use std::cmp::Ordering; use std::path::PathBuf; #[derive(Debug, Clone)] pub struct DeviceInfo { pub name: String, pub path: PathBuf, + pub phys: String, } impl DeviceInfo { @@ -18,18 +20,51 @@ impl DeviceInfo { Ok(Self { name: input.name().unwrap_or("").to_string(), + phys: input.phys().unwrap_or("").to_string(), path, }) } - pub fn with_name(name: &str) -> Result<Self> { - let devices = Self::obtain_device_list()?; - for item in devices { - if item.name == name { - return Ok(item); + pub fn with_name(name: &str, phys: Option<&str>) -> Result<Self> { + let mut devices = Self::obtain_device_list()?; + + if let Some(phys) = phys { + match devices.iter().position(|item| item.phys == phys) { + Some(idx) => return Ok(devices.remove(idx)), + None => { + bail!( + "Requested device `{}` with phys=`{}` was not found", + name, + phys + ); + } + } + } + + let mut devices_with_name: Vec<_> = devices + .into_iter() + .filter(|item| item.name == name) + .collect(); + + if devices_with_name.is_empty() { + bail!("No device found with name `{}`", name); + } + + if devices_with_name.len() > 1 { + log::warn!("The following devices match name `{}`:", name); + for dev in &devices_with_name { + log::warn!("{:?}", dev); } + log::warn!( + "evremap will use the first entry. If you want to \ + use one of the others, add the corresponding phys \ + value to your configuration, for example, \ + `phys = \"{}\"` for the second entry in the list.", + devices_with_name[1].phys + ); } - bail!("No device found with name `{}`", name); + + Ok(devices_with_name.remove(0)) } fn obtain_device_list() -> Result<Vec<DeviceInfo>> { @@ -52,20 +87,38 @@ impl DeviceInfo { match DeviceInfo::with_path(path) { Ok(item) => devices.push(item), - Err(err) => log::error!("{}", err), + Err(err) => log::error!("{:#}", err), } } - devices.sort_by(|a, b| a.name.cmp(&b.name)); + // Order by name, but when multiple devices have the same name, + // order by the event device unit number + devices.sort_by(|a, b| match a.name.cmp(&b.name) { + Ordering::Equal => { + event_number_from_path(&a.path).cmp(&event_number_from_path(&b.path)) + } + different => different, + }); Ok(devices) } } +fn event_number_from_path(path: &PathBuf) -> u32 { + match path.to_str() { + Some(s) => match s.rfind("event") { + Some(idx) => s[idx + 5..].parse().unwrap_or(0), + None => 0, + }, + None => 0, + } +} + pub fn list_devices() -> Result<()> { let devices = DeviceInfo::obtain_device_list()?; for item in &devices { println!("Name: {}", item.name); println!("Path: {}", item.path.display()); + println!("Phys: {}", item.phys); println!(); } Ok(()) diff --git a/src/main.rs b/src/main.rs index 64a83ae..b1feb46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,8 +49,18 @@ pub fn list_keys() -> Result<()> { Ok(()) } +fn setup_logger() { + let mut builder = pretty_env_logger::formatted_timed_builder(); + if let Ok(s) = std::env::var("EVREMAP_LOG") { + builder.parse_filters(&s); + } else { + builder.filter(None, log::LevelFilter::Info); + } + builder.init(); +} + fn main() -> Result<()> { - pretty_env_logger::init(); + setup_logger(); let opt = Opt::from_args(); match opt { @@ -65,7 +75,10 @@ fn main() -> Result<()> { log::error!("Short delay: release any keys now!"); std::thread::sleep(Duration::new(2, 0)); - let device_info = deviceinfo::DeviceInfo::with_name(&mapping_config.device_name)?; + let device_info = deviceinfo::DeviceInfo::with_name( + &mapping_config.device_name, + mapping_config.phys.as_deref(), + )?; let mut mapper = InputMapper::create_mapper(device_info.path, mapping_config.mappings)?; mapper.run_mapper() diff --git a/src/mapping.rs b/src/mapping.rs index c2dca77..6ffc274 100644 --- a/src/mapping.rs +++ b/src/mapping.rs @@ -8,6 +8,7 @@ use thiserror::Error; #[derive(Debug, Clone)] pub struct MappingConfig { pub device_name: String, + pub phys: Option<String>, pub mappings: Vec<Mapping>, } @@ -27,6 +28,7 @@ impl MappingConfig { } Ok(Self { device_name: config_file.device_name, + phys: config_file.phys, mappings, }) } @@ -113,6 +115,8 @@ impl Into<Mapping> for RemapConfig { #[derive(Debug, Deserialize)] struct ConfigFile { device_name: String, + #[serde(default)] + phys: Option<String>, #[serde(default)] dual_role: Vec<DualRoleConfig>, |