refactor(core): move engine into separate module

This commit is contained in:
Federico Terzi 2021-08-14 11:08:20 +02:00
parent 974e405b23
commit 1713b078be
71 changed files with 332 additions and 3374 deletions

20
Cargo.lock generated
View File

@ -522,6 +522,7 @@ dependencies = [
"espanso-clipboard", "espanso-clipboard",
"espanso-config", "espanso-config",
"espanso-detect", "espanso-detect",
"espanso-engine",
"espanso-info", "espanso-info",
"espanso-inject", "espanso-inject",
"espanso-ipc", "espanso-ipc",
@ -535,13 +536,11 @@ dependencies = [
"espanso-ui", "espanso-ui",
"fs2", "fs2",
"fs_extra", "fs_extra",
"html2text",
"lazy_static", "lazy_static",
"libc", "libc",
"log", "log",
"log-panics", "log-panics",
"maplit", "maplit",
"markdown",
"named_pipe", "named_pipe",
"notify", "notify",
"opener", "opener",
@ -611,6 +610,19 @@ dependencies = [
"widestring", "widestring",
] ]
[[package]]
name = "espanso-engine"
version = "0.1.0"
dependencies = [
"anyhow",
"crossbeam",
"html2text",
"log",
"markdown",
"tempdir",
"thiserror",
]
[[package]] [[package]]
name = "espanso-info" name = "espanso-info"
version = "0.1.0" version = "0.1.0"
@ -1873,9 +1885,9 @@ dependencies = [
[[package]] [[package]]
name = "siphasher" name = "siphasher"
version = "0.3.5" version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbce6d4507c7e4a3962091436e56e95290cb71fa302d0d270e32130b75fbff27" checksum = "729a25c17d72b06c68cb47955d44fda88ad2d3e7d77e025663fdd69b93dd71a1"
[[package]] [[package]]
name = "slab" name = "slab"

View File

@ -16,4 +16,5 @@ members = [
"espanso-migrate", "espanso-migrate",
"espanso-mac-utils", "espanso-mac-utils",
"espanso-kvs", "espanso-kvs",
"espanso-engine",
] ]

View File

@ -33,6 +33,7 @@ espanso-ipc = { path = "../espanso-ipc" }
espanso-modulo = { path = "../espanso-modulo", optional = true } espanso-modulo = { path = "../espanso-modulo", optional = true }
espanso-migrate = { path = "../espanso-migrate" } espanso-migrate = { path = "../espanso-migrate" }
espanso-kvs = { path = "../espanso-kvs" } espanso-kvs = { path = "../espanso-kvs" }
espanso-engine = { path = "../espanso-engine" }
maplit = "1.0.2" maplit = "1.0.2"
simplelog = "0.9.0" simplelog = "0.9.0"
log = "0.4.14" log = "0.4.14"
@ -45,8 +46,6 @@ enum-as-inner = "0.3.3"
dirs = "3.0.1" dirs = "3.0.1"
serde = { version = "1.0.123", features = ["derive"] } serde = { version = "1.0.123", features = ["derive"] }
serde_json = "1.0.62" serde_json = "1.0.62"
markdown = "0.3.0"
html2text = "0.2.1"
log-panics = "2.0.0" log-panics = "2.0.0"
fs2 = "0.4.3" fs2 = "0.4.3"
serde_yaml = "0.8.17" serde_yaml = "0.8.17"

View File

@ -17,10 +17,9 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use crate::{ use espanso_engine::event::{effect::TextInjectRequest, EventType};
cli::worker::builtin::generate_next_builtin_id,
engine::event::{effect::TextInjectRequest, EventType}, use crate::cli::worker::builtin::generate_next_builtin_id;
};
use super::BuiltInMatch; use super::BuiltInMatch;

View File

@ -21,7 +21,7 @@ use std::cell::Cell;
use espanso_config::config::Config; use espanso_config::config::Config;
use crate::engine::event::EventType; use espanso_engine::event::EventType;
use super::context::Context; use super::context::Context;

View File

@ -17,19 +17,22 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use crate::{cli::worker::builtin::generate_next_builtin_id, engine::event::{EventType}}; use espanso_engine::event::EventType;
use crate::cli::worker::builtin::generate_next_builtin_id;
use super::BuiltInMatch; use super::BuiltInMatch;
pub fn create_match_trigger_search_bar(trigger: Option<String>, hotkey: Option<String>) -> BuiltInMatch { pub fn create_match_trigger_search_bar(
trigger: Option<String>,
hotkey: Option<String>,
) -> BuiltInMatch {
BuiltInMatch { BuiltInMatch {
id: generate_next_builtin_id(), id: generate_next_builtin_id(),
label: "Open search bar", label: "Open search bar",
triggers: trigger.map(|trigger| vec![trigger]).unwrap_or_default(), triggers: trigger.map(|trigger| vec![trigger]).unwrap_or_default(),
hotkey, hotkey,
action: |_| { action: |_| EventType::ShowSearchBar,
EventType::ShowSearchBar
},
..Default::default() ..Default::default()
} }
} }

View File

@ -73,7 +73,7 @@ fn to_app_properties(info: &AppInfo) -> AppProperties {
} }
} }
impl<'a> crate::engine::process::MatchFilter for ConfigManager<'a> { impl<'a> espanso_engine::process::MatchFilter for ConfigManager<'a> {
fn filter_active(&self, matches_ids: &[i32]) -> Vec<i32> { fn filter_active(&self, matches_ids: &[i32]) -> Vec<i32> {
let ids_set: HashSet<i32> = HashSet::from_iter(matches_ids.iter().copied()); let ids_set: HashSet<i32> = HashSet::from_iter(matches_ids.iter().copied());
let (_, match_set) = self.active_context(); let (_, match_set) = self.active_context();
@ -115,13 +115,13 @@ impl<'a> super::engine::process::middleware::render::ConfigProvider<'a> for Conf
} }
} }
impl<'a> crate::engine::dispatch::ModeProvider for ConfigManager<'a> { impl<'a> espanso_engine::dispatch::ModeProvider for ConfigManager<'a> {
fn active_mode(&self) -> crate::engine::dispatch::Mode { fn active_mode(&self) -> espanso_engine::dispatch::Mode {
let config = self.active(); let config = self.active();
match config.backend() { match config.backend() {
espanso_config::config::Backend::Inject => crate::engine::dispatch::Mode::Event, espanso_config::config::Backend::Inject => espanso_engine::dispatch::Mode::Event,
espanso_config::config::Backend::Clipboard => crate::engine::dispatch::Mode::Clipboard, espanso_config::config::Backend::Clipboard => espanso_engine::dispatch::Mode::Clipboard,
espanso_config::config::Backend::Auto => crate::engine::dispatch::Mode::Auto { espanso_config::config::Backend::Auto => espanso_engine::dispatch::Mode::Auto {
clipboard_threshold: config.clipboard_threshold(), clipboard_threshold: config.clipboard_threshold(),
}, },
} }
@ -155,7 +155,7 @@ impl<'a> super::engine::dispatch::executor::InjectParamsProvider for ConfigManag
} }
} }
impl<'a> crate::engine::process::MatcherMiddlewareConfigProvider for ConfigManager<'a> { impl<'a> espanso_engine::process::MatcherMiddlewareConfigProvider for ConfigManager<'a> {
fn max_history_size(&self) -> usize { fn max_history_size(&self) -> usize {
self.default().backspace_limit() self.default().backspace_limit()
} }

View File

@ -17,13 +17,14 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use std::{path::Path}; use std::path::Path;
use anyhow::Result; use anyhow::Result;
use crossbeam::channel::Sender; use crossbeam::channel::Sender;
use espanso_engine::event::ExitMode;
use log::{error, info, warn}; use log::{error, info, warn};
use crate::{engine::event::ExitMode, lock::acquire_daemon_lock}; use crate::lock::acquire_daemon_lock;
const DAEMON_STATUS_CHECK_INTERVAL: u64 = 1000; const DAEMON_STATUS_CHECK_INTERVAL: u64 = 1000;
@ -55,6 +56,8 @@ fn daemon_monitor_main(runtime_dir: &Path, exit_notify: Sender<ExitMode>) {
break; break;
} }
std::thread::sleep(std::time::Duration::from_millis(DAEMON_STATUS_CHECK_INTERVAL)); std::thread::sleep(std::time::Duration::from_millis(
DAEMON_STATUS_CHECK_INTERVAL,
));
} }
} }

View File

@ -23,7 +23,7 @@ use espanso_clipboard::Clipboard;
use espanso_inject::{keys::Key, InjectionOptions, Injector}; use espanso_inject::{keys::Key, InjectionOptions, Injector};
use log::error; use log::error;
use crate::engine::{ use espanso_engine::{
dispatch::HtmlInjector, dispatch::HtmlInjector,
dispatch::{ImageInjector, TextInjector}, dispatch::{ImageInjector, TextInjector},
}; };

View File

@ -19,7 +19,7 @@
use espanso_ui::UIRemote; use espanso_ui::UIRemote;
use crate::engine::dispatch::ContextMenuHandler; use espanso_engine::dispatch::ContextMenuHandler;
pub struct ContextMenuHandlerAdapter<'a> { pub struct ContextMenuHandlerAdapter<'a> {
remote: &'a dyn UIRemote, remote: &'a dyn UIRemote,
@ -32,7 +32,7 @@ impl<'a> ContextMenuHandlerAdapter<'a> {
} }
impl<'a> ContextMenuHandler for ContextMenuHandlerAdapter<'a> { impl<'a> ContextMenuHandler for ContextMenuHandlerAdapter<'a> {
fn show_context_menu(&self, items: &[crate::engine::event::ui::MenuItem]) -> anyhow::Result<()> { fn show_context_menu(&self, items: &[espanso_engine::event::ui::MenuItem]) -> anyhow::Result<()> {
let ui_menu_items: Vec<espanso_ui::menu::MenuItem> = let ui_menu_items: Vec<espanso_ui::menu::MenuItem> =
items.iter().map(convert_to_ui_menu_item).collect(); items.iter().map(convert_to_ui_menu_item).collect();
let ui_menu = espanso_ui::menu::Menu { let ui_menu = espanso_ui::menu::Menu {
@ -46,16 +46,16 @@ impl<'a> ContextMenuHandler for ContextMenuHandlerAdapter<'a> {
} }
fn convert_to_ui_menu_item( fn convert_to_ui_menu_item(
item: &crate::engine::event::ui::MenuItem, item: &espanso_engine::event::ui::MenuItem,
) -> espanso_ui::menu::MenuItem { ) -> espanso_ui::menu::MenuItem {
match item { match item {
crate::engine::event::ui::MenuItem::Simple(simple) => { espanso_engine::event::ui::MenuItem::Simple(simple) => {
espanso_ui::menu::MenuItem::Simple(espanso_ui::menu::SimpleMenuItem { espanso_ui::menu::MenuItem::Simple(espanso_ui::menu::SimpleMenuItem {
id: simple.id, id: simple.id,
label: simple.label.clone(), label: simple.label.clone(),
}) })
} }
crate::engine::event::ui::MenuItem::Sub(sub) => { espanso_engine::event::ui::MenuItem::Sub(sub) => {
espanso_ui::menu::MenuItem::Sub(espanso_ui::menu::SubMenuItem { espanso_ui::menu::MenuItem::Sub(espanso_ui::menu::SubMenuItem {
label: sub.label.clone(), label: sub.label.clone(),
items: sub items: sub
@ -65,6 +65,6 @@ fn convert_to_ui_menu_item(
.collect(), .collect(),
}) })
} }
crate::engine::event::ui::MenuItem::Separator => espanso_ui::menu::MenuItem::Separator, espanso_engine::event::ui::MenuItem::Separator => espanso_ui::menu::MenuItem::Separator,
} }
} }

View File

@ -21,7 +21,7 @@ use std::convert::TryInto;
use espanso_inject::{InjectionOptions, Injector}; use espanso_inject::{InjectionOptions, Injector};
use crate::engine::dispatch::TextInjector; use espanso_engine::dispatch::TextInjector;
use super::InjectParamsProvider; use super::InjectParamsProvider;

View File

@ -19,7 +19,7 @@
use espanso_ui::{UIRemote, icons::TrayIcon}; use espanso_ui::{UIRemote, icons::TrayIcon};
use crate::engine::{dispatch::IconHandler, event::ui::IconStatus}; use espanso_engine::{dispatch::IconHandler, event::ui::IconStatus};
pub struct IconHandlerAdapter<'a> { pub struct IconHandlerAdapter<'a> {
remote: &'a dyn UIRemote, remote: &'a dyn UIRemote,

View File

@ -20,7 +20,7 @@
use std::convert::TryInto; use std::convert::TryInto;
use espanso_inject::{InjectionOptions, Injector}; use espanso_inject::{InjectionOptions, Injector};
use crate::engine::dispatch::KeyInjector; use espanso_engine::dispatch::KeyInjector;
use super::InjectParamsProvider; use super::InjectParamsProvider;
@ -36,7 +36,7 @@ impl<'a> KeyInjectorAdapter<'a> {
} }
impl<'a> KeyInjector for KeyInjectorAdapter<'a> { impl<'a> KeyInjector for KeyInjectorAdapter<'a> {
fn inject_sequence(&self, keys: &[crate::engine::event::input::Key]) -> anyhow::Result<()> { fn inject_sequence(&self, keys: &[espanso_engine::event::input::Key]) -> anyhow::Result<()> {
let params = self.params_provider.get(); let params = self.params_provider.get();
let injection_options = InjectionOptions { let injection_options = InjectionOptions {
@ -53,47 +53,47 @@ impl<'a> KeyInjector for KeyInjectorAdapter<'a> {
} }
} }
fn convert_to_inject_key(key: &crate::engine::event::input::Key) -> espanso_inject::keys::Key { fn convert_to_inject_key(key: &espanso_engine::event::input::Key) -> espanso_inject::keys::Key {
match key { match key {
crate::engine::event::input::Key::Alt => espanso_inject::keys::Key::Alt, espanso_engine::event::input::Key::Alt => espanso_inject::keys::Key::Alt,
crate::engine::event::input::Key::CapsLock => espanso_inject::keys::Key::CapsLock, espanso_engine::event::input::Key::CapsLock => espanso_inject::keys::Key::CapsLock,
crate::engine::event::input::Key::Control => espanso_inject::keys::Key::Control, espanso_engine::event::input::Key::Control => espanso_inject::keys::Key::Control,
crate::engine::event::input::Key::Meta => espanso_inject::keys::Key::Meta, espanso_engine::event::input::Key::Meta => espanso_inject::keys::Key::Meta,
crate::engine::event::input::Key::NumLock => espanso_inject::keys::Key::NumLock, espanso_engine::event::input::Key::NumLock => espanso_inject::keys::Key::NumLock,
crate::engine::event::input::Key::Shift => espanso_inject::keys::Key::Shift, espanso_engine::event::input::Key::Shift => espanso_inject::keys::Key::Shift,
crate::engine::event::input::Key::Enter => espanso_inject::keys::Key::Enter, espanso_engine::event::input::Key::Enter => espanso_inject::keys::Key::Enter,
crate::engine::event::input::Key::Tab => espanso_inject::keys::Key::Tab, espanso_engine::event::input::Key::Tab => espanso_inject::keys::Key::Tab,
crate::engine::event::input::Key::Space => espanso_inject::keys::Key::Space, espanso_engine::event::input::Key::Space => espanso_inject::keys::Key::Space,
crate::engine::event::input::Key::ArrowDown => espanso_inject::keys::Key::ArrowDown, espanso_engine::event::input::Key::ArrowDown => espanso_inject::keys::Key::ArrowDown,
crate::engine::event::input::Key::ArrowLeft => espanso_inject::keys::Key::ArrowLeft, espanso_engine::event::input::Key::ArrowLeft => espanso_inject::keys::Key::ArrowLeft,
crate::engine::event::input::Key::ArrowRight => espanso_inject::keys::Key::ArrowRight, espanso_engine::event::input::Key::ArrowRight => espanso_inject::keys::Key::ArrowRight,
crate::engine::event::input::Key::ArrowUp => espanso_inject::keys::Key::ArrowUp, espanso_engine::event::input::Key::ArrowUp => espanso_inject::keys::Key::ArrowUp,
crate::engine::event::input::Key::End => espanso_inject::keys::Key::End, espanso_engine::event::input::Key::End => espanso_inject::keys::Key::End,
crate::engine::event::input::Key::Home => espanso_inject::keys::Key::Home, espanso_engine::event::input::Key::Home => espanso_inject::keys::Key::Home,
crate::engine::event::input::Key::PageDown => espanso_inject::keys::Key::PageDown, espanso_engine::event::input::Key::PageDown => espanso_inject::keys::Key::PageDown,
crate::engine::event::input::Key::PageUp => espanso_inject::keys::Key::PageUp, espanso_engine::event::input::Key::PageUp => espanso_inject::keys::Key::PageUp,
crate::engine::event::input::Key::Escape => espanso_inject::keys::Key::Escape, espanso_engine::event::input::Key::Escape => espanso_inject::keys::Key::Escape,
crate::engine::event::input::Key::Backspace => espanso_inject::keys::Key::Backspace, espanso_engine::event::input::Key::Backspace => espanso_inject::keys::Key::Backspace,
crate::engine::event::input::Key::F1 => espanso_inject::keys::Key::F1, espanso_engine::event::input::Key::F1 => espanso_inject::keys::Key::F1,
crate::engine::event::input::Key::F2 => espanso_inject::keys::Key::F2, espanso_engine::event::input::Key::F2 => espanso_inject::keys::Key::F2,
crate::engine::event::input::Key::F3 => espanso_inject::keys::Key::F3, espanso_engine::event::input::Key::F3 => espanso_inject::keys::Key::F3,
crate::engine::event::input::Key::F4 => espanso_inject::keys::Key::F4, espanso_engine::event::input::Key::F4 => espanso_inject::keys::Key::F4,
crate::engine::event::input::Key::F5 => espanso_inject::keys::Key::F5, espanso_engine::event::input::Key::F5 => espanso_inject::keys::Key::F5,
crate::engine::event::input::Key::F6 => espanso_inject::keys::Key::F6, espanso_engine::event::input::Key::F6 => espanso_inject::keys::Key::F6,
crate::engine::event::input::Key::F7 => espanso_inject::keys::Key::F7, espanso_engine::event::input::Key::F7 => espanso_inject::keys::Key::F7,
crate::engine::event::input::Key::F8 => espanso_inject::keys::Key::F8, espanso_engine::event::input::Key::F8 => espanso_inject::keys::Key::F8,
crate::engine::event::input::Key::F9 => espanso_inject::keys::Key::F9, espanso_engine::event::input::Key::F9 => espanso_inject::keys::Key::F9,
crate::engine::event::input::Key::F10 => espanso_inject::keys::Key::F10, espanso_engine::event::input::Key::F10 => espanso_inject::keys::Key::F10,
crate::engine::event::input::Key::F11 => espanso_inject::keys::Key::F11, espanso_engine::event::input::Key::F11 => espanso_inject::keys::Key::F11,
crate::engine::event::input::Key::F12 => espanso_inject::keys::Key::F12, espanso_engine::event::input::Key::F12 => espanso_inject::keys::Key::F12,
crate::engine::event::input::Key::F13 => espanso_inject::keys::Key::F13, espanso_engine::event::input::Key::F13 => espanso_inject::keys::Key::F13,
crate::engine::event::input::Key::F14 => espanso_inject::keys::Key::F14, espanso_engine::event::input::Key::F14 => espanso_inject::keys::Key::F14,
crate::engine::event::input::Key::F15 => espanso_inject::keys::Key::F15, espanso_engine::event::input::Key::F15 => espanso_inject::keys::Key::F15,
crate::engine::event::input::Key::F16 => espanso_inject::keys::Key::F16, espanso_engine::event::input::Key::F16 => espanso_inject::keys::Key::F16,
crate::engine::event::input::Key::F17 => espanso_inject::keys::Key::F17, espanso_engine::event::input::Key::F17 => espanso_inject::keys::Key::F17,
crate::engine::event::input::Key::F18 => espanso_inject::keys::Key::F18, espanso_engine::event::input::Key::F18 => espanso_inject::keys::Key::F18,
crate::engine::event::input::Key::F19 => espanso_inject::keys::Key::F19, espanso_engine::event::input::Key::F19 => espanso_inject::keys::Key::F19,
crate::engine::event::input::Key::F20 => espanso_inject::keys::Key::F20, espanso_engine::event::input::Key::F20 => espanso_inject::keys::Key::F20,
crate::engine::event::input::Key::Other(raw) => espanso_inject::keys::Key::Raw(*raw), espanso_engine::event::input::Key::Other(raw) => espanso_inject::keys::Key::Raw(*raw),
} }
} }

View File

@ -21,7 +21,7 @@ use std::process::Stdio;
use anyhow::{bail, Context}; use anyhow::{bail, Context};
use log::{error, info}; use log::{error, info};
use crate::engine::dispatch::SecureInputManager; use espanso_engine::dispatch::SecureInputManager;
pub struct SecureInputManagerAdapter {} pub struct SecureInputManagerAdapter {}

View File

@ -18,9 +18,15 @@
*/ */
use crossbeam::channel::{Receiver, Select, SelectedOperation}; use crossbeam::channel::{Receiver, Select, SelectedOperation};
use espanso_detect::event::{InputEvent}; use espanso_detect::event::InputEvent;
use crate::engine::{event::{Event, EventType, SourceId, input::{HotKeyEvent, Key, KeyboardEvent, MouseButton, MouseEvent, Status, Variant}}, funnel}; use espanso_engine::{
event::{
input::{HotKeyEvent, Key, KeyboardEvent, MouseButton, MouseEvent, Status, Variant},
Event, EventType, SourceId,
},
funnel,
};
pub struct DetectSource { pub struct DetectSource {
pub receiver: Receiver<(InputEvent, SourceId)>, pub receiver: Receiver<(InputEvent, SourceId)>,
@ -39,31 +45,32 @@ impl<'a> funnel::Source<'a> for DetectSource {
InputEvent::Keyboard(keyboard_event) => Event { InputEvent::Keyboard(keyboard_event) => Event {
source_id, source_id,
etype: EventType::Keyboard(KeyboardEvent { etype: EventType::Keyboard(KeyboardEvent {
key: keyboard_event.key.into(), key: convert_to_engine_key(keyboard_event.key),
value: keyboard_event.value, value: keyboard_event.value,
status: keyboard_event.status.into(), status: convert_to_engine_status(keyboard_event.status),
variant: keyboard_event.variant.map(|variant| variant.into()), variant: keyboard_event
.variant
.map(|variant| convert_to_engine_variant(variant)),
}), }),
}, },
InputEvent::Mouse(mouse_event) => Event { InputEvent::Mouse(mouse_event) => Event {
source_id, source_id,
etype: EventType::Mouse(MouseEvent { etype: EventType::Mouse(MouseEvent {
status: mouse_event.status.into(), status: convert_to_engine_status(mouse_event.status),
button: mouse_event.button.into(), button: convert_to_engine_mouse_button(mouse_event.button),
}), }),
}, },
InputEvent::HotKey(hotkey_event) => Event { InputEvent::HotKey(hotkey_event) => Event {
source_id, source_id,
etype: EventType::HotKey(HotKeyEvent { etype: EventType::HotKey(HotKeyEvent {
hotkey_id: hotkey_event.hotkey_id, hotkey_id: hotkey_event.hotkey_id,
}) }),
} },
} }
} }
} }
impl From<espanso_detect::event::Key> for Key { pub fn convert_to_engine_key(key: espanso_detect::event::Key) -> Key {
fn from(key: espanso_detect::event::Key) -> Self {
match key { match key {
espanso_detect::event::Key::Alt => Key::Alt, espanso_detect::event::Key::Alt => Key::Alt,
espanso_detect::event::Key::CapsLock => Key::CapsLock, espanso_detect::event::Key::CapsLock => Key::CapsLock,
@ -107,28 +114,22 @@ impl From<espanso_detect::event::Key> for Key {
espanso_detect::event::Key::Other(code) => Key::Other(code), espanso_detect::event::Key::Other(code) => Key::Other(code),
} }
} }
}
impl From<espanso_detect::event::Variant> for Variant { pub fn convert_to_engine_variant(variant: espanso_detect::event::Variant) -> Variant {
fn from(variant: espanso_detect::event::Variant) -> Self {
match variant { match variant {
espanso_detect::event::Variant::Left => Variant::Left, espanso_detect::event::Variant::Left => Variant::Left,
espanso_detect::event::Variant::Right => Variant::Right, espanso_detect::event::Variant::Right => Variant::Right,
} }
} }
}
impl From<espanso_detect::event::Status> for Status { pub fn convert_to_engine_status(status: espanso_detect::event::Status) -> Status {
fn from(status: espanso_detect::event::Status) -> Self {
match status { match status {
espanso_detect::event::Status::Pressed => Status::Pressed, espanso_detect::event::Status::Pressed => Status::Pressed,
espanso_detect::event::Status::Released => Status::Released, espanso_detect::event::Status::Released => Status::Released,
} }
} }
}
impl From<espanso_detect::event::MouseButton> for MouseButton { pub fn convert_to_engine_mouse_button(button: espanso_detect::event::MouseButton) -> MouseButton {
fn from(button: espanso_detect::event::MouseButton) -> Self {
match button { match button {
espanso_detect::event::MouseButton::Left => MouseButton::Left, espanso_detect::event::MouseButton::Left => MouseButton::Left,
espanso_detect::event::MouseButton::Right => MouseButton::Right, espanso_detect::event::MouseButton::Right => MouseButton::Right,
@ -140,4 +141,3 @@ impl From<espanso_detect::event::MouseButton> for MouseButton {
espanso_detect::event::MouseButton::Button5 => MouseButton::Button5, espanso_detect::event::MouseButton::Button5 => MouseButton::Button5,
} }
} }
}

View File

@ -19,7 +19,7 @@
use crossbeam::channel::{Receiver, Select, SelectedOperation}; use crossbeam::channel::{Receiver, Select, SelectedOperation};
use crate::engine::{event::{Event, EventType, ExitMode}, funnel}; use espanso_engine::{event::{Event, EventType, ExitMode}, funnel};
use super::sequencer::Sequencer; use super::sequencer::Sequencer;

View File

@ -24,7 +24,7 @@ use std::{
use log::warn; use log::warn;
use crate::engine::process::ModifierStatusProvider; use espanso_engine::process::ModifierStatusProvider;
/// This duration represents the maximum length for which a pressed modifier /// This duration represents the maximum length for which a pressed modifier
/// event is considered valid. This is useful when the "release" event is /// event is considered valid. This is useful when the "release" event is

View File

@ -19,12 +19,10 @@
use crossbeam::channel::{Receiver, Select, SelectedOperation}; use crossbeam::channel::{Receiver, Select, SelectedOperation};
use crate::{ use crate::cli::worker::secure_input::SecureInputEvent;
cli::worker::secure_input::SecureInputEvent, use espanso_engine::{
engine::{
event::{internal::SecureInputEnabledEvent, Event, EventType}, event::{internal::SecureInputEnabledEvent, Event, EventType},
funnel, funnel,
},
}; };
use super::sequencer::Sequencer; use super::sequencer::Sequencer;

View File

@ -22,7 +22,7 @@ use std::sync::{
Arc, Arc,
}; };
use crate::engine::{event::SourceId, process::EventSequenceProvider}; use espanso_engine::{event::SourceId, process::EventSequenceProvider};
#[derive(Clone)] #[derive(Clone)]
pub struct Sequencer { pub struct Sequencer {

View File

@ -20,7 +20,7 @@
use crossbeam::channel::{Receiver, Select, SelectedOperation}; use crossbeam::channel::{Receiver, Select, SelectedOperation};
use espanso_ui::event::UIEvent; use espanso_ui::event::UIEvent;
use crate::engine::{ use espanso_engine::{
event::{input::ContextMenuClickedEvent, Event, EventType}, event::{input::ContextMenuClickedEvent, Event, EventType},
funnel, funnel,
}; };

View File

@ -23,6 +23,7 @@ use anyhow::Result;
use crossbeam::channel::Receiver; use crossbeam::channel::Receiver;
use espanso_config::{config::ConfigStore, matches::store::MatchStore}; use espanso_config::{config::ConfigStore, matches::store::MatchStore};
use espanso_detect::SourceCreationOptions; use espanso_detect::SourceCreationOptions;
use espanso_engine::event::ExitMode;
use espanso_inject::{InjectorCreationOptions, KeyboardStateProvider}; use espanso_inject::{InjectorCreationOptions, KeyboardStateProvider};
use espanso_path::Paths; use espanso_path::Paths;
use espanso_ui::{event::UIEvent, UIRemote}; use espanso_ui::{event::UIEvent, UIRemote};
@ -41,7 +42,7 @@ use crate::{cli::worker::{context::Context, engine::{dispatch::executor::{clipbo
extension::{clipboard::ClipboardAdapter, form::FormProviderAdapter}, extension::{clipboard::ClipboardAdapter, form::FormProviderAdapter},
RendererAdapter, RendererAdapter,
}, },
}}, match_cache::{CombinedMatchCache, MatchCache}}, common_flags::{WORKER_START_REASON_CONFIG_CHANGED, WORKER_START_REASON_KEYBOARD_LAYOUT_CHANGED, WORKER_START_REASON_MANUAL}, engine::event::ExitMode, preferences::Preferences}; }}, match_cache::{CombinedMatchCache, MatchCache}}, common_flags::{WORKER_START_REASON_CONFIG_CHANGED, WORKER_START_REASON_KEYBOARD_LAYOUT_CHANGED, WORKER_START_REASON_MANUAL}, preferences::Preferences};
use super::secure_input::SecureInputEvent; use super::secure_input::SecureInputEvent;
@ -100,13 +101,13 @@ pub fn initialize_and_spawn(
secure_input_receiver, secure_input_receiver,
&sequencer, &sequencer,
); );
let sources: Vec<&dyn crate::engine::funnel::Source> = vec![ let sources: Vec<&dyn espanso_engine::funnel::Source> = vec![
&detect_source, &detect_source,
&exit_source, &exit_source,
&ui_source, &ui_source,
&secure_input_source, &secure_input_source,
]; ];
let funnel = crate::engine::funnel::default(&sources); let funnel = espanso_engine::funnel::default(&sources);
let rolling_matcher = RollingMatcherAdapter::new( let rolling_matcher = RollingMatcherAdapter::new(
&match_converter.get_rolling_matches(), &match_converter.get_rolling_matches(),
@ -121,7 +122,7 @@ pub fn initialize_and_spawn(
}, },
); );
let matchers: Vec< let matchers: Vec<
&dyn crate::engine::process::Matcher< &dyn espanso_engine::process::Matcher<
super::engine::process::middleware::matcher::MatcherState, super::engine::process::middleware::matcher::MatcherState,
>, >,
> = vec![&rolling_matcher, &regex_matcher]; > = vec![&rolling_matcher, &regex_matcher];
@ -171,7 +172,7 @@ pub fn initialize_and_spawn(
let disable_options = let disable_options =
process::middleware::disable::extract_disable_options(&*config_manager.default()); process::middleware::disable::extract_disable_options(&*config_manager.default());
let mut processor = crate::engine::process::default( let mut processor = espanso_engine::process::default(
&matchers, &matchers,
&config_manager, &config_manager,
&selector, &selector,
@ -193,7 +194,7 @@ pub fn initialize_and_spawn(
let context_menu_adapter = ContextMenuHandlerAdapter::new(&*ui_remote); let context_menu_adapter = ContextMenuHandlerAdapter::new(&*ui_remote);
let icon_adapter = IconHandlerAdapter::new(&*ui_remote); let icon_adapter = IconHandlerAdapter::new(&*ui_remote);
let secure_input_adapter = SecureInputManagerAdapter::new(); let secure_input_adapter = SecureInputManagerAdapter::new();
let dispatcher = crate::engine::dispatch::default( let dispatcher = espanso_engine::dispatch::default(
&event_injector, &event_injector,
&clipboard_injector, &clipboard_injector,
&config_manager, &config_manager,
@ -233,7 +234,7 @@ pub fn initialize_and_spawn(
} }
} }
let mut engine = crate::engine::Engine::new(&funnel, &mut processor, &dispatcher); let mut engine = espanso_engine::Engine::new(&funnel, &mut processor, &dispatcher);
let exit_mode = engine.run(); let exit_mode = engine.run();
info!("engine eventloop has terminated, propagating exit event..."); info!("engine eventloop has terminated, propagating exit event...");

View File

@ -19,8 +19,11 @@
use std::time::Duration; use std::time::Duration;
use crate::engine::{event::input::{Key, Variant}, process::DisableOptions};
use espanso_config::config::Config; use espanso_config::config::Config;
use espanso_engine::{
event::input::{Key, Variant},
process::DisableOptions,
};
pub fn extract_disable_options(config: &dyn Config) -> DisableOptions { pub fn extract_disable_options(config: &dyn Config) -> DisableOptions {
let (toggle_key, variant) = match config.toggle_key() { let (toggle_key, variant) = match config.toggle_key() {

View File

@ -19,7 +19,7 @@
use espanso_path::Paths; use espanso_path::Paths;
use crate::engine::process::PathProvider; use espanso_engine::process::PathProvider;
pub struct PathProviderAdapter<'a> { pub struct PathProviderAdapter<'a> {
paths: &'a Paths, paths: &'a Paths,
@ -27,9 +27,7 @@ pub struct PathProviderAdapter<'a> {
impl<'a> PathProviderAdapter<'a> { impl<'a> PathProviderAdapter<'a> {
pub fn new(paths: &'a Paths) -> Self { pub fn new(paths: &'a Paths) -> Self {
Self { Self { paths }
paths,
}
} }
} }

View File

@ -17,12 +17,10 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use espanso_engine::process::MatchSelector;
use log::error; use log::error;
use crate::{ use crate::gui::{SearchItem, SearchUI};
engine::process::MatchSelector,
gui::{SearchItem, SearchUI},
};
const MAX_LABEL_LEN: usize = 100; const MAX_LABEL_LEN: usize = 100;

View File

@ -17,18 +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 espanso_match::rolling::matcher::RollingMatcherState; use espanso_engine::{
use espanso_match::regex::RegexMatcherState;
use crate::engine::{
event::input::Key, event::input::Key,
process::{MatchResult, MatcherEvent}, process::{MatchResult, MatcherEvent},
}; };
use espanso_match::regex::RegexMatcherState;
use espanso_match::rolling::matcher::RollingMatcherState;
use enum_as_inner::EnumAsInner; use enum_as_inner::EnumAsInner;
pub mod rolling;
pub mod regex;
pub mod convert; pub mod convert;
pub mod regex;
pub mod rolling;
#[derive(Clone, EnumAsInner)] #[derive(Clone, EnumAsInner)]
pub enum MatcherState<'a> { pub enum MatcherState<'a> {
@ -36,21 +36,18 @@ pub enum MatcherState<'a> {
Regex(RegexMatcherState), Regex(RegexMatcherState),
} }
impl From<&MatcherEvent> for espanso_match::event::Event { pub fn convert_to_match_event(event: &MatcherEvent) -> espanso_match::event::Event {
fn from(event: &MatcherEvent) -> Self {
match event { match event {
MatcherEvent::Key { key, chars } => espanso_match::event::Event::Key { MatcherEvent::Key { key, chars } => espanso_match::event::Event::Key {
key: key.clone().into(), key: convert_to_match_key(key.clone()),
chars: chars.to_owned(), chars: chars.to_owned(),
}, },
MatcherEvent::VirtualSeparator => espanso_match::event::Event::VirtualSeparator, MatcherEvent::VirtualSeparator => espanso_match::event::Event::VirtualSeparator,
} }
} }
}
impl From<espanso_match::MatchResult<i32>> for MatchResult { pub fn convert_to_engine_result(result: espanso_match::MatchResult<i32>) -> MatchResult {
fn from(result: espanso_match::MatchResult<i32>) -> Self { MatchResult {
Self {
id: result.id, id: result.id,
trigger: result.trigger, trigger: result.trigger,
left_separator: result.left_separator, left_separator: result.left_separator,
@ -58,10 +55,8 @@ impl From<espanso_match::MatchResult<i32>> for MatchResult {
args: result.vars, args: result.vars,
} }
} }
}
impl From<Key> for espanso_match::event::Key { pub fn convert_to_match_key(key: Key) -> espanso_match::event::Key {
fn from(key: Key) -> Self {
match key { match key {
Key::Alt => espanso_match::event::Key::Alt, Key::Alt => espanso_match::event::Key::Alt,
Key::CapsLock => espanso_match::event::Key::CapsLock, Key::CapsLock => espanso_match::event::Key::CapsLock,
@ -105,4 +100,3 @@ impl From<Key> for espanso_match::event::Key {
Key::Other(_) => espanso_match::event::Key::Other, Key::Other(_) => espanso_match::event::Key::Other,
} }
} }
}

View File

@ -17,10 +17,10 @@
* 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::process::{MatchResult, Matcher, MatcherEvent}; use espanso_engine::process::{MatchResult, Matcher, MatcherEvent};
use espanso_match::regex::{RegexMatch, RegexMatcher, RegexMatcherOptions}; use espanso_match::regex::{RegexMatch, RegexMatcher, RegexMatcherOptions};
use super::MatcherState; use super::{convert_to_engine_result, convert_to_match_event, MatcherState};
pub struct RegexMatcherAdapterOptions { pub struct RegexMatcherAdapterOptions {
pub max_buffer_size: usize, pub max_buffer_size: usize,
@ -32,9 +32,12 @@ pub struct RegexMatcherAdapter {
impl RegexMatcherAdapter { impl RegexMatcherAdapter {
pub fn new(matches: &[RegexMatch<i32>], options: &RegexMatcherAdapterOptions) -> Self { pub fn new(matches: &[RegexMatch<i32>], options: &RegexMatcherAdapterOptions) -> Self {
let matcher = RegexMatcher::new(matches, RegexMatcherOptions { let matcher = RegexMatcher::new(
matches,
RegexMatcherOptions {
max_buffer_size: options.max_buffer_size, max_buffer_size: options.max_buffer_size,
}); },
);
Self { matcher } Self { matcher }
} }
@ -55,12 +58,12 @@ impl<'a> Matcher<'a, MatcherState<'a>> for RegexMatcherAdapter {
panic!("invalid state type received in RegexMatcherAdapter") panic!("invalid state type received in RegexMatcherAdapter")
} }
}); });
let event = event.into(); let event = convert_to_match_event(event);
let (state, results) = self.matcher.process(prev_state, event); let (state, results) = self.matcher.process(prev_state, event);
let enum_state = MatcherState::Regex(state); let enum_state = MatcherState::Regex(state);
let results: Vec<MatchResult> = results.into_iter().map(|result| result.into()).collect(); let results: Vec<MatchResult> = results.into_iter().map(convert_to_engine_result).collect();
(enum_state, results) (enum_state, results)
} }

View File

@ -22,11 +22,9 @@ use espanso_match::rolling::{
RollingMatch, RollingMatch,
}; };
use crate::engine::{ use espanso_engine::process::{MatchResult, Matcher, MatcherEvent};
process::{MatchResult, Matcher, MatcherEvent},
};
use super::MatcherState; use super::{convert_to_engine_result, convert_to_match_event, MatcherState};
pub struct RollingMatcherAdapterOptions { pub struct RollingMatcherAdapterOptions {
pub char_word_separators: Vec<String>, pub char_word_separators: Vec<String>,
@ -65,12 +63,12 @@ impl<'a> Matcher<'a, MatcherState<'a>> for RollingMatcherAdapter {
panic!("invalid state type received in RollingMatcherAdapter") panic!("invalid state type received in RollingMatcherAdapter")
} }
}); });
let event = event.into(); let event = convert_to_match_event(event);
let (state, results) = self.matcher.process(prev_state, event); let (state, results) = self.matcher.process(prev_state, event);
let enum_state = MatcherState::Rolling(state); let enum_state = MatcherState::Rolling(state);
let results: Vec<MatchResult> = results.into_iter().map(|result| result.into()).collect(); let results: Vec<MatchResult> = results.into_iter().map(convert_to_engine_result).collect();
(enum_state, results) (enum_state, results)
} }

View File

@ -19,16 +19,14 @@
use espanso_config::matches::{Match, MatchEffect}; use espanso_config::matches::{Match, MatchEffect};
use crate::{ use crate::cli::worker::{builtin::BuiltInMatch, context::Context};
cli::worker::{builtin::BuiltInMatch, context::Context}, use espanso_engine::{
engine::{
event::{ event::{
internal::DetectedMatch, internal::DetectedMatch,
internal::{ImageRequestedEvent, RenderingRequestedEvent, TextFormat}, internal::{ImageRequestedEvent, RenderingRequestedEvent, TextFormat},
EventType, EventType,
}, },
process::Multiplexer, process::Multiplexer,
},
}; };
pub trait MatchProvider<'a> { pub trait MatchProvider<'a> {
@ -46,7 +44,7 @@ pub struct MultiplexAdapter<'a> {
} }
impl<'a> MultiplexAdapter<'a> { impl<'a> MultiplexAdapter<'a> {
pub fn new(provider: &'a dyn MatchProvider<'a>, context: &'a Context) -> Self { pub fn new(provider: &'a dyn MatchProvider<'a>, context: &'a dyn Context) -> Self {
Self { provider, context } Self { provider, context }
} }
} }

View File

@ -27,9 +27,7 @@ use espanso_config::{
}; };
use espanso_render::{CasingStyle, Context, RenderOptions, Template, Value, Variable}; use espanso_render::{CasingStyle, Context, RenderOptions, Template, Value, Variable};
use crate::{ use espanso_engine::process::{Renderer, RendererError};
engine::process::{Renderer, RendererError},
};
pub trait MatchProvider<'a> { pub trait MatchProvider<'a> {
fn matches(&self) -> Vec<&'a Match>; fn matches(&self) -> Vec<&'a Match>;
@ -221,11 +219,14 @@ impl<'a> Renderer<'a> for RendererAdapter<'a> {
for (name, value) in trigger_vars { for (name, value) in trigger_vars {
let mut params = espanso_render::Params::new(); let mut params = espanso_render::Params::new();
params.insert("echo".to_string(), Value::String(value)); params.insert("echo".to_string(), Value::String(value));
augmented.vars.insert(0, Variable { augmented.vars.insert(
0,
Variable {
name, name,
var_type: "echo".to_string(), var_type: "echo".to_string(),
params, params,
}) },
)
} }
Some(augmented) Some(augmented)
} else { } else {

View File

@ -21,10 +21,11 @@ use std::path::Path;
use anyhow::Result; use anyhow::Result;
use crossbeam::channel::Sender; use crossbeam::channel::Sender;
use espanso_engine::event::ExitMode;
use espanso_ipc::{EventHandlerResponse, IPCServer}; use espanso_ipc::{EventHandlerResponse, IPCServer};
use log::{error, warn}; use log::{error, warn};
use crate::{engine::event::ExitMode, ipc::IPCEvent}; use crate::ipc::IPCEvent;
pub fn initialize_and_spawn(runtime_dir: &Path, exit_notify: Sender<ExitMode>) -> Result<()> { pub fn initialize_and_spawn(runtime_dir: &Path, exit_notify: Sender<ExitMode>) -> Result<()> {
let server = crate::ipc::create_worker_ipc_server(runtime_dir)?; let server = crate::ipc::create_worker_ipc_server(runtime_dir)?;
@ -32,8 +33,8 @@ pub fn initialize_and_spawn(runtime_dir: &Path, exit_notify: Sender<ExitMode>) -
std::thread::Builder::new() std::thread::Builder::new()
.name("worker-ipc-handler".to_string()) .name("worker-ipc-handler".to_string())
.spawn(move || { .spawn(move || {
server.run(Box::new(move |event| { server
match event { .run(Box::new(move |event| match event {
IPCEvent::Exit => { IPCEvent::Exit => {
if let Err(err) = exit_notify.send(ExitMode::Exit) { if let Err(err) = exit_notify.send(ExitMode::Exit) {
error!( error!(
@ -62,8 +63,8 @@ pub fn initialize_and_spawn(runtime_dir: &Path, exit_notify: Sender<ExitMode>) -
EventHandlerResponse::NoResponse EventHandlerResponse::NoResponse
} }
} }))
})).expect("unable to spawn IPC server"); .expect("unable to spawn IPC server");
})?; })?;
Ok(()) Ok(())

View File

@ -59,17 +59,17 @@ impl<'a> super::engine::process::middleware::render::MatchProvider<'a> for Match
} }
} }
impl<'a> crate::engine::process::MatchInfoProvider for MatchCache<'a> { impl<'a> espanso_engine::process::MatchInfoProvider for MatchCache<'a> {
fn get_force_mode(&self, match_id: i32) -> Option<crate::engine::event::effect::TextInjectMode> { fn get_force_mode(&self, match_id: i32) -> Option<espanso_engine::event::effect::TextInjectMode> {
let m = self.cache.get(&match_id)?; let m = self.cache.get(&match_id)?;
if let MatchEffect::Text(text_effect) = &m.effect { if let MatchEffect::Text(text_effect) = &m.effect {
if let Some(force_mode) = &text_effect.force_mode { if let Some(force_mode) = &text_effect.force_mode {
match force_mode { match force_mode {
espanso_config::matches::TextInjectMode::Keys => { espanso_config::matches::TextInjectMode::Keys => {
return Some(crate::engine::event::effect::TextInjectMode::Keys) return Some(espanso_engine::event::effect::TextInjectMode::Keys)
} }
espanso_config::matches::TextInjectMode::Clipboard => { espanso_config::matches::TextInjectMode::Clipboard => {
return Some(crate::engine::event::effect::TextInjectMode::Clipboard) return Some(espanso_engine::event::effect::TextInjectMode::Clipboard)
} }
} }
} }
@ -155,7 +155,7 @@ impl<'a> super::engine::process::middleware::multiplex::MatchProvider<'a>
} }
} }
impl<'a> crate::engine::process::MatchProvider for CombinedMatchCache<'a> { impl<'a> espanso_engine::process::MatchProvider for CombinedMatchCache<'a> {
fn get_all_matches_ids(&self) -> Vec<i32> { fn get_all_matches_ids(&self) -> Vec<i32> {
let mut ids: Vec<i32> = self.builtin_match_cache.keys().copied().collect(); let mut ids: Vec<i32> = self.builtin_match_cache.keys().copied().collect();
ids.extend(self.user_match_cache.ids()); ids.extend(self.user_match_cache.ids());

View File

@ -18,10 +18,10 @@
*/ */
use crossbeam::channel::unbounded; use crossbeam::channel::unbounded;
use espanso_engine::event::ExitMode;
use log::{debug, error, info}; use log::{debug, error, info};
use crate::{ use crate::{
engine::event::ExitMode,
exit_code::{ exit_code::{
WORKER_ALREADY_RUNNING, WORKER_EXIT_ALL_PROCESSES, WORKER_GENERAL_ERROR, WORKER_ALREADY_RUNNING, WORKER_EXIT_ALL_PROCESSES, WORKER_GENERAL_ERROR,
WORKER_LEGACY_ALREADY_RUNNING, WORKER_RESTART, WORKER_SUCCESS, WORKER_LEGACY_ALREADY_RUNNING, WORKER_RESTART, WORKER_SUCCESS,
@ -62,9 +62,7 @@ fn worker_main(args: CliModuleArgs) -> i32 {
let cli_args = args.cli_args.expect("missing cli_args in worker main"); let cli_args = args.cli_args.expect("missing cli_args in worker main");
// When restarted, the daemon passes the reason why the worker was restarted (config_change, etc) // When restarted, the daemon passes the reason why the worker was restarted (config_change, etc)
let start_reason = cli_args let start_reason = cli_args.value_of("start-reason").map(String::from);
.value_of("start-reason")
.map(String::from);
debug!("starting with start-reason = {:?}", start_reason); debug!("starting with start-reason = {:?}", start_reason);
// Avoid running multiple worker instances // Avoid running multiple worker instances

View File

@ -1,77 +0,0 @@
/*
* 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::{ContextMenuHandler, Event, IconHandler, ImageInjector, SecureInputManager};
use super::{ModeProvider, Dispatcher, Executor, KeyInjector, TextInjector, HtmlInjector};
pub struct DefaultDispatcher<'a> {
executors: Vec<Box<dyn Executor + 'a>>,
}
impl<'a> DefaultDispatcher<'a> {
pub fn new(
event_injector: &'a dyn TextInjector,
clipboard_injector: &'a dyn TextInjector,
mode_provider: &'a dyn ModeProvider,
key_injector: &'a dyn KeyInjector,
html_injector: &'a dyn HtmlInjector,
image_injector: &'a dyn ImageInjector,
context_menu_handler: &'a dyn ContextMenuHandler,
icon_handler: &'a dyn IconHandler,
secure_input_manager: &'a dyn SecureInputManager,
) -> Self {
Self {
executors: vec![
Box::new(super::executor::text_inject::TextInjectExecutor::new(
event_injector,
clipboard_injector,
mode_provider,
)),
Box::new(super::executor::key_inject::KeyInjectExecutor::new(
key_injector,
)),
Box::new(super::executor::html_inject::HtmlInjectExecutor::new(
html_injector,
)),
Box::new(super::executor::image_inject::ImageInjectExecutor::new(
image_injector,
)),
Box::new(super::executor::context_menu::ContextMenuExecutor::new(
context_menu_handler,
)),
Box::new(super::executor::icon_update::IconUpdateExecutor::new(
icon_handler,
)),
Box::new(super::executor::secure_input::SecureInputExecutor::new(
secure_input_manager,
)),
],
}
}
}
impl<'a> Dispatcher for DefaultDispatcher<'a> {
fn dispatch(&self, event: Event) {
for executor in self.executors.iter() {
if executor.execute(&event) {
break;
}
}
}
}

View File

@ -1,53 +0,0 @@
/*
* 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};
use crate::engine::event::{ui::MenuItem, EventType};
use anyhow::Result;
use log::error;
pub trait ContextMenuHandler {
fn show_context_menu(&self, items: &[MenuItem]) -> Result<()>;
}
pub struct ContextMenuExecutor<'a> {
handler: &'a dyn ContextMenuHandler,
}
impl<'a> ContextMenuExecutor<'a> {
pub fn new(handler: &'a dyn ContextMenuHandler) -> Self {
Self { handler }
}
}
impl<'a> Executor for ContextMenuExecutor<'a> {
fn execute(&self, event: &Event) -> bool {
if let EventType::ShowContextMenu(context_menu_event) = &event.etype {
if let Err(error) = self.handler.show_context_menu(&context_menu_event.items) {
error!("context menu handler reported an error: {:?}", error);
}
return true;
}
false
}
}
// TODO: test

View File

@ -1,61 +0,0 @@
/*
* 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};
use crate::engine::event::EventType;
use anyhow::Result;
use log::error;
pub trait HtmlInjector {
fn inject_html(&self, html: &str, fallback: &str) -> Result<()>;
}
pub struct HtmlInjectExecutor<'a> {
injector: &'a dyn HtmlInjector,
}
impl<'a> HtmlInjectExecutor<'a> {
pub fn new(injector: &'a dyn HtmlInjector) -> Self {
Self { injector }
}
}
impl<'a> Executor for HtmlInjectExecutor<'a> {
fn execute(&self, event: &Event) -> bool {
if let EventType::HtmlInject(inject_event) = &event.etype {
// Render the text fallback for those applications that don't support HTML clipboard
let decorator = html2text::render::text_renderer::TrivialDecorator::new();
let fallback_text =
html2text::from_read_with_decorator(inject_event.html.as_bytes(), 1000000, decorator);
if let Err(error) = self
.injector
.inject_html(&inject_event.html, &fallback_text)
{
error!("html injector reported an error: {:?}", error);
}
return true;
}
false
}
}
// TODO: test

View File

@ -1,53 +0,0 @@
/*
* 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};
use crate::engine::event::{EventType, ui::{IconStatus}};
use anyhow::Result;
use log::error;
pub trait IconHandler {
fn update_icon(&self, status: &IconStatus) -> Result<()>;
}
pub struct IconUpdateExecutor<'a> {
handler: &'a dyn IconHandler,
}
impl<'a> IconUpdateExecutor<'a> {
pub fn new(handler: &'a dyn IconHandler) -> Self {
Self { handler }
}
}
impl<'a> Executor for IconUpdateExecutor<'a> {
fn execute(&self, event: &Event) -> bool {
if let EventType::IconStatusChange(m_event) = &event.etype {
if let Err(error) = self.handler.update_icon(&m_event.status) {
error!("icon handler reported an error: {:?}", error);
}
return true;
}
false
}
}
// TODO: test

View File

@ -1,56 +0,0 @@
/*
* 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};
use crate::engine::event::EventType;
use anyhow::Result;
use log::error;
pub trait ImageInjector {
fn inject_image(&self, path: &str) -> Result<()>;
}
pub struct ImageInjectExecutor<'a> {
injector: &'a dyn ImageInjector,
}
impl<'a> ImageInjectExecutor<'a> {
pub fn new(injector: &'a dyn ImageInjector) -> Self {
Self { injector }
}
}
impl<'a> Executor for ImageInjectExecutor<'a> {
fn execute(&self, event: &Event) -> bool {
if let EventType::ImageInject(inject_event) = &event.etype {
if let Err(error) = self
.injector
.inject_image(&inject_event.image_path)
{
error!("image injector reported an error: {:?}", error);
}
return true;
}
false
}
}
// TODO: test

View File

@ -1,46 +0,0 @@
/*
* 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::event::EventType;
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 EventType::KeySequenceInject(inject_event) = &event.etype {
if let Err(error) = self.injector.inject_sequence(&inject_event.keys) {
error!("key injector reported an error: {}", error);
}
return true;
}
false
}
}

View File

@ -1,26 +0,0 @@
/*
* 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 context_menu;
pub mod icon_update;
pub mod image_inject;
pub mod html_inject;
pub mod key_inject;
pub mod secure_input;
pub mod text_inject;

View File

@ -1,58 +0,0 @@
/*
* 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::event::EventType;
use super::super::{Event, Executor};
use anyhow::Result;
use log::error;
pub trait SecureInputManager {
fn display_secure_input_troubleshoot(&self) -> Result<()>;
fn launch_secure_input_autofix(&self) -> Result<()>;
}
pub struct SecureInputExecutor<'a> {
manager: &'a dyn SecureInputManager,
}
impl<'a> SecureInputExecutor<'a> {
pub fn new(manager: &'a dyn SecureInputManager) -> Self {
Self { manager }
}
}
impl<'a> Executor for SecureInputExecutor<'a> {
fn execute(&self, event: &Event) -> bool {
if let EventType::DisplaySecureInputTroubleshoot = &event.etype {
if let Err(error) = self.manager.display_secure_input_troubleshoot() {
error!("unable to display secure input troubleshoot: {}", error);
}
return true;
} else if let EventType::LaunchSecureInputAutoFix = &event.etype {
if let Err(error) = self.manager.launch_secure_input_autofix() {
error!("unable to launch secure input autofix: {}", error);
}
return true;
}
false
}
}

View File

@ -1,108 +0,0 @@
/*
* 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 anyhow::Result;
use super::super::{Event, Executor};
use crate::engine::event::{EventType, effect::TextInjectMode};
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> {
event_injector: &'a dyn TextInjector,
clipboard_injector: &'a dyn TextInjector,
mode_provider: &'a dyn ModeProvider,
}
impl<'a> TextInjectExecutor<'a> {
pub fn new(
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> {
fn execute(&self, event: &Event) -> bool {
if let EventType::TextInject(inject_event) = &event.etype {
let active_mode = self.mode_provider.active_mode();
let injector = if let Some(force_mode) = &inject_event.force_mode {
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;
}
false
}
}
// TODO: test

View File

@ -1,70 +0,0 @@
/*
* 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 anyhow::Result;
use super::{event::input::Key, Event};
mod default;
mod executor;
pub trait Executor {
fn execute(&self, event: &Event) -> bool;
}
pub trait Dispatcher {
fn dispatch(&self, event: Event);
}
// Re-export dependency injection entities
pub use executor::html_inject::HtmlInjector;
pub use executor::text_inject::{Mode, ModeProvider, TextInjector};
pub use executor::image_inject::{ImageInjector};
pub use executor::context_menu::{ContextMenuHandler};
pub use executor::icon_update::IconHandler;
pub use executor::secure_input::SecureInputManager;
// TODO: move into module
pub trait KeyInjector {
fn inject_sequence(&self, keys: &[Key]) -> Result<()>;
}
pub fn default<'a>(
event_injector: &'a dyn TextInjector,
clipboard_injector: &'a dyn TextInjector,
mode_provider: &'a dyn ModeProvider,
key_injector: &'a dyn KeyInjector,
html_injector: &'a dyn HtmlInjector,
image_injector: &'a dyn ImageInjector,
context_menu_handler: &'a dyn ContextMenuHandler,
icon_handler: &'a dyn IconHandler,
secure_input_manager: &'a dyn SecureInputManager,
) -> impl Dispatcher + 'a {
default::DefaultDispatcher::new(
event_injector,
clipboard_injector,
mode_provider,
key_injector,
html_injector,
image_injector,
context_menu_handler,
icon_handler,
secure_input_manager,
)
}

View File

@ -1,63 +0,0 @@
/*
* 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::input::Key;
#[derive(Debug, Clone, PartialEq)]
pub struct TriggerCompensationEvent {
pub trigger: String,
pub left_separator: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CursorHintCompensationEvent {
pub cursor_hint_back_count: usize,
}
#[derive(Debug, Clone)]
pub struct TextInjectRequest {
pub text: String,
pub force_mode: Option<TextInjectMode>,
}
#[derive(Debug, Clone)]
pub struct MarkdownInjectRequest {
pub markdown: String,
}
#[derive(Debug, Clone)]
pub struct HtmlInjectRequest {
pub html: String,
}
#[derive(Debug, PartialEq, Clone)]
pub enum TextInjectMode {
Keys,
Clipboard,
}
#[derive(Debug, Clone)]
pub struct KeySequenceInjectRequest {
pub keys: Vec<Key>,
}
#[derive(Debug, Clone)]
pub struct ImageInjectRequest {
pub image_path: String,
}

View File

@ -1,123 +0,0 @@
/*
* 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/>.
*/
#[derive(Debug, PartialEq, Clone)]
pub enum Status {
Pressed,
Released,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Variant {
Left,
Right,
}
#[derive(Debug, PartialEq, Clone)]
pub struct KeyboardEvent {
pub key: Key,
pub value: Option<String>,
pub status: Status,
pub variant: Option<Variant>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum MouseButton {
Left,
Right,
Middle,
Button1,
Button2,
Button3,
Button4,
Button5,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MouseEvent {
pub button: MouseButton,
pub status: Status,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Key {
// Modifiers
Alt,
CapsLock,
Control,
Meta,
NumLock,
Shift,
// Whitespace
Enter,
Tab,
Space,
// Navigation
ArrowDown,
ArrowLeft,
ArrowRight,
ArrowUp,
End,
Home,
PageDown,
PageUp,
// UI
Escape,
// Editing keys
Backspace,
// Function keys
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
F13,
F14,
F15,
F16,
F17,
F18,
F19,
F20,
// Other keys, includes the raw code provided by the operating system
Other(i32),
}
#[derive(Debug, Clone, PartialEq)]
pub struct ContextMenuClickedEvent {
pub context_item_id: u32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct HotKeyEvent {
pub hotkey_id: i32,
}

View File

@ -1,91 +0,0 @@
/*
* 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::collections::HashMap;
#[derive(Debug, Clone, PartialEq)]
pub struct MatchesDetectedEvent {
pub matches: Vec<DetectedMatch>,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct DetectedMatch {
pub id: i32,
pub trigger: Option<String>,
pub left_separator: Option<String>,
pub right_separator: Option<String>,
pub args: HashMap<String, String>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MatchSelectedEvent {
pub chosen: DetectedMatch,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CauseCompensatedMatchEvent {
pub m: DetectedMatch,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RenderingRequestedEvent {
pub match_id: i32,
pub trigger: Option<String>,
pub left_separator: Option<String>,
pub right_separator: Option<String>,
pub trigger_args: HashMap<String, String>,
pub format: TextFormat,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TextFormat {
Plain,
Markdown,
Html,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ImageRequestedEvent {
pub match_id: i32,
pub image_path: String,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ImageResolvedEvent {
pub image_path: String,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RenderedEvent {
pub match_id: i32,
pub body: String,
pub format: TextFormat,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DiscardPreviousEvent {
// All Events with a source_id smaller than this one will be discarded
pub minimum_source_id: u32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SecureInputEnabledEvent {
pub app_name: String,
pub app_path: String,
}

View File

@ -1,106 +0,0 @@
/*
* 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 input;
pub mod effect;
pub mod internal;
pub mod ui;
pub type SourceId = u32;
#[derive(Debug, Clone)]
pub struct Event {
// The source id is a unique, monothonically increasing number
// that is given to each event by the source and is propagated
// to all consequential events.
// For example, if a keyboard event with source_id = 5 generates
// a detected match event, this event will have source_id = 5
pub source_id: SourceId,
pub etype: EventType,
}
impl Event {
pub fn caused_by(cause_id: SourceId, event_type: EventType) -> Event {
Event {
source_id: cause_id,
etype: event_type,
}
}
}
#[derive(Debug, Clone)]
pub enum EventType {
NOOP,
ProcessingError(String),
ExitRequested(ExitMode),
Exit(ExitMode),
Heartbeat,
// Inputs
Keyboard(input::KeyboardEvent),
Mouse(input::MouseEvent),
HotKey(input::HotKeyEvent),
TrayIconClicked,
ContextMenuClicked(input::ContextMenuClickedEvent),
// Internal
MatchesDetected(internal::MatchesDetectedEvent),
MatchSelected(internal::MatchSelectedEvent),
CauseCompensatedMatch(internal::CauseCompensatedMatchEvent),
RenderingRequested(internal::RenderingRequestedEvent),
ImageRequested(internal::ImageRequestedEvent),
Rendered(internal::RenderedEvent),
ImageResolved(internal::ImageResolvedEvent),
MatchInjected,
DiscardPrevious(internal::DiscardPreviousEvent),
Disabled,
Enabled,
DisableRequest,
EnableRequest,
SecureInputEnabled(internal::SecureInputEnabledEvent),
SecureInputDisabled,
// Effects
TriggerCompensation(effect::TriggerCompensationEvent),
CursorHintCompensation(effect::CursorHintCompensationEvent),
KeySequenceInject(effect::KeySequenceInjectRequest),
TextInject(effect::TextInjectRequest),
MarkdownInject(effect::MarkdownInjectRequest),
HtmlInject(effect::HtmlInjectRequest),
ImageInject(effect::ImageInjectRequest),
// UI
ShowContextMenu(ui::ShowContextMenuEvent),
IconStatusChange(ui::IconStatusChangeEvent),
DisplaySecureInputTroubleshoot,
ShowSearchBar,
// Other
LaunchSecureInputAutoFix,
}
#[derive(Debug, Clone)]
pub enum ExitMode {
Exit,
ExitAllProcesses,
RestartWorker,
}

View File

@ -1,54 +0,0 @@
/*
* 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/>.
*/
#[derive(Debug, Clone, PartialEq)]
pub struct ShowContextMenuEvent {
pub items: Vec<MenuItem>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum MenuItem {
Simple(SimpleMenuItem),
Sub(SubMenuItem),
Separator,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SimpleMenuItem {
pub id: u32,
pub label: String,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SubMenuItem {
pub label: String,
pub items: Vec<MenuItem>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct IconStatusChangeEvent {
pub status: IconStatus
}
#[derive(Debug, Clone, PartialEq)]
pub enum IconStatus {
Enabled,
Disabled,
SecureInputDisabled,
}

View File

@ -1,56 +0,0 @@
/*
* 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 crossbeam::channel::Select;
use super::{Funnel, FunnelResult, Source};
pub struct DefaultFunnel<'a> {
sources: &'a [&'a dyn Source<'a>],
}
impl <'a> DefaultFunnel<'a> {
pub fn new(sources: &'a [&'a dyn Source<'a>]) -> Self {
Self {
sources
}
}
}
impl <'a> Funnel for DefaultFunnel<'a> {
fn receive(&self) -> FunnelResult {
let mut select = Select::new();
// First register all the sources to the select operation
for source in self.sources.iter() {
source.register(&mut select);
}
// Wait for the first source (blocking operation)
let op = select.select();
let source = self
.sources
.get(op.index())
.expect("invalid source index returned by select operation");
// Receive (and convert) the event
let event = source.receive(op);
FunnelResult::Event(event)
}
}

View File

@ -1,44 +0,0 @@
/*
* 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 crossbeam::{channel::Select, channel::SelectedOperation};
use self::default::DefaultFunnel;
use super::Event;
mod default;
pub trait Source<'a> {
fn register<'b>(&'a self, select: &mut Select<'a>) -> usize;
fn receive<'b>(&'a self, op: SelectedOperation) -> Event;
}
pub trait Funnel {
fn receive(&self) -> FunnelResult;
}
pub enum FunnelResult {
Event(Event),
EndOfStream,
}
pub fn default<'a>(sources: &'a [&'a dyn Source<'a>]) -> impl Funnel + 'a {
DefaultFunnel::new(sources)
}

View File

@ -1,65 +0,0 @@
/*
* 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};
use self::{dispatch::Dispatcher, event::{Event, EventType, ExitMode}, funnel::{Funnel, FunnelResult}, process::Processor};
pub mod dispatch;
pub mod event;
pub mod process;
pub mod funnel;
pub struct Engine<'a> {
funnel: &'a dyn Funnel,
processor: &'a mut dyn Processor,
dispatcher: &'a dyn Dispatcher,
}
impl <'a> Engine<'a> {
pub fn new(funnel: &'a dyn Funnel, processor: &'a mut dyn Processor, dispatcher: &'a dyn Dispatcher) -> Self {
Self {
funnel,
processor,
dispatcher,
}
}
pub fn run(&mut self) -> ExitMode {
loop {
match self.funnel.receive() {
FunnelResult::Event(event) => {
let processed_events = self.processor.process(event);
for event in processed_events {
if let EventType::Exit(mode) = &event.etype {
debug!("exit event received with mode {:?}, exiting engine", mode);
return mode.clone();
}
self.dispatcher.dispatch(event);
}
}
FunnelResult::EndOfStream => {
debug!("end of stream received");
return ExitMode::Exit;
}
}
}
}
}

View File

@ -1,124 +0,0 @@
/*
* 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::trace;
use super::{DisableOptions, MatchFilter, MatchInfoProvider, MatchProvider, MatchSelector, Matcher, MatcherMiddlewareConfigProvider, Middleware, Multiplexer, PathProvider, Processor, Renderer, middleware::{
match_select::MatchSelectMiddleware, matcher::MatcherMiddleware, multiplex::MultiplexMiddleware,
render::RenderMiddleware, action::{ActionMiddleware, EventSequenceProvider}, cursor_hint::CursorHintMiddleware, cause::CauseCompensateMiddleware,
delay_modifiers::{DelayForModifierReleaseMiddleware, ModifierStatusProvider}, markdown::MarkdownMiddleware,
past_discard::PastEventsDiscardMiddleware,
}};
use crate::engine::{event::{Event, EventType}, process::middleware::{context_menu::ContextMenuMiddleware, disable::DisableMiddleware, exit::ExitMiddleware, hotkey::HotKeyMiddleware, icon_status::IconStatusMiddleware, image_resolve::ImageResolverMiddleware, search::SearchMiddleware}};
use std::collections::VecDeque;
pub struct DefaultProcessor<'a> {
event_queue: VecDeque<Event>,
middleware: Vec<Box<dyn Middleware + 'a>>,
}
impl<'a> DefaultProcessor<'a> {
pub fn new<MatcherState>(
matchers: &'a [&'a dyn Matcher<'a, MatcherState>],
match_filter: &'a dyn MatchFilter,
match_selector: &'a dyn MatchSelector,
multiplexer: &'a dyn Multiplexer,
renderer: &'a dyn Renderer<'a>,
match_info_provider: &'a dyn MatchInfoProvider,
modifier_status_provider: &'a dyn ModifierStatusProvider,
event_sequence_provider: &'a dyn EventSequenceProvider,
path_provider: &'a dyn PathProvider,
disable_options: DisableOptions,
matcher_options_provider: &'a dyn MatcherMiddlewareConfigProvider,
match_provider: &'a dyn MatchProvider,
) -> DefaultProcessor<'a> {
Self {
event_queue: VecDeque::new(),
middleware: vec![
Box::new(PastEventsDiscardMiddleware::new()),
Box::new(DisableMiddleware::new(disable_options)),
Box::new(IconStatusMiddleware::new()),
Box::new(MatcherMiddleware::new(matchers, matcher_options_provider)),
Box::new(ContextMenuMiddleware::new()),
Box::new(HotKeyMiddleware::new()),
Box::new(MatchSelectMiddleware::new(match_filter, match_selector)),
Box::new(CauseCompensateMiddleware::new()),
Box::new(MultiplexMiddleware::new(multiplexer)),
Box::new(RenderMiddleware::new(renderer)),
Box::new(ImageResolverMiddleware::new(path_provider)),
Box::new(CursorHintMiddleware::new()),
Box::new(ExitMiddleware::new()),
Box::new(ActionMiddleware::new(match_info_provider, event_sequence_provider)),
Box::new(SearchMiddleware::new(match_provider)),
Box::new(MarkdownMiddleware::new()),
Box::new(DelayForModifierReleaseMiddleware::new(modifier_status_provider)),
],
}
}
fn process_one(&mut self) -> Option<Event> {
if let Some(event) = self.event_queue.pop_back() {
let mut current_event = event;
let mut current_queue = VecDeque::new();
let mut dispatch = |event: Event| {
trace!("dispatched event: {:?}", event);
current_queue.push_front(event);
};
trace!("--------------- new event -----------------");
for middleware in self.middleware.iter() {
trace!("middleware '{}' received event: {:?}", middleware.name(), current_event);
current_event = middleware.next(current_event, &mut dispatch);
trace!("middleware '{}' produced event: {:?}", middleware.name(), current_event);
if let EventType::NOOP = current_event.etype {
trace!("interrupting chain as the event is NOOP");
break;
}
}
while let Some(event) = current_queue.pop_back() {
self.event_queue.push_front(event);
}
Some(current_event)
} else {
None
}
}
}
impl<'a> Processor for DefaultProcessor<'a> {
fn process(&mut self, event: Event) -> Vec<Event> {
self.event_queue.push_front(event);
let mut processed_events = Vec::new();
while !self.event_queue.is_empty() {
if let Some(event) = self.process_one() {
processed_events.push(event);
}
}
processed_events
}
}

View File

@ -1,136 +0,0 @@
/*
* 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::Middleware;
use crate::engine::event::{
effect::{
HtmlInjectRequest, ImageInjectRequest, KeySequenceInjectRequest, MarkdownInjectRequest,
TextInjectMode, TextInjectRequest,
},
input::Key,
internal::{DiscardPreviousEvent, TextFormat},
Event, EventType,
};
pub trait MatchInfoProvider {
fn get_force_mode(&self, match_id: i32) -> Option<TextInjectMode>;
}
pub trait EventSequenceProvider {
fn get_next_id(&self) -> u32;
}
pub struct ActionMiddleware<'a> {
match_info_provider: &'a dyn MatchInfoProvider,
event_sequence_provider: &'a dyn EventSequenceProvider,
}
impl<'a> ActionMiddleware<'a> {
pub fn new(
match_info_provider: &'a dyn MatchInfoProvider,
event_sequence_provider: &'a dyn EventSequenceProvider,
) -> Self {
Self {
match_info_provider,
event_sequence_provider,
}
}
}
impl<'a> Middleware for ActionMiddleware<'a> {
fn name(&self) -> &'static str {
"action"
}
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
match &event.etype {
EventType::Rendered(_) | EventType::ImageResolved(_) => {
dispatch(Event::caused_by(event.source_id, EventType::MatchInjected));
dispatch(Event::caused_by(
event.source_id,
EventType::DiscardPrevious(DiscardPreviousEvent {
minimum_source_id: self.event_sequence_provider.get_next_id(),
}),
));
match &event.etype {
EventType::Rendered(m_event) => Event::caused_by(
event.source_id,
match m_event.format {
TextFormat::Plain => EventType::TextInject(TextInjectRequest {
text: m_event.body.clone(),
force_mode: self.match_info_provider.get_force_mode(m_event.match_id),
}),
TextFormat::Html => EventType::HtmlInject(HtmlInjectRequest {
html: m_event.body.clone(),
}),
TextFormat::Markdown => EventType::MarkdownInject(MarkdownInjectRequest {
markdown: m_event.body.clone(),
}),
},
),
EventType::ImageResolved(m_event) => Event::caused_by(
event.source_id,
EventType::ImageInject(ImageInjectRequest {
image_path: m_event.image_path.clone(),
}),
),
_ => unreachable!()
}
}
EventType::CursorHintCompensation(m_event) => {
dispatch(Event::caused_by(
event.source_id,
EventType::DiscardPrevious(DiscardPreviousEvent {
minimum_source_id: self.event_sequence_provider.get_next_id(),
}),
));
Event::caused_by(
event.source_id,
EventType::KeySequenceInject(KeySequenceInjectRequest {
keys: (0..m_event.cursor_hint_back_count)
.map(|_| Key::ArrowLeft)
.collect(),
}),
)
}
EventType::TriggerCompensation(m_event) => {
let mut backspace_count = m_event.trigger.chars().count();
// We want to preserve the left separator if present
if let Some(left_separator) = &m_event.left_separator {
backspace_count -= left_separator.chars().count();
}
Event::caused_by(
event.source_id,
EventType::KeySequenceInject(KeySequenceInjectRequest {
keys: (0..backspace_count).map(|_| Key::Backspace).collect(),
}),
)
}
_ => event,
}
// TODO: handle images
}
}
// TODO: test

View File

@ -1,58 +0,0 @@
/*
* 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::Middleware;
use crate::engine::{event::{Event, EventType, effect::TriggerCompensationEvent, internal::CauseCompensatedMatchEvent}};
pub struct CauseCompensateMiddleware {}
impl CauseCompensateMiddleware {
pub fn new() -> Self {
Self {}
}
}
impl Middleware for CauseCompensateMiddleware {
fn name(&self) -> &'static str {
"cause_compensate"
}
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
if let EventType::MatchSelected(m_event) = &event.etype {
let compensated_event =
Event::caused_by(event.source_id, EventType::CauseCompensatedMatch(CauseCompensatedMatchEvent { m: m_event.chosen.clone() }));
if let Some(trigger) = &m_event.chosen.trigger {
dispatch(compensated_event);
// Before the event, place a trigger compensation
return Event::caused_by(event.source_id, EventType::TriggerCompensation(TriggerCompensationEvent {
trigger: trigger.clone(),
left_separator: m_event.chosen.left_separator.clone(),
}));
} else {
return compensated_event;
}
}
event
}
}
// TODO: test

View File

@ -1,172 +0,0 @@
/*
* This file is part of espanso.
*
* Copyright id: (), label: () id: (), label: () id: (), label: ()(C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use std::cell::RefCell;
use super::super::Middleware;
use crate::engine::event::{
ui::{MenuItem, ShowContextMenuEvent, SimpleMenuItem},
Event, EventType, ExitMode,
};
const CONTEXT_ITEM_EXIT: u32 = 0;
const CONTEXT_ITEM_RELOAD: u32 = 1;
const CONTEXT_ITEM_ENABLE: u32 = 2;
const CONTEXT_ITEM_DISABLE: u32 = 3;
const CONTEXT_ITEM_SECURE_INPUT_EXPLAIN: u32 = 4;
const CONTEXT_ITEM_SECURE_INPUT_TRIGGER_WORKAROUND: u32 = 5;
const CONTEXT_ITEM_OPEN_SEARCH: u32 = 6;
pub struct ContextMenuMiddleware {
is_enabled: RefCell<bool>,
is_secure_input_enabled: RefCell<bool>,
}
impl ContextMenuMiddleware {
pub fn new() -> Self {
Self {
is_enabled: RefCell::new(true),
is_secure_input_enabled: RefCell::new(false),
}
}
}
impl Middleware for ContextMenuMiddleware {
fn name(&self) -> &'static str {
"context_menu"
}
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
let mut is_enabled = self.is_enabled.borrow_mut();
let mut is_secure_input_enabled = self.is_secure_input_enabled.borrow_mut();
match &event.etype {
EventType::TrayIconClicked => {
// TODO: fetch top matches for the active config to be added
let mut items = vec![
MenuItem::Simple(if *is_enabled {
SimpleMenuItem {
id: CONTEXT_ITEM_DISABLE,
label: "Disable".to_string(),
}
} else {
SimpleMenuItem {
id: CONTEXT_ITEM_ENABLE,
label: "Enable".to_string(),
}
}),
MenuItem::Simple(SimpleMenuItem {
id: CONTEXT_ITEM_OPEN_SEARCH,
label: "Open search bar".to_string(),
}),
MenuItem::Separator,
MenuItem::Simple(SimpleMenuItem {
id: CONTEXT_ITEM_RELOAD,
label: "Reload config".to_string(),
}),
MenuItem::Separator,
MenuItem::Simple(SimpleMenuItem {
id: CONTEXT_ITEM_EXIT,
label: "Exit espanso".to_string(),
}),
];
if *is_secure_input_enabled {
items.insert(0, MenuItem::Simple(SimpleMenuItem {
id: CONTEXT_ITEM_SECURE_INPUT_EXPLAIN,
label: "Why is espanso not working?".to_string(),
}));
items.insert(1, MenuItem::Simple(SimpleMenuItem {
id: CONTEXT_ITEM_SECURE_INPUT_TRIGGER_WORKAROUND,
label: "Launch SecureInput auto-fix".to_string(),
}));
items.insert(2, MenuItem::Separator);
}
// TODO: my idea is to use a set of reserved u32 ids for built-in
// actions such as Exit, Open Editor etc
// then we need some u32 for the matches, so we need to create
// a mapping structure match_id <-> context-menu-id
return Event::caused_by(
event.source_id,
EventType::ShowContextMenu(ShowContextMenuEvent {
// TODO: add actual entries
items,
}),
);
}
EventType::ContextMenuClicked(context_click_event) => {
match context_click_event.context_item_id {
CONTEXT_ITEM_EXIT => Event::caused_by(
event.source_id,
EventType::ExitRequested(ExitMode::ExitAllProcesses),
),
CONTEXT_ITEM_RELOAD => Event::caused_by(
event.source_id,
EventType::ExitRequested(ExitMode::RestartWorker),
),
CONTEXT_ITEM_ENABLE => {
dispatch(Event::caused_by(event.source_id, EventType::EnableRequest));
Event::caused_by(event.source_id, EventType::NOOP)
}
CONTEXT_ITEM_DISABLE => {
dispatch(Event::caused_by(event.source_id, EventType::DisableRequest));
Event::caused_by(event.source_id, EventType::NOOP)
}
CONTEXT_ITEM_SECURE_INPUT_EXPLAIN => {
dispatch(Event::caused_by(event.source_id, EventType::DisplaySecureInputTroubleshoot));
Event::caused_by(event.source_id, EventType::NOOP)
}
CONTEXT_ITEM_SECURE_INPUT_TRIGGER_WORKAROUND => {
dispatch(Event::caused_by(event.source_id, EventType::LaunchSecureInputAutoFix));
Event::caused_by(event.source_id, EventType::NOOP)
}
CONTEXT_ITEM_OPEN_SEARCH => {
dispatch(Event::caused_by(event.source_id, EventType::ShowSearchBar));
Event::caused_by(event.source_id, EventType::NOOP)
}
custom => {
// TODO: handle dynamic items
todo!()
}
}
}
EventType::Disabled => {
*is_enabled = false;
event
}
EventType::Enabled => {
*is_enabled = true;
event
}
EventType::SecureInputEnabled(_) => {
*is_secure_input_enabled = true;
event
}
EventType::SecureInputDisabled => {
*is_secure_input_enabled = false;
event
}
_ => event,
}
}
}
// TODO: test

View File

@ -1,77 +0,0 @@
/*
* 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::Middleware;
use crate::engine::{event::{Event, EventType, effect::CursorHintCompensationEvent, internal::RenderedEvent}};
pub struct CursorHintMiddleware {}
impl CursorHintMiddleware {
pub fn new() -> Self {
Self {}
}
}
impl Middleware for CursorHintMiddleware {
fn name(&self) -> &'static str {
"cursor_hint"
}
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
if let EventType::Rendered(m_event) = event.etype {
let (body, cursor_hint_back_count) = process_cursor_hint(m_event.body);
if let Some(cursor_hint_back_count) = cursor_hint_back_count {
dispatch(Event::caused_by(event.source_id, EventType::CursorHintCompensation(CursorHintCompensationEvent {
cursor_hint_back_count,
})));
}
// Alter the rendered event to remove the cursor hint from the body
return Event::caused_by(event.source_id, EventType::Rendered(RenderedEvent {
body,
..m_event
}));
}
event
}
}
// TODO: test
fn process_cursor_hint(body: String) -> (String, Option<usize>) {
if let Some(index) = body.find("$|$") {
// Convert the byte index to a char index
let char_str = &body[0..index];
let char_index = char_str.chars().count();
let total_size = body.chars().count();
// Remove the $|$ placeholder
let body = body.replace("$|$", "");
// Calculate the amount of rewind moves needed (LEFT ARROW).
// Subtract also 3, equal to the number of chars of the placeholder "$|$"
let moves = total_size - char_index - 3;
(body, Some(moves))
} else {
(body, None)
}
}
// TODO: test

View File

@ -1,88 +0,0 @@
/*
* 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::{
time::{Duration, Instant},
};
use log::{trace, warn};
use super::super::Middleware;
use crate::engine::event::{Event, EventType};
/// Maximum time to wait for modifiers being released before
/// giving up.
const MODIFIER_DELAY_TIMEOUT: Duration = Duration::from_secs(3);
pub trait ModifierStatusProvider {
fn is_any_conflicting_modifier_pressed(&self) -> bool;
}
/// This middleware is used to delay the injection of text until
/// all modifiers have been released. This is needed as otherwise,
/// injections might misbehave as pressed modifiers might alter
/// the keys being injected.
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.etype) {
let start = Instant::now();
while self.provider.is_any_conflicting_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;
}
// TODO: here we might show a popup window to tell the users to release those keys
trace!("delaying injection event as some modifiers are pressed");
std::thread::sleep(Duration::from_millis(100));
}
}
event
}
}
fn is_injection_event(event_type: &EventType) -> bool {
match event_type {
EventType::TriggerCompensation(_) => true,
EventType::CursorHintCompensation(_) => true,
EventType::KeySequenceInject(_) => true,
EventType::TextInject(_) => true,
_ => false,
}
}
// TODO: test

View File

@ -1,138 +0,0 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use std::{
cell::RefCell,
time::{Duration, Instant},
};
use log::info;
use super::super::Middleware;
use crate::engine::event::{
input::{Key, KeyboardEvent, Status, Variant},
Event, EventType,
};
pub struct DisableOptions {
pub toggle_key: Option<Key>,
pub toggle_key_variant: Option<Variant>,
pub toggle_key_maximum_window: Duration,
// TODO: toggle shortcut?
}
pub struct DisableMiddleware {
enabled: RefCell<bool>,
last_toggle_press: RefCell<Option<Instant>>,
options: DisableOptions,
}
impl DisableMiddleware {
pub fn new(options: DisableOptions) -> Self {
Self {
enabled: RefCell::new(true),
last_toggle_press: RefCell::new(None),
options,
}
}
}
impl Middleware for DisableMiddleware {
fn name(&self) -> &'static str {
"disable"
}
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
let mut has_status_changed = false;
let mut enabled = self.enabled.borrow_mut();
match &event.etype {
EventType::Keyboard(m_event) => {
if is_toggle_key(m_event, &self.options) {
let mut last_toggle_press = self.last_toggle_press.borrow_mut();
if let Some(previous_press) = *last_toggle_press {
if previous_press.elapsed() < self.options.toggle_key_maximum_window {
*enabled = !*enabled;
*last_toggle_press = None;
has_status_changed = true;
} else {
*last_toggle_press = Some(Instant::now());
}
} else {
*last_toggle_press = Some(Instant::now());
}
}
},
EventType::EnableRequest => {
*enabled = true;
has_status_changed = true;
},
EventType::DisableRequest => {
*enabled = false;
has_status_changed = true;
}
_ => {}
}
if has_status_changed {
info!("toggled enabled state, is_enabled = {}", *enabled);
dispatch(Event::caused_by(
event.source_id,
if *enabled {
EventType::Enabled
} else {
EventType::Disabled
},
))
}
// Block keyboard events when disabled
if let EventType::Keyboard(_) = &event.etype {
if !*enabled {
return Event::caused_by(event.source_id, EventType::NOOP);
}
}
// TODO: also ignore hotkey and mouse events
event
}
}
fn is_toggle_key(event: &KeyboardEvent, options: &DisableOptions) -> bool {
if event.status != Status::Released {
return false;
}
if options
.toggle_key
.as_ref()
.map(|key| key == &event.key)
.unwrap_or(false)
{
if let (Some(variant), Some(e_variant)) = (&options.toggle_key_variant, &event.variant) {
variant == e_variant
} else {
true
}
} else {
false
}
}
// TODO: test

View File

@ -1,48 +0,0 @@
/*
* 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;
use super::super::Middleware;
use crate::engine::{event::{Event, EventType}};
pub struct ExitMiddleware {}
impl ExitMiddleware {
pub fn new() -> Self {
Self {}
}
}
impl Middleware for ExitMiddleware {
fn name(&self) -> &'static str {
"exit"
}
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
if let EventType::ExitRequested(mode) = &event.etype {
debug!("received ExitRequested event with mode: {:?}, dispatching exit", mode);
return Event::caused_by(event.source_id, EventType::Exit(mode.clone()));
}
event
}
}
// TODO: test

View File

@ -1,52 +0,0 @@
/*
* This file is part of espanso id: (), trigger: (), trigger: (), left_separator: (), right_separator: (), args: () left_separator: (), right_separator: (), args: () id: (), trigger: (), left_separator: (), right_separator: (), args: ().
*
* 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::Middleware;
use crate::engine::{event::{Event, EventType, internal::{DetectedMatch, MatchesDetectedEvent}}};
pub struct HotKeyMiddleware {}
impl HotKeyMiddleware {
pub fn new() -> Self {
Self {}
}
}
impl Middleware for HotKeyMiddleware {
fn name(&self) -> &'static str {
"hotkey"
}
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
if let EventType::HotKey(m_event) = &event.etype {
return Event::caused_by(event.source_id, EventType::MatchesDetected(MatchesDetectedEvent {
matches: vec![
DetectedMatch {
id: m_event.hotkey_id,
..Default::default()
}
],
}));
}
event
}
}
// TODO: test

View File

@ -1,79 +0,0 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use std::{
cell::RefCell,
};
use super::super::Middleware;
use crate::engine::event::{Event, EventType, ui::{IconStatus, IconStatusChangeEvent}};
pub struct IconStatusMiddleware {
enabled: RefCell<bool>,
secure_input_enabled: RefCell<bool>,
}
impl IconStatusMiddleware {
pub fn new() -> Self {
Self {
enabled: RefCell::new(true),
secure_input_enabled: RefCell::new(false),
}
}
}
impl Middleware for IconStatusMiddleware {
fn name(&self) -> &'static str {
"icon_status"
}
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
let mut enabled = self.enabled.borrow_mut();
let mut secure_input_enabled = self.secure_input_enabled.borrow_mut();
let mut did_update = true;
match &event.etype {
EventType::Enabled => *enabled = true,
EventType::Disabled => *enabled = false,
EventType::SecureInputEnabled(_) => *secure_input_enabled = true,
EventType::SecureInputDisabled => *secure_input_enabled = false,
_ => did_update = false,
}
if did_update {
let status = if *enabled {
if *secure_input_enabled {
IconStatus::SecureInputDisabled
} else {
IconStatus::Enabled
}
} else {
IconStatus::Disabled
};
dispatch(Event::caused_by(event.source_id, EventType::IconStatusChange(IconStatusChangeEvent {
status,
})));
}
event
}
}
// TODO: test

View File

@ -1,81 +0,0 @@
/*
* 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::{
path::Path,
};
use log::{error};
use super::super::Middleware;
use crate::engine::event::{Event, EventType, internal::ImageResolvedEvent};
pub trait PathProvider {
fn get_config_path(&self) -> &Path;
}
pub struct ImageResolverMiddleware<'a> {
provider: &'a dyn PathProvider,
}
impl<'a> ImageResolverMiddleware<'a> {
pub fn new(provider: &'a dyn PathProvider) -> Self {
Self { provider }
}
}
impl<'a> Middleware for ImageResolverMiddleware<'a> {
fn name(&self) -> &'static str {
"image_resolve"
}
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
if let EventType::ImageRequested(m_event) = &event.etype {
// On Windows, we have to replace the forward / with the backslash \ in the path
let path = if cfg!(target_os = "windows") {
m_event.image_path.replace("/", "\\")
} else {
m_event.image_path.to_owned()
};
let path = if path.contains("$CONFIG") {
let config_path = match self.provider.get_config_path().canonicalize() {
Ok(path) => path,
Err(err) => {
error!("unable to canonicalize the config path into the image resolver: {}", err);
self.provider.get_config_path().to_owned()
},
};
path.replace("$CONFIG", &config_path.to_string_lossy())
} else {
path
};
return Event::caused_by(event.source_id, EventType::ImageResolved(ImageResolvedEvent {
image_path: path,
}))
}
event
}
}
// TODO: test

View File

@ -1,60 +0,0 @@
/*
* 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::Middleware;
use crate::engine::event::{Event, EventType, effect::{HtmlInjectRequest}};
// Convert markdown injection requests to HTML on the fly
pub struct MarkdownMiddleware {}
impl MarkdownMiddleware {
pub fn new() -> Self {
Self {}
}
}
impl Middleware for MarkdownMiddleware {
fn name(&self) -> &'static str {
"markdown"
}
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
if let EventType::MarkdownInject(m_event) = &event.etype {
// Render the markdown into HTML
let html = markdown::to_html(&m_event.markdown);
let mut html = html.trim();
// Remove the surrounding paragraph
if html.starts_with("<p>") {
html = html.trim_start_matches("<p>");
}
if html.ends_with("</p>") {
html = html.trim_end_matches("</p>");
}
return Event::caused_by(event.source_id, EventType::HtmlInject(HtmlInjectRequest {
html: html.to_owned(),
}))
}
event
}
}
// TODO: test

View File

@ -1,91 +0,0 @@
/*
* 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, EventType, internal::{MatchSelectedEvent}}, process::{MatchFilter, MatchSelector}};
pub struct MatchSelectMiddleware<'a> {
match_filter: &'a dyn MatchFilter,
match_selector: &'a dyn MatchSelector,
}
impl<'a> MatchSelectMiddleware<'a> {
pub fn new(match_filter: &'a dyn MatchFilter, match_selector: &'a dyn MatchSelector) -> Self {
Self {
match_filter,
match_selector,
}
}
}
impl<'a> Middleware for MatchSelectMiddleware<'a> {
fn name(&self) -> &'static str {
"match_select"
}
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
if let EventType::MatchesDetected(m_event) = event.etype {
let matches_ids: Vec<i32> = m_event.matches.iter().map(|m| m.id).collect();
// Find the matches that are actually valid in the current context
let valid_ids = self.match_filter.filter_active(&matches_ids);
return match valid_ids.len() {
0 => Event::caused_by(event.source_id, EventType::NOOP), // No valid matches, consume the event
1 => {
// Only one match, no need to show a selection dialog
let m = m_event
.matches
.into_iter()
.find(|m| m.id == *valid_ids.first().unwrap());
if let Some(m) = m {
Event::caused_by(event.source_id, EventType::MatchSelected(MatchSelectedEvent { chosen: m }))
} else {
error!("MatchSelectMiddleware could not find the correspondent match");
Event::caused_by(event.source_id, EventType::NOOP)
}
}
_ => {
// Multiple matches, we need to ask the user which one to use
if let Some(selected_id) = self.match_selector.select(&valid_ids) {
let m = m_event
.matches
.into_iter()
.find(|m| m.id == selected_id);
if let Some(m) = m {
Event::caused_by(event.source_id, EventType::MatchSelected(MatchSelectedEvent { chosen: m }))
} else {
error!("MatchSelectMiddleware could not find the correspondent match");
Event::caused_by(event.source_id, EventType::NOOP)
}
} else {
debug!("MatchSelectMiddleware did not receive any match selection");
Event::caused_by(event.source_id, EventType::NOOP)
}
}
};
}
event
}
}
// TODO: test

View File

@ -1,187 +0,0 @@
/*
* 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::trace;
use std::{cell::RefCell, collections::VecDeque};
use super::super::Middleware;
use crate::engine::{
event::{
input::{Key, Status},
internal::{DetectedMatch, MatchesDetectedEvent},
Event, EventType,
},
process::{Matcher, MatcherEvent},
};
pub trait MatcherMiddlewareConfigProvider {
fn max_history_size(&self) -> usize;
}
pub struct MatcherMiddleware<'a, State> {
matchers: &'a [&'a dyn Matcher<'a, State>],
matcher_states: RefCell<VecDeque<Vec<State>>>,
max_history_size: usize,
}
impl<'a, State> MatcherMiddleware<'a, State> {
pub fn new(matchers: &'a [&'a dyn Matcher<'a, State>], options_provider: &'a dyn MatcherMiddlewareConfigProvider) -> Self {
let max_history_size = options_provider.max_history_size();
Self {
matchers,
matcher_states: RefCell::new(VecDeque::new()),
max_history_size,
}
}
}
impl<'a, State> Middleware for MatcherMiddleware<'a, State> {
fn name(&self) -> &'static str {
"matcher"
}
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
if is_event_of_interest(&event.etype) {
let mut matcher_states = self.matcher_states.borrow_mut();
let prev_states = if !matcher_states.is_empty() {
matcher_states.get(matcher_states.len() - 1)
} else {
None
};
if let EventType::Keyboard(keyboard_event) = &event.etype {
// Backspace handling
if keyboard_event.key == Key::Backspace {
trace!("popping the last matcher state");
matcher_states.pop_back();
return event;
}
}
// Some keys (such as the arrow keys) and mouse clicks prevent espanso from building
// an accurate key buffer, so we need to invalidate it.
if is_invalidating_event(&event.etype) {
trace!("invalidating event detected, clearing matching state");
matcher_states.clear();
return event;
}
let mut all_results = Vec::new();
if let Some(matcher_event) = convert_to_matcher_event(&event.etype) {
let mut new_states = Vec::new();
for (i, matcher) in self.matchers.iter().enumerate() {
let prev_state = prev_states.and_then(|states| states.get(i));
let (state, results) = matcher.process(prev_state, &matcher_event);
all_results.extend(results);
new_states.push(state);
}
matcher_states.push_back(new_states);
if matcher_states.len() > self.max_history_size {
matcher_states.pop_front();
}
if !all_results.is_empty() {
return Event::caused_by(
event.source_id,
EventType::MatchesDetected(MatchesDetectedEvent {
matches: all_results
.into_iter()
.map(|result| DetectedMatch {
id: result.id,
trigger: Some(result.trigger),
right_separator: result.right_separator,
left_separator: result.left_separator,
args: result.args,
})
.collect(),
}),
);
}
}
}
event
}
}
fn is_event_of_interest(event_type: &EventType) -> bool {
match event_type {
EventType::Keyboard(keyboard_event) => {
if keyboard_event.status != Status::Pressed {
// Skip non-press events
false
} else {
match keyboard_event.key {
// Skip modifier keys
Key::Alt => false,
Key::Shift => false,
Key::CapsLock => false,
Key::Meta => false,
Key::NumLock => false,
Key::Control => false,
_ => true,
}
}
}
EventType::Mouse(mouse_event) => mouse_event.status == Status::Pressed,
EventType::MatchInjected => true,
_ => false,
}
}
fn convert_to_matcher_event(event_type: &EventType) -> Option<MatcherEvent> {
match event_type {
EventType::Keyboard(keyboard_event) => Some(MatcherEvent::Key {
key: keyboard_event.key.clone(),
chars: keyboard_event.value.clone(),
}),
EventType::Mouse(_) => Some(MatcherEvent::VirtualSeparator),
EventType::MatchInjected => Some(MatcherEvent::VirtualSeparator),
_ => None,
}
}
fn is_invalidating_event(event_type: &EventType) -> bool {
match event_type {
EventType::Keyboard(keyboard_event) => match keyboard_event.key {
Key::ArrowDown => true,
Key::ArrowLeft => true,
Key::ArrowRight => true,
Key::ArrowUp => true,
Key::End => true,
Key::Home => true,
Key::PageDown => true,
Key::PageUp => true,
Key::Escape => true,
_ => false,
},
EventType::Mouse(_) => true,
_ => false,
}
}
// TODO: test

View File

@ -1,36 +0,0 @@
/*
* 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 action;
pub mod cause;
pub mod context_menu;
pub mod cursor_hint;
pub mod delay_modifiers;
pub mod disable;
pub mod exit;
pub mod hotkey;
pub mod icon_status;
pub mod image_resolve;
pub mod match_select;
pub mod matcher;
pub mod markdown;
pub mod multiplex;
pub mod past_discard;
pub mod render;
pub mod search;

View File

@ -1,58 +0,0 @@
/*
* 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::error;
use super::super::Middleware;
use crate::engine::{
event::{Event, EventType},
process::Multiplexer,
};
pub struct MultiplexMiddleware<'a> {
multiplexer: &'a dyn Multiplexer,
}
impl<'a> MultiplexMiddleware<'a> {
pub fn new(multiplexer: &'a dyn Multiplexer) -> Self {
Self { multiplexer }
}
}
impl<'a> Middleware for MultiplexMiddleware<'a> {
fn name(&self) -> &'static str {
"multiplex"
}
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
if let EventType::CauseCompensatedMatch(m_event) = event.etype {
return match self.multiplexer.convert(m_event.m) {
Some(new_event) => Event::caused_by(event.source_id, new_event),
None => {
error!("match multiplexing failed");
Event::caused_by(event.source_id, EventType::NOOP)
}
};
}
event
}
}
// TODO: test

View File

@ -1,66 +0,0 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use std::cell::RefCell;
use log::trace;
use super::super::Middleware;
use crate::engine::{event::{Event, EventType, SourceId}};
/// This middleware discards all events that have a source_id smaller than its
/// configured threshold. This useful to discard past events that might have
/// been stuck in the event queue for too long.
pub struct PastEventsDiscardMiddleware {
source_id_threshold: RefCell<SourceId>,
}
impl PastEventsDiscardMiddleware {
pub fn new() -> Self {
Self {
source_id_threshold: RefCell::new(0),
}
}
}
impl Middleware for PastEventsDiscardMiddleware {
fn name(&self) -> &'static str {
"past_discard"
}
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
let mut source_id_threshold = self.source_id_threshold.borrow_mut();
// Filter out previous events
if event.source_id < *source_id_threshold {
trace!("discarding previous event: {:?}", event);
return Event::caused_by(event.source_id, EventType::NOOP);
}
// Update the minimum threshold
if let EventType::DiscardPrevious(m_event) = &event.etype {
trace!("updating minimum source id threshold for events to: {}", m_event.minimum_source_id);
*source_id_threshold = m_event.minimum_source_id;
}
event
}
}
// TODO: test

View File

@ -1,83 +0,0 @@
/*
* 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::error;
use super::super::Middleware;
use crate::engine::{
event::{
internal::RenderedEvent,
Event, EventType,
},
process::{Renderer, RendererError},
};
pub struct RenderMiddleware<'a> {
renderer: &'a dyn Renderer<'a>,
}
impl<'a> RenderMiddleware<'a> {
pub fn new(renderer: &'a dyn Renderer<'a>) -> Self {
Self { renderer }
}
}
impl<'a> Middleware for RenderMiddleware<'a> {
fn name(&self) -> &'static str {
"render"
}
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
if let EventType::RenderingRequested(m_event) = event.etype {
match self.renderer.render(
m_event.match_id,
m_event.trigger.as_deref(),
m_event.trigger_args,
) {
Ok(body) => {
let body = if let Some(right_separator) = m_event.right_separator {
format!("{}{}", body, right_separator)
} else {
body
};
return Event::caused_by(
event.source_id,
EventType::Rendered(RenderedEvent {
match_id: m_event.match_id,
body,
format: m_event.format,
}),
);
}
Err(err) => match err.downcast_ref::<RendererError>() {
Some(RendererError::Aborted) => return Event::caused_by(event.source_id, EventType::NOOP),
_ => {
error!("error during rendering: {}", err);
return Event::caused_by(event.source_id, EventType::ProcessingError("An error has occurred during rendering, please examine the logs or contact support.".to_string()));
}
},
}
}
event
}
}
// TODO: test

View File

@ -1,75 +0,0 @@
/*
* 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::collections::HashMap;
use super::super::Middleware;
use crate::engine::{event::{
internal::{DetectedMatch, MatchesDetectedEvent},
Event, EventType,
}};
pub trait MatchProvider {
fn get_all_matches_ids(&self) -> Vec<i32>;
}
pub struct SearchMiddleware<'a> {
match_provider: &'a dyn MatchProvider,
}
impl<'a> SearchMiddleware<'a> {
pub fn new(match_provider: &'a dyn MatchProvider) -> Self {
Self { match_provider }
}
}
impl<'a> Middleware for SearchMiddleware<'a> {
fn name(&self) -> &'static str {
"search"
}
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
if let EventType::ShowSearchBar = event.etype {
let detected_matches = Event::caused_by(
event.source_id,
EventType::MatchesDetected(MatchesDetectedEvent {
matches: self
.match_provider
.get_all_matches_ids()
.into_iter()
.map(|id| DetectedMatch {
id,
trigger: None,
left_separator: None,
right_separator: None,
args: HashMap::new(),
})
.collect(),
}),
);
dispatch(detected_matches);
return Event::caused_by(event.source_id, EventType::NOOP);
}
event
}
}
// TODO: test

View File

@ -1,130 +0,0 @@
/*
* 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::{Event, event::{EventType, input::Key, internal::DetectedMatch}};
use anyhow::Result;
use std::collections::HashMap;
use thiserror::Error;
mod default;
mod middleware;
pub trait Middleware {
fn name(&self) -> &'static str;
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event;
}
pub trait Processor {
fn process(&mut self, event: Event) -> Vec<Event>;
}
// Dependency inversion entities
// TODO: move these traits inside the various modules and then re-export it
pub trait Matcher<'a, State> {
fn process(
&'a self,
prev_state: Option<&State>,
event: &MatcherEvent,
) -> (State, Vec<MatchResult>);
}
#[derive(Debug)]
pub enum MatcherEvent {
Key { key: Key, chars: Option<String> },
VirtualSeparator,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MatchResult {
pub id: i32,
pub trigger: String,
pub left_separator: Option<String>,
pub right_separator: Option<String>,
pub args: HashMap<String, String>,
}
pub trait MatchFilter {
fn filter_active(&self, matches_ids: &[i32]) -> Vec<i32>;
}
pub trait MatchSelector {
fn select(&self, matches_ids: &[i32]) -> Option<i32>;
}
pub trait Multiplexer {
fn convert(
&self,
m: DetectedMatch,
) -> Option<EventType>;
}
pub trait Renderer<'a> {
fn render(&'a self, match_id: i32, trigger: Option<&str>, trigger_args: HashMap<String, String>) -> Result<String>;
}
#[derive(Error, Debug)]
pub enum RendererError {
#[error("rendering error")]
RenderingError(#[from] anyhow::Error),
#[error("match not found")]
NotFound,
#[error("aborted")]
Aborted,
}
pub use middleware::action::{MatchInfoProvider, EventSequenceProvider};
pub use middleware::delay_modifiers::ModifierStatusProvider;
pub use middleware::image_resolve::PathProvider;
pub use middleware::disable::DisableOptions;
pub use middleware::matcher::MatcherMiddlewareConfigProvider;
pub use middleware::search::MatchProvider;
pub fn default<'a, MatcherState>(
matchers: &'a [&'a dyn Matcher<'a, MatcherState>],
match_filter: &'a dyn MatchFilter,
match_selector: &'a dyn MatchSelector,
multiplexer: &'a dyn Multiplexer,
renderer: &'a dyn Renderer<'a>,
match_info_provider: &'a dyn MatchInfoProvider,
modifier_status_provider: &'a dyn ModifierStatusProvider,
event_sequence_provider: &'a dyn EventSequenceProvider,
path_provider: &'a dyn PathProvider,
disable_options: DisableOptions,
matcher_options_provider: &'a dyn MatcherMiddlewareConfigProvider,
match_provider: &'a dyn MatchProvider,
) -> impl Processor + 'a {
default::DefaultProcessor::new(
matchers,
match_filter,
match_selector,
multiplexer,
renderer,
match_info_provider,
modifier_status_provider,
event_sequence_provider,
path_provider,
disable_options,
matcher_options_provider,
match_provider,
)
}

View File

@ -42,7 +42,6 @@ mod capabilities;
mod cli; mod cli;
mod common_flags; mod common_flags;
mod config; mod config;
mod engine;
mod exit_code; mod exit_code;
mod gui; mod gui;
mod icon; mod icon;