feat(core): implement delay-for-modifier-release mechanism
This commit is contained in:
parent
3dfde8e830
commit
7db39f4c74
|
@ -53,8 +53,8 @@ pub fn initialize_and_spawn(
|
||||||
|
|
||||||
let modulo_manager = ui::modulo::ModuloManager::new();
|
let modulo_manager = ui::modulo::ModuloManager::new();
|
||||||
|
|
||||||
let detect_source = super::engine::source::detect::init_and_spawn()
|
let (detect_source, modifier_state_store) =
|
||||||
.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);
|
||||||
|
|
||||||
|
@ -85,7 +85,8 @@ pub fn initialize_and_spawn(
|
||||||
&paths.packages,
|
&paths.packages,
|
||||||
);
|
);
|
||||||
let shell_extension = espanso_render::extension::shell::ShellExtension::new(&paths.config);
|
let shell_extension = espanso_render::extension::shell::ShellExtension::new(&paths.config);
|
||||||
let form_adapter = ui::modulo::form::ModuloFormProviderAdapter::new(&modulo_manager, icon_paths.form_icon);
|
let form_adapter =
|
||||||
|
ui::modulo::form::ModuloFormProviderAdapter::new(&modulo_manager, icon_paths.form_icon);
|
||||||
let form_extension = espanso_render::extension::form::FormExtension::new(&form_adapter);
|
let form_extension = espanso_render::extension::form::FormExtension::new(&form_adapter);
|
||||||
let renderer = espanso_render::create(vec![
|
let renderer = espanso_render::create(vec![
|
||||||
&clipboard_extension,
|
&clipboard_extension,
|
||||||
|
@ -106,6 +107,7 @@ pub fn initialize_and_spawn(
|
||||||
&multiplexer,
|
&multiplexer,
|
||||||
&renderer_adapter,
|
&renderer_adapter,
|
||||||
&match_cache,
|
&match_cache,
|
||||||
|
&modifier_state_store,
|
||||||
);
|
);
|
||||||
|
|
||||||
let event_injector =
|
let event_injector =
|
||||||
|
|
|
@ -17,22 +17,19 @@
|
||||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use crossbeam::channel::{Receiver, Select, SelectedOperation};
|
use crossbeam::channel::{Receiver, Select, SelectedOperation};
|
||||||
use espanso_detect::{event::InputEvent, Source};
|
use espanso_detect::{event::InputEvent};
|
||||||
use log::{error, trace};
|
|
||||||
|
|
||||||
use crate::engine::{
|
use crate::engine::{
|
||||||
event::{
|
event::{
|
||||||
input::{Key, KeyboardEvent, MouseButton, MouseEvent, Status, Variant},
|
input::{Key, KeyboardEvent, MouseButton, MouseEvent, Status, Variant},
|
||||||
Event,
|
Event,
|
||||||
},
|
},
|
||||||
funnel, process,
|
funnel
|
||||||
};
|
};
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
pub struct DetectSource {
|
pub struct DetectSource {
|
||||||
receiver: Receiver<InputEvent>,
|
pub receiver: Receiver<InputEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> funnel::Source<'a> for DetectSource {
|
impl<'a> funnel::Source<'a> for DetectSource {
|
||||||
|
@ -60,67 +57,6 @@ impl<'a> funnel::Source<'a> for DetectSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: pass options
|
|
||||||
pub fn init_and_spawn() -> Result<DetectSource> {
|
|
||||||
let (sender, receiver) = crossbeam::channel::unbounded();
|
|
||||||
let (init_tx, init_rx) = crossbeam::channel::unbounded();
|
|
||||||
|
|
||||||
if let Err(error) = std::thread::Builder::new()
|
|
||||||
.name("detect thread".to_string())
|
|
||||||
.spawn(
|
|
||||||
move || match espanso_detect::get_source(Default::default()) {
|
|
||||||
Ok(mut source) => {
|
|
||||||
if source.initialize().is_err() {
|
|
||||||
init_tx
|
|
||||||
.send(false)
|
|
||||||
.expect("unable to send to the init_tx channel");
|
|
||||||
} else {
|
|
||||||
init_tx
|
|
||||||
.send(true)
|
|
||||||
.expect("unable to send to the init_tx channel");
|
|
||||||
|
|
||||||
source
|
|
||||||
.eventloop(Box::new(move |event| {
|
|
||||||
sender
|
|
||||||
.send(event)
|
|
||||||
.expect("unable to send to the source channel");
|
|
||||||
}))
|
|
||||||
.expect("detect eventloop crashed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
error!("cannot initialize event source: {:?}", error);
|
|
||||||
init_tx
|
|
||||||
.send(false)
|
|
||||||
.expect("unable to send to the init_tx channel");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
{
|
|
||||||
error!("detection thread initialization failed: {:?}", error);
|
|
||||||
return Err(DetectSourceError::ThreadInitFailed.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for the initialization status
|
|
||||||
let has_initialized = init_rx
|
|
||||||
.recv()
|
|
||||||
.expect("unable to receive from the init_rx channel");
|
|
||||||
if !has_initialized {
|
|
||||||
return Err(DetectSourceError::InitFailed.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(DetectSource { receiver })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum DetectSourceError {
|
|
||||||
#[error("detection thread initialization failed")]
|
|
||||||
ThreadInitFailed,
|
|
||||||
|
|
||||||
#[error("detection source initialization failed")]
|
|
||||||
InitFailed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<espanso_detect::event::Key> for Key {
|
impl From<espanso_detect::event::Key> for Key {
|
||||||
fn from(key: espanso_detect::event::Key) -> Self {
|
fn from(key: espanso_detect::event::Key) -> Self {
|
||||||
match key {
|
match key {
|
||||||
|
|
|
@ -17,4 +17,105 @@
|
||||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use espanso_detect::event::{InputEvent, KeyboardEvent, Status};
|
||||||
|
use log::{error};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use detect::DetectSource;
|
||||||
|
|
||||||
|
use self::modifier::{Modifier, ModifierStateStore};
|
||||||
|
|
||||||
pub mod detect;
|
pub mod detect;
|
||||||
|
pub mod modifier;
|
||||||
|
|
||||||
|
// TODO: pass options
|
||||||
|
pub fn init_and_spawn() -> Result<(DetectSource, ModifierStateStore)> {
|
||||||
|
let (sender, receiver) = crossbeam::channel::unbounded();
|
||||||
|
let (init_tx, init_rx) = crossbeam::channel::unbounded();
|
||||||
|
|
||||||
|
let modifier_state_store = ModifierStateStore::new();
|
||||||
|
|
||||||
|
let state_store_clone = modifier_state_store.clone();
|
||||||
|
if let Err(error) = std::thread::Builder::new()
|
||||||
|
.name("detect thread".to_string())
|
||||||
|
.spawn(
|
||||||
|
move || match espanso_detect::get_source(Default::default()) {
|
||||||
|
Ok(mut source) => {
|
||||||
|
if source.initialize().is_err() {
|
||||||
|
init_tx
|
||||||
|
.send(false)
|
||||||
|
.expect("unable to send to the init_tx channel");
|
||||||
|
} else {
|
||||||
|
init_tx
|
||||||
|
.send(true)
|
||||||
|
.expect("unable to send to the init_tx channel");
|
||||||
|
|
||||||
|
source
|
||||||
|
.eventloop(Box::new(move |event| {
|
||||||
|
// Update the modifiers state
|
||||||
|
if let Some((modifier, is_pressed)) = get_modifier_status(&event) {
|
||||||
|
state_store_clone.update_state(modifier, is_pressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
sender
|
||||||
|
.send(event)
|
||||||
|
.expect("unable to send to the source channel");
|
||||||
|
}))
|
||||||
|
.expect("detect eventloop crashed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
error!("cannot initialize event source: {:?}", error);
|
||||||
|
init_tx
|
||||||
|
.send(false)
|
||||||
|
.expect("unable to send to the init_tx channel");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
{
|
||||||
|
error!("detection thread initialization failed: {:?}", error);
|
||||||
|
return Err(DetectSourceError::ThreadInitFailed.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the initialization status
|
||||||
|
let has_initialized = init_rx
|
||||||
|
.recv()
|
||||||
|
.expect("unable to receive from the init_rx channel");
|
||||||
|
if !has_initialized {
|
||||||
|
return Err(DetectSourceError::InitFailed.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((DetectSource { receiver }, modifier_state_store))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum DetectSourceError {
|
||||||
|
#[error("detection thread initialization failed")]
|
||||||
|
ThreadInitFailed,
|
||||||
|
|
||||||
|
#[error("detection source initialization failed")]
|
||||||
|
InitFailed,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_modifier_status(event: &InputEvent) -> Option<(Modifier, bool)> {
|
||||||
|
match event {
|
||||||
|
InputEvent::Keyboard(KeyboardEvent {
|
||||||
|
key,
|
||||||
|
status,
|
||||||
|
value: _,
|
||||||
|
variant: _,
|
||||||
|
code: _,
|
||||||
|
}) => {
|
||||||
|
let is_pressed = *status == Status::Pressed;
|
||||||
|
match key {
|
||||||
|
espanso_detect::event::Key::Alt => Some((Modifier::Alt, is_pressed)),
|
||||||
|
espanso_detect::event::Key::Control => Some((Modifier::Ctrl, is_pressed)),
|
||||||
|
espanso_detect::event::Key::Meta => Some((Modifier::Meta, is_pressed)),
|
||||||
|
espanso_detect::event::Key::Shift => Some((Modifier::Shift, is_pressed)),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
134
espanso/src/cli/worker/engine/source/modifier.rs
Normal file
134
espanso/src/cli/worker/engine/source/modifier.rs
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
* 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::{Arc, Mutex},
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
|
use log::warn;
|
||||||
|
|
||||||
|
use crate::engine::process::ModifierStatusProvider;
|
||||||
|
|
||||||
|
// TODO: explain
|
||||||
|
const MAXIMUM_MODIFIERS_PRESS_TIME_RECORD: Duration = Duration::from_secs(30);
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||||
|
pub enum Modifier {
|
||||||
|
Ctrl,
|
||||||
|
Shift,
|
||||||
|
Alt,
|
||||||
|
Meta,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ModifierStateStore {
|
||||||
|
state: Arc<Mutex<ModifiersState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModifierStateStore {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
state: Arc::new(Mutex::new(ModifiersState::default())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_any_modifier_pressed(&self) -> bool {
|
||||||
|
let mut state = self.state.lock().expect("unable to obtain modifier state");
|
||||||
|
let mut is_any_modifier_pressed = false;
|
||||||
|
for (modifier, status) in &mut state.modifiers {
|
||||||
|
if status.is_outdated() {
|
||||||
|
warn!(
|
||||||
|
"detected outdated modifier records for {:?}, releasing the state",
|
||||||
|
modifier
|
||||||
|
);
|
||||||
|
status.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
if status.is_pressed() {
|
||||||
|
is_any_modifier_pressed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is_any_modifier_pressed
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_state(&self, modifier: Modifier, is_pressed: bool) {
|
||||||
|
let mut state = self.state.lock().expect("unable to obtain modifier state");
|
||||||
|
for (curr_modifier, status) in &mut state.modifiers {
|
||||||
|
if curr_modifier == &modifier {
|
||||||
|
if is_pressed {
|
||||||
|
status.press();
|
||||||
|
} else {
|
||||||
|
status.release();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ModifiersState {
|
||||||
|
modifiers: Vec<(Modifier, ModifierStatus)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ModifiersState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
modifiers: vec![
|
||||||
|
(Modifier::Ctrl, ModifierStatus { pressed_at: None }),
|
||||||
|
(Modifier::Alt, ModifierStatus { pressed_at: None }),
|
||||||
|
(Modifier::Shift, ModifierStatus { pressed_at: None }),
|
||||||
|
(Modifier::Meta, ModifierStatus { pressed_at: None }),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ModifierStatus {
|
||||||
|
pressed_at: Option<Instant>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModifierStatus {
|
||||||
|
fn is_pressed(&self) -> bool {
|
||||||
|
self.pressed_at.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_outdated(&self) -> bool {
|
||||||
|
let now = Instant::now();
|
||||||
|
if let Some(pressed_at) = self.pressed_at {
|
||||||
|
now.duration_since(pressed_at) > MAXIMUM_MODIFIERS_PRESS_TIME_RECORD
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn release(&mut self) {
|
||||||
|
self.pressed_at = None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn press(&mut self) {
|
||||||
|
self.pressed_at = Some(Instant::now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModifierStatusProvider for ModifierStateStore {
|
||||||
|
fn is_any_modifier_pressed(&self) -> bool {
|
||||||
|
self.is_any_modifier_pressed()
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ use log::trace;
|
||||||
use super::{Event, MatchFilter, MatchInfoProvider, MatchSelector, Matcher, Middleware, Multiplexer, Processor, Renderer, middleware::{
|
use super::{Event, 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, cursor_hint::CursorHintMiddleware, cause::CauseCompensateMiddleware,
|
||||||
|
delay_modifiers::{DelayForModifierReleaseMiddleware, ModifierStatusProvider},
|
||||||
}};
|
}};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ impl<'a> DefaultProcessor<'a> {
|
||||||
multiplexer: &'a dyn Multiplexer,
|
multiplexer: &'a dyn Multiplexer,
|
||||||
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,
|
||||||
) -> DefaultProcessor<'a> {
|
) -> DefaultProcessor<'a> {
|
||||||
Self {
|
Self {
|
||||||
event_queue: VecDeque::new(),
|
event_queue: VecDeque::new(),
|
||||||
|
@ -49,6 +51,7 @@ impl<'a> DefaultProcessor<'a> {
|
||||||
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)),
|
||||||
|
Box::new(DelayForModifierReleaseMiddleware::new(modifier_status_provider)),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +66,7 @@ impl<'a> DefaultProcessor<'a> {
|
||||||
current_queue.push_front(event);
|
current_queue.push_front(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
trace!("--------------- new event -----------------");
|
||||||
for middleware in self.middleware.iter() {
|
for middleware in self.middleware.iter() {
|
||||||
trace!("middleware '{}' received event: {:?}", middleware.name(), current_event);
|
trace!("middleware '{}' received event: {:?}", middleware.name(), current_event);
|
||||||
|
|
||||||
|
|
84
espanso/src/engine/process/middleware/delay_modifiers.rs
Normal file
84
espanso/src/engine/process/middleware/delay_modifiers.rs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: explain why this is needed
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
|
use log::{trace, warn};
|
||||||
|
|
||||||
|
use super::super::Middleware;
|
||||||
|
use crate::engine::event::{
|
||||||
|
Event,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: pass through config
|
||||||
|
const MODIFIER_DELAY_TIMEOUT: Duration = Duration::from_secs(3);
|
||||||
|
|
||||||
|
pub trait ModifierStatusProvider {
|
||||||
|
fn is_any_modifier_pressed(&self) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DelayForModifierReleaseMiddleware<'a> {
|
||||||
|
provider: &'a dyn ModifierStatusProvider,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'a> DelayForModifierReleaseMiddleware<'a> {
|
||||||
|
pub fn new(provider: &'a dyn ModifierStatusProvider) -> Self {
|
||||||
|
Self {
|
||||||
|
provider
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'a> Middleware for DelayForModifierReleaseMiddleware<'a> {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"delay_modifiers"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
||||||
|
if is_injection_event(&event) {
|
||||||
|
let start = Instant::now();
|
||||||
|
while self.provider.is_any_modifier_pressed() {
|
||||||
|
if Instant::now().duration_since(start) > MODIFIER_DELAY_TIMEOUT {
|
||||||
|
warn!("injection delay has timed out, please release the modifier keys (SHIFT, CTRL, ALT, CMD) to trigger an expansion");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
trace!("delaying injection event as some modifiers are pressed");
|
||||||
|
std::thread::sleep(Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_injection_event(event: &Event) -> bool {
|
||||||
|
match event {
|
||||||
|
Event::TriggerCompensation(_) => true,
|
||||||
|
Event::CursorHintCompensation(_) => true,
|
||||||
|
Event::KeySequenceInject(_) => true,
|
||||||
|
Event::TextInject(_) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test
|
|
@ -20,6 +20,7 @@
|
||||||
pub mod action;
|
pub mod action;
|
||||||
pub mod cause;
|
pub mod cause;
|
||||||
pub mod cursor_hint;
|
pub mod cursor_hint;
|
||||||
|
pub mod delay_modifiers;
|
||||||
pub mod match_select;
|
pub mod match_select;
|
||||||
pub mod matcher;
|
pub mod matcher;
|
||||||
pub mod multiplex;
|
pub mod multiplex;
|
||||||
|
|
|
@ -93,6 +93,7 @@ pub enum RendererError {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use middleware::action::MatchInfoProvider;
|
pub use middleware::action::MatchInfoProvider;
|
||||||
|
pub use middleware::delay_modifiers::ModifierStatusProvider;
|
||||||
|
|
||||||
pub fn default<'a, MatcherState>(
|
pub fn default<'a, MatcherState>(
|
||||||
matchers: &'a [&'a dyn Matcher<'a, MatcherState>],
|
matchers: &'a [&'a dyn Matcher<'a, MatcherState>],
|
||||||
|
@ -101,6 +102,7 @@ pub fn default<'a, MatcherState>(
|
||||||
multiplexer: &'a dyn Multiplexer,
|
multiplexer: &'a dyn Multiplexer,
|
||||||
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,
|
||||||
) -> impl Processor + 'a {
|
) -> impl Processor + 'a {
|
||||||
default::DefaultProcessor::new(
|
default::DefaultProcessor::new(
|
||||||
matchers,
|
matchers,
|
||||||
|
@ -109,5 +111,6 @@ pub fn default<'a, MatcherState>(
|
||||||
multiplexer,
|
multiplexer,
|
||||||
renderer,
|
renderer,
|
||||||
match_info_provider,
|
match_info_provider,
|
||||||
|
modifier_status_provider,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user