Support device-specific remapping in config (#380)

* begin threading InputDevice down to `find_modmap`/`find_keymap`

why did i comment this out?

* add to config

* new approach for event/device (wip)

* try passing input_device as its own thing -- same problem

* aha! this works!

* this works too!

* start wiring it together

* that might do it?

* fallback to false, like for application

* fix device.not

* tests (wip)

* tests (wip) | well it compiles

* tests (wip) | just placeholder for now

* move device out of event

* Revert "move device out of event"

This reverts commit c9486ed2adee5f001ebf96d630621fabcb2127a5.

* device desc struct (k0kubun's suggestion)

* get tests passing

* s/InputDeviceDescriptor/InputDeviceInfo/

* add a test

* misc cleanup

* use &str and &Path instead of String and PathBuf

* cargo fmt

* move InputDeviceInfo in device.rs

* more misc cleanup

* documentation

* get rid of extraneous `matches` function

* readme tweak
pull/381/head
Elliott Shugerman 6 months ago committed by GitHub
parent 6f298b93b8
commit fd3992e486
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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.

@ -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<Vec<String>>,
#[serde(default, deserialize_with = "deserialize_string_or_vec")]
pub not: Option<Vec<String>>,
}

@ -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<KeyPress, Vec<KeymapAction>>,
pub application: Option<Application>,
pub device: Option<Device>,
#[serde(default, deserialize_with = "deserialize_string_or_vec")]
pub mode: Option<Vec<String>>,
#[serde(default)]
@ -40,6 +42,7 @@ pub struct KeymapEntry {
pub actions: Vec<KeymapAction>,
pub modifiers: Vec<Modifier>,
pub application: Option<Application>,
pub device: Option<Device>,
pub mode: Option<Vec<String>>,
pub exact_match: bool,
}
@ -62,6 +65,7 @@ pub fn build_keymap_table(keymaps: &Vec<Keymap>) -> HashMap<Key, Vec<KeymapEntry
actions: actions.to_vec(),
modifiers: key_press.modifiers.clone(),
application: keymap.application.clone(),
device: keymap.device.clone(),
mode: keymap.mode.clone(),
exact_match: keymap.exact_match,
});

@ -1,4 +1,5 @@
pub mod application;
pub mod device;
mod key;
pub mod key_press;
pub mod keymap;

@ -5,6 +5,8 @@ use evdev::Key;
use serde::{Deserialize, Deserializer};
use std::collections::HashMap;
use super::device::Device;
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Modmap {
@ -13,6 +15,7 @@ pub struct Modmap {
#[serde(deserialize_with = "deserialize_remap")]
pub remap: HashMap<Key, ModmapAction>,
pub application: Option<Application>,
pub device: Option<Device>,
}
fn deserialize_remap<'de, D>(deserializer: D) -> Result<HashMap<Key, ModmapAction>, D::Error>

@ -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 {

@ -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

@ -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<RelativeEvent> = 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<bool, Box<dyn Error>> {
fn on_key_event(
&mut self,
event: &KeyEvent,
config: &Config,
device: &InputDeviceInfo,
) -> Result<bool, Box<dyn Error>> {
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<RelativeEvent>,
config: &Config,
device: &InputDeviceInfo,
) -> Result<(), Box<dyn Error>> {
// 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<ModmapAction> {
fn find_modmap(&mut self, config: &Config, key: &Key, device: &InputDeviceInfo) -> Option<ModmapAction> {
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<Option<Vec<TaggedAction>>, Box<dyn Error>> {
fn find_keymap(
&mut self,
config: &Config,
key: &Key,
device: &InputDeviceInfo,
) -> Result<Option<Vec<TaggedAction>>, Box<dyn Error>> {
if !self.override_remaps.is_empty() {
let entries: Vec<OverrideEntry> = 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);

@ -226,20 +226,18 @@ fn handle_input_events(
dispatcher: &mut ActionDispatcher,
config: &mut Config,
) -> anyhow::Result<bool> {
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<Event> = 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

@ -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)),

Loading…
Cancel
Save