diff --git a/src/config/mod.rs b/src/config/mod.rs index f841121..e1c0595 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -61,7 +61,6 @@ fn default_passive_arg_escape() -> char { '\\' } fn default_passive_key() -> KeyModifier { KeyModifier::OFF } fn default_enable_passive() -> bool { false } fn default_enable_active() -> bool { true } -fn default_action_noop_interval() -> u128 { 500 } fn default_backspace_limit() -> i32 { 3 } fn default_restore_clipboard_delay() -> i32 { 300 } fn default_exclude_default_entries() -> bool {false} @@ -130,9 +129,6 @@ pub struct Configs { #[serde(default = "default_enable_active")] pub enable_active: bool, - #[serde(default = "default_action_noop_interval")] - pub action_noop_interval: u128, - #[serde(default)] pub paste_shortcut: PasteShortcut, @@ -193,7 +189,6 @@ impl Configs { validate_field!(result, self.passive_arg_delimiter, default_passive_arg_delimiter()); validate_field!(result, self.passive_arg_escape, default_passive_arg_escape()); validate_field!(result, self.passive_key, default_passive_key()); - validate_field!(result, self.action_noop_interval, default_action_noop_interval()); validate_field!(result, self.restore_clipboard_delay, default_restore_clipboard_delay()); result diff --git a/src/context/linux.rs b/src/context/linux.rs index 9bed592..67185f3 100644 --- a/src/context/linux.rs +++ b/src/context/linux.rs @@ -26,14 +26,18 @@ use std::process::exit; use log::{debug, error, info}; use std::ffi::CStr; use std::{thread, time}; +use std::sync::atomic::AtomicBool; +use std::sync::Arc; +use std::sync::atomic::Ordering::Acquire; #[repr(C)] pub struct LinuxContext { - pub send_channel: Sender + pub send_channel: Sender, + is_injecting: Arc, } impl LinuxContext { - pub fn new(send_channel: Sender) -> Box { + pub fn new(send_channel: Sender, is_injecting: Arc) -> Box { // Check if the X11 context is available let x11_available = unsafe { check_x11() @@ -46,6 +50,7 @@ impl LinuxContext { let context = Box::new(LinuxContext { send_channel, + is_injecting }); unsafe { @@ -85,6 +90,14 @@ extern fn keypress_callback(_self: *mut c_void, raw_buffer: *const u8, len: i32, unsafe { let _self = _self as *mut LinuxContext; + // If espanso is currently injecting text, we should avoid processing + // external events, as it could happen that espanso reinterpret its + // own input. + if (*_self).is_injecting.load(Acquire) { + debug!("Input ignored while espanso is injecting text..."); + return; + } + if is_modifier == 0 { // Char event // Convert the received buffer to a string let c_str = CStr::from_ptr(raw_buffer as (*const c_char)); diff --git a/src/context/mod.rs b/src/context/mod.rs index 782538f..ac9f9fe 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -30,7 +30,8 @@ use std::sync::mpsc::Sender; use crate::event::Event; use std::path::PathBuf; use std::fs::create_dir_all; -use std::sync::Once; +use std::sync::{Once, Arc}; +use std::sync::atomic::AtomicBool; pub trait Context { fn eventloop(&self); @@ -44,8 +45,8 @@ pub fn new(send_channel: Sender) -> Box { // LINUX IMPLEMENTATION #[cfg(target_os = "linux")] -pub fn new(send_channel: Sender) -> Box { - linux::LinuxContext::new(send_channel) +pub fn new(send_channel: Sender, is_injecting: Arc,) -> Box { + linux::LinuxContext::new(send_channel, is_injecting) } // WINDOWS IMPLEMENTATION diff --git a/src/engine.rs b/src/engine.rs index bb3a5ed..622f169 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -33,6 +33,9 @@ use std::collections::HashMap; use std::path::PathBuf; use regex::{Regex, Captures}; use std::time::SystemTime; +use std::sync::Arc; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering::{Relaxed, Release, Acquire, AcqRel, SeqCst}; pub struct Engine<'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>, U: UIManager, R: Renderer> { @@ -41,29 +44,28 @@ pub struct Engine<'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager< config_manager: &'a M, ui_manager: &'a U, renderer: &'a R, + is_injecting: Arc, enabled: RefCell, last_action_time: RefCell, // Used to block espanso from re-interpreting it's own inputs - action_noop_interval: u128, } impl <'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>, U: UIManager, R: Renderer> Engine<'a, S, C, M, U, R> { pub fn new(keyboard_manager: &'a S, clipboard_manager: &'a C, config_manager: &'a M, ui_manager: &'a U, - renderer: &'a R) -> Engine<'a, S, C, M, U, R> { + renderer: &'a R, is_injecting: Arc) -> Engine<'a, S, C, M, U, R> { let enabled = RefCell::new(true); let last_action_time = RefCell::new(SystemTime::now()); - let action_noop_interval = config_manager.default_config().action_noop_interval; Engine{keyboard_manager, clipboard_manager, config_manager, ui_manager, renderer, + is_injecting, enabled, last_action_time, - action_noop_interval, } } @@ -139,11 +141,8 @@ impl <'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>, U: UIMa return; } - // avoid espanso reinterpreting its own actions - if self.check_last_action_and_set(self.action_noop_interval) { - debug!("Last action was too near, nooping the action."); - return; - } + // Block espanso from reinterpreting its own actions + self.is_injecting.store(true, Release); let char_count = if trailing_separator.is_none() { m.triggers[trigger_offset].chars().count() as i32 @@ -246,14 +245,12 @@ impl <'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>, U: UIMa self.clipboard_manager.set_clipboard(&previous_clipboard_content); } + + // Re-allow espanso to interpret actions + self.is_injecting.store(false, Release); } fn on_enable_update(&self, status: bool) { - // avoid espanso reinterpreting its own actions - if self.check_last_action_and_set(self.action_noop_interval) { - return; - } - let message = if status { "espanso enabled" }else{ @@ -269,11 +266,6 @@ impl <'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>, U: UIMa } fn on_passive(&self) { - // avoid espanso reinterpreting its own actions - if self.check_last_action_and_set(self.action_noop_interval) { - return; - } - let config = self.config_manager.active_config(); if !config.enable_passive { diff --git a/src/main.rs b/src/main.rs index 78e8d0d..0505a6f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,7 +23,7 @@ extern crate lazy_static; use std::thread; use std::fs::{File, OpenOptions}; use std::process::exit; -use std::sync::mpsc; +use std::sync::{mpsc, Arc}; use std::sync::mpsc::Receiver; use std::time::Duration; @@ -44,6 +44,7 @@ use crate::protocol::*; use std::io::{BufReader, BufRead}; use crate::package::default::DefaultPackageManager; use crate::package::{PackageManager, InstallResult, UpdateResult, RemoveResult}; +use std::sync::atomic::AtomicBool; mod ui; mod edit; @@ -319,11 +320,15 @@ fn daemon_main(config_set: ConfigSet) { let (send_channel, receive_channel) = mpsc::channel(); - let context = context::new(send_channel.clone()); + // This atomic bool is used to "disable" espanso when espanding its own matches, otherwise + // we could reinterpret the characters we are injecting + let is_injecting = Arc::new(std::sync::atomic::AtomicBool::new(false)); + + let context = context::new(send_channel.clone(), is_injecting.clone()); let config_set_copy = config_set.clone(); thread::Builder::new().name("daemon_background".to_string()).spawn(move || { - daemon_background(receive_channel, config_set_copy); + daemon_background(receive_channel, config_set_copy, is_injecting); }).expect("Unable to spawn daemon background thread"); let ipc_server = protocol::get_ipc_server(config_set, send_channel.clone()); @@ -333,7 +338,7 @@ fn daemon_main(config_set: ConfigSet) { } /// Background thread worker for the daemon -fn daemon_background(receive_channel: Receiver, config_set: ConfigSet) { +fn daemon_background(receive_channel: Receiver, config_set: ConfigSet, is_injecting: Arc) { let system_manager = system::get_manager(); let config_manager = RuntimeConfigManager::new(config_set, system_manager); @@ -354,6 +359,7 @@ fn daemon_background(receive_channel: Receiver, config_set: ConfigSet) { &config_manager, &ui_manager, &renderer, + is_injecting, ); let matcher = ScrollingMatcher::new(&config_manager, &engine);