feat(core): improve basic core structure
This commit is contained in:
parent
a4958ff352
commit
c0de39fdd0
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -288,6 +288,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"crossbeam",
|
"crossbeam",
|
||||||
|
"enum-as-inner",
|
||||||
"espanso-clipboard",
|
"espanso-clipboard",
|
||||||
"espanso-config",
|
"espanso-config",
|
||||||
"espanso-detect",
|
"espanso-detect",
|
||||||
|
|
|
@ -28,9 +28,9 @@ mod util;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct MatchResult<Id> {
|
pub struct MatchResult<Id> {
|
||||||
id: Id,
|
pub id: Id,
|
||||||
trigger: String,
|
pub trigger: String,
|
||||||
vars: HashMap<String, String>,
|
pub vars: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Id: Default> Default for MatchResult<Id> {
|
impl<Id: Default> Default for MatchResult<Id> {
|
||||||
|
|
|
@ -29,4 +29,5 @@ anyhow = "1.0.38"
|
||||||
thiserror = "1.0.23"
|
thiserror = "1.0.23"
|
||||||
clap = "2.33.3"
|
clap = "2.33.3"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
crossbeam = "0.8.0"
|
crossbeam = "0.8.0"
|
||||||
|
enum-as-inner = "0.3.3"
|
20
espanso/src/cli/worker/executor/mod.rs
Normal file
20
espanso/src/cli/worker/executor/mod.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub mod text_injector;
|
37
espanso/src/cli/worker/executor/text_injector.rs
Normal file
37
espanso/src/cli/worker/executor/text_injector.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* 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 crate::engine::dispatch::TextInjector;
|
||||||
|
|
||||||
|
pub struct TextInjectorAdapter {}
|
||||||
|
|
||||||
|
impl TextInjectorAdapter {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextInjector for TextInjectorAdapter {
|
||||||
|
fn inject(&self, text: &str) -> anyhow::Result<()> {
|
||||||
|
// TODO: implement
|
||||||
|
println!("INJECT: {}", text);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
30
espanso/src/cli/worker/matcher/mod.rs
Normal file
30
espanso/src/cli/worker/matcher/mod.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
use espanso_match::rolling::matcher::RollingMatcherState;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 enum_as_inner::EnumAsInner;
|
||||||
|
|
||||||
|
pub mod rolling;
|
||||||
|
|
||||||
|
#[derive(Clone, EnumAsInner)]
|
||||||
|
pub enum MatcherState<'a> {
|
||||||
|
Rolling(RollingMatcherState<'a, i32>),
|
||||||
|
// TODO: regex
|
||||||
|
}
|
140
espanso/src/cli/worker/matcher/rolling.rs
Normal file
140
espanso/src/cli/worker/matcher/rolling.rs
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
/*
|
||||||
|
* 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_match::rolling::{matcher::RollingMatcher, RollingMatch};
|
||||||
|
|
||||||
|
use crate::engine::{
|
||||||
|
event::keyboard::Key,
|
||||||
|
process::{MatchResult, Matcher, MatcherEvent},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::MatcherState;
|
||||||
|
|
||||||
|
pub struct RollingMatcherAdapter {
|
||||||
|
matcher: RollingMatcher<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RollingMatcherAdapter {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
// TODO: pass actual matches
|
||||||
|
let matches = vec![
|
||||||
|
RollingMatch::from_string(1, "esp", &Default::default()),
|
||||||
|
RollingMatch::from_string(2, "test", &Default::default()),
|
||||||
|
];
|
||||||
|
|
||||||
|
let matcher = RollingMatcher::new(&matches, Default::default());
|
||||||
|
|
||||||
|
Self { matcher }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'a> Matcher<'a, MatcherState<'a>> for RollingMatcherAdapter {
|
||||||
|
fn process(
|
||||||
|
&'a self,
|
||||||
|
prev_state: Option<&MatcherState<'a>>,
|
||||||
|
event: &MatcherEvent,
|
||||||
|
) -> (MatcherState<'a>, Vec<MatchResult>) {
|
||||||
|
use espanso_match::Matcher;
|
||||||
|
|
||||||
|
let prev_state = prev_state.map(|state| {
|
||||||
|
if let Some(state) = state.as_rolling() {
|
||||||
|
state
|
||||||
|
} else {
|
||||||
|
panic!("invalid state type received in RollingMatcherAdapter")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let event = event.into();
|
||||||
|
|
||||||
|
let (state, results) = self.matcher.process(prev_state, event);
|
||||||
|
|
||||||
|
let enum_state = MatcherState::Rolling(state);
|
||||||
|
let results: Vec<MatchResult> = results.into_iter().map(|result| result.into()).collect();
|
||||||
|
|
||||||
|
(enum_state, results)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&MatcherEvent> for espanso_match::event::Event {
|
||||||
|
fn from(event: &MatcherEvent) -> Self {
|
||||||
|
match event {
|
||||||
|
MatcherEvent::Key { key, chars } => espanso_match::event::Event::Key {
|
||||||
|
key: key.clone().into(),
|
||||||
|
chars: chars.to_owned(),
|
||||||
|
},
|
||||||
|
MatcherEvent::VirtualSeparator => espanso_match::event::Event::VirtualSeparator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<espanso_match::MatchResult<i32>> for MatchResult {
|
||||||
|
fn from(result: espanso_match::MatchResult<i32>) -> Self {
|
||||||
|
Self {
|
||||||
|
id: result.id,
|
||||||
|
trigger: result.trigger,
|
||||||
|
vars: result.vars,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Key> for espanso_match::event::Key {
|
||||||
|
fn from(key: Key) -> Self {
|
||||||
|
match key {
|
||||||
|
Key::Alt => espanso_match::event::Key::Alt,
|
||||||
|
Key::CapsLock => espanso_match::event::Key::CapsLock,
|
||||||
|
Key::Control => espanso_match::event::Key::Control,
|
||||||
|
Key::Meta => espanso_match::event::Key::Meta,
|
||||||
|
Key::NumLock => espanso_match::event::Key::NumLock,
|
||||||
|
Key::Shift => espanso_match::event::Key::Shift,
|
||||||
|
Key::Enter => espanso_match::event::Key::Enter,
|
||||||
|
Key::Tab => espanso_match::event::Key::Tab,
|
||||||
|
Key::Space => espanso_match::event::Key::Space,
|
||||||
|
Key::ArrowDown => espanso_match::event::Key::ArrowDown,
|
||||||
|
Key::ArrowLeft => espanso_match::event::Key::ArrowLeft,
|
||||||
|
Key::ArrowRight => espanso_match::event::Key::ArrowRight,
|
||||||
|
Key::ArrowUp => espanso_match::event::Key::ArrowUp,
|
||||||
|
Key::End => espanso_match::event::Key::End,
|
||||||
|
Key::Home => espanso_match::event::Key::Home,
|
||||||
|
Key::PageDown => espanso_match::event::Key::PageDown,
|
||||||
|
Key::PageUp => espanso_match::event::Key::PageUp,
|
||||||
|
Key::Escape => espanso_match::event::Key::Escape,
|
||||||
|
Key::Backspace => espanso_match::event::Key::Backspace,
|
||||||
|
Key::F1 => espanso_match::event::Key::F1,
|
||||||
|
Key::F2 => espanso_match::event::Key::F2,
|
||||||
|
Key::F3 => espanso_match::event::Key::F3,
|
||||||
|
Key::F4 => espanso_match::event::Key::F4,
|
||||||
|
Key::F5 => espanso_match::event::Key::F5,
|
||||||
|
Key::F6 => espanso_match::event::Key::F6,
|
||||||
|
Key::F7 => espanso_match::event::Key::F7,
|
||||||
|
Key::F8 => espanso_match::event::Key::F8,
|
||||||
|
Key::F9 => espanso_match::event::Key::F9,
|
||||||
|
Key::F10 => espanso_match::event::Key::F10,
|
||||||
|
Key::F11 => espanso_match::event::Key::F11,
|
||||||
|
Key::F12 => espanso_match::event::Key::F12,
|
||||||
|
Key::F13 => espanso_match::event::Key::F13,
|
||||||
|
Key::F14 => espanso_match::event::Key::F14,
|
||||||
|
Key::F15 => espanso_match::event::Key::F15,
|
||||||
|
Key::F16 => espanso_match::event::Key::F16,
|
||||||
|
Key::F17 => espanso_match::event::Key::F17,
|
||||||
|
Key::F18 => espanso_match::event::Key::F18,
|
||||||
|
Key::F19 => espanso_match::event::Key::F19,
|
||||||
|
Key::F20 => espanso_match::event::Key::F20,
|
||||||
|
Key::Other(_) => espanso_match::event::Key::Other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,10 +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 crate::engine::{Engine, funnel};
|
use funnel::Source;
|
||||||
|
use process::Matcher;
|
||||||
|
|
||||||
|
use crate::engine::{Engine, funnel, process, dispatch};
|
||||||
use super::{CliModule, CliModuleArgs};
|
use super::{CliModule, CliModuleArgs};
|
||||||
|
|
||||||
mod source;
|
mod source;
|
||||||
|
mod matcher;
|
||||||
|
mod executor;
|
||||||
|
|
||||||
pub fn new() -> CliModule {
|
pub fn new() -> CliModule {
|
||||||
#[allow(clippy::needless_update)]
|
#[allow(clippy::needless_update)]
|
||||||
|
@ -35,10 +40,18 @@ pub fn new() -> CliModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn worker_main(args: CliModuleArgs) {
|
fn worker_main(args: CliModuleArgs) {
|
||||||
let funnel = funnel::default(vec![
|
let detect_source = source::detect::init_and_spawn().unwrap(); // TODO: handle error
|
||||||
Box::new(),
|
let sources: Vec<&dyn Source> = vec![&detect_source];
|
||||||
]);
|
let funnel = funnel::default(&sources);
|
||||||
|
|
||||||
let engine = Engine::new(funnel);
|
let matcher = matcher::rolling::RollingMatcherAdapter::new();
|
||||||
|
let matchers: Vec<&dyn Matcher<matcher::MatcherState>> = vec![&matcher];
|
||||||
|
let mut processor = process::default(&matchers);
|
||||||
|
|
||||||
|
let text_injector = executor::text_injector::TextInjectorAdapter::new();
|
||||||
|
let dispatcher = dispatch::default(&text_injector);
|
||||||
|
|
||||||
|
let mut engine = Engine::new(&funnel, &mut processor, &dispatcher);
|
||||||
|
engine.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,25 +17,166 @@
|
||||||
* 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;
|
use espanso_detect::{event::InputEvent, Source};
|
||||||
|
use log::{error, trace};
|
||||||
|
|
||||||
use crate::engine::{event::Event, funnel::Source};
|
use crate::engine::{
|
||||||
|
event::{
|
||||||
|
keyboard::{Key, KeyboardEvent, Status, Variant},
|
||||||
|
Event,
|
||||||
|
},
|
||||||
|
funnel, process,
|
||||||
|
};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
pub struct DetectorSource {
|
pub struct DetectSource {
|
||||||
receiver: Receiver<InputEvent>,
|
receiver: Receiver<InputEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Source for DetectorSource {
|
impl<'a> funnel::Source<'a> for DetectSource {
|
||||||
fn register(&self, select: &Select) -> i32 {
|
fn register(&'a self, select: &mut Select<'a>) -> usize {
|
||||||
todo!()
|
select.recv(&self.receiver)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn receive(&self, select: &SelectedOperation) -> Event {
|
fn receive(&self, op: SelectedOperation) -> Event {
|
||||||
todo!()
|
let input_event = 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(_) => todo!(), // TODO
|
||||||
|
InputEvent::HotKey(_) => todo!(), // TODO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new() {
|
// TODO: pass options
|
||||||
todo!()
|
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 {
|
||||||
|
fn from(key: espanso_detect::event::Key) -> Self {
|
||||||
|
match key {
|
||||||
|
espanso_detect::event::Key::Alt => Key::Alt,
|
||||||
|
espanso_detect::event::Key::CapsLock => Key::CapsLock,
|
||||||
|
espanso_detect::event::Key::Control => Key::Control,
|
||||||
|
espanso_detect::event::Key::Meta => Key::Meta,
|
||||||
|
espanso_detect::event::Key::NumLock => Key::NumLock,
|
||||||
|
espanso_detect::event::Key::Shift => Key::Shift,
|
||||||
|
espanso_detect::event::Key::Enter => Key::Enter,
|
||||||
|
espanso_detect::event::Key::Tab => Key::Tab,
|
||||||
|
espanso_detect::event::Key::Space => Key::Space,
|
||||||
|
espanso_detect::event::Key::ArrowDown => Key::ArrowDown,
|
||||||
|
espanso_detect::event::Key::ArrowLeft => Key::ArrowLeft,
|
||||||
|
espanso_detect::event::Key::ArrowRight => Key::ArrowRight,
|
||||||
|
espanso_detect::event::Key::ArrowUp => Key::ArrowUp,
|
||||||
|
espanso_detect::event::Key::End => Key::End,
|
||||||
|
espanso_detect::event::Key::Home => Key::Home,
|
||||||
|
espanso_detect::event::Key::PageDown => Key::PageDown,
|
||||||
|
espanso_detect::event::Key::PageUp => Key::PageUp,
|
||||||
|
espanso_detect::event::Key::Escape => Key::Escape,
|
||||||
|
espanso_detect::event::Key::Backspace => Key::Backspace,
|
||||||
|
espanso_detect::event::Key::F1 => Key::F1,
|
||||||
|
espanso_detect::event::Key::F2 => Key::F2,
|
||||||
|
espanso_detect::event::Key::F3 => Key::F3,
|
||||||
|
espanso_detect::event::Key::F4 => Key::F4,
|
||||||
|
espanso_detect::event::Key::F5 => Key::F5,
|
||||||
|
espanso_detect::event::Key::F6 => Key::F6,
|
||||||
|
espanso_detect::event::Key::F7 => Key::F7,
|
||||||
|
espanso_detect::event::Key::F8 => Key::F8,
|
||||||
|
espanso_detect::event::Key::F9 => Key::F9,
|
||||||
|
espanso_detect::event::Key::F10 => Key::F10,
|
||||||
|
espanso_detect::event::Key::F11 => Key::F11,
|
||||||
|
espanso_detect::event::Key::F12 => Key::F12,
|
||||||
|
espanso_detect::event::Key::F13 => Key::F13,
|
||||||
|
espanso_detect::event::Key::F14 => Key::F14,
|
||||||
|
espanso_detect::event::Key::F15 => Key::F15,
|
||||||
|
espanso_detect::event::Key::F16 => Key::F16,
|
||||||
|
espanso_detect::event::Key::F17 => Key::F17,
|
||||||
|
espanso_detect::event::Key::F18 => Key::F18,
|
||||||
|
espanso_detect::event::Key::F19 => Key::F19,
|
||||||
|
espanso_detect::event::Key::F20 => Key::F20,
|
||||||
|
espanso_detect::event::Key::Other(code) => Key::Other(code),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<espanso_detect::event::Variant> for Variant {
|
||||||
|
fn from(variant: espanso_detect::event::Variant) -> Self {
|
||||||
|
match variant {
|
||||||
|
espanso_detect::event::Variant::Left => Variant::Left,
|
||||||
|
espanso_detect::event::Variant::Right => Variant::Right,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<espanso_detect::event::Status> for Status {
|
||||||
|
fn from(status: espanso_detect::event::Status) -> Self {
|
||||||
|
match status {
|
||||||
|
espanso_detect::event::Status::Pressed => Status::Pressed,
|
||||||
|
espanso_detect::event::Status::Released => Status::Released,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -20,12 +20,12 @@
|
||||||
use super::{Dispatcher, Executor, TextInjector};
|
use super::{Dispatcher, Executor, TextInjector};
|
||||||
use super::Event;
|
use super::Event;
|
||||||
|
|
||||||
pub struct DefaultDispatcher {
|
pub struct DefaultDispatcher<'a> {
|
||||||
executors: Vec<Box<dyn Executor>>,
|
executors: Vec<Box<dyn Executor + 'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DefaultDispatcher {
|
impl <'a> DefaultDispatcher<'a> {
|
||||||
pub fn new(text_injector: impl TextInjector + 'static) -> Self {
|
pub fn new(text_injector: &'a dyn TextInjector) -> 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)),
|
||||||
|
@ -34,7 +34,7 @@ impl DefaultDispatcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dispatcher for DefaultDispatcher {
|
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) {
|
||||||
|
|
|
@ -21,17 +21,17 @@ use super::super::{Event, Executor, TextInjector};
|
||||||
use crate::engine::event::inject::TextInjectMode;
|
use crate::engine::event::inject::TextInjectMode;
|
||||||
use log::error;
|
use log::error;
|
||||||
|
|
||||||
pub struct TextInjectExecutor<T: TextInjector> {
|
pub struct TextInjectExecutor<'a> {
|
||||||
injector: T,
|
injector: &'a dyn TextInjector,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: TextInjector> TextInjectExecutor<T> {
|
impl<'a> TextInjectExecutor<'a> {
|
||||||
pub fn new(injector: T) -> Self {
|
pub fn new(injector: &'a dyn TextInjector) -> Self {
|
||||||
Self { injector }
|
Self { injector }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: TextInjector> Executor for TextInjectExecutor<T> {
|
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 inject_event.mode == TextInjectMode::Keys {
|
if inject_event.mode == TextInjectMode::Keys {
|
||||||
|
|
|
@ -35,7 +35,7 @@ pub trait TextInjector {
|
||||||
fn inject(&self, text: &str) -> Result<()>;
|
fn inject(&self, text: &str) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default(text_injector: impl TextInjector + 'static) -> impl Dispatcher {
|
pub fn default<'a>(text_injector: &'a dyn TextInjector) -> impl Dispatcher + 'a {
|
||||||
default::DefaultDispatcher::new(
|
default::DefaultDispatcher::new(
|
||||||
text_injector,
|
text_injector,
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,25 +21,25 @@ use crossbeam::channel::Select;
|
||||||
|
|
||||||
use super::{Funnel, FunnelResult, Source};
|
use super::{Funnel, FunnelResult, Source};
|
||||||
|
|
||||||
pub struct DefaultFunnel {
|
pub struct DefaultFunnel<'a> {
|
||||||
sources: Vec<Box<dyn Source>>,
|
sources: &'a [&'a dyn Source<'a>],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DefaultFunnel {
|
impl <'a> DefaultFunnel<'a> {
|
||||||
pub fn new(sources: Vec<Box<dyn Source>>) -> Self {
|
pub fn new(sources: &'a [&'a dyn Source<'a>]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sources
|
sources
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Funnel for DefaultFunnel {
|
impl <'a> Funnel for DefaultFunnel<'a> {
|
||||||
fn receive(&self) -> FunnelResult {
|
fn receive(&self) -> FunnelResult {
|
||||||
let mut select = Select::new();
|
let mut select = Select::new();
|
||||||
|
|
||||||
// First register all the sources to the select operation
|
// First register all the sources to the select operation
|
||||||
for source in self.sources.iter() {
|
for source in self.sources.iter() {
|
||||||
source.register(&select);
|
source.register(&mut select);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the first source (blocking operation)
|
// Wait for the first source (blocking operation)
|
||||||
|
@ -50,7 +50,7 @@ impl Funnel for DefaultFunnel {
|
||||||
.expect("invalid source index returned by select operation");
|
.expect("invalid source index returned by select operation");
|
||||||
|
|
||||||
// Receive (and convert) the event
|
// Receive (and convert) the event
|
||||||
let event = source.receive(&op);
|
let event = source.receive(op);
|
||||||
FunnelResult::Event(event)
|
FunnelResult::Event(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,9 @@ use super::Event;
|
||||||
|
|
||||||
mod default;
|
mod default;
|
||||||
|
|
||||||
pub trait Source {
|
pub trait Source<'a> {
|
||||||
fn register(&self, select: &Select) -> i32;
|
fn register<'b>(&'a self, select: &mut Select<'a>) -> usize;
|
||||||
fn receive(&self, select: &SelectedOperation) -> Event;
|
fn receive<'b>(&'a self, op: SelectedOperation) -> Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Funnel {
|
pub trait Funnel {
|
||||||
|
@ -39,6 +39,6 @@ pub enum FunnelResult {
|
||||||
EndOfStream,
|
EndOfStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default(sources: Vec<Box<dyn Source>>) -> impl Funnel {
|
pub fn default<'a>(sources: &'a [&'a dyn Source<'a>]) -> impl Funnel + 'a {
|
||||||
DefaultFunnel::new(sources)
|
DefaultFunnel::new(sources)
|
||||||
}
|
}
|
|
@ -18,7 +18,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use std::collections::VecDeque;
|
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
dispatch::Dispatcher,
|
dispatch::Dispatcher,
|
||||||
|
@ -32,14 +31,14 @@ pub mod event;
|
||||||
pub mod process;
|
pub mod process;
|
||||||
pub mod funnel;
|
pub mod funnel;
|
||||||
|
|
||||||
pub struct Engine<TFunnel: Funnel, TProcessor: Processor, TDispatcher: Dispatcher> {
|
pub struct Engine<'a> {
|
||||||
funnel: TFunnel,
|
funnel: &'a dyn Funnel,
|
||||||
processor: TProcessor,
|
processor: &'a mut dyn Processor,
|
||||||
dispatcher: TDispatcher,
|
dispatcher: &'a dyn Dispatcher,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <TFunnel: Funnel, TProcessor: Processor, TDispatcher: Dispatcher> Engine<TFunnel, TProcessor, TDispatcher> {
|
impl <'a> Engine<'a> {
|
||||||
pub fn new(funnel: TFunnel, processor: TProcessor, dispatcher: TDispatcher) -> Self {
|
pub fn new(funnel: &'a dyn Funnel, processor: &'a mut dyn Processor, dispatcher: &'a dyn Dispatcher) -> Self {
|
||||||
Self {
|
Self {
|
||||||
funnel,
|
funnel,
|
||||||
processor,
|
processor,
|
||||||
|
@ -51,8 +50,6 @@ impl <TFunnel: Funnel, TProcessor: Processor, TDispatcher: Dispatcher> Engine<TF
|
||||||
loop {
|
loop {
|
||||||
match self.funnel.receive() {
|
match self.funnel.receive() {
|
||||||
FunnelResult::Event(event) => {
|
FunnelResult::Event(event) => {
|
||||||
trace!("received event from stream: {:?}", event);
|
|
||||||
|
|
||||||
let processed_events = self.processor.process(event);
|
let processed_events = self.processor.process(event);
|
||||||
for event in processed_events {
|
for event in processed_events {
|
||||||
self.dispatcher.dispatch(event);
|
self.dispatcher.dispatch(event);
|
||||||
|
|
|
@ -17,16 +17,18 @@
|
||||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use log::trace;
|
||||||
|
|
||||||
use super::{Event, Matcher, Middleware, Processor, middleware::matcher::MatchMiddleware};
|
use super::{Event, Matcher, Middleware, Processor, middleware::matcher::MatchMiddleware};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
pub struct DefaultProcessor {
|
pub struct DefaultProcessor<'a> {
|
||||||
event_queue: VecDeque<Event>,
|
event_queue: VecDeque<Event>,
|
||||||
middleware: Vec<Box<dyn Middleware>>,
|
middleware: Vec<Box<dyn Middleware + 'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DefaultProcessor {
|
impl <'a> DefaultProcessor<'a> {
|
||||||
pub fn new<MatcherState: 'static>(matchers: Vec<Box<dyn Matcher<MatcherState>>>) -> Self {
|
pub fn new<MatcherState>(matchers: &'a [&'a dyn Matcher<'a, MatcherState>]) -> DefaultProcessor<'a> {
|
||||||
Self {
|
Self {
|
||||||
event_queue: VecDeque::new(),
|
event_queue: VecDeque::new(),
|
||||||
middleware: vec![
|
middleware: vec![
|
||||||
|
@ -41,13 +43,16 @@ impl DefaultProcessor {
|
||||||
|
|
||||||
let mut current_queue = VecDeque::new();
|
let mut current_queue = VecDeque::new();
|
||||||
let dispatch = |event: Event| {
|
let dispatch = |event: Event| {
|
||||||
// TODO: add tracing information
|
trace!("dispatched event: {:?}", event);
|
||||||
current_queue.push_front(event);
|
current_queue.push_front(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
for middleware in self.middleware.iter() {
|
for middleware in self.middleware.iter() {
|
||||||
// TODO: add tracing information
|
trace!("middleware received event: {:?}", current_event);
|
||||||
|
|
||||||
current_event = middleware.next(current_event, &dispatch);
|
current_event = middleware.next(current_event, &dispatch);
|
||||||
|
|
||||||
|
trace!("middleware produced event: {:?}", current_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some(event) = current_queue.pop_back() {
|
while let Some(event) = current_queue.pop_back() {
|
||||||
|
@ -61,7 +66,7 @@ impl DefaultProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Processor for DefaultProcessor {
|
impl <'a> Processor for DefaultProcessor<'a> {
|
||||||
fn process(&mut self, event: Event) -> Vec<Event> {
|
fn process(&mut self, event: Event) -> Vec<Event> {
|
||||||
self.event_queue.push_front(event);
|
self.event_queue.push_front(event);
|
||||||
|
|
||||||
|
|
|
@ -25,14 +25,14 @@ use crate::engine::{event::{Event, keyboard::{Key, Status}, matches_detected::{M
|
||||||
|
|
||||||
const MAX_HISTORY: usize = 3; // TODO: get as parameter
|
const MAX_HISTORY: usize = 3; // TODO: get as parameter
|
||||||
|
|
||||||
pub struct MatchMiddleware<State> {
|
pub struct MatchMiddleware<'a, State> {
|
||||||
matchers: Vec<Box<dyn Matcher<State>>>,
|
matchers: &'a [&'a dyn Matcher<'a, State>],
|
||||||
|
|
||||||
matcher_states: RefCell<VecDeque<Vec<State>>>,
|
matcher_states: RefCell<VecDeque<Vec<State>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<State> MatchMiddleware<State> {
|
impl<'a, State> MatchMiddleware<'a, State> {
|
||||||
pub fn new(matchers: Vec<Box<dyn Matcher<State>>>) -> Self {
|
pub fn new(matchers: &'a [&'a dyn Matcher<'a, State>]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
matchers,
|
matchers,
|
||||||
matcher_states: RefCell::new(VecDeque::new()),
|
matcher_states: RefCell::new(VecDeque::new()),
|
||||||
|
@ -40,7 +40,7 @@ impl<State> MatchMiddleware<State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<State> Middleware for MatchMiddleware<State> {
|
impl<'a, State> Middleware for MatchMiddleware<'a, State> {
|
||||||
fn next(&self, event: Event, _: &dyn FnMut(Event)) -> Event {
|
fn next(&self, event: Event, _: &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() {
|
||||||
|
@ -85,6 +85,8 @@ impl<State> Middleware for MatchMiddleware<State> {
|
||||||
matcher_states.pop_front();
|
matcher_states.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("results: {:?}", all_results);
|
||||||
|
|
||||||
if !all_results.is_empty() {
|
if !all_results.is_empty() {
|
||||||
return Event::MatchesDetected(MatchesDetectedEvent {
|
return Event::MatchesDetected(MatchesDetectedEvent {
|
||||||
results: all_results.into_iter().map(|result | {
|
results: all_results.into_iter().map(|result | {
|
||||||
|
|
|
@ -35,10 +35,11 @@ pub trait Processor {
|
||||||
|
|
||||||
// Dependency inversion entities
|
// Dependency inversion entities
|
||||||
|
|
||||||
pub trait Matcher<State> {
|
pub trait Matcher<'a, State> {
|
||||||
fn process(&self, prev_state: Option<&State>, event: &MatcherEvent) -> (State, Vec<MatchResult>);
|
fn process(&'a self, prev_state: Option<&State>, event: &MatcherEvent) -> (State, Vec<MatchResult>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum MatcherEvent {
|
pub enum MatcherEvent {
|
||||||
Key { key: Key, chars: Option<String> },
|
Key { key: Key, chars: Option<String> },
|
||||||
VirtualSeparator,
|
VirtualSeparator,
|
||||||
|
@ -46,11 +47,11 @@ pub enum MatcherEvent {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct MatchResult {
|
pub struct MatchResult {
|
||||||
id: i32,
|
pub id: i32,
|
||||||
trigger: String,
|
pub trigger: String,
|
||||||
vars: HashMap<String, String>,
|
pub vars: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default<MatcherState: 'static>(matchers: Vec<Box<dyn Matcher<MatcherState>>>) -> impl Processor {
|
pub fn default<'a, MatcherState>(matchers: &'a [&'a dyn Matcher<'a, MatcherState>]) -> impl Processor + 'a {
|
||||||
default::DefaultProcessor::new(matchers)
|
default::DefaultProcessor::new(matchers)
|
||||||
}
|
}
|
|
@ -201,7 +201,8 @@ fn main() {
|
||||||
let log_level = match matches.occurrences_of("v") {
|
let log_level = match matches.occurrences_of("v") {
|
||||||
0 => LevelFilter::Warn,
|
0 => LevelFilter::Warn,
|
||||||
1 => LevelFilter::Info,
|
1 => LevelFilter::Info,
|
||||||
_ => LevelFilter::Debug,
|
2 => LevelFilter::Debug,
|
||||||
|
_ => LevelFilter::Trace,
|
||||||
};
|
};
|
||||||
|
|
||||||
let handler = CLI_HANDLERS
|
let handler = CLI_HANDLERS
|
||||||
|
|
Loading…
Reference in New Issue
Block a user