diff --git a/espanso/src/cli/worker/engine/mod.rs b/espanso/src/cli/worker/engine/mod.rs index 9917235..c5c136f 100644 --- a/espanso/src/cli/worker/engine/mod.rs +++ b/espanso/src/cli/worker/engine/mod.rs @@ -17,10 +17,97 @@ * along with espanso. If not, see . */ -pub mod ui; -pub mod source; -pub mod render; -pub mod matcher; +use anyhow::Result; +use espanso_config::{config::ConfigStore, matches::store::MatchStore}; +use espanso_path::Paths; +use ui::selector::MatchSelectorAdapter; + pub mod executor; +pub mod match_cache; +pub mod matcher; pub mod multiplex; -pub mod match_cache; \ No newline at end of file +pub mod render; +pub mod source; +pub mod ui; + +pub fn initialize_and_spawn(paths: Paths, config_store: Box, match_store: Box) -> Result<()> { + std::thread::Builder::new() + .name("engine thread".to_string()) + .spawn(move || { + let app_info_provider = + espanso_info::get_provider().expect("unable to initialize app info provider"); + let config_manager = + super::config::ConfigManager::new(&*config_store, &*match_store, &*app_info_provider); + let match_converter = + super::engine::matcher::convert::MatchConverter::new(&*config_store, &*match_store); + let match_cache = super::engine::match_cache::MatchCache::load(&*config_store, &*match_store); + + let detect_source = + super::engine::source::detect::init_and_spawn().expect("failed to initialize detector module"); + let sources: Vec<&dyn crate::engine::funnel::Source> = vec![&detect_source]; + let funnel = crate::engine::funnel::default(&sources); + + let matcher = super::engine::matcher::rolling::RollingMatcherAdapter::new( + &match_converter.get_rolling_matches(), + ); + let matchers: Vec<&dyn crate::engine::process::Matcher> = vec![&matcher]; + let selector = MatchSelectorAdapter::new(); + let multiplexer = super::engine::multiplex::MultiplexAdapter::new(&match_cache); + + let injector = espanso_inject::get_injector(Default::default()) + .expect("failed to initialize injector module"); // TODO: handle the options + let clipboard = espanso_clipboard::get_clipboard(Default::default()) + .expect("failed to initialize clipboard module"); // TODO: handle options + + let clipboard_adapter = super::engine::render::clipboard::ClipboardAdapter::new(&*clipboard); + let clipboard_extension = + espanso_render::extension::clipboard::ClipboardExtension::new(&clipboard_adapter); + let date_extension = espanso_render::extension::date::DateExtension::new(); + let echo_extension = espanso_render::extension::echo::EchoExtension::new(); + let random_extension = espanso_render::extension::random::RandomExtension::new(); + let home_path = dirs::home_dir().expect("unable to obtain home dir path"); + let script_extension = espanso_render::extension::script::ScriptExtension::new( + &paths.config, + &home_path, + &paths.packages, + ); + let shell_extension = espanso_render::extension::shell::ShellExtension::new(&paths.config); + let renderer = espanso_render::create(vec![ + &clipboard_extension, + &date_extension, + &echo_extension, + &random_extension, + &script_extension, + &shell_extension, + ]); + let renderer_adapter = + super::engine::render::RendererAdapter::new(&match_cache, &config_manager, &renderer); + + let mut processor = crate::engine::process::default( + &matchers, + &config_manager, + &selector, + &multiplexer, + &renderer_adapter, + &match_cache, + ); + + let event_injector = super::engine::executor::event_injector::EventInjectorAdapter::new(&*injector); + let clipboard_injector = super::engine::executor::clipboard_injector::ClipboardInjectorAdapter::new( + &*injector, + &*clipboard, + ); + let key_injector = super::engine::executor::key_injector::KeyInjectorAdapter::new(&*injector); + let dispatcher = crate::engine::dispatch::default( + &event_injector, + &clipboard_injector, + &config_manager, + &key_injector, + ); + + let mut engine = crate::engine::Engine::new(&funnel, &mut processor, &dispatcher); + engine.run(); + })?; + + Ok(()) +} diff --git a/espanso/src/cli/worker/mod.rs b/espanso/src/cli/worker/mod.rs index ca9691b..fca51be 100644 --- a/espanso/src/cli/worker/mod.rs +++ b/espanso/src/cli/worker/mod.rs @@ -17,15 +17,13 @@ * along with espanso. If not, see . */ -use engine::ui::selector::MatchSelectorAdapter; -use funnel::Source; -use process::Matcher; +use self::ui::util::convert_icon_paths_to_tray_vec; use super::{CliModule, CliModuleArgs}; -use crate::engine::{dispatch, funnel, process, Engine}; mod config; mod engine; +mod ui; pub fn new() -> CliModule { #[allow(clippy::needless_update)] @@ -46,73 +44,32 @@ fn worker_main(args: CliModuleArgs) { let match_store = args .match_store .expect("missing match store in worker main"); - + let paths = args.paths.expect("missing paths in worker main"); - let app_info_provider = - espanso_info::get_provider().expect("unable to initialize app info provider"); - let config_manager = - config::ConfigManager::new(&*config_store, &*match_store, &*app_info_provider); - let match_converter = - engine::matcher::convert::MatchConverter::new(&*config_store, &*match_store); - let match_cache = engine::match_cache::MatchCache::load(&*config_store, &*match_store); + let icon_paths = + self::ui::icon::load_icon_paths(&paths.runtime).expect("unable to initialize icons"); - let detect_source = - engine::source::detect::init_and_spawn().expect("failed to initialize detector module"); - let sources: Vec<&dyn Source> = vec![&detect_source]; - let funnel = funnel::default(&sources); + let (remote, mut eventloop) = espanso_ui::create_ui(espanso_ui::UIOptions { + // TODO: handle show icon + icon_paths: convert_icon_paths_to_tray_vec(&icon_paths), + notification_icon_path: icon_paths + .logo + .map(|path| path.to_string_lossy().to_string()), + ..Default::default() + }) + .expect("unable to create tray icon UI module"); - let matcher = - engine::matcher::rolling::RollingMatcherAdapter::new(&match_converter.get_rolling_matches()); - let matchers: Vec<&dyn Matcher> = vec![&matcher]; - let selector = MatchSelectorAdapter::new(); - let multiplexer = engine::multiplex::MultiplexAdapter::new(&match_cache); + eventloop + .initialize() + .expect("unable to initialize UI module"); - let injector = - espanso_inject::get_injector(Default::default()).expect("failed to initialize injector module"); // TODO: handle the options - let clipboard = espanso_clipboard::get_clipboard(Default::default()) - .expect("failed to initialize clipboard module"); // TODO: handle options + // TODO: pass the remote + // Initialize the engine on another thread and start it + engine::initialize_and_spawn(paths.clone(), config_store, match_store) + .expect("unable to initialize engine"); - let clipboard_adapter = engine::render::clipboard::ClipboardAdapter::new(&*clipboard); - let clipboard_extension = espanso_render::extension::clipboard::ClipboardExtension::new(&clipboard_adapter); - let date_extension = espanso_render::extension::date::DateExtension::new(); - let echo_extension = espanso_render::extension::echo::EchoExtension::new(); - let random_extension = espanso_render::extension::random::RandomExtension::new(); - let home_path = dirs::home_dir().expect("unable to obtain home dir path"); - let script_extension = espanso_render::extension::script::ScriptExtension::new(&paths.config, &home_path, &paths.packages); - let shell_extension = espanso_render::extension::shell::ShellExtension::new(&paths.config); - let renderer = espanso_render::create(vec![ - &clipboard_extension, - &date_extension, - &echo_extension, - &random_extension, - &script_extension, - &shell_extension, - ]); - let renderer_adapter = - engine::render::RendererAdapter::new(&match_cache, &config_manager, &renderer); - - let mut processor = process::default( - &matchers, - &config_manager, - &selector, - &multiplexer, - &renderer_adapter, - &match_cache, - ); - - - let event_injector = engine::executor::event_injector::EventInjectorAdapter::new(&*injector); - let clipboard_injector = - engine::executor::clipboard_injector::ClipboardInjectorAdapter::new(&*injector, &*clipboard); - let key_injector = engine::executor::key_injector::KeyInjectorAdapter::new(&*injector); - let dispatcher = dispatch::default( - &event_injector, - &clipboard_injector, - &config_manager, - &key_injector, - ); - - let mut engine = Engine::new(&funnel, &mut processor, &dispatcher); - engine.run(); + eventloop.run(Box::new(move |event| { + // TODO: handle event + })); } diff --git a/espanso/src/cli/worker/ui/icon.rs b/espanso/src/cli/worker/ui/icon.rs new file mode 100644 index 0000000..20fcc8b --- /dev/null +++ b/espanso/src/cli/worker/ui/icon.rs @@ -0,0 +1,69 @@ +/* + * 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 . + */ + +use anyhow::Result; +use log::{debug, info}; +use std::path::{Path, PathBuf}; + +const ICON_BINARY: &[u8] = include_bytes!("../../../res/icon.png"); + +#[cfg(target_os = "windows")] +const WINDOWS_ICO_BINARY: &[u8] = include_bytes!("../../../res/windows/espanso.ico"); +#[cfg(target_os = "windows")] +const WINDOWS_RED_ICO_BINARY: &[u8] = include_bytes!("../../../res/windows/espansored.ico"); + +// TODO: macos +// TODO: linux + +#[derive(Debug, Default)] +pub struct IconPaths { + pub tray_icon_normal: Option, + pub tray_icon_disabled: Option, + pub tray_icon_system_disabled: Option, // TODO: secure input + + pub logo: Option, +} + +#[cfg(target_os = "windows")] +pub fn load_icon_paths(runtime_dir: &Path) -> Result { + Ok(IconPaths { + tray_icon_normal: Some(extract_icon(WINDOWS_ICO_BINARY, &runtime_dir.join("normal.ico"))?), + tray_icon_disabled: Some(extract_icon(WINDOWS_RED_ICO_BINARY, &runtime_dir.join("disabled.ico"))?), + logo: Some(extract_icon(ICON_BINARY, &runtime_dir.join("icon.png"))?), + ..Default::default() + }) +} + +// TODO: macos +// TODO: linux + +// TODO: test +fn extract_icon(data: &[u8], target_file: &Path) -> Result { + if target_file.exists() { + debug!( + "skipping extraction for '{:?}', as it's already present", + target_file + ); + Ok(target_file.to_owned()) + } else { + std::fs::write(target_file, data)?; + info!("extracted icon to: {:?}", target_file); + Ok(target_file.to_owned()) + } +} diff --git a/espanso/src/cli/worker/ui/mod.rs b/espanso/src/cli/worker/ui/mod.rs new file mode 100644 index 0000000..5de9509 --- /dev/null +++ b/espanso/src/cli/worker/ui/mod.rs @@ -0,0 +1,21 @@ +/* + * 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 . + */ + +pub mod icon; +pub mod util; \ No newline at end of file diff --git a/espanso/src/cli/worker/ui/util.rs b/espanso/src/cli/worker/ui/util.rs new file mode 100644 index 0000000..a16edaa --- /dev/null +++ b/espanso/src/cli/worker/ui/util.rs @@ -0,0 +1,41 @@ +/* + * 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 . + */ + +use espanso_ui::icons::TrayIcon; + +use super::icon::IconPaths; + +// TODO: test +pub fn convert_icon_paths_to_tray_vec(icon_paths: &IconPaths) -> Vec<(TrayIcon, String)> { + let mut paths = Vec::new(); + + if let Some(normal) = &icon_paths.tray_icon_normal { + paths.push((TrayIcon::Normal, normal.to_string_lossy().to_string())); + } + + if let Some(disabled) = &icon_paths.tray_icon_disabled { + paths.push((TrayIcon::Disabled, disabled.to_string_lossy().to_string())); + } + + if let Some(system_disabled) = &icon_paths.tray_icon_system_disabled { + paths.push((TrayIcon::SystemDisabled, system_disabled.to_string_lossy().to_string())); + } + + paths +} \ No newline at end of file diff --git a/espanso/src/res/icon.png b/espanso/src/res/icon.png new file mode 100644 index 0000000..9bde0d2 Binary files /dev/null and b/espanso/src/res/icon.png differ diff --git a/espanso/src/res/linux/icon.png b/espanso/src/res/linux/icon.png new file mode 100644 index 0000000..9bde0d2 Binary files /dev/null and b/espanso/src/res/linux/icon.png differ diff --git a/espanso/src/res/macos/icon.png b/espanso/src/res/macos/icon.png new file mode 100644 index 0000000..cde3e91 Binary files /dev/null and b/espanso/src/res/macos/icon.png differ diff --git a/espanso/src/res/macos/icondisabled.png b/espanso/src/res/macos/icondisabled.png new file mode 100644 index 0000000..a289156 Binary files /dev/null and b/espanso/src/res/macos/icondisabled.png differ diff --git a/espanso/src/res/windows/espanso.ico b/espanso/src/res/windows/espanso.ico new file mode 100644 index 0000000..1d1c06c Binary files /dev/null and b/espanso/src/res/windows/espanso.ico differ diff --git a/espanso/src/res/windows/espansored.ico b/espanso/src/res/windows/espansored.ico new file mode 100644 index 0000000..334bab0 Binary files /dev/null and b/espanso/src/res/windows/espansored.ico differ