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,
)
}