feat(core): introduce basic injection backend switch logic
This commit is contained in:
parent
e532a377b1
commit
3a51efda2c
|
@ -22,7 +22,7 @@ use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::engine::process::MatchFilter;
|
use crate::engine::{dispatch::ModeProvider, process::MatchFilter};
|
||||||
use espanso_config::{
|
use espanso_config::{
|
||||||
config::{AppProperties, Config, ConfigStore},
|
config::{AppProperties, Config, ConfigStore},
|
||||||
matches::store::{MatchSet, MatchStore},
|
matches::store::{MatchSet, MatchStore},
|
||||||
|
@ -89,12 +89,24 @@ impl<'a> MatchFilter for ConfigManager<'a> {
|
||||||
|
|
||||||
impl<'a> ConfigProvider<'a> for ConfigManager<'a> {
|
impl<'a> ConfigProvider<'a> for ConfigManager<'a> {
|
||||||
fn configs(&self) -> Vec<(&'a dyn Config, MatchSet)> {
|
fn configs(&self) -> Vec<(&'a dyn Config, MatchSet)> {
|
||||||
self.config_store.configs().into_iter().map(|config| {
|
self
|
||||||
(config, self.match_store.query(config.match_paths()))
|
.config_store
|
||||||
}).collect()
|
.configs()
|
||||||
|
.into_iter()
|
||||||
|
.map(|config| (config, self.match_store.query(config.match_paths())))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn active(&self) -> (&'a dyn Config, MatchSet) {
|
fn active(&self) -> (&'a dyn Config, MatchSet) {
|
||||||
self.active_context()
|
self.active_context()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> ModeProvider for ConfigManager<'a> {
|
||||||
|
fn active_mode(&self) -> crate::engine::dispatch::Mode {
|
||||||
|
// TODO: implement the actual active mode detection starting from the active config
|
||||||
|
crate::engine::dispatch::Mode::Auto {
|
||||||
|
clipboard_threshold: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
56
espanso/src/cli/worker/engine/executor/clipboard_injector.rs
Normal file
56
espanso/src/cli/worker/engine/executor/clipboard_injector.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* 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 espanso_inject::{Injector, keys::Key};
|
||||||
|
use espanso_clipboard::Clipboard;
|
||||||
|
|
||||||
|
use crate::engine::{dispatch::TextInjector};
|
||||||
|
|
||||||
|
pub struct ClipboardInjectorAdapter<'a> {
|
||||||
|
injector: &'a dyn Injector,
|
||||||
|
clipboard: &'a dyn Clipboard,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'a> ClipboardInjectorAdapter<'a> {
|
||||||
|
pub fn new(injector: &'a dyn Injector, clipboard: &'a dyn Clipboard) -> Self {
|
||||||
|
Self {
|
||||||
|
injector,
|
||||||
|
clipboard,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'a> TextInjector for ClipboardInjectorAdapter<'a> {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"clipboard"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inject_text(&self, text: &str) -> anyhow::Result<()> {
|
||||||
|
// TODO: handle clipboard restoration
|
||||||
|
self.clipboard.set_text(text)?;
|
||||||
|
|
||||||
|
// TODO: handle delay duration
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||||
|
|
||||||
|
// TODO: handle options
|
||||||
|
self.injector.send_key_combination(&[Key::Control, Key::V], Default::default())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,11 +21,11 @@ use espanso_inject::Injector;
|
||||||
|
|
||||||
use crate::engine::dispatch::TextInjector;
|
use crate::engine::dispatch::TextInjector;
|
||||||
|
|
||||||
pub struct TextInjectorAdapter<'a> {
|
pub struct EventInjectorAdapter<'a> {
|
||||||
injector: &'a dyn Injector,
|
injector: &'a dyn Injector,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a> TextInjectorAdapter<'a> {
|
impl <'a> EventInjectorAdapter<'a> {
|
||||||
pub fn new(injector: &'a dyn Injector) -> Self {
|
pub fn new(injector: &'a dyn Injector) -> Self {
|
||||||
Self {
|
Self {
|
||||||
injector
|
injector
|
||||||
|
@ -33,7 +33,11 @@ impl <'a> TextInjectorAdapter<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a> TextInjector for TextInjectorAdapter<'a> {
|
impl <'a> TextInjector for EventInjectorAdapter<'a> {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"event"
|
||||||
|
}
|
||||||
|
|
||||||
fn inject_text(&self, text: &str) -> anyhow::Result<()> {
|
fn inject_text(&self, text: &str) -> anyhow::Result<()> {
|
||||||
// TODO: handle injection options
|
// TODO: handle injection options
|
||||||
self.injector.send_string(text, Default::default())
|
self.injector.send_string(text, Default::default())
|
|
@ -17,5 +17,6 @@
|
||||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub mod text_injector;
|
pub mod clipboard_injector;
|
||||||
|
pub mod event_injector;
|
||||||
pub mod key_injector;
|
pub mod key_injector;
|
|
@ -19,10 +19,9 @@
|
||||||
|
|
||||||
use std::{collections::HashMap, iter::FromIterator};
|
use std::{collections::HashMap, iter::FromIterator};
|
||||||
|
|
||||||
use espanso_config::{
|
use espanso_config::{config::ConfigStore, matches::{Match, MatchEffect, store::MatchStore}};
|
||||||
config::ConfigStore,
|
|
||||||
matches::{store::MatchStore, Match},
|
use crate::engine::process::MatchInfoProvider;
|
||||||
};
|
|
||||||
|
|
||||||
use super::{multiplex::MatchProvider, render::MatchIterator};
|
use super::{multiplex::MatchProvider, render::MatchIterator};
|
||||||
|
|
||||||
|
@ -56,3 +55,14 @@ impl<'a> MatchIterator<'a> for MatchCache<'a> {
|
||||||
self.cache.iter().map(|(_, m)| *m).collect()
|
self.cache.iter().map(|(_, m)| *m).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> MatchInfoProvider for MatchCache<'a> {
|
||||||
|
fn get_force_mode(&self, match_id: i32) -> Option<crate::engine::event::text::TextInjectMode> {
|
||||||
|
let m = self.cache.get(&match_id)?;
|
||||||
|
if let MatchEffect::Text(text_effect) = &m.effect {
|
||||||
|
// TODO: read match effect and convert it to the actual injection mode
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,15 +17,15 @@
|
||||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use engine::ui::selector::MatchSelectorAdapter;
|
||||||
use funnel::Source;
|
use funnel::Source;
|
||||||
use process::Matcher;
|
use process::Matcher;
|
||||||
use engine::ui::selector::MatchSelectorAdapter;
|
|
||||||
|
|
||||||
use crate::engine::{Engine, funnel, process, dispatch};
|
|
||||||
use super::{CliModule, CliModuleArgs};
|
use super::{CliModule, CliModuleArgs};
|
||||||
|
use crate::engine::{dispatch, funnel, process, Engine};
|
||||||
|
|
||||||
mod engine;
|
|
||||||
mod config;
|
mod config;
|
||||||
|
mod engine;
|
||||||
|
|
||||||
pub fn new() -> CliModule {
|
pub fn new() -> CliModule {
|
||||||
#[allow(clippy::needless_update)]
|
#[allow(clippy::needless_update)]
|
||||||
|
@ -40,34 +40,61 @@ pub fn new() -> CliModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn worker_main(args: CliModuleArgs) {
|
fn worker_main(args: CliModuleArgs) {
|
||||||
let config_store = args.config_store.expect("missing config store in worker main");
|
let config_store = args
|
||||||
let match_store = args.match_store.expect("missing match store in worker main");
|
.config_store
|
||||||
|
.expect("missing config store in worker main");
|
||||||
|
let match_store = args
|
||||||
|
.match_store
|
||||||
|
.expect("missing match store in worker main");
|
||||||
|
|
||||||
let app_info_provider = espanso_info::get_provider().expect("unable to initialize app info provider");
|
let app_info_provider =
|
||||||
let config_manager = config::ConfigManager::new(&*config_store, &*match_store, &*app_info_provider);
|
espanso_info::get_provider().expect("unable to initialize app info provider");
|
||||||
let match_converter = engine::matcher::convert::MatchConverter::new(&*config_store, &*match_store);
|
let config_manager =
|
||||||
|
config::ConfigManager::new(&*config_store, &*match_store, &*app_info_provider);
|
||||||
|
let match_converter =
|
||||||
|
engine::matcher::convert::MatchConverter::new(&*config_store, &*match_store);
|
||||||
let match_cache = engine::match_cache::MatchCache::load(&*config_store, &*match_store);
|
let match_cache = engine::match_cache::MatchCache::load(&*config_store, &*match_store);
|
||||||
|
|
||||||
let detect_source = engine::source::detect::init_and_spawn().expect("failed to initialize detector module");
|
let detect_source =
|
||||||
|
engine::source::detect::init_and_spawn().expect("failed to initialize detector module");
|
||||||
let sources: Vec<&dyn Source> = vec![&detect_source];
|
let sources: Vec<&dyn Source> = vec![&detect_source];
|
||||||
let funnel = funnel::default(&sources);
|
let funnel = funnel::default(&sources);
|
||||||
|
|
||||||
let matcher = engine::matcher::rolling::RollingMatcherAdapter::new(&match_converter.get_rolling_matches());
|
let matcher =
|
||||||
|
engine::matcher::rolling::RollingMatcherAdapter::new(&match_converter.get_rolling_matches());
|
||||||
let matchers: Vec<&dyn Matcher<engine::matcher::MatcherState>> = vec![&matcher];
|
let matchers: Vec<&dyn Matcher<engine::matcher::MatcherState>> = vec![&matcher];
|
||||||
let selector = MatchSelectorAdapter::new();
|
let selector = MatchSelectorAdapter::new();
|
||||||
let multiplexer = engine::multiplex::MultiplexAdapter::new(&match_cache);
|
let multiplexer = engine::multiplex::MultiplexAdapter::new(&match_cache);
|
||||||
|
|
||||||
// TODO: add extensions
|
// TODO: add extensions
|
||||||
let renderer = espanso_render::create(Vec::new());
|
let renderer = espanso_render::create(Vec::new());
|
||||||
let renderer_adapter = engine::render::RendererAdapter::new(&match_cache, &config_manager, &renderer);
|
let renderer_adapter =
|
||||||
|
engine::render::RendererAdapter::new(&match_cache, &config_manager, &renderer);
|
||||||
|
|
||||||
let mut processor = process::default(&matchers, &config_manager, &selector, &multiplexer, &renderer_adapter);
|
let mut processor = process::default(
|
||||||
|
&matchers,
|
||||||
|
&config_manager,
|
||||||
|
&selector,
|
||||||
|
&multiplexer,
|
||||||
|
&renderer_adapter,
|
||||||
|
&match_cache,
|
||||||
|
);
|
||||||
|
|
||||||
let injector = espanso_inject::get_injector(Default::default()).expect("failed to initialize injector module"); // TODO: handle the options
|
let injector =
|
||||||
|
espanso_inject::get_injector(Default::default()).expect("failed to initialize injector module"); // TODO: handle the options
|
||||||
|
let clipboard = espanso_clipboard::get_clipboard(Default::default())
|
||||||
|
.expect("failed to initialize clipboard module"); // TODO: handle options
|
||||||
|
|
||||||
let text_injector = engine::executor::text_injector::TextInjectorAdapter::new(&*injector);
|
let event_injector = engine::executor::event_injector::EventInjectorAdapter::new(&*injector);
|
||||||
|
let clipboard_injector =
|
||||||
|
engine::executor::clipboard_injector::ClipboardInjectorAdapter::new(&*injector, &*clipboard);
|
||||||
let key_injector = engine::executor::key_injector::KeyInjectorAdapter::new(&*injector);
|
let key_injector = engine::executor::key_injector::KeyInjectorAdapter::new(&*injector);
|
||||||
let dispatcher = dispatch::default(&text_injector, &key_injector);
|
let dispatcher = dispatch::default(
|
||||||
|
&event_injector,
|
||||||
|
&clipboard_injector,
|
||||||
|
&config_manager,
|
||||||
|
&key_injector,
|
||||||
|
);
|
||||||
|
|
||||||
let mut engine = Engine::new(&funnel, &mut processor, &dispatcher);
|
let mut engine = Engine::new(&funnel, &mut processor, &dispatcher);
|
||||||
engine.run();
|
engine.run();
|
||||||
|
|
|
@ -17,20 +17,31 @@
|
||||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::{Dispatcher, Executor, KeyInjector, TextInjector};
|
|
||||||
use super::Event;
|
use super::Event;
|
||||||
|
use super::{ModeProvider, Dispatcher, Executor, KeyInjector, TextInjector};
|
||||||
|
|
||||||
pub struct DefaultDispatcher<'a> {
|
pub struct DefaultDispatcher<'a> {
|
||||||
executors: Vec<Box<dyn Executor + 'a>>,
|
executors: Vec<Box<dyn Executor + 'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DefaultDispatcher<'a> {
|
impl<'a> DefaultDispatcher<'a> {
|
||||||
pub fn new(text_injector: &'a dyn TextInjector, key_injector: &'a dyn KeyInjector) -> Self {
|
pub fn new(
|
||||||
|
event_injector: &'a dyn TextInjector,
|
||||||
|
clipboard_injector: &'a dyn TextInjector,
|
||||||
|
mode_provider: &'a dyn ModeProvider,
|
||||||
|
key_injector: &'a dyn KeyInjector,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
executors: vec![
|
executors: vec![
|
||||||
Box::new(super::executor::text_inject::TextInjectExecutor::new(text_injector)),
|
Box::new(super::executor::text_inject::TextInjectExecutor::new(
|
||||||
Box::new(super::executor::key_inject::KeyInjectExecutor::new(key_injector)),
|
event_injector,
|
||||||
]
|
clipboard_injector,
|
||||||
|
mode_provider,
|
||||||
|
)),
|
||||||
|
Box::new(super::executor::key_inject::KeyInjectExecutor::new(
|
||||||
|
key_injector,
|
||||||
|
)),
|
||||||
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +50,7 @@ impl <'a> Dispatcher for DefaultDispatcher<'a> {
|
||||||
fn dispatch(&self, event: Event) {
|
fn dispatch(&self, event: Event) {
|
||||||
for executor in self.executors.iter() {
|
for executor in self.executors.iter() {
|
||||||
if executor.execute(&event) {
|
if executor.execute(&event) {
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,31 +17,92 @@
|
||||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::super::{Event, Executor, TextInjector};
|
use anyhow::Result;
|
||||||
|
use super::super::{Event, Executor};
|
||||||
use crate::engine::event::text::TextInjectMode;
|
use crate::engine::event::text::TextInjectMode;
|
||||||
use log::error;
|
use log::{error, trace};
|
||||||
|
|
||||||
|
pub trait TextInjector {
|
||||||
|
fn name(&self) -> &'static str;
|
||||||
|
fn inject_text(&self, text: &str) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ModeProvider {
|
||||||
|
fn active_mode(&self) -> Mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Mode {
|
||||||
|
Event,
|
||||||
|
Clipboard,
|
||||||
|
Auto {
|
||||||
|
// Maximum size after which the clipboard backend
|
||||||
|
// is used over the event one to speed up the injection.
|
||||||
|
clipboard_threshold: usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TextInjectExecutor<'a> {
|
pub struct TextInjectExecutor<'a> {
|
||||||
injector: &'a dyn TextInjector,
|
event_injector: &'a dyn TextInjector,
|
||||||
|
clipboard_injector: &'a dyn TextInjector,
|
||||||
|
mode_provider: &'a dyn ModeProvider,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TextInjectExecutor<'a> {
|
impl<'a> TextInjectExecutor<'a> {
|
||||||
pub fn new(injector: &'a dyn TextInjector) -> Self {
|
pub fn new(
|
||||||
Self { injector }
|
event_injector: &'a dyn TextInjector,
|
||||||
|
clipboard_injector: &'a dyn TextInjector,
|
||||||
|
mode_provider: &'a dyn ModeProvider,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
event_injector,
|
||||||
|
clipboard_injector,
|
||||||
|
mode_provider,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 Event::TextInject(inject_event) = event {
|
||||||
if let Some(TextInjectMode::Keys) = inject_event.force_mode {
|
let active_mode = self.mode_provider.active_mode();
|
||||||
if let Err(error) = self.injector.inject_text(&inject_event.text) {
|
|
||||||
error!("text injector reported an error: {:?}", error);
|
let injector = if let Some(force_mode) = &inject_event.force_mode {
|
||||||
|
if let TextInjectMode::Keys = force_mode {
|
||||||
|
self.event_injector
|
||||||
|
} else {
|
||||||
|
self.clipboard_injector
|
||||||
}
|
}
|
||||||
|
} else if let Mode::Clipboard = active_mode {
|
||||||
|
self.clipboard_injector
|
||||||
|
} else if let Mode::Event = active_mode {
|
||||||
|
self.event_injector
|
||||||
|
} else if let Mode::Auto { clipboard_threshold } = active_mode {
|
||||||
|
if inject_event.text.chars().count() > clipboard_threshold {
|
||||||
|
self.clipboard_injector
|
||||||
|
} else if cfg!(target_os = "linux") {
|
||||||
|
if inject_event.text.chars().all(|c| c.is_ascii()) {
|
||||||
|
self.event_injector
|
||||||
|
} else {
|
||||||
|
self.clipboard_injector
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.event_injector
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.event_injector
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!("using injector: {}", injector.name());
|
||||||
|
|
||||||
|
if let Err(error) = injector.inject_text(&inject_event.text) {
|
||||||
|
error!("text injector ({}) reported an error: {:?}", injector.name(), error);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: test
|
||||||
|
|
|
@ -18,10 +18,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use super::{Event, event::keyboard::Key};
|
|
||||||
|
|
||||||
mod executor;
|
use super::{event::keyboard::Key, Event};
|
||||||
|
|
||||||
mod default;
|
mod default;
|
||||||
|
mod executor;
|
||||||
|
|
||||||
pub trait Executor {
|
pub trait Executor {
|
||||||
fn execute(&self, event: &Event) -> bool;
|
fn execute(&self, event: &Event) -> bool;
|
||||||
|
@ -31,17 +32,19 @@ pub trait Dispatcher {
|
||||||
fn dispatch(&self, event: Event);
|
fn dispatch(&self, event: Event);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TextInjector {
|
// Re-export dependency injection entities
|
||||||
fn inject_text(&self, text: &str) -> Result<()>;
|
pub use executor::text_inject::{ModeProvider, Mode, TextInjector};
|
||||||
}
|
|
||||||
|
|
||||||
|
// TODO: move into module
|
||||||
pub trait KeyInjector {
|
pub trait KeyInjector {
|
||||||
fn inject_sequence(&self, keys: &[Key]) -> Result<()>;
|
fn inject_sequence(&self, keys: &[Key]) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default<'a>(text_injector: &'a dyn TextInjector, key_injector: &'a dyn KeyInjector) -> impl Dispatcher + 'a {
|
pub fn default<'a>(
|
||||||
default::DefaultDispatcher::new(
|
event_injector: &'a dyn TextInjector,
|
||||||
text_injector,
|
clipboard_injector: &'a dyn TextInjector,
|
||||||
key_injector,
|
mode_provider: &'a dyn ModeProvider,
|
||||||
)
|
key_injector: &'a dyn KeyInjector,
|
||||||
|
) -> impl Dispatcher + 'a {
|
||||||
|
default::DefaultDispatcher::new(event_injector, clipboard_injector, mode_provider, key_injector)
|
||||||
}
|
}
|
|
@ -28,6 +28,8 @@ pub struct RenderingRequestedEvent {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct RenderedEvent {
|
pub struct RenderedEvent {
|
||||||
|
pub match_id: i32,
|
||||||
|
|
||||||
pub trigger: String,
|
pub trigger: String,
|
||||||
pub body: String,
|
pub body: String,
|
||||||
|
|
||||||
|
|
|
@ -19,13 +19,10 @@
|
||||||
|
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
|
||||||
use super::{
|
use super::{Event, MatchFilter, MatchInfoProvider, MatchSelector, Matcher, Middleware, Multiplexer, Processor, Renderer, middleware::{
|
||||||
middleware::{
|
|
||||||
match_select::MatchSelectMiddleware, matcher::MatchMiddleware, multiplex::MultiplexMiddleware,
|
match_select::MatchSelectMiddleware, matcher::MatchMiddleware, multiplex::MultiplexMiddleware,
|
||||||
render::RenderMiddleware, action::ActionMiddleware,
|
render::RenderMiddleware, action::ActionMiddleware,
|
||||||
},
|
}};
|
||||||
Event, MatchFilter, MatchSelector, Matcher, Middleware, Multiplexer, Processor, Renderer,
|
|
||||||
};
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
pub struct DefaultProcessor<'a> {
|
pub struct DefaultProcessor<'a> {
|
||||||
|
@ -40,6 +37,7 @@ impl<'a> DefaultProcessor<'a> {
|
||||||
match_selector: &'a dyn MatchSelector,
|
match_selector: &'a dyn MatchSelector,
|
||||||
multiplexer: &'a dyn Multiplexer,
|
multiplexer: &'a dyn Multiplexer,
|
||||||
renderer: &'a dyn Renderer<'a>,
|
renderer: &'a dyn Renderer<'a>,
|
||||||
|
match_info_provider: &'a dyn MatchInfoProvider,
|
||||||
) -> DefaultProcessor<'a> {
|
) -> DefaultProcessor<'a> {
|
||||||
Self {
|
Self {
|
||||||
event_queue: VecDeque::new(),
|
event_queue: VecDeque::new(),
|
||||||
|
@ -48,7 +46,7 @@ impl<'a> DefaultProcessor<'a> {
|
||||||
Box::new(MatchSelectMiddleware::new(match_filter, match_selector)),
|
Box::new(MatchSelectMiddleware::new(match_filter, match_selector)),
|
||||||
Box::new(MultiplexMiddleware::new(multiplexer)),
|
Box::new(MultiplexMiddleware::new(multiplexer)),
|
||||||
Box::new(RenderMiddleware::new(renderer)),
|
Box::new(RenderMiddleware::new(renderer)),
|
||||||
Box::new(ActionMiddleware::new()),
|
Box::new(ActionMiddleware::new(match_info_provider)),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,18 +18,32 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::super::Middleware;
|
use super::super::Middleware;
|
||||||
use crate::engine::{event::{Event, keyboard::{Key, KeySequenceInjectRequest}, text::{TextInjectMode, TextInjectRequest}}, process::{MatchFilter, MatchSelector, Multiplexer}};
|
use crate::engine::{
|
||||||
|
dispatch::Mode,
|
||||||
|
event::{
|
||||||
|
keyboard::{Key, KeySequenceInjectRequest},
|
||||||
|
text::{TextInjectMode, TextInjectRequest},
|
||||||
|
Event,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
pub struct ActionMiddleware {
|
pub trait MatchInfoProvider {
|
||||||
|
fn get_force_mode(&self, match_id: i32) -> Option<TextInjectMode>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActionMiddleware {
|
pub struct ActionMiddleware<'a> {
|
||||||
pub fn new() -> Self {
|
match_info_provider: &'a dyn MatchInfoProvider,
|
||||||
Self {}
|
}
|
||||||
|
|
||||||
|
impl<'a> ActionMiddleware<'a> {
|
||||||
|
pub fn new(match_info_provider: &'a dyn MatchInfoProvider) -> Self {
|
||||||
|
Self {
|
||||||
|
match_info_provider,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Middleware for ActionMiddleware {
|
impl<'a> Middleware for ActionMiddleware<'a> {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"action"
|
"action"
|
||||||
}
|
}
|
||||||
|
@ -38,19 +52,23 @@ impl Middleware for ActionMiddleware {
|
||||||
if let Event::Rendered(m_event) = &event {
|
if let Event::Rendered(m_event) = &event {
|
||||||
dispatch(Event::TextInject(TextInjectRequest {
|
dispatch(Event::TextInject(TextInjectRequest {
|
||||||
text: m_event.body.clone(),
|
text: m_event.body.clone(),
|
||||||
force_mode: Some(TextInjectMode::Keys), // TODO: determine this one dynamically
|
force_mode: self.match_info_provider.get_force_mode(m_event.match_id),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if let Some(cursor_hint_back_count) = m_event.cursor_hint_back_count {
|
if let Some(cursor_hint_back_count) = m_event.cursor_hint_back_count {
|
||||||
dispatch(Event::KeySequenceInject(KeySequenceInjectRequest {
|
dispatch(Event::KeySequenceInject(KeySequenceInjectRequest {
|
||||||
keys: (0..cursor_hint_back_count).map(|_| Key::ArrowLeft).collect(),
|
keys: (0..cursor_hint_back_count)
|
||||||
|
.map(|_| Key::ArrowLeft)
|
||||||
|
.collect(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is executed before the dispatched event
|
// This is executed before the dispatched event
|
||||||
return Event::KeySequenceInject(KeySequenceInjectRequest {
|
return Event::KeySequenceInject(KeySequenceInjectRequest {
|
||||||
keys: (0..m_event.trigger.chars().count()).map(|_| Key::Backspace).collect()
|
keys: (0..m_event.trigger.chars().count())
|
||||||
})
|
.map(|_| Key::Backspace)
|
||||||
|
.collect(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: handle images
|
// TODO: handle images
|
||||||
|
|
|
@ -50,6 +50,7 @@ impl<'a> Middleware for RenderMiddleware<'a> {
|
||||||
let (body, cursor_hint_back_count) = process_cursor_hint(body);
|
let (body, cursor_hint_back_count) = process_cursor_hint(body);
|
||||||
|
|
||||||
return Event::Rendered(RenderedEvent {
|
return Event::Rendered(RenderedEvent {
|
||||||
|
match_id: m_event.match_id,
|
||||||
trigger: m_event.trigger,
|
trigger: m_event.trigger,
|
||||||
body,
|
body,
|
||||||
cursor_hint_back_count,
|
cursor_hint_back_count,
|
||||||
|
|
|
@ -36,6 +36,8 @@ pub trait Processor {
|
||||||
|
|
||||||
// Dependency inversion entities
|
// Dependency inversion entities
|
||||||
|
|
||||||
|
// TODO: move these traits inside the various modules and then re-export it
|
||||||
|
|
||||||
pub trait Matcher<'a, State> {
|
pub trait Matcher<'a, State> {
|
||||||
fn process(
|
fn process(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
@ -66,7 +68,12 @@ pub trait MatchSelector {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Multiplexer {
|
pub trait Multiplexer {
|
||||||
fn convert(&self, match_id: i32, trigger: String, trigger_args: HashMap<String, String>) -> Option<Event>;
|
fn convert(
|
||||||
|
&self,
|
||||||
|
match_id: i32,
|
||||||
|
trigger: String,
|
||||||
|
trigger_args: HashMap<String, String>,
|
||||||
|
) -> Option<Event>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Renderer<'a> {
|
pub trait Renderer<'a> {
|
||||||
|
@ -85,12 +92,22 @@ pub enum RendererError {
|
||||||
Aborted,
|
Aborted,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub use middleware::action::MatchInfoProvider;
|
||||||
|
|
||||||
pub fn default<'a, MatcherState>(
|
pub fn default<'a, MatcherState>(
|
||||||
matchers: &'a [&'a dyn Matcher<'a, MatcherState>],
|
matchers: &'a [&'a dyn Matcher<'a, MatcherState>],
|
||||||
match_filter: &'a dyn MatchFilter,
|
match_filter: &'a dyn MatchFilter,
|
||||||
match_selector: &'a dyn MatchSelector,
|
match_selector: &'a dyn MatchSelector,
|
||||||
multiplexer: &'a dyn Multiplexer,
|
multiplexer: &'a dyn Multiplexer,
|
||||||
renderer: &'a dyn Renderer<'a>,
|
renderer: &'a dyn Renderer<'a>,
|
||||||
|
match_info_provider: &'a dyn MatchInfoProvider,
|
||||||
) -> impl Processor + 'a {
|
) -> impl Processor + 'a {
|
||||||
default::DefaultProcessor::new(matchers, match_filter, match_selector, multiplexer, renderer)
|
default::DefaultProcessor::new(
|
||||||
|
matchers,
|
||||||
|
match_filter,
|
||||||
|
match_selector,
|
||||||
|
multiplexer,
|
||||||
|
renderer,
|
||||||
|
match_info_provider,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user