diff --git a/src/client/mod.rs b/src/client/mod.rs index ccc6c16..1f24fd4 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,4 +1,4 @@ -trait Client { +pub trait Client { fn supported(&mut self) -> bool; fn current_application(&mut self) -> Option; } @@ -11,7 +11,7 @@ pub struct WMClient { } impl WMClient { - fn new(name: &str, client: Box) -> WMClient { + pub fn new(name: &str, client: Box) -> WMClient { WMClient { name: name.to_string(), client, diff --git a/src/config/keymap.rs b/src/config/keymap.rs index 558970a..0db2f64 100644 --- a/src/config/keymap.rs +++ b/src/config/keymap.rs @@ -76,10 +76,14 @@ pub fn build_keymap_table(keymaps: &Vec) -> HashMap, pub modifiers: Vec, + pub exact_match: bool, } // This is executed on runtime unlike build_keymap_table, but hopefully not called so often. -pub fn build_override_table(remap: &HashMap>) -> HashMap> { +pub fn build_override_table( + remap: &HashMap>, + exact_match: bool, +) -> HashMap> { let mut table: HashMap> = HashMap::new(); for (key_press, actions) in remap.iter() { let mut entries: Vec = match table.get(&key_press.key) { @@ -89,6 +93,7 @@ pub fn build_override_table(remap: &HashMap>) -> Has entries.push(OverrideEntry { actions: actions.to_vec(), modifiers: key_press.modifiers.clone(), + exact_match, }); table.insert(key_press.key, entries); } diff --git a/src/event_handler.rs b/src/event_handler.rs index e79bef2..a62b953 100644 --- a/src/event_handler.rs +++ b/src/event_handler.rs @@ -1,5 +1,5 @@ use crate::action::Action; -use crate::client::{build_client, WMClient}; +use crate::client::WMClient; use crate::config::application::Application; use crate::config::key_press::{KeyPress, Modifier}; use crate::config::keymap::{build_override_table, OverrideEntry}; @@ -30,9 +30,7 @@ pub struct EventHandler { // State machine for multi-purpose keys multi_purpose_keys: HashMap, // Current nested remaps - override_remap: Option>>, - // Whether to use exact match for override remaps - override_remap_exact_match: bool, + override_remaps: Vec>>, // Key triggered on a timeout of nested remaps override_timeout_key: Option, // Trigger a timeout of nested remaps through select(2) @@ -49,17 +47,21 @@ pub struct EventHandler { actions: Vec, } +struct TaggedAction { + action: KeymapAction, + exact_match: bool, +} + impl EventHandler { - pub fn new(timer: TimerFd, mode: &str, keypress_delay: Duration) -> EventHandler { + pub fn new(timer: TimerFd, mode: &str, keypress_delay: Duration, application_client: WMClient) -> EventHandler { EventHandler { modifiers: HashSet::new(), extra_modifiers: HashSet::new(), pressed_keys: HashMap::new(), - application_client: build_client(), + application_client, application_cache: None, multi_purpose_keys: HashMap::new(), - override_remap: None, - override_remap_exact_match: false, + override_remaps: vec![], override_timeout_key: None, override_timer: timer, mode: mode.to_string(), @@ -126,7 +128,7 @@ impl EventHandler { fn remove_override(&mut self) -> Result<(), Box> { self.override_timer.unset()?; - self.override_remap = None; + self.override_remaps.clear(); self.override_timeout_key = None; Ok(()) } @@ -207,11 +209,17 @@ impl EventHandler { ModmapAction::PressReleaseKey(PressReleaseKey { press, release }) => { // Just hook actions, and then emit the original event. We might want to // support reordering the key event and dispatched actions later. - if value == PRESS { - self.dispatch_actions(&press, &key)?; - } - if value == RELEASE { - self.dispatch_actions(&release, &key)?; + if value == PRESS || value == RELEASE { + self.dispatch_actions( + &(if value == PRESS { press } else { release }) + .into_iter() + .map(|action| TaggedAction { + action, + exact_match: false, + }) + .collect(), + &key, + )?; } // Dispatch the original key as well vec![(key, value)] @@ -255,28 +263,50 @@ impl EventHandler { None } - fn find_keymap(&mut self, config: &Config, key: &Key) -> Result>, Box> { - if let Some(override_remap) = &self.override_remap { - if let Some(entries) = override_remap.clone().get(key) { + fn find_keymap(&mut self, config: &Config, key: &Key) -> Result>, Box> { + if !self.override_remaps.is_empty() { + let entries: Vec = self + .override_remaps + .iter() + .flat_map(|map| map.get(key).cloned().unwrap_or_default()) + .collect(); + + if !entries.is_empty() { self.remove_override()?; + for exact_match in [true, false] { - if self.override_remap_exact_match && !exact_match { - continue; - } - for entry in entries { + let mut remaps = vec![]; + for entry in &entries { + if entry.exact_match && !exact_match { + continue; + } let (extra_modifiers, missing_modifiers) = self.diff_modifiers(&entry.modifiers); if (exact_match && extra_modifiers.len() > 0) || missing_modifiers.len() > 0 { continue; } - return Ok(Some(with_extra_modifiers(&entry.actions, &extra_modifiers))); + + let actions = with_extra_modifiers(&entry.actions, &extra_modifiers, entry.exact_match); + let is_remap = is_remap(&entry.actions); + + // If the first/top match was a remap, continue to find rest of the eligible remaps for this key + if remaps.is_empty() && !is_remap { + return Ok(Some(actions)); + } else if is_remap { + remaps.extend(actions); + } + } + if !remaps.is_empty() { + return Ok(Some(remaps)); } } } // An override remap is set but not used. Flush the pending key. self.timeout_override()?; } + if let Some(entries) = config.keymap_table.get(key) { for exact_match in [true, false] { + let mut remaps = vec![]; for entry in entries { if entry.exact_match && !exact_match { continue; @@ -295,36 +325,54 @@ impl EventHandler { continue; } } - self.override_remap_exact_match = entry.exact_match; - return Ok(Some(with_extra_modifiers(&entry.actions, &extra_modifiers))); + + let actions = with_extra_modifiers(&entry.actions, &extra_modifiers, entry.exact_match); + let is_remap = is_remap(&entry.actions); + + // If the first/top match was a remap, continue to find rest of the eligible remaps for this key + if remaps.is_empty() && !is_remap { + return Ok(Some(actions)); + } else if is_remap { + remaps.extend(actions) + } + } + if !remaps.is_empty() { + return Ok(Some(remaps)); } } } Ok(None) } - fn dispatch_actions(&mut self, actions: &Vec, key: &Key) -> Result<(), Box> { + fn dispatch_actions(&mut self, actions: &Vec, key: &Key) -> Result<(), Box> { for action in actions { self.dispatch_action(action, key)?; } Ok(()) } - fn dispatch_action(&mut self, action: &KeymapAction, key: &Key) -> Result<(), Box> { - match action { + fn dispatch_action(&mut self, action: &TaggedAction, key: &Key) -> Result<(), Box> { + match &action.action { KeymapAction::KeyPress(key_press) => self.send_key_press(key_press), KeymapAction::Remap(Remap { remap, timeout, timeout_key, }) => { - self.override_remap = Some(build_override_table(remap)); - if let Some(timeout) = timeout { - let expiration = Expiration::OneShot(TimeSpec::from_duration(*timeout)); - // TODO: Consider handling the timer in ActionDispatcher - self.override_timer.unset()?; - self.override_timer.set(expiration, TimerSetTimeFlags::empty())?; - self.override_timeout_key = timeout_key.or_else(|| Some(*key)); + let set_timeout = self.override_remaps.is_empty(); + self.override_remaps + .push(build_override_table(remap, action.exact_match)); + + // Set timeout only if this is the first of multiple eligible remaps, + // so the behaviour is consistent with how current normal keymap override works + if set_timeout { + if let Some(timeout) = timeout { + let expiration = Expiration::OneShot(TimeSpec::from_duration(*timeout)); + // TODO: Consider handling the timer in ActionDispatcher + self.override_timer.unset()?; + self.override_timer.set(expiration, TimerSetTimeFlags::empty())?; + self.override_timeout_key = timeout_key.or_else(|| Some(*key)); + } } } KeymapAction::Launch(command) => self.run_command(command.clone()), @@ -456,16 +504,36 @@ impl EventHandler { } } -fn with_extra_modifiers(actions: &Vec, extra_modifiers: &Vec) -> Vec { - let mut result: Vec = vec![]; +fn is_remap(actions: &Vec) -> bool { + actions.iter().all(|x| match x { + KeymapAction::Remap(..) => true, + _ => false, + }) +} + +fn with_extra_modifiers( + actions: &Vec, + extra_modifiers: &Vec, + exact_match: bool, +) -> Vec { + let mut result: Vec = vec![]; if extra_modifiers.len() > 0 { // Virtually release extra modifiers so that they won't be physically released on KeyPress - result.push(KeymapAction::SetExtraModifiers(extra_modifiers.clone())); + result.push(TaggedAction { + action: KeymapAction::SetExtraModifiers(extra_modifiers.clone()), + exact_match, + }); } - result.extend(actions.clone()); + result.extend(actions.iter().map(|action| TaggedAction { + action: action.clone(), + exact_match, + })); if extra_modifiers.len() > 0 { // Resurrect the modifier status - result.push(KeymapAction::SetExtraModifiers(vec![])); + result.push(TaggedAction { + action: KeymapAction::SetExtraModifiers(vec![]), + exact_match, + }); } return result; } diff --git a/src/main.rs b/src/main.rs index 7104a21..307f54f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use action_dispatcher::ActionDispatcher; use anyhow::{anyhow, bail, Context}; use clap::{AppSettings, ArgEnum, IntoApp, Parser}; use clap_complete::Shell; +use client::build_client; use config::{config_watcher, load_config}; use device::InputDevice; use event::Event; @@ -123,7 +124,7 @@ fn main() -> anyhow::Result<()> { let device_watcher = device_watcher(watch_devices).context("Setting up device watcher")?; let config_watcher = config_watcher(watch_config, &config_path).context("Setting up config watcher")?; let watchers: Vec<_> = device_watcher.iter().chain(config_watcher.iter()).collect(); - let mut handler = EventHandler::new(timer, &config.default_mode, delay); + let mut handler = EventHandler::new(timer, &config.default_mode, delay, build_client()); let output_device = match output_device(input_devices.values().next().map(InputDevice::bus_type)) { Ok(output_device) => output_device, Err(e) => bail!("Failed to prepare an output device: {}", e), diff --git a/src/tests.rs b/src/tests.rs index 4698257..d2ca7c2 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -3,6 +3,7 @@ use indoc::indoc; use nix::sys::timerfd::{ClockId, TimerFd, TimerFlags}; use std::time::Duration; +use crate::client::{Client, WMClient}; use crate::{ action::Action, config::{keymap::build_keymap_table, Config}, @@ -10,6 +11,20 @@ use crate::{ event_handler::EventHandler, }; +struct StaticClient { + current_application: Option, +} + +impl Client for StaticClient { + fn supported(&mut self) -> bool { + true + } + + fn current_application(&mut self) -> Option { + self.current_application.clone() + } +} + #[test] fn test_basic_modmap() { assert_actions( @@ -197,11 +212,182 @@ fn test_exact_match_false_nested() { ) } +#[test] +fn test_application_override() { + let config = indoc! {" + keymap: + + - name: firefox + application: + only: [firefox] + remap: + a: C-c + + - name: generic + remap: + a: C-b + "}; + + assert_actions( + config, + vec![Event::KeyEvent(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::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + ], + ); + + assert_actions_with_current_application( + config, + Some(String::from("firefox")), + vec![Event::KeyEvent(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::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + ], + ); +} + +#[test] +fn test_merge_remaps() { + let config = indoc! {" + keymap: + - remap: + C-x: + remap: + h: C-a + - remap: + C-x: + remap: + k: C-w + "}; + + 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)), + ], + vec![ + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Release)), + Action::Delay(Duration::from_nanos(0)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + ], + ); + + 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)), + ], + vec![ + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_W, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_W, KeyValue::Release)), + Action::Delay(Duration::from_nanos(0)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + ], + ) +} + +#[test] +fn test_merge_remaps_with_override() { + let config = indoc! {" + keymap: + - remap: + C-x: + remap: + h: C-a + - remap: + C-x: + remap: + h: C-b + c: C-q + "}; + + 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)), + ], + vec![ + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Release)), + Action::Delay(Duration::from_nanos(0)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + ], + ); + + 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)), + ], + vec![ + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_Q, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_Q, KeyValue::Release)), + Action::Delay(Duration::from_nanos(0)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + ], + ) +} + fn assert_actions(config_yaml: &str, events: Vec, actions: Vec) { + assert_actions_with_current_application(config_yaml, None, events, actions); +} + +fn assert_actions_with_current_application( + config_yaml: &str, + current_application: Option, + events: Vec, + actions: Vec, +) { let timer = TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap(); let mut config: Config = serde_yaml::from_str(config_yaml).unwrap(); config.keymap_table = build_keymap_table(&config.keymap); - let mut event_handler = EventHandler::new(timer, "default", Duration::from_micros(0)); + let mut event_handler = EventHandler::new( + timer, + "default", + Duration::from_micros(0), + WMClient::new("static", Box::new(StaticClient { current_application })), + ); let mut actual: Vec = vec![]; for event in &events { actual.append(&mut event_handler.on_event(event, &config).unwrap());