diff --git a/README.md b/README.md index 38a4ec9..10b1fd5 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ * Remap a key sequence as well. You could do something like Emacs's `C-x C-c`. * Remap a key to two different keys depending on whether it's pressed alone or held. * Application-specific remapping. Even if it's not supported by your application, xremap can. +* Device-specific remapping. * Automatically remap newly connected devices by starting xremap with `--watch`. * Support [Emacs-like key remapping](example/emacs.yml), including the mark mode. * Trigger commands on key press/release events. @@ -350,6 +351,31 @@ keymap: Note how Alt-f and Alt-b work in all apps, but the definition of Alt-f is slightly different in LibreOffice Writer. When that app is active, the first definition overrides the second definition; but for any other app, only the second definition is found. This is because xremap uses the first matching definition that it finds. +### device + +Much like [`application`](#application), you may specify `{keymap,modmap}.device.{not,only}` in your configuration for device-specific remapping. Consistent with the global `--device` flag, device-matching strings may be any of: +- the full path of the device +- the filename of the device +- the device name +- a substring of the device name + +To determine the names and paths of your devices, examine `xremap`'s log output at startup. + +```yml +device: + not: '/dev/input/event0' + # or + not: ['event0', ...] + # or + only: 'Some Cool Device Name' + # or + only: ['Cool Device', ...] + # etc... +``` + +Unlike for `application`, regexs are not supported for `device`. + + ### virtual\_modifiers You can declare keys that should act like a modifier. diff --git a/src/config/device.rs b/src/config/device.rs new file mode 100644 index 0000000..8d37831 --- /dev/null +++ b/src/config/device.rs @@ -0,0 +1,12 @@ +use crate::config::application::deserialize_string_or_vec; +use serde::Deserialize; + +// TODO: Use trait to allow only either `only` or `not` +#[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Device { + #[serde(default, deserialize_with = "deserialize_string_or_vec")] + pub only: Option>, + #[serde(default, deserialize_with = "deserialize_string_or_vec")] + pub not: Option>, +} diff --git a/src/config/keymap.rs b/src/config/keymap.rs index 0db2f64..baf085a 100644 --- a/src/config/keymap.rs +++ b/src/config/keymap.rs @@ -6,6 +6,7 @@ use evdev::Key; use serde::{Deserialize, Deserializer}; use std::collections::HashMap; +use super::device::Device; use super::key_press::Modifier; // Config interface @@ -17,6 +18,7 @@ pub struct Keymap { #[serde(deserialize_with = "deserialize_remap")] pub remap: HashMap>, pub application: Option, + pub device: Option, #[serde(default, deserialize_with = "deserialize_string_or_vec")] pub mode: Option>, #[serde(default)] @@ -40,6 +42,7 @@ pub struct KeymapEntry { pub actions: Vec, pub modifiers: Vec, pub application: Option, + pub device: Option, pub mode: Option>, pub exact_match: bool, } @@ -62,6 +65,7 @@ pub fn build_keymap_table(keymaps: &Vec) -> HashMap, pub application: Option, + pub device: Option, } fn deserialize_remap<'de, D>(deserializer: D) -> Result, D::Error> diff --git a/src/device.rs b/src/device.rs index 0c63485..f4a7a23 100644 --- a/src/device.rs +++ b/src/device.rs @@ -11,7 +11,7 @@ use std::error::Error; use std::fs::read_dir; use std::os::unix::ffi::OsStrExt; use std::os::unix::prelude::AsRawFd; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::{io, process}; static MOUSE_BTNS: [&str; 20] = [ @@ -133,6 +133,31 @@ pub fn get_input_devices( Ok(devices.into_iter().map(From::from).collect()) } +#[derive(Debug)] +pub struct InputDeviceInfo<'a> { + pub name: &'a str, + pub path: &'a Path, +} + +impl<'a> InputDeviceInfo<'a> { + pub fn matches(&self, filter: &String) -> bool { + let filter = filter.as_str(); + // Check exact matches for explicit selection + if self.path.as_os_str() == filter || self.name == filter { + return true; + } + // eventXX shorthand for /dev/input/eventXX + if filter.starts_with("event") && self.path.file_name().expect("every device path has a file name") == filter { + return true; + } + // Allow partial matches for device names + if self.name.contains(filter) { + return true; + } + return false; + } +} + #[derive_where(PartialEq, PartialOrd, Ord)] pub struct InputDevice { path: PathBuf, @@ -200,6 +225,13 @@ impl InputDevice { pub fn bus_type(&self) -> BusType { self.device.input_id().bus_type() } + + pub fn to_info(&self) -> InputDeviceInfo { + InputDeviceInfo { + name: self.device_name(), + path: &self.path, + } + } } impl InputDevice { @@ -210,8 +242,8 @@ impl InputDevice { (if device_filter.is_empty() { self.is_keyboard() || (mouse && self.is_mouse()) } else { - self.matches(device_filter) - }) && (ignore_filter.is_empty() || !self.matches(ignore_filter)) + self.matches_any(device_filter) + }) && (ignore_filter.is_empty() || !self.matches_any(ignore_filter)) } // We can't know the device path from evdev::enumerate(). So we re-implement it. @@ -246,31 +278,12 @@ impl InputDevice { .any(|device| return device.device_name().contains(device_name)) } - fn matches(&self, filter: &[String]) -> bool { + fn matches_any(&self, filter: &[String]) -> bool { // Force unmatch its own device if self.device_name() == Self::current_name() { return false; } - - for device_opt in filter { - let device_opt = device_opt.as_str(); - - // Check exact matches for explicit selection - if self.path.as_os_str() == device_opt || self.device_name() == device_opt { - return true; - } - // eventXX shorthand for /dev/input/eventXX - if device_opt.starts_with("event") - && self.path.file_name().expect("every device path has a file name") == device_opt - { - return true; - } - // Allow partial matches for device names - if self.device_name().contains(device_opt) { - return true; - } - } - false + return filter.iter().any(|f| self.to_info().matches(f)); } fn is_keyboard(&self) -> bool { diff --git a/src/event.rs b/src/event.rs index 24783c1..f924d23 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,12 +1,14 @@ use evdev::{EventType, InputEvent, Key}; +use crate::device::InputDeviceInfo; + // Input to EventHandler. This should only contain things that are easily testable. #[derive(Debug)] -pub enum Event { +pub enum Event<'a> { // InputEvent (EventType::KEY) sent from evdev - KeyEvent(KeyEvent), + KeyEvent(InputDeviceInfo<'a>, KeyEvent), // InputEvent (EventType::Relative) sent from evdev - RelativeEvent(RelativeEvent), + RelativeEvent(InputDeviceInfo<'a>, RelativeEvent), // Any other InputEvent type sent from evdev OtherEvents(InputEvent), // Timer for nested override reached its timeout @@ -15,7 +17,7 @@ pub enum Event { #[derive(Debug)] pub struct KeyEvent { - key: Key, + pub key: Key, value: KeyValue, } @@ -31,12 +33,12 @@ pub enum KeyValue { Release, Repeat, } -impl Event { +impl<'a> Event<'a> { // Convert evdev's raw InputEvent to xremap's internal Event - pub fn new(event: InputEvent) -> Event { + pub fn new(device: InputDeviceInfo, event: InputEvent) -> Event { let event = match event.event_type() { - EventType::KEY => Event::KeyEvent(KeyEvent::new_with(event.code(), event.value())), - EventType::RELATIVE => Event::RelativeEvent(RelativeEvent::new_with(event.code(), event.value())), + EventType::KEY => Event::KeyEvent(device, KeyEvent::new_with(event.code(), event.value())), + EventType::RELATIVE => Event::RelativeEvent(device, RelativeEvent::new_with(event.code(), event.value())), _ => Event::OtherEvents(event), }; event diff --git a/src/event_handler.rs b/src/event_handler.rs index a01bf66..52a0d84 100644 --- a/src/event_handler.rs +++ b/src/event_handler.rs @@ -6,8 +6,9 @@ use crate::config::keymap::{build_override_table, OverrideEntry}; use crate::config::keymap_action::KeymapAction; use crate::config::modmap_action::{Keys, ModmapAction, MultiPurposeKey, PressReleaseKey}; use crate::config::remap::Remap; +use crate::device::InputDeviceInfo; use crate::event::{Event, KeyEvent, RelativeEvent}; -use crate::Config; +use crate::{config, Config}; use evdev::Key; use lazy_static::lazy_static; use log::debug; @@ -85,12 +86,12 @@ impl EventHandler { let mut mouse_movement_collection: Vec = Vec::new(); for event in events { match event { - Event::KeyEvent(key_event) => { - self.on_key_event(key_event, config)?; + Event::KeyEvent(device, key_event) => { + self.on_key_event(key_event, config, device)?; () } - Event::RelativeEvent(relative_event) => { - self.on_relative_event(relative_event, &mut mouse_movement_collection, config)? + Event::RelativeEvent(device, relative_event) => { + self.on_relative_event(relative_event, &mut mouse_movement_collection, config, device)? } Event::OtherEvents(event) => self.send_action(Action::InputEvent(*event)), @@ -105,13 +106,18 @@ impl EventHandler { } // Handle EventType::KEY - fn on_key_event(&mut self, event: &KeyEvent, config: &Config) -> Result> { + fn on_key_event( + &mut self, + event: &KeyEvent, + config: &Config, + device: &InputDeviceInfo, + ) -> Result> { self.application_cache = None; // expire cache let key = Key::new(event.code()); debug!("=> {}: {:?}", event.value(), &key); // Apply modmap - let mut key_values = if let Some(key_action) = self.find_modmap(config, &key) { + let mut key_values = if let Some(key_action) = self.find_modmap(config, &key, device) { self.dispatch_keys(key_action, key, event.value())? } else { vec![(key, event.value())] @@ -132,7 +138,7 @@ impl EventHandler { } else if is_pressed(value) { if self.escape_next_key { self.escape_next_key = false - } else if let Some(actions) = self.find_keymap(config, &key)? { + } else if let Some(actions) = self.find_keymap(config, &key, device)? { self.dispatch_actions(&actions, &key)?; continue; } @@ -159,6 +165,7 @@ impl EventHandler { event: &RelativeEvent, mouse_movement_collection: &mut Vec, config: &Config, + device: &InputDeviceInfo, ) -> Result<(), Box> { // Because a "full" RELATIVE event is only one event, // it doesn't translate very well into a KEY event (because those have a "press" event and an "unpress" event). @@ -200,7 +207,7 @@ impl EventHandler { }; // Sending a RELATIVE event "disguised" as a "fake" KEY event press to on_key_event. - match self.on_key_event(&KeyEvent::new_with(key, PRESS), config)? { + match self.on_key_event(&KeyEvent::new_with(key, PRESS), config, &device)? { // the boolean value is from a variable at the end of on_key_event from event_handler, // used to indicate whether the event got through unchanged. true => { @@ -229,7 +236,7 @@ impl EventHandler { } // Sending the "unpressed" version of the "fake" KEY event. - self.on_key_event(&KeyEvent::new_with(key, RELEASE), config)?; + self.on_key_event(&KeyEvent::new_with(key, RELEASE), config, &device)?; Ok(()) } @@ -365,7 +372,7 @@ impl EventHandler { } } - fn find_modmap(&mut self, config: &Config, key: &Key) -> Option { + fn find_modmap(&mut self, config: &Config, key: &Key, device: &InputDeviceInfo) -> Option { for modmap in &config.modmap { if let Some(key_action) = modmap.remap.get(key) { if let Some(application_matcher) = &modmap.application { @@ -373,13 +380,23 @@ impl EventHandler { continue; } } + if let Some(device_matcher) = &modmap.device { + if !self.match_device(device_matcher, device) { + continue; + } + } return Some(key_action.clone()); } } None } - fn find_keymap(&mut self, config: &Config, key: &Key) -> Result>, Box> { + fn find_keymap( + &mut self, + config: &Config, + key: &Key, + device: &InputDeviceInfo, + ) -> Result>, Box> { if !self.override_remaps.is_empty() { let entries: Vec = self .override_remaps @@ -436,6 +453,11 @@ impl EventHandler { continue; } } + if let Some(device_matcher) = &entry.device { + if !self.match_device(device_matcher, device) { + continue; + } + } if let Some(modes) = &entry.mode { if !modes.contains(&self.mode) { continue; @@ -612,6 +634,16 @@ impl EventHandler { false } + fn match_device(&self, device_matcher: &config::device::Device, device: &InputDeviceInfo) -> bool { + if let Some(device_only) = &device_matcher.only { + return device_only.iter().any(|m| device.matches(m)); + } + if let Some(device_not) = &device_matcher.not { + return device_not.iter().all(|m| !device.matches(m)); + } + false + } + fn update_modifier(&mut self, key: Key, value: i32) { if value == PRESS { self.modifiers.insert(key); diff --git a/src/main.rs b/src/main.rs index 8f61cba..0053f62 100644 --- a/src/main.rs +++ b/src/main.rs @@ -226,20 +226,18 @@ fn handle_input_events( dispatcher: &mut ActionDispatcher, config: &mut Config, ) -> anyhow::Result { - match input_device.fetch_events().map_err(|e| (e.raw_os_error(), e)) { - Err((Some(ENODEV), _)) => Ok(false), - Err((_, error)) => Err(error).context("Error fetching input events"), - Ok(events) => { - let mut input_events: Vec = Vec::new(); - for event in events { - let event = Event::new(event); - input_events.push(event); - } - handle_events(handler, dispatcher, config, input_events)?; - - Ok(true) + let mut device_exists = true; + let events = match input_device.fetch_events().map_err(|e| (e.raw_os_error(), e)) { + Err((Some(ENODEV), _)) => { + device_exists = false; + Ok(Vec::new()) } - } + Err((_, error)) => Err(error).context("Error fetching input events"), + Ok(events) => Ok(events.collect()), + }?; + let input_events = events.iter().map(|e| Event::new(input_device.to_info(), *e)).collect(); + handle_events(handler, dispatcher, config, input_events)?; + Ok(device_exists) } // Handle an Event with EventHandler, and dispatch Actions with ActionDispatcher diff --git a/src/tests.rs b/src/tests.rs index 12ad5c6..b805ece 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -3,9 +3,11 @@ use evdev::InputEvent; use evdev::Key; use indoc::indoc; use nix::sys::timerfd::{ClockId, TimerFd, TimerFlags}; +use std::path::Path; use std::time::Duration; use crate::client::{Client, WMClient}; +use crate::device::InputDeviceInfo; use crate::{ action::Action, config::{keymap::build_keymap_table, Config}, @@ -27,6 +29,13 @@ impl Client for StaticClient { } } +fn get_input_device_info<'a>() -> InputDeviceInfo<'a> { + InputDeviceInfo { + name: "Some Device", + path: &Path::new("/dev/input/event0"), + } +} + #[test] fn test_basic_modmap() { assert_actions( @@ -36,10 +45,10 @@ fn test_basic_modmap() { a: b "}, vec![ - Event::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Release)), - Event::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Release)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_A, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_A, KeyValue::Release)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_B, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_B, KeyValue::Release)), ], vec![ Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Press)), @@ -93,7 +102,10 @@ fn test_relative_events() { - remap: XRIGHTCURSOR: b "}, - vec![Event::RelativeEvent(RelativeEvent::new_with(_REL_X, _POSITIVE))], + vec![Event::RelativeEvent( + get_input_device_info(), + RelativeEvent::new_with(_REL_X, _POSITIVE), + )], vec![ Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Release)), @@ -122,8 +134,8 @@ fn test_mouse_movement_event_accumulation() { assert_actions( indoc! {""}, vec![ - Event::RelativeEvent(RelativeEvent::new_with(_REL_X, _POSITIVE)), - Event::RelativeEvent(RelativeEvent::new_with(_REL_Y, _POSITIVE)), + Event::RelativeEvent(get_input_device_info(), RelativeEvent::new_with(_REL_X, _POSITIVE)), + Event::RelativeEvent(get_input_device_info(), RelativeEvent::new_with(_REL_Y, _POSITIVE)), ], vec![Action::MouseMovementEventCollection(vec![ RelativeEvent::new_with(_REL_X, _POSITIVE), @@ -251,8 +263,8 @@ fn test_interleave_modifiers() { M-f: C-right "}, vec![ - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_F, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_F, KeyValue::Press)), ], vec![ Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), @@ -278,9 +290,9 @@ fn test_exact_match_true() { M-f: C-right "}, vec![ - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_F, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_F, KeyValue::Press)), ], vec![ Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), @@ -300,9 +312,9 @@ fn test_exact_match_false() { M-f: C-right "}, vec![ - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_F, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_F, KeyValue::Press)), ], vec![ Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), @@ -328,9 +340,9 @@ fn test_exact_match_default() { M-f: C-right "}, vec![ - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_F, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_F, KeyValue::Press)), ], vec![ Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), @@ -359,12 +371,12 @@ fn test_exact_match_true_nested() { h: C-a "}, vec![ - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)), - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_H, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Release)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_H, KeyValue::Press)), ], vec![ Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), @@ -388,12 +400,12 @@ fn test_exact_match_false_nested() { h: C-a "}, vec![ - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)), - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_H, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Release)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_H, KeyValue::Press)), ], vec![ Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), @@ -428,7 +440,10 @@ fn test_application_override() { assert_actions( config, - vec![Event::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Press))], + vec![Event::KeyEvent( + get_input_device_info(), + KeyEvent::new(Key::KEY_A, KeyValue::Press), + )], vec![ Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Press)), @@ -442,7 +457,65 @@ fn test_application_override() { assert_actions_with_current_application( config, Some(String::from("firefox")), - vec![Event::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Press))], + vec![Event::KeyEvent( + get_input_device_info(), + KeyEvent::new(Key::KEY_A, KeyValue::Press), + )], + vec![ + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_C, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_C, KeyValue::Release)), + Action::Delay(Duration::from_nanos(0)), + Action::Delay(Duration::from_nanos(0)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + ], + ); +} + +#[test] +fn test_device_override() { + let config = indoc! {" + keymap: + + - name: event1 + device: + only: [event1] + remap: + a: C-c + + - name: event0 + remap: + a: C-b + "}; + + assert_actions( + config, + vec![Event::KeyEvent( + InputDeviceInfo { + name: "Some Device", + path: &Path::new("/dev/input/event0"), + }, + KeyEvent::new(Key::KEY_A, KeyValue::Press), + )], + vec![ + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Release)), + Action::Delay(Duration::from_nanos(0)), + Action::Delay(Duration::from_nanos(0)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + ], + ); + + assert_actions( + config, + vec![Event::KeyEvent( + InputDeviceInfo { + name: "Other Device", + path: &Path::new("/dev/input/event1"), + }, + KeyEvent::new(Key::KEY_A, KeyValue::Press), + )], vec![ Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_C, KeyValue::Press)), @@ -471,11 +544,11 @@ fn test_merge_remaps() { assert_actions( config, vec![ - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)), - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), - Event::KeyEvent(KeyEvent::new(Key::KEY_H, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Release)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_H, KeyValue::Press)), ], vec![ Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), @@ -493,11 +566,11 @@ fn test_merge_remaps() { assert_actions( config, vec![ - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)), - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), - Event::KeyEvent(KeyEvent::new(Key::KEY_K, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Release)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_K, KeyValue::Press)), ], vec![ Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), @@ -531,11 +604,11 @@ fn test_merge_remaps_with_override() { assert_actions( config, vec![ - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)), - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), - Event::KeyEvent(KeyEvent::new(Key::KEY_H, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Release)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_H, KeyValue::Press)), ], vec![ Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), @@ -553,11 +626,11 @@ fn test_merge_remaps_with_override() { assert_actions( config, vec![ - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)), - Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)), - Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), - Event::KeyEvent(KeyEvent::new(Key::KEY_C, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Press)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Release)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_C, KeyValue::Press)), ], vec![ Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),