feat(core): add source_id parameter to events and introduce past events discarding mechanism
This commit is contained in:
parent
7db39f4c74
commit
302a45aa88
|
@ -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 =
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
48
espanso/src/cli/worker/engine/source/sequencer.rs
Normal file
48
espanso/src/cli/worker/engine/source/sequencer.rs
Normal 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()
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
}
|
|
@ -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),
|
||||
|
|
|
@ -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},
|
||||
};
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
66
espanso/src/engine/process/middleware/past_discard.rs
Normal file
66
espanso/src/engine/process/middleware/past_discard.rs
Normal 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
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user