feat(core): wire up injectors

This commit is contained in:
Federico Terzi 2021-04-11 19:19:52 +02:00
parent 4af4a434a3
commit 518f0f8376
20 changed files with 243 additions and 35 deletions

View 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/>.
*/
use espanso_inject::Injector;
use crate::engine::dispatch::KeyInjector;
pub struct KeyInjectorAdapter<'a> {
injector: &'a dyn Injector,
}
impl<'a> KeyInjectorAdapter<'a> {
pub fn new(injector: &'a dyn Injector) -> Self {
Self { injector }
}
}
impl<'a> KeyInjector for KeyInjectorAdapter<'a> {
fn inject_sequence(&self, keys: &[crate::engine::event::keyboard::Key]) -> anyhow::Result<()> {
let converted_keys: Vec<_> = keys.iter().map(convert_to_inject_key).collect();
self.injector.send_keys(&converted_keys, Default::default()) // TODO: handle options
}
}
fn convert_to_inject_key(key: &crate::engine::event::keyboard::Key) -> espanso_inject::keys::Key {
match key {
crate::engine::event::keyboard::Key::Alt => espanso_inject::keys::Key::Alt,
crate::engine::event::keyboard::Key::CapsLock => espanso_inject::keys::Key::CapsLock,
crate::engine::event::keyboard::Key::Control => espanso_inject::keys::Key::Control,
crate::engine::event::keyboard::Key::Meta => espanso_inject::keys::Key::Meta,
crate::engine::event::keyboard::Key::NumLock => espanso_inject::keys::Key::NumLock,
crate::engine::event::keyboard::Key::Shift => espanso_inject::keys::Key::Shift,
crate::engine::event::keyboard::Key::Enter => espanso_inject::keys::Key::Enter,
crate::engine::event::keyboard::Key::Tab => espanso_inject::keys::Key::Tab,
crate::engine::event::keyboard::Key::Space => espanso_inject::keys::Key::Space,
crate::engine::event::keyboard::Key::ArrowDown => espanso_inject::keys::Key::ArrowDown,
crate::engine::event::keyboard::Key::ArrowLeft => espanso_inject::keys::Key::ArrowLeft,
crate::engine::event::keyboard::Key::ArrowRight => espanso_inject::keys::Key::ArrowRight,
crate::engine::event::keyboard::Key::ArrowUp => espanso_inject::keys::Key::ArrowUp,
crate::engine::event::keyboard::Key::End => espanso_inject::keys::Key::End,
crate::engine::event::keyboard::Key::Home => espanso_inject::keys::Key::Home,
crate::engine::event::keyboard::Key::PageDown => espanso_inject::keys::Key::PageDown,
crate::engine::event::keyboard::Key::PageUp => espanso_inject::keys::Key::PageUp,
crate::engine::event::keyboard::Key::Escape => espanso_inject::keys::Key::Escape,
crate::engine::event::keyboard::Key::Backspace => espanso_inject::keys::Key::Backspace,
crate::engine::event::keyboard::Key::F1 => espanso_inject::keys::Key::F1,
crate::engine::event::keyboard::Key::F2 => espanso_inject::keys::Key::F2,
crate::engine::event::keyboard::Key::F3 => espanso_inject::keys::Key::F3,
crate::engine::event::keyboard::Key::F4 => espanso_inject::keys::Key::F4,
crate::engine::event::keyboard::Key::F5 => espanso_inject::keys::Key::F5,
crate::engine::event::keyboard::Key::F6 => espanso_inject::keys::Key::F6,
crate::engine::event::keyboard::Key::F7 => espanso_inject::keys::Key::F7,
crate::engine::event::keyboard::Key::F8 => espanso_inject::keys::Key::F8,
crate::engine::event::keyboard::Key::F9 => espanso_inject::keys::Key::F9,
crate::engine::event::keyboard::Key::F10 => espanso_inject::keys::Key::F10,
crate::engine::event::keyboard::Key::F11 => espanso_inject::keys::Key::F11,
crate::engine::event::keyboard::Key::F12 => espanso_inject::keys::Key::F12,
crate::engine::event::keyboard::Key::F13 => espanso_inject::keys::Key::F13,
crate::engine::event::keyboard::Key::F14 => espanso_inject::keys::Key::F14,
crate::engine::event::keyboard::Key::F15 => espanso_inject::keys::Key::F15,
crate::engine::event::keyboard::Key::F16 => espanso_inject::keys::Key::F16,
crate::engine::event::keyboard::Key::F17 => espanso_inject::keys::Key::F17,
crate::engine::event::keyboard::Key::F18 => espanso_inject::keys::Key::F18,
crate::engine::event::keyboard::Key::F19 => espanso_inject::keys::Key::F19,
crate::engine::event::keyboard::Key::F20 => espanso_inject::keys::Key::F20,
crate::engine::event::keyboard::Key::Other(raw) => espanso_inject::keys::Key::Raw(*raw),
}
}

View File

@ -18,3 +18,4 @@
*/ */
pub mod text_injector; pub mod text_injector;
pub mod key_injector;

View File

@ -17,21 +17,25 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use espanso_inject::Injector;
use crate::engine::dispatch::TextInjector; use crate::engine::dispatch::TextInjector;
pub struct TextInjectorAdapter {} pub struct TextInjectorAdapter<'a> {
injector: &'a dyn Injector,
}
impl TextInjectorAdapter { impl <'a> TextInjectorAdapter<'a> {
pub fn new() -> Self { pub fn new(injector: &'a dyn Injector) -> Self {
Self {} Self {
injector
}
} }
} }
impl TextInjector for TextInjectorAdapter { impl <'a> TextInjector for TextInjectorAdapter<'a> {
fn inject(&self, text: &str) -> anyhow::Result<()> { fn inject_text(&self, text: &str) -> anyhow::Result<()> {
// TODO: implement // TODO: handle injection options
println!("INJECT: {}", text); self.injector.send_string(text, Default::default())
Ok(())
} }
} }

View File

@ -63,8 +63,11 @@ fn worker_main(args: CliModuleArgs) {
let mut processor = process::default(&matchers, &config_manager, &selector, &multiplexer, &renderer_adapter); let mut processor = process::default(&matchers, &config_manager, &selector, &multiplexer, &renderer_adapter);
let text_injector = engine::executor::text_injector::TextInjectorAdapter::new(); let injector = espanso_inject::get_injector(Default::default()).expect("failed to initialize injector module"); // TODO: handle the options
let dispatcher = dispatch::default(&text_injector);
let text_injector = engine::executor::text_injector::TextInjectorAdapter::new(&*injector);
let key_injector = engine::executor::key_injector::KeyInjectorAdapter::new(&*injector);
let dispatcher = dispatch::default(&text_injector, &key_injector);
let mut engine = Engine::new(&funnel, &mut processor, &dispatcher); let mut engine = Engine::new(&funnel, &mut processor, &dispatcher);
engine.run(); engine.run();

View File

@ -17,7 +17,7 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use super::{Dispatcher, Executor, TextInjector}; use super::{Dispatcher, Executor, KeyInjector, TextInjector};
use super::Event; use super::Event;
pub struct DefaultDispatcher<'a> { pub struct DefaultDispatcher<'a> {
@ -25,10 +25,11 @@ pub struct DefaultDispatcher<'a> {
} }
impl <'a> DefaultDispatcher<'a> { impl <'a> DefaultDispatcher<'a> {
pub fn new(text_injector: &'a dyn TextInjector) -> Self { pub fn new(text_injector: &'a dyn TextInjector, 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(text_injector)),
Box::new(super::executor::key_inject::KeyInjectExecutor::new(key_injector)),
] ]
} }
} }

View File

@ -0,0 +1,44 @@
/*
* 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 super::super::{Event, Executor, KeyInjector};
use log::error;
pub struct KeyInjectExecutor<'a> {
injector: &'a dyn KeyInjector,
}
impl<'a> KeyInjectExecutor<'a> {
pub fn new(injector: &'a dyn KeyInjector) -> Self {
Self { injector }
}
}
impl<'a> Executor for KeyInjectExecutor<'a> {
fn execute(&self, event: &Event) -> bool {
if let Event::KeySequenceInject(inject_event) = event {
if let Err(error) = self.injector.inject_sequence(&inject_event.keys) {
error!("key injector reported an error: {}", error);
}
return true;
}
false
}
}

View File

@ -18,3 +18,4 @@
*/ */
pub mod text_inject; pub mod text_inject;
pub mod key_inject;

View File

@ -18,7 +18,7 @@
*/ */
use super::super::{Event, Executor, TextInjector}; use super::super::{Event, Executor, TextInjector};
use crate::engine::event::inject::TextInjectMode; use crate::engine::event::text::TextInjectMode;
use log::error; use log::error;
pub struct TextInjectExecutor<'a> { pub struct TextInjectExecutor<'a> {
@ -35,7 +35,7 @@ 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 { if let Some(TextInjectMode::Keys) = inject_event.force_mode {
if let Err(error) = self.injector.inject(&inject_event.text) { if let Err(error) = self.injector.inject_text(&inject_event.text) {
error!("text injector reported an error: {:?}", error); error!("text injector reported an error: {:?}", error);
} }
return true; return true;

View File

@ -18,7 +18,7 @@
*/ */
use anyhow::Result; use anyhow::Result;
use super::Event; use super::{Event, event::keyboard::Key};
mod executor; mod executor;
mod default; mod default;
@ -32,11 +32,16 @@ pub trait Dispatcher {
} }
pub trait TextInjector { pub trait TextInjector {
fn inject(&self, text: &str) -> Result<()>; fn inject_text(&self, text: &str) -> Result<()>;
} }
pub fn default<'a>(text_injector: &'a dyn TextInjector) -> impl Dispatcher + 'a { pub trait KeyInjector {
fn inject_sequence(&self, keys: &[Key]) -> Result<()>;
}
pub fn default<'a>(text_injector: &'a dyn TextInjector, key_injector: &'a dyn KeyInjector) -> impl Dispatcher + 'a {
default::DefaultDispatcher::new( default::DefaultDispatcher::new(
text_injector, text_injector,
key_injector,
) )
} }

View File

@ -37,6 +37,11 @@ pub struct KeyboardEvent {
pub variant: Option<Variant>, pub variant: Option<Variant>,
} }
#[derive(Debug)]
pub struct KeySequenceInjectRequest {
pub keys: Vec<Key>,
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Key { pub enum Key {
// Modifiers // Modifiers

View File

@ -18,7 +18,7 @@
*/ */
pub mod keyboard; pub mod keyboard;
pub mod inject; pub mod text;
pub mod matches; pub mod matches;
pub mod render; pub mod render;
@ -38,5 +38,6 @@ pub enum Event {
Rendered(render::RenderedEvent), Rendered(render::RenderedEvent),
// Effects // Effects
TextInject(inject::TextInjectRequest), KeySequenceInject(keyboard::KeySequenceInjectRequest),
TextInject(text::TextInjectRequest),
} }

View File

@ -19,7 +19,6 @@
#[derive(Debug)] #[derive(Debug)]
pub struct TextInjectRequest { pub struct TextInjectRequest {
pub delete_count: i32,
pub text: String, pub text: String,
pub force_mode: Option<TextInjectMode>, pub force_mode: Option<TextInjectMode>,
} }
@ -29,3 +28,4 @@ pub enum TextInjectMode {
Keys, Keys,
Clipboard, Clipboard,
} }

View File

@ -22,7 +22,7 @@ use log::trace;
use super::{ use super::{
middleware::{ middleware::{
match_select::MatchSelectMiddleware, matcher::MatchMiddleware, multiplex::MultiplexMiddleware, match_select::MatchSelectMiddleware, matcher::MatchMiddleware, multiplex::MultiplexMiddleware,
render::RenderMiddleware, render::RenderMiddleware, action::ActionMiddleware,
}, },
Event, MatchFilter, MatchSelector, Matcher, Middleware, Multiplexer, Processor, Renderer, Event, MatchFilter, MatchSelector, Matcher, Middleware, Multiplexer, Processor, Renderer,
}; };
@ -48,6 +48,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()),
], ],
} }
} }
@ -57,7 +58,7 @@ impl<'a> DefaultProcessor<'a> {
let mut current_event = event; let mut current_event = event;
let mut current_queue = VecDeque::new(); let mut current_queue = VecDeque::new();
let dispatch = |event: Event| { let mut dispatch = |event: Event| {
trace!("dispatched event: {:?}", event); trace!("dispatched event: {:?}", event);
current_queue.push_front(event); current_queue.push_front(event);
}; };
@ -65,7 +66,7 @@ impl<'a> DefaultProcessor<'a> {
for middleware in self.middleware.iter() { for middleware in self.middleware.iter() {
trace!("middleware received event: {:?}", current_event); trace!("middleware received event: {:?}", current_event);
current_event = middleware.next(current_event, &dispatch); current_event = middleware.next(current_event, &mut dispatch);
trace!("middleware produced event: {:?}", current_event); trace!("middleware produced event: {:?}", current_event);
} }

View File

@ -0,0 +1,57 @@
/*
* 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 log::{debug, error};
use super::super::Middleware;
use crate::engine::{event::{Event, keyboard::{Key, KeySequenceInjectRequest}, matches::MatchSelectedEvent, text::{TextInjectMode, TextInjectRequest}}, process::{MatchFilter, MatchSelector, Multiplexer}};
pub struct ActionMiddleware {
}
impl ActionMiddleware {
pub fn new() -> Self {
Self {}
}
}
impl Middleware for ActionMiddleware {
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
if let Event::Rendered(m_event) = &event {
let delete_count = m_event.trigger.len();
let delete_sequence: Vec<_> = (0..delete_count).map(|_| Key::Backspace).collect();
dispatch(Event::TextInject(TextInjectRequest {
text: m_event.body.clone(),
force_mode: Some(TextInjectMode::Keys), // TODO: determine this one dynamically
}));
// This is executed before the dispatched event
return Event::KeySequenceInject(KeySequenceInjectRequest {
keys: delete_sequence
})
}
// TODO: handle images
event
}
}
// TODO: test

View File

@ -43,7 +43,7 @@ impl<'a> MatchSelectMiddleware<'a> {
} }
impl<'a> Middleware for MatchSelectMiddleware<'a> { impl<'a> Middleware for MatchSelectMiddleware<'a> {
fn next(&self, event: Event, _: &dyn FnMut(Event)) -> Event { fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
if let Event::MatchesDetected(m_event) = event { if let Event::MatchesDetected(m_event) = event {
let matches_ids: Vec<i32> = m_event.matches.iter().map(|m| m.id).collect(); let matches_ids: Vec<i32> = m_event.matches.iter().map(|m| m.id).collect();

View File

@ -41,7 +41,7 @@ impl<'a, State> MatchMiddleware<'a, State> {
} }
impl<'a, State> Middleware for MatchMiddleware<'a, State> { impl<'a, State> Middleware for MatchMiddleware<'a, State> {
fn next(&self, event: Event, _: &dyn FnMut(Event)) -> Event { fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
let mut matcher_states = self.matcher_states.borrow_mut(); let mut matcher_states = self.matcher_states.borrow_mut();
let prev_states = if !matcher_states.is_empty() { let prev_states = if !matcher_states.is_empty() {
matcher_states.get(matcher_states.len() - 1) matcher_states.get(matcher_states.len() - 1)

View File

@ -17,6 +17,7 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
pub mod action;
pub mod render; pub mod render;
pub mod matcher; pub mod matcher;
pub mod multiplex; pub mod multiplex;

View File

@ -20,7 +20,7 @@
use log::{debug, error}; use log::{debug, error};
use super::super::Middleware; use super::super::Middleware;
use crate::engine::{event::{Event, inject::{TextInjectRequest, TextInjectMode}, matches::MatchSelectedEvent}, process::{MatchFilter, MatchSelector, Multiplexer}}; use crate::engine::{event::{Event, text::{TextInjectRequest, TextInjectMode}, matches::MatchSelectedEvent}, process::{MatchFilter, MatchSelector, Multiplexer}};
pub struct MultiplexMiddleware<'a> { pub struct MultiplexMiddleware<'a> {
multiplexer: &'a dyn Multiplexer, multiplexer: &'a dyn Multiplexer,
@ -33,7 +33,7 @@ impl<'a> MultiplexMiddleware<'a> {
} }
impl<'a> Middleware for MultiplexMiddleware<'a> { impl<'a> Middleware for MultiplexMiddleware<'a> {
fn next(&self, event: Event, _: &dyn FnMut(Event)) -> Event { fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
if let Event::MatchSelected(m_event) = event { if let Event::MatchSelected(m_event) = event {
return match self.multiplexer.convert(m_event.chosen.id, m_event.chosen.trigger, m_event.chosen.args) { return match self.multiplexer.convert(m_event.chosen.id, m_event.chosen.trigger, m_event.chosen.args) {
Some(event) => event, Some(event) => event,

View File

@ -20,7 +20,7 @@
use log::{debug, error}; use log::{debug, error};
use super::super::Middleware; use super::super::Middleware;
use crate::engine::{event::{Event, inject::{TextInjectRequest, TextInjectMode}, matches::MatchSelectedEvent, render::RenderedEvent}, process::{MatchFilter, MatchSelector, Renderer, RendererError}}; use crate::engine::{event::{Event, text::{TextInjectRequest, TextInjectMode}, matches::MatchSelectedEvent, render::RenderedEvent}, process::{MatchFilter, MatchSelector, Renderer, RendererError}};
pub struct RenderMiddleware<'a> { pub struct RenderMiddleware<'a> {
renderer: &'a dyn Renderer<'a>, renderer: &'a dyn Renderer<'a>,
@ -33,7 +33,7 @@ impl<'a> RenderMiddleware<'a> {
} }
impl<'a> Middleware for RenderMiddleware<'a> { impl<'a> Middleware for RenderMiddleware<'a> {
fn next(&self, event: Event, _: &dyn FnMut(Event)) -> Event { fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
if let Event::RenderingRequested(m_event) = event { if let Event::RenderingRequested(m_event) = event {
match self.renderer.render(m_event.match_id, m_event.trigger_args) { match self.renderer.render(m_event.match_id, m_event.trigger_args) {
Ok(body) => { Ok(body) => {

View File

@ -26,7 +26,7 @@ mod default;
mod middleware; mod middleware;
pub trait Middleware { pub trait Middleware {
fn next(&self, event: Event, dispatch: &dyn FnMut(Event)) -> Event; fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event;
} }
pub trait Processor { pub trait Processor {