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 (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 =

View File

@ -17,11 +17,9 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
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<Event> {
fn convert(&self, detected_match: DetectedMatch) -> Option<EventType> {
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,

View File

@ -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<InputEvent>,
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
}
}

View File

@ -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)]

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/>.
*/
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);
}

View File

@ -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 {

View File

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

View File

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

View File

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

View File

@ -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() {

View File

@ -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<TextInjectMode>;
}
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,
}

View File

@ -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;
}

View File

@ -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

View File

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

View File

@ -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<i32> = 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)
}
}
};

View File

@ -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<MatcherEvent> {
match event {
Event::Keyboard(keyboard_event) => Some(MatcherEvent::Key {
fn convert_to_matcher_event(event_type: &EventType) -> Option<MatcherEvent> {
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,
}
}

View File

@ -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;

View File

@ -17,10 +17,13 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
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

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 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::<RendererError>() {
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::<RendererError>() {
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()));
}
}
},
}
}

View File

@ -17,7 +17,7 @@
* 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 std::collections::HashMap;
use thiserror::Error;
@ -73,7 +73,7 @@ pub trait Multiplexer {
fn convert(
&self,
m: DetectedMatch,
) -> Option<Event>;
) -> Option<EventType>;
}
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,
)
}