summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--src/deviceinfo.rs69
-rw-r--r--src/main.rs17
-rw-r--r--src/mapping.rs4
4 files changed, 84 insertions, 10 deletions
diff --git a/README.md b/README.md
index 6656baf..0b4b2ca 100644
--- a/README.md
+++ b/README.md
@@ -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>,