diff --git a/src/config/mod.rs b/src/config/mod.rs index 91228e6..9f1b6da 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -8,7 +8,7 @@ use std::io::Read; use serde::{Serialize, Deserialize}; use crate::event::KeyModifier; use std::collections::HashSet; -use log::{debug, info, warn, error}; +use log::{error}; use std::fmt; use std::error::Error; diff --git a/src/config/runtime.rs b/src/config/runtime.rs index 1e7e405..ae40fa3 100644 --- a/src/config/runtime.rs +++ b/src/config/runtime.rs @@ -2,7 +2,7 @@ use regex::Regex; use crate::system::SystemManager; use std::cell::RefCell; use std::time::SystemTime; -use log::{debug, info, warn, error}; +use log::{debug, warn}; use super::{Configs, ConfigSet}; use crate::matcher::Match; diff --git a/src/context/macos.rs b/src/context/macos.rs index 2d28be0..dd97efa 100644 --- a/src/context/macos.rs +++ b/src/context/macos.rs @@ -6,7 +6,8 @@ use crate::event::KeyModifier::*; use std::fs::create_dir_all; use std::ffi::CString; use std::fs; -use log::{info}; +use log::{info, error}; +use std::path::PathBuf; const STATUS_ICON_BINARY : &'static [u8] = include_bytes!("../res/mac/icon.png"); @@ -21,15 +22,15 @@ impl MacContext { }); // Initialize the status icon path - let data_dir = dirs::data_dir().expect("Can't obtain data_dir(), terminating."); - let espanso_dir = data_dir.join("espanso"); - let res = create_dir_all(&espanso_dir); + let espanso_dir = MacContext::get_data_dir(); let status_icon_target = espanso_dir.join("icon.png"); if status_icon_target.exists() { info!("Status icon already initialized, skipping."); }else { - fs::write(&status_icon_target, STATUS_ICON_BINARY); + fs::write(&status_icon_target, STATUS_ICON_BINARY).unwrap_or_else(|e| { + error!("Error copying the Status Icon to the espanso data directory: {}", e); + }); } unsafe { @@ -45,6 +46,13 @@ impl MacContext { context } + + pub fn get_data_dir() -> PathBuf { + let data_dir = dirs::data_dir().expect("Can't obtain data_dir(), terminating."); + let espanso_dir = data_dir.join("espanso"); + create_dir_all(&espanso_dir).expect("Error creating espanso data directory"); + espanso_dir + } } impl super::Context for MacContext { diff --git a/src/context/mod.rs b/src/context/mod.rs index 39db451..ec5ce45 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -5,11 +5,10 @@ mod windows; mod linux; #[cfg(target_os = "macos")] -mod macos; +pub(crate) mod macos; use std::sync::mpsc::Sender; use crate::event::Event; -use std::sync::Arc; pub trait Context { fn eventloop(&self); diff --git a/src/engine.rs b/src/engine.rs index d9ad72c..e8bb5d1 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,5 +1,5 @@ use crate::matcher::{Match, MatchReceiver}; -use crate::keyboard::KeyboardSender; +use crate::keyboard::KeyboardManager; use crate::config::ConfigManager; use crate::config::BackendType; use crate::clipboard::ClipboardManager; @@ -7,21 +7,22 @@ use log::{info}; use crate::ui::{UIManager, MenuItem, MenuItemType}; use crate::event::{ActionEventReceiver, ActionEvent, ActionType}; use std::cell::RefCell; +use std::process::exit; -pub struct Engine<'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>, +pub struct Engine<'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>, U: UIManager> { - sender: S, + keyboard_manager: &'a S, clipboard_manager: &'a C, config_manager: &'a M, ui_manager: &'a U, enabled: RefCell, } -impl <'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>, U: UIManager> +impl <'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>, U: UIManager> Engine<'a, S, C, M, U> { - pub fn new(sender: S, clipboard_manager: &'a C, config_manager: &'a M, ui_manager: &'a U) -> Engine<'a, S, C, M, U> { + pub fn new(keyboard_manager: &'a S, clipboard_manager: &'a C, config_manager: &'a M, ui_manager: &'a U) -> Engine<'a, S, C, M, U> { let enabled = RefCell::new(true); - Engine{sender, clipboard_manager, config_manager, ui_manager, enabled } + Engine{keyboard_manager, clipboard_manager, config_manager, ui_manager, enabled } } fn build_menu(&self) -> Vec { @@ -55,7 +56,7 @@ impl <'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>, U: UIMan } } -impl <'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>, U: UIManager> +impl <'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>, U: UIManager> MatchReceiver for Engine<'a, S, C, M, U>{ fn on_match(&self, m: &Match) { @@ -65,7 +66,7 @@ impl <'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>, U: UIMan return; } - self.sender.delete_string(m.trigger.len() as i32); + self.keyboard_manager.delete_string(m.trigger.len() as i32); match config.backend { BackendType::Inject => { @@ -73,23 +74,23 @@ impl <'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>, U: UIMan // while on windows and macos, we need to emulate a Enter key press. if cfg!(target_os = "linux") { - self.sender.send_string(m.replace.as_str()); + self.keyboard_manager.send_string(m.replace.as_str()); }else{ // To handle newlines, substitute each "\n" char with an Enter key press. let splits = m.replace.lines(); for (i, split) in splits.enumerate() { if i > 0 { - self.sender.send_enter(); + self.keyboard_manager.send_enter(); } - self.sender.send_string(split); + self.keyboard_manager.send_string(split); } } }, BackendType::Clipboard => { self.clipboard_manager.set_clipboard(m.replace.as_str()); - self.sender.trigger_paste(); + self.keyboard_manager.trigger_paste(); }, } } @@ -110,7 +111,7 @@ impl <'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>, U: UIMan } } -impl <'a, S: KeyboardSender, C: ClipboardManager, +impl <'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>, U: UIManager> ActionEventReceiver for Engine<'a, S, C, M, U>{ fn on_action_event(&self, e: ActionEvent) { @@ -119,7 +120,13 @@ impl <'a, S: KeyboardSender, C: ClipboardManager, self.ui_manager.show_menu(self.build_menu()); }, ActionEvent::ContextMenuClick(id) => { - println!("{:?}",id); + match id { + ActionType::Exit => { + info!("Terminating expanso from the context menu"); + exit(0); + }, + _ => {} + } } } } diff --git a/src/event/mod.rs b/src/event/mod.rs index 294a2d0..ead9efa 100644 --- a/src/event/mod.rs +++ b/src/event/mod.rs @@ -59,5 +59,5 @@ pub trait KeyEventReceiver { } pub trait ActionEventReceiver { - fn on_action_event(&self, e: ActionEvent); // TODO: Action event + fn on_action_event(&self, e: ActionEvent); } \ No newline at end of file diff --git a/src/keyboard/linux.rs b/src/keyboard/linux.rs index e36ec18..06ca82b 100644 --- a/src/keyboard/linux.rs +++ b/src/keyboard/linux.rs @@ -32,10 +32,10 @@ impl Drop for LinuxKeyboardInterceptor { } } -pub struct LinuxKeyboardSender { +pub struct LinuxKeyboardManager { } -impl super::KeyboardSender for LinuxKeyboardSender { +impl super::KeyboardManager for LinuxKeyboardManager { fn send_string(&self, s: &str) { let res = CString::new(s); match res { diff --git a/src/keyboard/macos.rs b/src/keyboard/macos.rs index f842fea..d6a53a4 100644 --- a/src/keyboard/macos.rs +++ b/src/keyboard/macos.rs @@ -1,12 +1,10 @@ -use std::sync::mpsc; -use std::os::raw::{c_char, c_void}; use std::ffi::CString; use crate::bridge::macos::*; -pub struct MacKeyboardSender { +pub struct MacKeyboardManager { } -impl super::KeyboardSender for MacKeyboardSender { +impl super::KeyboardManager for MacKeyboardManager { fn send_string(&self, s: &str) { let res = CString::new(s); match res { diff --git a/src/keyboard/mod.rs b/src/keyboard/mod.rs index f3d477b..b9fdecf 100644 --- a/src/keyboard/mod.rs +++ b/src/keyboard/mod.rs @@ -7,7 +7,7 @@ mod linux; #[cfg(target_os = "macos")] mod macos; -pub trait KeyboardSender { // TODO: rename KeyboardManager +pub trait KeyboardManager { fn send_string(&self, s: &str); fn send_enter(&self); fn trigger_paste(&self); @@ -16,18 +16,18 @@ pub trait KeyboardSender { // TODO: rename KeyboardManager // WINDOWS IMPLEMENTATION #[cfg(target_os = "windows")] -pub fn get_sender() -> impl KeyboardSender { - windows::WindowsKeyboardSender{} +pub fn get_manager() -> impl KeyboardManager { + windows::WindowsKeyboardManager{} } // LINUX IMPLEMENTATION #[cfg(target_os = "linux")] -pub fn get_sender() -> impl KeyboardSender { - linux::LinuxKeyboardSender{} +pub fn get_manager() -> impl KeyboardManager { + linux::LinuxKeyboardManager{} } // MAC IMPLEMENTATION #[cfg(target_os = "macos")] -pub fn get_sender() -> impl KeyboardSender { - macos::MacKeyboardSender{} +pub fn get_manager() -> impl KeyboardManager { + macos::MacKeyboardManager{} } \ No newline at end of file diff --git a/src/keyboard/windows.rs b/src/keyboard/windows.rs index 921ea58..a7f2dd9 100644 --- a/src/keyboard/windows.rs +++ b/src/keyboard/windows.rs @@ -3,10 +3,10 @@ use std::os::raw::{c_void}; use widestring::{U16CString}; use crate::bridge::windows::*; -pub struct WindowsKeyboardSender { +pub struct WindowsKeyboardManager { } -impl super::KeyboardSender for WindowsKeyboardSender { +impl super::KeyboardManager for WindowsKeyboardManager { fn send_string(&self, s: &str) { let res = U16CString::from_str(s); match res { diff --git a/src/main.rs b/src/main.rs index ffd5a73..e02f12e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,6 @@ use crate::engine::Engine; use crate::config::ConfigSet; use crate::config::runtime::RuntimeConfigManager; use crate::ui::UIManager; -use crate::context::Context; use crate::event::*; use crate::event::manager::{EventManager, DefaultEventManager}; use std::{thread}; @@ -107,10 +106,9 @@ fn espanso_background(receive_channel: Receiver, config_set: ConfigSet) { let clipboard_manager = clipboard::get_manager(); - let sender = keyboard::get_sender(); // TODO: rename manager + let manager = keyboard::get_manager(); - // TODO: change sender from move to reference - let engine = Engine::new(sender, + let engine = Engine::new(&manager, &clipboard_manager, &config_manager, &ui_manager diff --git a/src/matcher/mod.rs b/src/matcher/mod.rs index 670fac0..8759990 100644 --- a/src/matcher/mod.rs +++ b/src/matcher/mod.rs @@ -1,4 +1,3 @@ -use std::sync::mpsc::Receiver; use serde::{Serialize, Deserialize}; use crate::event::{KeyEvent, KeyModifier}; use crate::event::KeyEventReceiver; diff --git a/src/ui/macos.rs b/src/ui/macos.rs index f718972..da0b6c9 100644 --- a/src/ui/macos.rs +++ b/src/ui/macos.rs @@ -1,11 +1,11 @@ -use std::fs::create_dir_all; use std::{fs, io}; use std::io::{Cursor}; use std::ffi::CString; -use log::{info, debug}; +use log::{info, warn, debug}; use std::path::PathBuf; use std::process::Command; use crate::ui::{MenuItem, MenuItemType}; +use crate::context::macos::MacContext; use crate::bridge::macos::{MacMenuItem, show_context_menu}; use std::os::raw::c_char; @@ -25,6 +25,10 @@ impl super::UIManager for MacUIManager { let res = Command::new(executable_path) .args(&["espanso", message, &DEFAULT_NOTIFICATION_DELAY.to_string()]) .spawn(); + + if let Err(e) = res { + warn!("Error while dispatching Notify Helper {}", e) + } } fn show_menu(&self, menu: Vec) { @@ -65,11 +69,7 @@ impl MacUIManager { } fn initialize_notify_helper() -> PathBuf { - let data_dir = dirs::data_dir().expect("Can't obtain data_dir(), terminating."); - - let espanso_dir = data_dir.join("espanso"); - - let res = create_dir_all(&espanso_dir); + let espanso_dir = MacContext::get_data_dir(); info!("Initializing EspansoNotifyHelper in {}", espanso_dir.as_path().display()); @@ -78,42 +78,40 @@ impl MacUIManager { if espanso_target.exists() { info!("EspansoNotifyHelper already initialized, skipping."); }else{ - if let Ok(_) = res { - // Extract zip file - let reader = Cursor::new(NOTIFY_HELPER_BINARY); + // Extract zip file + let reader = Cursor::new(NOTIFY_HELPER_BINARY); - let mut archive = zip::ZipArchive::new(reader).unwrap(); + let mut archive = zip::ZipArchive::new(reader).unwrap(); - for i in 0..archive.len() { - let mut file = archive.by_index(i).unwrap(); - let outpath = espanso_dir.join(file.sanitized_name()); + for i in 0..archive.len() { + let mut file = archive.by_index(i).unwrap(); + let outpath = espanso_dir.join(file.sanitized_name()); - { - let comment = file.comment(); - if !comment.is_empty() { - debug!("File {} comment: {}", i, comment); + { + let comment = file.comment(); + if !comment.is_empty() { + debug!("File {} comment: {}", i, comment); + } + } + + if (&*file.name()).ends_with('/') { + debug!("File {} extracted to \"{}\"", i, outpath.as_path().display()); + fs::create_dir_all(&outpath).unwrap(); + } else { + debug!("File {} extracted to \"{}\" ({} bytes)", i, outpath.as_path().display(), file.size()); + if let Some(p) = outpath.parent() { + if !p.exists() { + fs::create_dir_all(&p).unwrap(); } } + let mut outfile = fs::File::create(&outpath).unwrap(); + io::copy(&mut file, &mut outfile).unwrap(); + } - if (&*file.name()).ends_with('/') { - debug!("File {} extracted to \"{}\"", i, outpath.as_path().display()); - fs::create_dir_all(&outpath).unwrap(); - } else { - debug!("File {} extracted to \"{}\" ({} bytes)", i, outpath.as_path().display(), file.size()); - if let Some(p) = outpath.parent() { - if !p.exists() { - fs::create_dir_all(&p).unwrap(); - } - } - let mut outfile = fs::File::create(&outpath).unwrap(); - io::copy(&mut file, &mut outfile).unwrap(); - } + use std::os::unix::fs::PermissionsExt; - use std::os::unix::fs::PermissionsExt; - - if let Some(mode) = file.unix_mode() { - fs::set_permissions(&outpath, fs::Permissions::from_mode(mode)).unwrap(); - } + if let Some(mode) = file.unix_mode() { + fs::set_permissions(&outpath, fs::Permissions::from_mode(mode)).unwrap(); } } }