From 302a45aa88daf03c94b6422fbc91349098353001 Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Sun, 25 Apr 2021 21:03:13 +0200 Subject: [PATCH] feat(core): add source_id parameter to events and introduce past events discarding mechanism --- espanso/src/cli/worker/engine/mod.rs | 3 +- espanso/src/cli/worker/engine/multiplex.rs | 8 +- .../src/cli/worker/engine/source/detect.rs | 40 +++++----- espanso/src/cli/worker/engine/source/mod.rs | 14 +++- .../src/cli/worker/engine/source/sequencer.rs | 48 ++++++++++++ .../engine/dispatch/executor/key_inject.rs | 4 +- .../engine/dispatch/executor/text_inject.rs | 4 +- espanso/src/engine/event/internal.rs | 6 ++ espanso/src/engine/event/mod.rs | 25 +++++- espanso/src/engine/mod.rs | 4 +- espanso/src/engine/process/default.rs | 15 +++- .../src/engine/process/middleware/action.rs | 78 +++++++++++++------ .../src/engine/process/middleware/cause.rs | 18 ++--- .../engine/process/middleware/cursor_hint.rs | 12 +-- .../process/middleware/delay_modifiers.rs | 18 ++--- .../engine/process/middleware/match_select.rs | 22 ++---- .../src/engine/process/middleware/matcher.rs | 59 +++++++------- espanso/src/engine/process/middleware/mod.rs | 1 + .../engine/process/middleware/multiplex.rs | 17 ++-- .../engine/process/middleware/past_discard.rs | 66 ++++++++++++++++ .../src/engine/process/middleware/render.rs | 36 +++++---- espanso/src/engine/process/mod.rs | 8 +- 22 files changed, 347 insertions(+), 159 deletions(-) create mode 100644 espanso/src/cli/worker/engine/source/sequencer.rs create mode 100644 espanso/src/engine/process/middleware/past_discard.rs diff --git a/espanso/src/cli/worker/engine/mod.rs b/espanso/src/cli/worker/engine/mod.rs index f80d254..0333fd6 100644 --- a/espanso/src/cli/worker/engine/mod.rs +++ b/espanso/src/cli/worker/engine/mod.rs @@ -53,7 +53,7 @@ pub fn initialize_and_spawn( let modulo_manager = ui::modulo::ModuloManager::new(); - let (detect_source, modifier_state_store) = + let (detect_source, modifier_state_store, sequencer) = super::engine::source::init_and_spawn().expect("failed to initialize detector module"); let sources: Vec<&dyn crate::engine::funnel::Source> = vec![&detect_source]; let funnel = crate::engine::funnel::default(&sources); @@ -108,6 +108,7 @@ pub fn initialize_and_spawn( &renderer_adapter, &match_cache, &modifier_state_store, + &sequencer, ); let event_injector = diff --git a/espanso/src/cli/worker/engine/multiplex.rs b/espanso/src/cli/worker/engine/multiplex.rs index 574f8e3..45b7059 100644 --- a/espanso/src/cli/worker/engine/multiplex.rs +++ b/espanso/src/cli/worker/engine/multiplex.rs @@ -17,11 +17,9 @@ * along with espanso. If not, see . */ -use std::collections::HashMap; - use espanso_config::matches::{Match, MatchEffect}; -use crate::engine::{event::{Event, internal::DetectedMatch, internal::RenderingRequestedEvent}, process::Multiplexer}; +use crate::engine::{event::{EventType, internal::DetectedMatch, internal::RenderingRequestedEvent}, process::Multiplexer}; pub trait MatchProvider<'a> { fn get(&self, match_id: i32) -> Option<&'a Match>; @@ -38,11 +36,11 @@ impl<'a> MultiplexAdapter<'a> { } impl<'a> Multiplexer for MultiplexAdapter<'a> { - fn convert(&self, detected_match: DetectedMatch) -> Option { + fn convert(&self, detected_match: DetectedMatch) -> Option { let m = self.provider.get(detected_match.id)?; match &m.effect { - MatchEffect::Text(_) => Some(Event::RenderingRequested(RenderingRequestedEvent { + MatchEffect::Text(_) => Some(EventType::RenderingRequested(RenderingRequestedEvent { match_id: detected_match.id, trigger: detected_match.trigger, left_separator: detected_match.left_separator, diff --git a/espanso/src/cli/worker/engine/source/detect.rs b/espanso/src/cli/worker/engine/source/detect.rs index 7d931d5..b4a77ed 100644 --- a/espanso/src/cli/worker/engine/source/detect.rs +++ b/espanso/src/cli/worker/engine/source/detect.rs @@ -18,18 +18,12 @@ */ use crossbeam::channel::{Receiver, Select, SelectedOperation}; -use espanso_detect::{event::InputEvent}; +use espanso_detect::event::InputEvent; -use crate::engine::{ - event::{ - input::{Key, KeyboardEvent, MouseButton, MouseEvent, Status, Variant}, - Event, - }, - funnel -}; +use crate::engine::{event::{Event, EventType, SourceId, input::{Key, KeyboardEvent, MouseButton, MouseEvent, Status, Variant}}, funnel}; pub struct DetectSource { - pub receiver: Receiver, + pub receiver: Receiver<(InputEvent, SourceId)>, } impl<'a> funnel::Source<'a> for DetectSource { @@ -38,20 +32,26 @@ impl<'a> funnel::Source<'a> for DetectSource { } fn receive(&self, op: SelectedOperation) -> Event { - let input_event = op + let (input_event, source_id) = op .recv(&self.receiver) .expect("unable to select data from DetectSource receiver"); match input_event { - InputEvent::Keyboard(keyboard_event) => Event::Keyboard(KeyboardEvent { - key: keyboard_event.key.into(), - value: keyboard_event.value, - status: keyboard_event.status.into(), - variant: keyboard_event.variant.map(|variant| variant.into()), - }), - InputEvent::Mouse(mouse_event) => Event::Mouse(MouseEvent { - status: mouse_event.status.into(), - button: mouse_event.button.into(), - }), + InputEvent::Keyboard(keyboard_event) => Event { + source_id, + etype: EventType::Keyboard(KeyboardEvent { + key: keyboard_event.key.into(), + value: keyboard_event.value, + status: keyboard_event.status.into(), + variant: keyboard_event.variant.map(|variant| variant.into()), + }), + }, + InputEvent::Mouse(mouse_event) => Event { + source_id, + etype: EventType::Mouse(MouseEvent { + status: mouse_event.status.into(), + button: mouse_event.button.into(), + }), + }, InputEvent::HotKey(_) => todo!(), // TODO } } diff --git a/espanso/src/cli/worker/engine/source/mod.rs b/espanso/src/cli/worker/engine/source/mod.rs index 788e985..a35b546 100644 --- a/espanso/src/cli/worker/engine/source/mod.rs +++ b/espanso/src/cli/worker/engine/source/mod.rs @@ -24,19 +24,22 @@ use thiserror::Error; use detect::DetectSource; -use self::modifier::{Modifier, ModifierStateStore}; +use self::{modifier::{Modifier, ModifierStateStore}, sequencer::Sequencer}; pub mod detect; pub mod modifier; +pub mod sequencer; // TODO: pass options -pub fn init_and_spawn() -> Result<(DetectSource, ModifierStateStore)> { +pub fn init_and_spawn() -> Result<(DetectSource, ModifierStateStore, Sequencer)> { let (sender, receiver) = crossbeam::channel::unbounded(); let (init_tx, init_rx) = crossbeam::channel::unbounded(); let modifier_state_store = ModifierStateStore::new(); + let sequencer = Sequencer::new(); let state_store_clone = modifier_state_store.clone(); + let sequencer_clone = sequencer.clone(); if let Err(error) = std::thread::Builder::new() .name("detect thread".to_string()) .spawn( @@ -58,8 +61,11 @@ pub fn init_and_spawn() -> Result<(DetectSource, ModifierStateStore)> { state_store_clone.update_state(modifier, is_pressed); } + // Generate a monotonically increasing id for the current event + let source_id = sequencer_clone.next_id(); + sender - .send(event) + .send((event, source_id)) .expect("unable to send to the source channel"); })) .expect("detect eventloop crashed"); @@ -86,7 +92,7 @@ pub fn init_and_spawn() -> Result<(DetectSource, ModifierStateStore)> { return Err(DetectSourceError::InitFailed.into()); } - Ok((DetectSource { receiver }, modifier_state_store)) + Ok((DetectSource { receiver }, modifier_state_store, sequencer)) } #[derive(Error, Debug)] diff --git a/espanso/src/cli/worker/engine/source/sequencer.rs b/espanso/src/cli/worker/engine/source/sequencer.rs new file mode 100644 index 0000000..91866bd --- /dev/null +++ b/espanso/src/cli/worker/engine/source/sequencer.rs @@ -0,0 +1,48 @@ +/* + * This file is part of espanso. + * + * Copyright (C) 2019-2021 Federico Terzi + * + * espanso is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +use std::sync::{ + atomic::{AtomicU32, Ordering}, + Arc, +}; + +use crate::engine::{event::SourceId, process::EventSequenceProvider}; + +#[derive(Clone)] +pub struct Sequencer { + current_id: Arc, +} + +impl Sequencer { + pub fn new() -> Self { + Self { + current_id: Arc::new(AtomicU32::new(0)), + } + } + + pub fn next_id(&self) -> SourceId { + self.current_id.fetch_add(1, Ordering::SeqCst) + } +} + +impl EventSequenceProvider for Sequencer { + fn get_next_id(&self) -> SourceId { + self.next_id() + } +} diff --git a/espanso/src/engine/dispatch/executor/key_inject.rs b/espanso/src/engine/dispatch/executor/key_inject.rs index 3182a38..851066e 100644 --- a/espanso/src/engine/dispatch/executor/key_inject.rs +++ b/espanso/src/engine/dispatch/executor/key_inject.rs @@ -17,6 +17,8 @@ * along with espanso. If not, see . */ +use crate::engine::event::EventType; + use super::super::{Event, Executor, KeyInjector}; use log::error; @@ -32,7 +34,7 @@ impl<'a> KeyInjectExecutor<'a> { impl<'a> Executor for KeyInjectExecutor<'a> { fn execute(&self, event: &Event) -> bool { - if let Event::KeySequenceInject(inject_event) = event { + if let EventType::KeySequenceInject(inject_event) = &event.etype { if let Err(error) = self.injector.inject_sequence(&inject_event.keys) { error!("key injector reported an error: {}", error); } diff --git a/espanso/src/engine/dispatch/executor/text_inject.rs b/espanso/src/engine/dispatch/executor/text_inject.rs index f0f8ac8..f0c2109 100644 --- a/espanso/src/engine/dispatch/executor/text_inject.rs +++ b/espanso/src/engine/dispatch/executor/text_inject.rs @@ -19,7 +19,7 @@ use anyhow::Result; use super::super::{Event, Executor}; -use crate::engine::event::effect::TextInjectMode; +use crate::engine::event::{EventType, effect::TextInjectMode}; use log::{error, trace}; pub trait TextInjector { @@ -63,7 +63,7 @@ impl<'a> TextInjectExecutor<'a> { impl<'a> Executor for TextInjectExecutor<'a> { fn execute(&self, event: &Event) -> bool { - if let Event::TextInject(inject_event) = event { + if let EventType::TextInject(inject_event) = &event.etype { let active_mode = self.mode_provider.active_mode(); let injector = if let Some(force_mode) = &inject_event.force_mode { diff --git a/espanso/src/engine/event/internal.rs b/espanso/src/engine/event/internal.rs index fd3e273..bb5b673 100644 --- a/espanso/src/engine/event/internal.rs +++ b/espanso/src/engine/event/internal.rs @@ -57,3 +57,9 @@ pub struct RenderedEvent { pub match_id: i32, pub body: String, } + +#[derive(Debug, Clone, PartialEq)] +pub struct DiscardPreviousEvent { + // All Events with a source_id smaller than this one will be discarded + pub minimum_source_id: u32, +} \ No newline at end of file diff --git a/espanso/src/engine/event/mod.rs b/espanso/src/engine/event/mod.rs index 5fe08dd..bdb3e94 100644 --- a/espanso/src/engine/event/mod.rs +++ b/espanso/src/engine/event/mod.rs @@ -21,8 +21,30 @@ pub mod input; pub mod effect; pub mod internal; +pub type SourceId = u32; + #[derive(Debug, Clone)] -pub enum Event { +pub struct Event { + // The source id is a unique, monothonically increasing number + // that is given to each event by the source and is propagated + // to all consequential events. + // For example, if a keyboard event with source_id = 5 generates + // a detected match event, this event will have source_id = 5 + pub source_id: SourceId, + pub etype: EventType, +} + +impl Event { + pub fn caused_by(cause_id: SourceId, event_type: EventType) -> Event { + Event { + source_id: cause_id, + etype: event_type, + } + } +} + +#[derive(Debug, Clone)] +pub enum EventType { NOOP, ProcessingError(String), @@ -39,6 +61,7 @@ pub enum Event { RenderingRequested(internal::RenderingRequestedEvent), Rendered(internal::RenderedEvent), MatchInjected, + DiscardPrevious(internal::DiscardPreviousEvent), // Effects TriggerCompensation(effect::TriggerCompensationEvent), diff --git a/espanso/src/engine/mod.rs b/espanso/src/engine/mod.rs index 0350cad..aae386c 100644 --- a/espanso/src/engine/mod.rs +++ b/espanso/src/engine/mod.rs @@ -17,11 +17,11 @@ * along with espanso. If not, see . */ -use log::{debug, trace}; +use log::{debug}; use self::{ dispatch::Dispatcher, - event::Event, + event::{Event}, process::Processor, funnel::{Funnel, FunnelResult}, }; diff --git a/espanso/src/engine/process/default.rs b/espanso/src/engine/process/default.rs index 2bfbf3e..216ddaf 100644 --- a/espanso/src/engine/process/default.rs +++ b/espanso/src/engine/process/default.rs @@ -19,11 +19,13 @@ use log::trace; -use super::{Event, MatchFilter, MatchInfoProvider, MatchSelector, Matcher, Middleware, Multiplexer, Processor, Renderer, middleware::{ +use super::{MatchFilter, MatchInfoProvider, MatchSelector, Matcher, Middleware, Multiplexer, Processor, Renderer, middleware::{ match_select::MatchSelectMiddleware, matcher::MatcherMiddleware, multiplex::MultiplexMiddleware, - render::RenderMiddleware, action::ActionMiddleware, cursor_hint::CursorHintMiddleware, cause::CauseCompensateMiddleware, + render::RenderMiddleware, action::{ActionMiddleware, EventSequenceProvider}, cursor_hint::CursorHintMiddleware, cause::CauseCompensateMiddleware, delay_modifiers::{DelayForModifierReleaseMiddleware, ModifierStatusProvider}, + past_discard::PastEventsDiscardMiddleware, }}; +use crate::engine::event::{Event, EventType}; use std::collections::VecDeque; pub struct DefaultProcessor<'a> { @@ -40,17 +42,19 @@ impl<'a> DefaultProcessor<'a> { renderer: &'a dyn Renderer<'a>, match_info_provider: &'a dyn MatchInfoProvider, modifier_status_provider: &'a dyn ModifierStatusProvider, + event_sequence_provider: &'a dyn EventSequenceProvider, ) -> DefaultProcessor<'a> { Self { event_queue: VecDeque::new(), middleware: vec![ + Box::new(PastEventsDiscardMiddleware::new()), Box::new(MatcherMiddleware::new(matchers)), Box::new(MatchSelectMiddleware::new(match_filter, match_selector)), Box::new(CauseCompensateMiddleware::new()), Box::new(MultiplexMiddleware::new(multiplexer)), Box::new(RenderMiddleware::new(renderer)), Box::new(CursorHintMiddleware::new()), - Box::new(ActionMiddleware::new(match_info_provider)), + Box::new(ActionMiddleware::new(match_info_provider, event_sequence_provider)), Box::new(DelayForModifierReleaseMiddleware::new(modifier_status_provider)), ], } @@ -73,6 +77,11 @@ impl<'a> DefaultProcessor<'a> { current_event = middleware.next(current_event, &mut dispatch); trace!("middleware '{}' produced event: {:?}", middleware.name(), current_event); + + if let EventType::NOOP = current_event.etype { + trace!("interrupting chain as the event is NOOP"); + break; + } } while let Some(event) = current_queue.pop_back() { diff --git a/espanso/src/engine/process/middleware/action.rs b/espanso/src/engine/process/middleware/action.rs index df10916..41ae3e2 100644 --- a/espanso/src/engine/process/middleware/action.rs +++ b/espanso/src/engine/process/middleware/action.rs @@ -18,26 +18,34 @@ */ use super::super::Middleware; -use crate::engine::{ - event::{ - input::{Key}, - effect::{TextInjectMode, TextInjectRequest, KeySequenceInjectRequest}, - Event, - }, +use crate::engine::event::{ + effect::{KeySequenceInjectRequest, TextInjectMode, TextInjectRequest}, + input::Key, + internal::DiscardPreviousEvent, + Event, EventType, }; pub trait MatchInfoProvider { fn get_force_mode(&self, match_id: i32) -> Option; } +pub trait EventSequenceProvider { + fn get_next_id(&self) -> u32; +} + pub struct ActionMiddleware<'a> { match_info_provider: &'a dyn MatchInfoProvider, + event_sequence_provider: &'a dyn EventSequenceProvider, } impl<'a> ActionMiddleware<'a> { - pub fn new(match_info_provider: &'a dyn MatchInfoProvider) -> Self { + pub fn new( + match_info_provider: &'a dyn MatchInfoProvider, + event_sequence_provider: &'a dyn EventSequenceProvider, + ) -> Self { Self { match_info_provider, + event_sequence_provider, } } } @@ -48,23 +56,42 @@ impl<'a> Middleware for ActionMiddleware<'a> { } fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event { - match &event { - Event::Rendered(m_event) => { - dispatch(Event::MatchInjected); + match &event.etype { + EventType::Rendered(m_event) => { + dispatch(Event::caused_by(event.source_id, EventType::MatchInjected)); + dispatch(Event::caused_by( + event.source_id, + EventType::DiscardPrevious(DiscardPreviousEvent { + minimum_source_id: self.event_sequence_provider.get_next_id(), + }), + )); - Event::TextInject(TextInjectRequest { - text: m_event.body.clone(), - force_mode: self.match_info_provider.get_force_mode(m_event.match_id), - }) + Event::caused_by( + event.source_id, + EventType::TextInject(TextInjectRequest { + text: m_event.body.clone(), + force_mode: self.match_info_provider.get_force_mode(m_event.match_id), + }), + ) } - Event::CursorHintCompensation(m_event) => { - Event::KeySequenceInject(KeySequenceInjectRequest { - keys: (0..m_event.cursor_hint_back_count) - .map(|_| Key::ArrowLeft) - .collect(), - }) + EventType::CursorHintCompensation(m_event) => { + dispatch(Event::caused_by( + event.source_id, + EventType::DiscardPrevious(DiscardPreviousEvent { + minimum_source_id: self.event_sequence_provider.get_next_id(), + }), + )); + + Event::caused_by( + event.source_id, + EventType::KeySequenceInject(KeySequenceInjectRequest { + keys: (0..m_event.cursor_hint_back_count) + .map(|_| Key::ArrowLeft) + .collect(), + }), + ) } - Event::TriggerCompensation(m_event) => { + EventType::TriggerCompensation(m_event) => { let mut backspace_count = m_event.trigger.chars().count(); // We want to preserve the left separator if present @@ -72,9 +99,12 @@ impl<'a> Middleware for ActionMiddleware<'a> { backspace_count -= left_separator.chars().count(); } - Event::KeySequenceInject(KeySequenceInjectRequest { - keys: (0..backspace_count).map(|_| Key::Backspace).collect(), - }) + Event::caused_by( + event.source_id, + EventType::KeySequenceInject(KeySequenceInjectRequest { + keys: (0..backspace_count).map(|_| Key::Backspace).collect(), + }), + ) } _ => event, } diff --git a/espanso/src/engine/process/middleware/cause.rs b/espanso/src/engine/process/middleware/cause.rs index 88305e3..521ef22 100644 --- a/espanso/src/engine/process/middleware/cause.rs +++ b/espanso/src/engine/process/middleware/cause.rs @@ -18,13 +18,7 @@ */ use super::super::Middleware; -use crate::engine::{ - event::{ - effect::TriggerCompensationEvent, - internal::CauseCompensatedMatchEvent, - Event, - }, -}; +use crate::engine::{event::{Event, EventType, effect::TriggerCompensationEvent, internal::CauseCompensatedMatchEvent}}; pub struct CauseCompensateMiddleware {} @@ -36,22 +30,22 @@ impl CauseCompensateMiddleware { impl Middleware for CauseCompensateMiddleware { fn name(&self) -> &'static str { - "cause_compensate" + "discard" } fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event { - if let Event::MatchSelected(m_event) = &event { + if let EventType::MatchSelected(m_event) = &event.etype { let compensated_event = - Event::CauseCompensatedMatch(CauseCompensatedMatchEvent { m: m_event.chosen.clone() }); + Event::caused_by(event.source_id, EventType::CauseCompensatedMatch(CauseCompensatedMatchEvent { m: m_event.chosen.clone() })); if let Some(trigger) = &m_event.chosen.trigger { dispatch(compensated_event); // Before the event, place a trigger compensation - return Event::TriggerCompensation(TriggerCompensationEvent { + return Event::caused_by(event.source_id, EventType::TriggerCompensation(TriggerCompensationEvent { trigger: trigger.clone(), left_separator: m_event.chosen.left_separator.clone(), - }); + })); } else { return compensated_event; } diff --git a/espanso/src/engine/process/middleware/cursor_hint.rs b/espanso/src/engine/process/middleware/cursor_hint.rs index eb5ed34..603150e 100644 --- a/espanso/src/engine/process/middleware/cursor_hint.rs +++ b/espanso/src/engine/process/middleware/cursor_hint.rs @@ -18,7 +18,7 @@ */ use super::super::Middleware; -use crate::engine::{event::{Event, effect::CursorHintCompensationEvent, internal::RenderedEvent}}; +use crate::engine::{event::{Event, EventType, effect::CursorHintCompensationEvent, internal::RenderedEvent}}; pub struct CursorHintMiddleware {} @@ -34,20 +34,20 @@ impl Middleware for CursorHintMiddleware { } fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event { - if let Event::Rendered(m_event) = event { + if let EventType::Rendered(m_event) = event.etype { let (body, cursor_hint_back_count) = process_cursor_hint(m_event.body); if let Some(cursor_hint_back_count) = cursor_hint_back_count { - dispatch(Event::CursorHintCompensation(CursorHintCompensationEvent { + dispatch(Event::caused_by(event.source_id, EventType::CursorHintCompensation(CursorHintCompensationEvent { cursor_hint_back_count, - })) + }))); } // Alter the rendered event to remove the cursor hint from the body - return Event::Rendered(RenderedEvent { + return Event::caused_by(event.source_id, EventType::Rendered(RenderedEvent { body, ..m_event - }) + })); } event diff --git a/espanso/src/engine/process/middleware/delay_modifiers.rs b/espanso/src/engine/process/middleware/delay_modifiers.rs index f6977a4..cc155ef 100644 --- a/espanso/src/engine/process/middleware/delay_modifiers.rs +++ b/espanso/src/engine/process/middleware/delay_modifiers.rs @@ -26,9 +26,7 @@ use std::{ use log::{trace, warn}; use super::super::Middleware; -use crate::engine::event::{ - Event, -}; +use crate::engine::event::{Event, EventType}; // TODO: pass through config const MODIFIER_DELAY_TIMEOUT: Duration = Duration::from_secs(3); @@ -55,7 +53,7 @@ impl <'a> Middleware for DelayForModifierReleaseMiddleware<'a> { } fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event { - if is_injection_event(&event) { + if is_injection_event(&event.etype) { let start = Instant::now(); while self.provider.is_any_modifier_pressed() { if Instant::now().duration_since(start) > MODIFIER_DELAY_TIMEOUT { @@ -71,12 +69,12 @@ impl <'a> Middleware for DelayForModifierReleaseMiddleware<'a> { } } -fn is_injection_event(event: &Event) -> bool { - match event { - Event::TriggerCompensation(_) => true, - Event::CursorHintCompensation(_) => true, - Event::KeySequenceInject(_) => true, - Event::TextInject(_) => true, +fn is_injection_event(event_type: &EventType) -> bool { + match event_type { + EventType::TriggerCompensation(_) => true, + EventType::CursorHintCompensation(_) => true, + EventType::KeySequenceInject(_) => true, + EventType::TextInject(_) => true, _ => false, } } diff --git a/espanso/src/engine/process/middleware/match_select.rs b/espanso/src/engine/process/middleware/match_select.rs index 5bdfcd1..6929e63 100644 --- a/espanso/src/engine/process/middleware/match_select.rs +++ b/espanso/src/engine/process/middleware/match_select.rs @@ -20,13 +20,7 @@ use log::{debug, error}; use super::super::Middleware; -use crate::engine::{ - event::{ - internal::{MatchSelectedEvent}, - Event, - }, - process::{MatchFilter, MatchSelector}, -}; +use crate::engine::{event::{Event, EventType, internal::{MatchSelectedEvent}}, process::{MatchFilter, MatchSelector}}; pub struct MatchSelectMiddleware<'a> { match_filter: &'a dyn MatchFilter, @@ -48,14 +42,14 @@ impl<'a> Middleware for MatchSelectMiddleware<'a> { } fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event { - if let Event::MatchesDetected(m_event) = event { + if let EventType::MatchesDetected(m_event) = event.etype { let matches_ids: Vec = m_event.matches.iter().map(|m| m.id).collect(); // Find the matches that are actually valid in the current context let valid_ids = self.match_filter.filter_active(&matches_ids); return match valid_ids.len() { - 0 => Event::NOOP, // No valid matches, consume the event + 0 => Event::caused_by(event.source_id, EventType::NOOP), // No valid matches, consume the event 1 => { // Only one match, no need to show a selection dialog let m = m_event @@ -63,10 +57,10 @@ impl<'a> Middleware for MatchSelectMiddleware<'a> { .into_iter() .find(|m| m.id == *valid_ids.first().unwrap()); if let Some(m) = m { - Event::MatchSelected(MatchSelectedEvent { chosen: m }) + Event::caused_by(event.source_id, EventType::MatchSelected(MatchSelectedEvent { chosen: m })) } else { error!("MatchSelectMiddleware could not find the correspondent match"); - Event::NOOP + Event::caused_by(event.source_id, EventType::NOOP) } } _ => { @@ -77,14 +71,14 @@ impl<'a> Middleware for MatchSelectMiddleware<'a> { .into_iter() .find(|m| m.id == selected_id); if let Some(m) = m { - Event::MatchSelected(MatchSelectedEvent { chosen: m }) + Event::caused_by(event.source_id, EventType::MatchSelected(MatchSelectedEvent { chosen: m })) } else { error!("MatchSelectMiddleware could not find the correspondent match"); - Event::NOOP + Event::caused_by(event.source_id, EventType::NOOP) } } else { debug!("MatchSelectMiddleware did not receive any match selection"); - Event::NOOP + Event::caused_by(event.source_id, EventType::NOOP) } } }; diff --git a/espanso/src/engine/process/middleware/matcher.rs b/espanso/src/engine/process/middleware/matcher.rs index 2b8a7dd..9e2932e 100644 --- a/espanso/src/engine/process/middleware/matcher.rs +++ b/espanso/src/engine/process/middleware/matcher.rs @@ -25,7 +25,7 @@ use crate::engine::{ event::{ input::{Key, Status}, internal::{DetectedMatch, MatchesDetectedEvent}, - Event, + Event, EventType, }, process::{Matcher, MatcherEvent}, }; @@ -53,7 +53,7 @@ impl<'a, State> Middleware for MatcherMiddleware<'a, State> { } fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event { - if is_event_of_interest(&event) { + if is_event_of_interest(&event.etype) { let mut matcher_states = self.matcher_states.borrow_mut(); let prev_states = if !matcher_states.is_empty() { matcher_states.get(matcher_states.len() - 1) @@ -61,7 +61,7 @@ impl<'a, State> Middleware for MatcherMiddleware<'a, State> { None }; - if let Event::Keyboard(keyboard_event) = &event { + if let EventType::Keyboard(keyboard_event) = &event.etype { // Backspace handling if keyboard_event.key == Key::Backspace { trace!("popping the last matcher state"); @@ -80,7 +80,7 @@ impl<'a, State> Middleware for MatcherMiddleware<'a, State> { let mut all_results = Vec::new(); - if let Some(matcher_event) = convert_to_matcher_event(&event) { + if let Some(matcher_event) = convert_to_matcher_event(&event.etype) { let mut new_states = Vec::new(); for (i, matcher) in self.matchers.iter().enumerate() { let prev_state = prev_states.and_then(|states| states.get(i)); @@ -97,18 +97,21 @@ impl<'a, State> Middleware for MatcherMiddleware<'a, State> { } if !all_results.is_empty() { - return Event::MatchesDetected(MatchesDetectedEvent { - matches: all_results - .into_iter() - .map(|result| DetectedMatch { - id: result.id, - trigger: Some(result.trigger), - right_separator: result.right_separator, - left_separator: result.left_separator, - args: result.args, - }) - .collect(), - }); + return Event::caused_by( + event.source_id, + EventType::MatchesDetected(MatchesDetectedEvent { + matches: all_results + .into_iter() + .map(|result| DetectedMatch { + id: result.id, + trigger: Some(result.trigger), + right_separator: result.right_separator, + left_separator: result.left_separator, + args: result.args, + }) + .collect(), + }), + ); } } } @@ -117,9 +120,9 @@ impl<'a, State> Middleware for MatcherMiddleware<'a, State> { } } -fn is_event_of_interest(event: &Event) -> bool { - match event { - Event::Keyboard(keyboard_event) => { +fn is_event_of_interest(event_type: &EventType) -> bool { + match event_type { + EventType::Keyboard(keyboard_event) => { if keyboard_event.status != Status::Pressed { // Skip non-press events false @@ -133,24 +136,24 @@ fn is_event_of_interest(event: &Event) -> bool { Key::NumLock => false, Key::Control => false, - _ => true + _ => true, } } - }, - Event::Mouse(mouse_event) => mouse_event.status == Status::Pressed, - Event::MatchInjected => true, + } + EventType::Mouse(mouse_event) => mouse_event.status == Status::Pressed, + EventType::MatchInjected => true, _ => false, } } -fn convert_to_matcher_event(event: &Event) -> Option { - match event { - Event::Keyboard(keyboard_event) => Some(MatcherEvent::Key { +fn convert_to_matcher_event(event_type: &EventType) -> Option { + match event_type { + EventType::Keyboard(keyboard_event) => Some(MatcherEvent::Key { key: keyboard_event.key.clone(), chars: keyboard_event.value.clone(), }), - Event::Mouse(_) => Some(MatcherEvent::VirtualSeparator), - Event::MatchInjected => Some(MatcherEvent::VirtualSeparator), + EventType::Mouse(_) => Some(MatcherEvent::VirtualSeparator), + EventType::MatchInjected => Some(MatcherEvent::VirtualSeparator), _ => None, } } diff --git a/espanso/src/engine/process/middleware/mod.rs b/espanso/src/engine/process/middleware/mod.rs index ff26462..c183a3e 100644 --- a/espanso/src/engine/process/middleware/mod.rs +++ b/espanso/src/engine/process/middleware/mod.rs @@ -24,4 +24,5 @@ pub mod delay_modifiers; pub mod match_select; pub mod matcher; pub mod multiplex; +pub mod past_discard; pub mod render; diff --git a/espanso/src/engine/process/middleware/multiplex.rs b/espanso/src/engine/process/middleware/multiplex.rs index 49391a4..41e4b83 100644 --- a/espanso/src/engine/process/middleware/multiplex.rs +++ b/espanso/src/engine/process/middleware/multiplex.rs @@ -17,10 +17,13 @@ * along with espanso. If not, see . */ -use log::{error}; +use log::error; use super::super::Middleware; -use crate::engine::{event::Event, process::Multiplexer}; +use crate::engine::{ + event::{Event, EventType}, + process::Multiplexer, +}; pub struct MultiplexMiddleware<'a> { multiplexer: &'a dyn Multiplexer, @@ -38,14 +41,14 @@ impl<'a> Middleware for MultiplexMiddleware<'a> { } fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event { - if let Event::CauseCompensatedMatch(m_event) = event { + if let EventType::CauseCompensatedMatch(m_event) = event.etype { return match self.multiplexer.convert(m_event.m) { - Some(event) => event, + Some(new_event) => Event::caused_by(event.source_id, new_event), None => { error!("match multiplexing failed"); - Event::NOOP - }, - } + Event::caused_by(event.source_id, EventType::NOOP) + } + }; } event diff --git a/espanso/src/engine/process/middleware/past_discard.rs b/espanso/src/engine/process/middleware/past_discard.rs new file mode 100644 index 0000000..c2ad8d7 --- /dev/null +++ b/espanso/src/engine/process/middleware/past_discard.rs @@ -0,0 +1,66 @@ +/* + * This file is part of espanso. + * + * Copyright (C) 2019-2021 Federico Terzi + * + * espanso is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +use std::cell::RefCell; + +use log::trace; + +use super::super::Middleware; +use crate::engine::{event::{Event, EventType, SourceId}}; + +/// This middleware discards all events that have a source_id smaller than its +/// configured threshold. This useful to discard past events that might have +/// been stuck in the event queue for too long. +pub struct PastEventsDiscardMiddleware { + source_id_threshold: RefCell, +} + +impl PastEventsDiscardMiddleware { + pub fn new() -> Self { + Self { + source_id_threshold: RefCell::new(0), + } + } +} + +impl Middleware for PastEventsDiscardMiddleware { + fn name(&self) -> &'static str { + "past_discard" + } + + fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event { + let mut source_id_threshold = self.source_id_threshold.borrow_mut(); + + // Filter out previous events + if event.source_id < *source_id_threshold { + trace!("discarding previous event: {:?}", event); + return Event::caused_by(event.source_id, EventType::NOOP); + } + + // Update the minimum threshold + if let EventType::DiscardPrevious(m_event) = &event.etype { + trace!("updating minimum source id threshold for events to: {}", m_event.minimum_source_id); + *source_id_threshold = m_event.minimum_source_id; + } + + event + } +} + +// TODO: test diff --git a/espanso/src/engine/process/middleware/render.rs b/espanso/src/engine/process/middleware/render.rs index 38211b7..ffbc3a1 100644 --- a/espanso/src/engine/process/middleware/render.rs +++ b/espanso/src/engine/process/middleware/render.rs @@ -22,9 +22,8 @@ use log::error; use super::super::Middleware; use crate::engine::{ event::{ - effect::{CursorHintCompensationEvent, TriggerCompensationEvent}, internal::RenderedEvent, - Event, + Event, EventType, }, process::{Renderer, RendererError}, }; @@ -45,8 +44,12 @@ impl<'a> Middleware for RenderMiddleware<'a> { } fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event { - if let Event::RenderingRequested(m_event) = event { - match self.renderer.render(m_event.match_id, m_event.trigger.as_deref(), m_event.trigger_args) { + if let EventType::RenderingRequested(m_event) = event.etype { + match self.renderer.render( + m_event.match_id, + m_event.trigger.as_deref(), + m_event.trigger_args, + ) { Ok(body) => { let body = if let Some(right_separator) = m_event.right_separator { format!("{}{}", body, right_separator) @@ -54,20 +57,21 @@ impl<'a> Middleware for RenderMiddleware<'a> { body }; - return Event::Rendered(RenderedEvent { - match_id: m_event.match_id, - body, - }); + return Event::caused_by( + event.source_id, + EventType::Rendered(RenderedEvent { + match_id: m_event.match_id, + body, + }), + ); } - Err(err) => { - match err.downcast_ref::() { - Some(RendererError::Aborted) => return Event::NOOP, - _ => { - error!("error during rendering: {}", err); - return Event::ProcessingError("An error has occurred during rendering, please examine the logs or contact support.".to_string()); - } + Err(err) => match err.downcast_ref::() { + Some(RendererError::Aborted) => return Event::caused_by(event.source_id, EventType::NOOP), + _ => { + error!("error during rendering: {}", err); + return Event::caused_by(event.source_id, EventType::ProcessingError("An error has occurred during rendering, please examine the logs or contact support.".to_string())); } - } + }, } } diff --git a/espanso/src/engine/process/mod.rs b/espanso/src/engine/process/mod.rs index e88c123..57c772d 100644 --- a/espanso/src/engine/process/mod.rs +++ b/espanso/src/engine/process/mod.rs @@ -17,7 +17,7 @@ * along with espanso. If not, see . */ -use super::{Event, event::{input::Key, internal::DetectedMatch}}; +use super::{Event, event::{EventType, input::Key, internal::DetectedMatch}}; use anyhow::Result; use std::collections::HashMap; use thiserror::Error; @@ -73,7 +73,7 @@ pub trait Multiplexer { fn convert( &self, m: DetectedMatch, - ) -> Option; + ) -> Option; } pub trait Renderer<'a> { @@ -92,7 +92,7 @@ pub enum RendererError { Aborted, } -pub use middleware::action::MatchInfoProvider; +pub use middleware::action::{MatchInfoProvider, EventSequenceProvider}; pub use middleware::delay_modifiers::ModifierStatusProvider; pub fn default<'a, MatcherState>( @@ -103,6 +103,7 @@ pub fn default<'a, MatcherState>( renderer: &'a dyn Renderer<'a>, match_info_provider: &'a dyn MatchInfoProvider, modifier_status_provider: &'a dyn ModifierStatusProvider, + event_sequence_provider: &'a dyn EventSequenceProvider, ) -> impl Processor + 'a { default::DefaultProcessor::new( matchers, @@ -112,5 +113,6 @@ pub fn default<'a, MatcherState>( renderer, match_info_provider, modifier_status_provider, + event_sequence_provider, ) }