diff --git a/src/config/action.rs b/src/config/action.rs index f8d8305..e6834e1 100644 --- a/src/config/action.rs +++ b/src/config/action.rs @@ -1,9 +1,11 @@ use crate::config::key_press::KeyPress; use std::collections::HashMap; +use crate::config::remap::Remap; use serde::de; use serde::{Deserialize, Deserializer}; use std::fmt::Debug; +use std::time::Duration; // Values in `keymap.remap` #[derive(Clone, Debug, Deserialize)] @@ -11,7 +13,7 @@ use std::fmt::Debug; pub enum Action { KeyPress(KeyPress), #[serde(deserialize_with = "deserialize_remap")] - Remap(HashMap>), + Remap(Remap), #[serde(deserialize_with = "deserialize_launch")] Launch(Vec), #[serde(deserialize_with = "deserialize_set_mark")] @@ -22,17 +24,15 @@ pub enum Action { EscapeNextKey(bool), } -fn deserialize_remap<'de, D>(deserializer: D) -> Result>, D::Error> +fn deserialize_remap<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { - let mut action = HashMap::>::deserialize(deserializer)?; - if let Some(remap) = action.remove("remap") { - if action.is_empty() { - return Ok(remap.into_iter().map(|(k, v)| (k, v.into_vec())).collect()); - } - } - Err(de::Error::custom("not a map with a single \"remap\" key")) + let action = RemapActions::deserialize(deserializer)?; + return Ok(Remap { + remap: action.remap.into_iter().map(|(k, v)| (k, v.into_vec())).collect(), + timeout: action.timeout_millis.map(|ms| Duration::from_millis(ms)), + }); } fn deserialize_launch<'de, D>(deserializer: D) -> Result, D::Error> @@ -88,7 +88,7 @@ where } // Used only for deserializing Vec -#[derive(Clone, Deserialize)] +#[derive(Clone, Debug, Deserialize)] #[serde(untagged)] pub enum Actions { Action(Action), @@ -103,3 +103,10 @@ impl Actions { } } } + +// Used only for deserializing Remap with Vec +#[derive(Debug, Deserialize)] +pub struct RemapActions { + pub remap: HashMap, + pub timeout_millis: Option, +} diff --git a/src/config/mod.rs b/src/config/mod.rs index 28d1512..ac60526 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -6,6 +6,7 @@ pub mod key_press; mod keymap; mod modmap; +mod remap; #[cfg(test)] mod tests; diff --git a/src/config/remap.rs b/src/config/remap.rs new file mode 100644 index 0000000..7ddc9d8 --- /dev/null +++ b/src/config/remap.rs @@ -0,0 +1,10 @@ +use crate::config::action::Action; +use crate::config::key_press::KeyPress; +use std::collections::HashMap; +use std::time::Duration; + +#[derive(Clone, Debug)] +pub struct Remap { + pub remap: HashMap>, + pub timeout: Option, +} diff --git a/src/config/tests.rs b/src/config/tests.rs index d13289b..56f4ff0 100644 --- a/src/config/tests.rs +++ b/src/config/tests.rs @@ -110,6 +110,7 @@ fn test_keymap_remap() { C-s: remap: x: C-z + timeout_millis: 1000 "}) } diff --git a/src/event_handler.rs b/src/event_handler.rs index f640476..d3cb603 100644 --- a/src/event_handler.rs +++ b/src/event_handler.rs @@ -10,6 +10,8 @@ use lazy_static::lazy_static; use log::{debug, error}; use nix::sys::signal; use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet}; +use nix::sys::time::TimeSpec; +use nix::sys::timerfd::{Expiration, TimerFd, TimerSetTimeFlags}; use std::collections::HashMap; use std::error::Error; use std::process::{Command, Stdio}; @@ -17,6 +19,7 @@ use std::time::Instant; pub struct EventHandler { device: VirtualDevice, + timer: TimerFd, shift: PressState, control: PressState, alt: PressState, @@ -25,15 +28,17 @@ pub struct EventHandler { application_cache: Option, multi_purpose_keys: HashMap, override_remap: Option>>, + override_timeout_key: Option, sigaction_set: bool, mark_set: bool, escape_next_key: bool, } impl EventHandler { - pub fn new(device: VirtualDevice) -> EventHandler { + pub fn new(device: VirtualDevice, timer: TimerFd) -> EventHandler { EventHandler { device, + timer, shift: PressState::new(), control: PressState::new(), alt: PressState::new(), @@ -42,6 +47,7 @@ impl EventHandler { application_cache: None, multi_purpose_keys: HashMap::new(), override_remap: None, + override_timeout_key: None, sigaction_set: false, mark_set: false, escape_next_key: false, @@ -71,9 +77,9 @@ 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)? { for action in &actions { - self.dispatch_action(action)?; + self.dispatch_action(action, &key)?; } continue; } @@ -90,6 +96,21 @@ impl EventHandler { self.device.emit(&[event]) } + pub fn timeout_override(&mut self) -> Result<(), Box> { + if let Some(key) = self.override_timeout_key { + self.send_key(&key, PRESS)?; + self.send_key(&key, RELEASE)?; + } + self.remove_override() + } + + fn remove_override(&mut self) -> Result<(), Box> { + self.timer.unset()?; + self.override_remap = None; + self.override_timeout_key = None; + Ok(()) + } + fn send_key(&mut self, key: &Key, value: i32) -> std::io::Result<()> { let event = InputEvent::new(EventType::KEY, key.code(), value); self.send_event(event) @@ -161,7 +182,7 @@ impl EventHandler { None } - fn find_keymap(&mut self, config: &Config, key: &Key) -> Option> { + fn find_keymap(&mut self, config: &Config, key: &Key) -> Result>, Box> { let key_press = KeyPress { key: *key, shift: self.shift.to_modifier_state(), @@ -170,10 +191,11 @@ impl EventHandler { windows: self.windows.to_modifier_state(), }; if let Some(override_remap) = &self.override_remap { - let override_remap = override_remap.clone(); - self.override_remap = None; - if let Some(actions) = override_remap.get(&key_press) { - return Some(actions.to_vec()); + if let Some(actions) = override_remap.clone().get(&key_press) { + self.remove_override()?; + return Ok(Some(actions.to_vec())); + } else { + self.timeout_override()?; } } for keymap in &config.keymap { @@ -183,21 +205,27 @@ impl EventHandler { continue; } } - return Some(actions.to_vec()); + return Ok(Some(actions.to_vec())); } } - None + Ok(None) } - fn dispatch_action(&mut self, action: &Action) -> Result<(), Box> { + fn dispatch_action(&mut self, action: &Action, key: &Key) -> Result<(), Box> { match action { Action::KeyPress(key_press) => self.send_key_press(key_press)?, - Action::Remap(remap) => { + Action::Remap(action) => { let mut override_remap: HashMap> = HashMap::new(); - for (key_press, actions) in remap.iter() { + for (key_press, actions) in action.remap.iter() { override_remap.insert(key_press.clone(), actions.to_vec()); } - self.override_remap = Some(override_remap) + self.override_remap = Some(override_remap); + if let Some(timeout) = action.timeout { + let expiration = Expiration::OneShot(TimeSpec::from_duration(timeout)); + self.timer.unset()?; + self.timer.set(expiration, TimerSetTimeFlags::empty())?; + self.override_timeout_key = Some(key.clone()); + } } Action::Launch(command) => self.run_command(command.clone()), Action::SetMark(set) => self.mark_set = *set, diff --git a/src/main.rs b/src/main.rs index 90274b9..8b65b77 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,8 +11,9 @@ use nix::libc::ENODEV; use nix::sys::inotify::{AddWatchFlags, Inotify}; use nix::sys::select::select; use nix::sys::select::FdSet; +use nix::sys::timerfd::{ClockId, TimerFd, TimerFlags}; use std::io::stdout; -use std::os::unix::io::AsRawFd; +use std::os::unix::io::{AsRawFd, RawFd}; use std::path::PathBuf; mod client; @@ -97,7 +98,9 @@ fn main() -> anyhow::Result<()> { Ok(output_device) => output_device, Err(e) => bail!("Failed to prepare an output device: {}", e), }; - let mut handler = EventHandler::new(output_device); + let timer = TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty())?; + let timer_fd = timer.as_raw_fd(); + let mut handler = EventHandler::new(output_device, timer); let mut input_devices = match get_input_devices(&device_filter, &ignore_filter, watch_devices) { Ok(input_devices) => input_devices, Err(e) => bail!("Failed to prepare input devices: {}", e), @@ -108,7 +111,13 @@ fn main() -> anyhow::Result<()> { loop { match 'event_loop: loop { - let readable_fds = select_readable(input_devices.values(), &watchers)?; + let readable_fds = select_readable(input_devices.values(), &watchers, timer_fd)?; + if readable_fds.contains(timer_fd) { + if let Err(error) = handler.timeout_override() { + println!("Error on remap timeout: {error}") + } + } + for input_device in input_devices.values_mut() { if readable_fds.contains(input_device.as_raw_fd()) { match input_device.fetch_events().map_err(|e| (e.raw_os_error(), e)) { @@ -210,8 +219,13 @@ enum Event { ReloadDevices, } -fn select_readable<'a>(devices: impl Iterator, watchers: &[&Inotify]) -> anyhow::Result { +fn select_readable<'a>( + devices: impl Iterator, + watchers: &[&Inotify], + timer_fd: RawFd, +) -> anyhow::Result { let mut read_fds = FdSet::new(); + read_fds.insert(timer_fd); for device in devices { read_fds.insert(device.as_raw_fd()); }