From 8d3d62559ed4ba9b96e3e3883d9863c34fcaf22d Mon Sep 17 00:00:00 2001 From: Lae Chen Date: Sun, 4 Dec 2022 10:40:28 +1300 Subject: [PATCH] Interleave pressing/releasing of modifier keys. (#208) * Interleave pressing/releasing of modifier keys. Pressing new modifier keys before releasing the old ones to avoid triggering actions in applications that recognize the old keys. For example, if we bind alt+f to ctrl+right, before this change, the following key sequence is generated with alt+f: (user) send Alt_L PRESS (xremap) send Alt_L RELEASE (xremap) send Control_L PRESS (xremap) send Right PRESS (xremap) send Right RELEASE (xremap) send Control_L RELEASE (xremap) send Alt_L PRESS (user) send Alt_L RELEASE The press + release of the alt key (both at the start and end of the sequence) is causing apps like Firefox to show/focus the menu bar. After this change, the following key sequence is generated with alt+f: (user) send Alt_L PRESS (xremap) send Control_L PRESS (xremap) send Alt_L RELEASE (xremap) send Right PRESS (xremap) send Right RELEASE (xremap) send Alt_L PRESS (xremap) send Control_L RELEASE (user) send Alt_L RELEASE So the difference here is that we press the ctrl key before releasing alt, so will not trigger apps like Firefox to show hide the menu when alt+f is used, and a single press and release of the alt key will still show the menu as their normal behavior. As far as I can observe, this is also the behavior of AutoHotKey on Windows. * Add test. --- src/event_handler.rs | 4 ++-- src/tests.rs | 30 ++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/event_handler.rs b/src/event_handler.rs index 7b7df52..5d3e7a2 100644 --- a/src/event_handler.rs +++ b/src/event_handler.rs @@ -343,8 +343,8 @@ impl EventHandler { missing_modifiers.retain(|key| MODIFIER_KEYS.contains(&key)); // Emulate the modifiers of KeyPress - self.send_keys(&extra_modifiers, RELEASE); self.send_keys(&missing_modifiers, PRESS); + self.send_keys(&extra_modifiers, RELEASE); // Press the main key self.send_key(&key_press.key, PRESS); @@ -353,8 +353,8 @@ impl EventHandler { self.send_action(Action::Delay(self.keypress_delay)); // Resurrect the original modifiers - self.send_keys(&missing_modifiers, RELEASE); self.send_keys(&extra_modifiers, PRESS); + self.send_keys(&missing_modifiers, RELEASE); } fn with_mark(&self, key_press: &KeyPress) -> KeyPress { diff --git a/src/tests.rs b/src/tests.rs index dea950d..56a4e1c 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -5,7 +5,7 @@ use std::time::Duration; use crate::{ action::Action, - config::Config, + config::{keymap::build_keymap_table, Config}, event::{Event, KeyEvent, KeyValue}, event_handler::EventHandler, }; @@ -33,9 +33,35 @@ fn test_basic_modmap() { ) } +#[test] +fn test_interleave_modifiers() { + assert_actions( + indoc! {" + keymap: + - remap: + M-f: C-right + "}, + vec![ + Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), + Event::KeyEvent(KeyEvent::new(Key::KEY_F, KeyValue::Press)), + ], + vec![ + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Release)), + Action::KeyEvent(KeyEvent::new(Key::KEY_RIGHT, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_RIGHT, KeyValue::Release)), + Action::Delay(Duration::from_nanos(0)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), + Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), + ], + ) +} + fn assert_actions(config_yaml: &str, events: Vec, actions: Vec) { let timer = TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap(); - let config: Config = serde_yaml::from_str(config_yaml).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 actual: Vec = vec![]; for event in &events {