feat(core): add source_id parameter to events and introduce past events discarding mechanism

This commit is contained in:
Federico Terzi 2021-04-25 21:03:13 +02:00
parent 7db39f4c74
commit 302a45aa88
22 changed files with 347 additions and 159 deletions

View File

@ -53,7 +53,7 @@ pub fn initialize_and_spawn(
let modulo_manager = ui::modulo::ModuloManager::new(); 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"); super::engine::source::init_and_spawn().expect("failed to initialize detector module");
let sources: Vec<&dyn crate::engine::funnel::Source> = vec![&detect_source]; let sources: Vec<&dyn crate::engine::funnel::Source> = vec![&detect_source];
let funnel = crate::engine::funnel::default(&sources); let funnel = crate::engine::funnel::default(&sources);
@ -108,6 +108,7 @@ pub fn initialize_and_spawn(
&renderer_adapter, &renderer_adapter,
&match_cache, &match_cache,
&modifier_state_store, &modifier_state_store,
&sequencer,
); );
let event_injector = let event_injector =

View File

@ -17,11 +17,9 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use std::collections::HashMap;
use espanso_config::matches::{Match, MatchEffect}; 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> { pub trait MatchProvider<'a> {
fn get(&self, match_id: i32) -> Option<&'a Match>; fn get(&self, match_id: i32) -> Option<&'a Match>;
@ -38,11 +36,11 @@ impl<'a> MultiplexAdapter<'a> {
} }
impl<'a> Multiplexer for MultiplexAdapter<'a> { impl<'a> Multiplexer for MultiplexAdapter<'a> {
fn convert(&self, detected_match: DetectedMatch) -> Option<Event> { fn convert(&self, detected_match: DetectedMatch) -> Option<EventType> {
let m = self.provider.get(detected_match.id)?; let m = self.provider.get(detected_match.id)?;
match &m.effect { match &m.effect {
MatchEffect::Text(_) => Some(Event::RenderingRequested(RenderingRequestedEvent { MatchEffect::Text(_) => Some(EventType::RenderingRequested(RenderingRequestedEvent {
match_id: detected_match.id, match_id: detected_match.id,
trigger: detected_match.trigger, trigger: detected_match.trigger,
left_separator: detected_match.left_separator, left_separator: detected_match.left_separator,

View File

@ -18,18 +18,12 @@
*/ */
use crossbeam::channel::{Receiver, Select, SelectedOperation}; use crossbeam::channel::{Receiver, Select, SelectedOperation};
use espanso_detect::{event::InputEvent}; use espanso_detect::event::InputEvent;
use crate::engine::{ use crate::engine::{event::{Event, EventType, SourceId, input::{Key, KeyboardEvent, MouseButton, MouseEvent, Status, Variant}}, funnel};
event::{
input::{Key, KeyboardEvent, MouseButton, MouseEvent, Status, Variant},
Event,
},
funnel
};
pub struct DetectSource { pub struct DetectSource {
pub receiver: Receiver<InputEvent>, pub receiver: Receiver<(InputEvent, SourceId)>,
} }
impl<'a> funnel::Source<'a> for DetectSource { impl<'a> funnel::Source<'a> for DetectSource {
@ -38,20 +32,26 @@ impl<'a> funnel::Source<'a> for DetectSource {
} }
fn receive(&self, op: SelectedOperation) -> Event { fn receive(&self, op: SelectedOperation) -> Event {
let input_event = op let (input_event, source_id) = op
.recv(&self.receiver) .recv(&self.receiver)
.expect("unable to select data from DetectSource receiver"); .expect("unable to select data from DetectSource receiver");
match input_event { match input_event {
InputEvent::Keyboard(keyboard_event) => Event::Keyboard(KeyboardEvent { InputEvent::Keyboard(keyboard_event) => Event {
source_id,
etype: EventType::Keyboard(KeyboardEvent {
key: keyboard_event.key.into(), key: keyboard_event.key.into(),
value: keyboard_event.value, value: keyboard_event.value,
status: keyboard_event.status.into(), status: keyboard_event.status.into(),
variant: keyboard_event.variant.map(|variant| variant.into()), variant: keyboard_event.variant.map(|variant| variant.into()),
}), }),
InputEvent::Mouse(mouse_event) => Event::Mouse(MouseEvent { },
InputEvent::Mouse(mouse_event) => Event {
source_id,
etype: EventType::Mouse(MouseEvent {
status: mouse_event.status.into(), status: mouse_event.status.into(),
button: mouse_event.button.into(), button: mouse_event.button.into(),
}), }),
},
InputEvent::HotKey(_) => todo!(), // TODO InputEvent::HotKey(_) => todo!(), // TODO
} }
} }

View File

@ -24,19 +24,22 @@ use thiserror::Error;
use detect::DetectSource; use detect::DetectSource;
use self::modifier::{Modifier, ModifierStateStore}; use self::{modifier::{Modifier, ModifierStateStore}, sequencer::Sequencer};
pub mod detect; pub mod detect;
pub mod modifier; pub mod modifier;
pub mod sequencer;
// TODO: pass options // 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 (sender, receiver) = crossbeam::channel::unbounded();
let (init_tx, init_rx) = crossbeam::channel::unbounded(); let (init_tx, init_rx) = crossbeam::channel::unbounded();
let modifier_state_store = ModifierStateStore::new(); let modifier_state_store = ModifierStateStore::new();
let sequencer = Sequencer::new();
let state_store_clone = modifier_state_store.clone(); let state_store_clone = modifier_state_store.clone();
let sequencer_clone = sequencer.clone();
if let Err(error) = std::thread::Builder::new() if let Err(error) = std::thread::Builder::new()
.name("detect thread".to_string()) .name("detect thread".to_string())
.spawn( .spawn(
@ -58,8 +61,11 @@ pub fn init_and_spawn() -> Result<(DetectSource, ModifierStateStore)> {
state_store_clone.update_state(modifier, is_pressed); 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 sender
.send(event) .send((event, source_id))
.expect("unable to send to the source channel"); .expect("unable to send to the source channel");
})) }))
.expect("detect eventloop crashed"); .expect("detect eventloop crashed");
@ -86,7 +92,7 @@ pub fn init_and_spawn() -> Result<(DetectSource, ModifierStateStore)> {
return Err(DetectSourceError::InitFailed.into()); return Err(DetectSourceError::InitFailed.into());
} }
Ok((DetectSource { receiver }, modifier_state_store)) Ok((DetectSource { receiver }, modifier_state_store, sequencer))
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
use std::sync::{
atomic::{AtomicU32, Ordering},
Arc,
};
use crate::engine::{event::SourceId, process::EventSequenceProvider};
#[derive(Clone)]
pub struct Sequencer {
current_id: Arc<AtomicU32>,
}
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()
}
}

View File

@ -17,6 +17,8 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use crate::engine::event::EventType;
use super::super::{Event, Executor, KeyInjector}; use super::super::{Event, Executor, KeyInjector};
use log::error; use log::error;
@ -32,7 +34,7 @@ impl<'a> KeyInjectExecutor<'a> {
impl<'a> Executor for KeyInjectExecutor<'a> { impl<'a> Executor for KeyInjectExecutor<'a> {
fn execute(&self, event: &Event) -> bool { 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) { if let Err(error) = self.injector.inject_sequence(&inject_event.keys) {
error!("key injector reported an error: {}", error); error!("key injector reported an error: {}", error);
} }

View File

@ -19,7 +19,7 @@
use anyhow::Result; use anyhow::Result;
use super::super::{Event, Executor}; use super::super::{Event, Executor};
use crate::engine::event::effect::TextInjectMode; use crate::engine::event::{EventType, effect::TextInjectMode};
use log::{error, trace}; use log::{error, trace};
pub trait TextInjector { pub trait TextInjector {
@ -63,7 +63,7 @@ impl<'a> TextInjectExecutor<'a> {
impl<'a> Executor for TextInjectExecutor<'a> { impl<'a> Executor for TextInjectExecutor<'a> {
fn execute(&self, event: &Event) -> bool { 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 active_mode = self.mode_provider.active_mode();
let injector = if let Some(force_mode) = &inject_event.force_mode { let injector = if let Some(force_mode) = &inject_event.force_mode {

View File

@ -57,3 +57,9 @@ pub struct RenderedEvent {
pub match_id: i32, pub match_id: i32,
pub body: String, 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,
}

View File

@ -21,8 +21,30 @@ pub mod input;
pub mod effect; pub mod effect;
pub mod internal; pub mod internal;
pub type SourceId = u32;
#[derive(Debug, Clone)] #[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, NOOP,
ProcessingError(String), ProcessingError(String),
@ -39,6 +61,7 @@ pub enum Event {
RenderingRequested(internal::RenderingRequestedEvent), RenderingRequested(internal::RenderingRequestedEvent),
Rendered(internal::RenderedEvent), Rendered(internal::RenderedEvent),
MatchInjected, MatchInjected,
DiscardPrevious(internal::DiscardPreviousEvent),
// Effects // Effects
TriggerCompensation(effect::TriggerCompensationEvent), TriggerCompensation(effect::TriggerCompensationEvent),

View File

@ -17,11 +17,11 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use log::{debug, trace}; use log::{debug};
use self::{ use self::{
dispatch::Dispatcher, dispatch::Dispatcher,
event::Event, event::{Event},
process::Processor, process::Processor,
funnel::{Funnel, FunnelResult}, funnel::{Funnel, FunnelResult},
}; };

View File

@ -19,11 +19,13 @@
use log::trace; 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, 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}, delay_modifiers::{DelayForModifierReleaseMiddleware, ModifierStatusProvider},
past_discard::PastEventsDiscardMiddleware,
}}; }};
use crate::engine::event::{Event, EventType};
use std::collections::VecDeque; use std::collections::VecDeque;
pub struct DefaultProcessor<'a> { pub struct DefaultProcessor<'a> {
@ -40,17 +42,19 @@ impl<'a> DefaultProcessor<'a> {
renderer: &'a dyn Renderer<'a>, renderer: &'a dyn Renderer<'a>,
match_info_provider: &'a dyn MatchInfoProvider, match_info_provider: &'a dyn MatchInfoProvider,
modifier_status_provider: &'a dyn ModifierStatusProvider, modifier_status_provider: &'a dyn ModifierStatusProvider,
event_sequence_provider: &'a dyn EventSequenceProvider,
) -> DefaultProcessor<'a> { ) -> DefaultProcessor<'a> {
Self { Self {
event_queue: VecDeque::new(), event_queue: VecDeque::new(),
middleware: vec![ middleware: vec![
Box::new(PastEventsDiscardMiddleware::new()),
Box::new(MatcherMiddleware::new(matchers)), Box::new(MatcherMiddleware::new(matchers)),
Box::new(MatchSelectMiddleware::new(match_filter, match_selector)), Box::new(MatchSelectMiddleware::new(match_filter, match_selector)),
Box::new(CauseCompensateMiddleware::new()), Box::new(CauseCompensateMiddleware::new()),
Box::new(MultiplexMiddleware::new(multiplexer)), Box::new(MultiplexMiddleware::new(multiplexer)),
Box::new(RenderMiddleware::new(renderer)), Box::new(RenderMiddleware::new(renderer)),
Box::new(CursorHintMiddleware::new()), 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)), Box::new(DelayForModifierReleaseMiddleware::new(modifier_status_provider)),
], ],
} }
@ -73,6 +77,11 @@ impl<'a> DefaultProcessor<'a> {
current_event = middleware.next(current_event, &mut dispatch); current_event = middleware.next(current_event, &mut dispatch);
trace!("middleware '{}' produced event: {:?}", middleware.name(), current_event); 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() { while let Some(event) = current_queue.pop_back() {

View File

@ -18,26 +18,34 @@
*/ */
use super::super::Middleware; use super::super::Middleware;
use crate::engine::{ use crate::engine::event::{
event::{ effect::{KeySequenceInjectRequest, TextInjectMode, TextInjectRequest},
input::{Key}, input::Key,
effect::{TextInjectMode, TextInjectRequest, KeySequenceInjectRequest}, internal::DiscardPreviousEvent,
Event, Event, EventType,
},
}; };
pub trait MatchInfoProvider { pub trait MatchInfoProvider {
fn get_force_mode(&self, match_id: i32) -> Option<TextInjectMode>; fn get_force_mode(&self, match_id: i32) -> Option<TextInjectMode>;
} }
pub trait EventSequenceProvider {
fn get_next_id(&self) -> u32;
}
pub struct ActionMiddleware<'a> { pub struct ActionMiddleware<'a> {
match_info_provider: &'a dyn MatchInfoProvider, match_info_provider: &'a dyn MatchInfoProvider,
event_sequence_provider: &'a dyn EventSequenceProvider,
} }
impl<'a> ActionMiddleware<'a> { 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 { Self {
match_info_provider, 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 { fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
match &event { match &event.etype {
Event::Rendered(m_event) => { EventType::Rendered(m_event) => {
dispatch(Event::MatchInjected); 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 { Event::caused_by(
event.source_id,
EventType::TextInject(TextInjectRequest {
text: m_event.body.clone(), text: m_event.body.clone(),
force_mode: self.match_info_provider.get_force_mode(m_event.match_id), force_mode: self.match_info_provider.get_force_mode(m_event.match_id),
}) }),
)
} }
Event::CursorHintCompensation(m_event) => { EventType::CursorHintCompensation(m_event) => {
Event::KeySequenceInject(KeySequenceInjectRequest { 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) keys: (0..m_event.cursor_hint_back_count)
.map(|_| Key::ArrowLeft) .map(|_| Key::ArrowLeft)
.collect(), .collect(),
}) }),
)
} }
Event::TriggerCompensation(m_event) => { EventType::TriggerCompensation(m_event) => {
let mut backspace_count = m_event.trigger.chars().count(); let mut backspace_count = m_event.trigger.chars().count();
// We want to preserve the left separator if present // 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(); backspace_count -= left_separator.chars().count();
} }
Event::KeySequenceInject(KeySequenceInjectRequest { Event::caused_by(
event.source_id,
EventType::KeySequenceInject(KeySequenceInjectRequest {
keys: (0..backspace_count).map(|_| Key::Backspace).collect(), keys: (0..backspace_count).map(|_| Key::Backspace).collect(),
}) }),
)
} }
_ => event, _ => event,
} }

View File

@ -18,13 +18,7 @@
*/ */
use super::super::Middleware; use super::super::Middleware;
use crate::engine::{ use crate::engine::{event::{Event, EventType, effect::TriggerCompensationEvent, internal::CauseCompensatedMatchEvent}};
event::{
effect::TriggerCompensationEvent,
internal::CauseCompensatedMatchEvent,
Event,
},
};
pub struct CauseCompensateMiddleware {} pub struct CauseCompensateMiddleware {}
@ -36,22 +30,22 @@ impl CauseCompensateMiddleware {
impl Middleware for CauseCompensateMiddleware { impl Middleware for CauseCompensateMiddleware {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"cause_compensate" "discard"
} }
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event { 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 = 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 { if let Some(trigger) = &m_event.chosen.trigger {
dispatch(compensated_event); dispatch(compensated_event);
// Before the event, place a trigger compensation // Before the event, place a trigger compensation
return Event::TriggerCompensation(TriggerCompensationEvent { return Event::caused_by(event.source_id, EventType::TriggerCompensation(TriggerCompensationEvent {
trigger: trigger.clone(), trigger: trigger.clone(),
left_separator: m_event.chosen.left_separator.clone(), left_separator: m_event.chosen.left_separator.clone(),
}); }));
} else { } else {
return compensated_event; return compensated_event;
} }

View File

@ -18,7 +18,7 @@
*/ */
use super::super::Middleware; 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 {} pub struct CursorHintMiddleware {}
@ -34,20 +34,20 @@ impl Middleware for CursorHintMiddleware {
} }
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event { 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); let (body, cursor_hint_back_count) = process_cursor_hint(m_event.body);
if let Some(cursor_hint_back_count) = cursor_hint_back_count { 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, cursor_hint_back_count,
})) })));
} }
// Alter the rendered event to remove the cursor hint from the body // 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, body,
..m_event ..m_event
}) }));
} }
event event

View File

@ -26,9 +26,7 @@ use std::{
use log::{trace, warn}; use log::{trace, warn};
use super::super::Middleware; use super::super::Middleware;
use crate::engine::event::{ use crate::engine::event::{Event, EventType};
Event,
};
// TODO: pass through config // TODO: pass through config
const MODIFIER_DELAY_TIMEOUT: Duration = Duration::from_secs(3); 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 { 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(); let start = Instant::now();
while self.provider.is_any_modifier_pressed() { while self.provider.is_any_modifier_pressed() {
if Instant::now().duration_since(start) > MODIFIER_DELAY_TIMEOUT { 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 { fn is_injection_event(event_type: &EventType) -> bool {
match event { match event_type {
Event::TriggerCompensation(_) => true, EventType::TriggerCompensation(_) => true,
Event::CursorHintCompensation(_) => true, EventType::CursorHintCompensation(_) => true,
Event::KeySequenceInject(_) => true, EventType::KeySequenceInject(_) => true,
Event::TextInject(_) => true, EventType::TextInject(_) => true,
_ => false, _ => false,
} }
} }

View File

@ -20,13 +20,7 @@
use log::{debug, error}; use log::{debug, error};
use super::super::Middleware; use super::super::Middleware;
use crate::engine::{ use crate::engine::{event::{Event, EventType, internal::{MatchSelectedEvent}}, process::{MatchFilter, MatchSelector}};
event::{
internal::{MatchSelectedEvent},
Event,
},
process::{MatchFilter, MatchSelector},
};
pub struct MatchSelectMiddleware<'a> { pub struct MatchSelectMiddleware<'a> {
match_filter: &'a dyn MatchFilter, 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 { 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<i32> = m_event.matches.iter().map(|m| m.id).collect(); let matches_ids: Vec<i32> = m_event.matches.iter().map(|m| m.id).collect();
// Find the matches that are actually valid in the current context // Find the matches that are actually valid in the current context
let valid_ids = self.match_filter.filter_active(&matches_ids); let valid_ids = self.match_filter.filter_active(&matches_ids);
return match valid_ids.len() { 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 => { 1 => {
// Only one match, no need to show a selection dialog // Only one match, no need to show a selection dialog
let m = m_event let m = m_event
@ -63,10 +57,10 @@ impl<'a> Middleware for MatchSelectMiddleware<'a> {
.into_iter() .into_iter()
.find(|m| m.id == *valid_ids.first().unwrap()); .find(|m| m.id == *valid_ids.first().unwrap());
if let Some(m) = m { if let Some(m) = m {
Event::MatchSelected(MatchSelectedEvent { chosen: m }) Event::caused_by(event.source_id, EventType::MatchSelected(MatchSelectedEvent { chosen: m }))
} else { } else {
error!("MatchSelectMiddleware could not find the correspondent match"); 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() .into_iter()
.find(|m| m.id == selected_id); .find(|m| m.id == selected_id);
if let Some(m) = m { if let Some(m) = m {
Event::MatchSelected(MatchSelectedEvent { chosen: m }) Event::caused_by(event.source_id, EventType::MatchSelected(MatchSelectedEvent { chosen: m }))
} else { } else {
error!("MatchSelectMiddleware could not find the correspondent match"); error!("MatchSelectMiddleware could not find the correspondent match");
Event::NOOP Event::caused_by(event.source_id, EventType::NOOP)
} }
} else { } else {
debug!("MatchSelectMiddleware did not receive any match selection"); debug!("MatchSelectMiddleware did not receive any match selection");
Event::NOOP Event::caused_by(event.source_id, EventType::NOOP)
} }
} }
}; };

View File

@ -25,7 +25,7 @@ use crate::engine::{
event::{ event::{
input::{Key, Status}, input::{Key, Status},
internal::{DetectedMatch, MatchesDetectedEvent}, internal::{DetectedMatch, MatchesDetectedEvent},
Event, Event, EventType,
}, },
process::{Matcher, MatcherEvent}, 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 { 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 mut matcher_states = self.matcher_states.borrow_mut();
let prev_states = if !matcher_states.is_empty() { let prev_states = if !matcher_states.is_empty() {
matcher_states.get(matcher_states.len() - 1) matcher_states.get(matcher_states.len() - 1)
@ -61,7 +61,7 @@ impl<'a, State> Middleware for MatcherMiddleware<'a, State> {
None None
}; };
if let Event::Keyboard(keyboard_event) = &event { if let EventType::Keyboard(keyboard_event) = &event.etype {
// Backspace handling // Backspace handling
if keyboard_event.key == Key::Backspace { if keyboard_event.key == Key::Backspace {
trace!("popping the last matcher state"); trace!("popping the last matcher state");
@ -80,7 +80,7 @@ impl<'a, State> Middleware for MatcherMiddleware<'a, State> {
let mut all_results = Vec::new(); 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(); let mut new_states = Vec::new();
for (i, matcher) in self.matchers.iter().enumerate() { for (i, matcher) in self.matchers.iter().enumerate() {
let prev_state = prev_states.and_then(|states| states.get(i)); let prev_state = prev_states.and_then(|states| states.get(i));
@ -97,7 +97,9 @@ impl<'a, State> Middleware for MatcherMiddleware<'a, State> {
} }
if !all_results.is_empty() { if !all_results.is_empty() {
return Event::MatchesDetected(MatchesDetectedEvent { return Event::caused_by(
event.source_id,
EventType::MatchesDetected(MatchesDetectedEvent {
matches: all_results matches: all_results
.into_iter() .into_iter()
.map(|result| DetectedMatch { .map(|result| DetectedMatch {
@ -108,7 +110,8 @@ impl<'a, State> Middleware for MatcherMiddleware<'a, State> {
args: result.args, args: result.args,
}) })
.collect(), .collect(),
}); }),
);
} }
} }
} }
@ -117,9 +120,9 @@ impl<'a, State> Middleware for MatcherMiddleware<'a, State> {
} }
} }
fn is_event_of_interest(event: &Event) -> bool { fn is_event_of_interest(event_type: &EventType) -> bool {
match event { match event_type {
Event::Keyboard(keyboard_event) => { EventType::Keyboard(keyboard_event) => {
if keyboard_event.status != Status::Pressed { if keyboard_event.status != Status::Pressed {
// Skip non-press events // Skip non-press events
false false
@ -133,24 +136,24 @@ fn is_event_of_interest(event: &Event) -> bool {
Key::NumLock => false, Key::NumLock => false,
Key::Control => false, Key::Control => false,
_ => true _ => true,
} }
} }
}, }
Event::Mouse(mouse_event) => mouse_event.status == Status::Pressed, EventType::Mouse(mouse_event) => mouse_event.status == Status::Pressed,
Event::MatchInjected => true, EventType::MatchInjected => true,
_ => false, _ => false,
} }
} }
fn convert_to_matcher_event(event: &Event) -> Option<MatcherEvent> { fn convert_to_matcher_event(event_type: &EventType) -> Option<MatcherEvent> {
match event { match event_type {
Event::Keyboard(keyboard_event) => Some(MatcherEvent::Key { EventType::Keyboard(keyboard_event) => Some(MatcherEvent::Key {
key: keyboard_event.key.clone(), key: keyboard_event.key.clone(),
chars: keyboard_event.value.clone(), chars: keyboard_event.value.clone(),
}), }),
Event::Mouse(_) => Some(MatcherEvent::VirtualSeparator), EventType::Mouse(_) => Some(MatcherEvent::VirtualSeparator),
Event::MatchInjected => Some(MatcherEvent::VirtualSeparator), EventType::MatchInjected => Some(MatcherEvent::VirtualSeparator),
_ => None, _ => None,
} }
} }

View File

@ -24,4 +24,5 @@ pub mod delay_modifiers;
pub mod match_select; pub mod match_select;
pub mod matcher; pub mod matcher;
pub mod multiplex; pub mod multiplex;
pub mod past_discard;
pub mod render; pub mod render;

View File

@ -17,10 +17,13 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use log::{error}; use log::error;
use super::super::Middleware; use super::super::Middleware;
use crate::engine::{event::Event, process::Multiplexer}; use crate::engine::{
event::{Event, EventType},
process::Multiplexer,
};
pub struct MultiplexMiddleware<'a> { pub struct MultiplexMiddleware<'a> {
multiplexer: &'a dyn Multiplexer, multiplexer: &'a dyn Multiplexer,
@ -38,14 +41,14 @@ impl<'a> Middleware for MultiplexMiddleware<'a> {
} }
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event { 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) { return match self.multiplexer.convert(m_event.m) {
Some(event) => event, Some(new_event) => Event::caused_by(event.source_id, new_event),
None => { None => {
error!("match multiplexing failed"); error!("match multiplexing failed");
Event::NOOP Event::caused_by(event.source_id, EventType::NOOP)
},
} }
};
} }
event event

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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<SourceId>,
}
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

View File

@ -22,9 +22,8 @@ use log::error;
use super::super::Middleware; use super::super::Middleware;
use crate::engine::{ use crate::engine::{
event::{ event::{
effect::{CursorHintCompensationEvent, TriggerCompensationEvent},
internal::RenderedEvent, internal::RenderedEvent,
Event, Event, EventType,
}, },
process::{Renderer, RendererError}, process::{Renderer, RendererError},
}; };
@ -45,8 +44,12 @@ impl<'a> Middleware for RenderMiddleware<'a> {
} }
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event { fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
if let Event::RenderingRequested(m_event) = event { 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) { match self.renderer.render(
m_event.match_id,
m_event.trigger.as_deref(),
m_event.trigger_args,
) {
Ok(body) => { Ok(body) => {
let body = if let Some(right_separator) = m_event.right_separator { let body = if let Some(right_separator) = m_event.right_separator {
format!("{}{}", body, right_separator) format!("{}{}", body, right_separator)
@ -54,20 +57,21 @@ impl<'a> Middleware for RenderMiddleware<'a> {
body body
}; };
return Event::Rendered(RenderedEvent { return Event::caused_by(
event.source_id,
EventType::Rendered(RenderedEvent {
match_id: m_event.match_id, match_id: m_event.match_id,
body, body,
}); }),
);
} }
Err(err) => { Err(err) => match err.downcast_ref::<RendererError>() {
match err.downcast_ref::<RendererError>() { Some(RendererError::Aborted) => return Event::caused_by(event.source_id, EventType::NOOP),
Some(RendererError::Aborted) => return Event::NOOP,
_ => { _ => {
error!("error during rendering: {}", err); error!("error during rendering: {}", err);
return Event::ProcessingError("An error has occurred during rendering, please examine the logs or contact support.".to_string()); return Event::caused_by(event.source_id, EventType::ProcessingError("An error has occurred during rendering, please examine the logs or contact support.".to_string()));
}
}
} }
},
} }
} }

View File

@ -17,7 +17,7 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use super::{Event, event::{input::Key, internal::DetectedMatch}}; use super::{Event, event::{EventType, input::Key, internal::DetectedMatch}};
use anyhow::Result; use anyhow::Result;
use std::collections::HashMap; use std::collections::HashMap;
use thiserror::Error; use thiserror::Error;
@ -73,7 +73,7 @@ pub trait Multiplexer {
fn convert( fn convert(
&self, &self,
m: DetectedMatch, m: DetectedMatch,
) -> Option<Event>; ) -> Option<EventType>;
} }
pub trait Renderer<'a> { pub trait Renderer<'a> {
@ -92,7 +92,7 @@ pub enum RendererError {
Aborted, Aborted,
} }
pub use middleware::action::MatchInfoProvider; pub use middleware::action::{MatchInfoProvider, EventSequenceProvider};
pub use middleware::delay_modifiers::ModifierStatusProvider; pub use middleware::delay_modifiers::ModifierStatusProvider;
pub fn default<'a, MatcherState>( pub fn default<'a, MatcherState>(
@ -103,6 +103,7 @@ pub fn default<'a, MatcherState>(
renderer: &'a dyn Renderer<'a>, renderer: &'a dyn Renderer<'a>,
match_info_provider: &'a dyn MatchInfoProvider, match_info_provider: &'a dyn MatchInfoProvider,
modifier_status_provider: &'a dyn ModifierStatusProvider, modifier_status_provider: &'a dyn ModifierStatusProvider,
event_sequence_provider: &'a dyn EventSequenceProvider,
) -> impl Processor + 'a { ) -> impl Processor + 'a {
default::DefaultProcessor::new( default::DefaultProcessor::new(
matchers, matchers,
@ -112,5 +113,6 @@ pub fn default<'a, MatcherState>(
renderer, renderer,
match_info_provider, match_info_provider,
modifier_status_provider, modifier_status_provider,
event_sequence_provider,
) )
} }