2019-09-13 09:55:42 +00:00
|
|
|
use std::sync::{mpsc};
|
2019-08-31 14:07:45 +00:00
|
|
|
use crate::matcher::scrolling::ScrollingMatcher;
|
|
|
|
use crate::engine::Engine;
|
2019-09-09 15:59:44 +00:00
|
|
|
use crate::config::ConfigSet;
|
|
|
|
use crate::config::runtime::RuntimeConfigManager;
|
2019-09-13 21:30:34 +00:00
|
|
|
use crate::system::SystemManager;
|
2019-09-06 20:30:20 +00:00
|
|
|
use crate::ui::UIManager;
|
2019-09-12 20:14:41 +00:00
|
|
|
use crate::event::*;
|
|
|
|
use crate::event::manager::{EventManager, DefaultEventManager};
|
2019-09-13 09:55:42 +00:00
|
|
|
use std::{thread};
|
2019-09-13 21:30:34 +00:00
|
|
|
use clap::{App, Arg, SubCommand};
|
2019-09-05 21:06:43 +00:00
|
|
|
use std::path::Path;
|
2019-09-07 14:13:13 +00:00
|
|
|
use std::sync::mpsc::Receiver;
|
2019-09-10 20:53:45 +00:00
|
|
|
use log::{info, error, LevelFilter};
|
2019-09-13 14:17:47 +00:00
|
|
|
use simplelog::{CombinedLogger, TermLogger, TerminalMode, SharedLogger};
|
2019-09-10 20:53:45 +00:00
|
|
|
use std::process::exit;
|
2019-09-13 21:30:34 +00:00
|
|
|
use std::time::Duration;
|
2019-08-30 16:32:10 +00:00
|
|
|
|
2019-09-07 11:35:45 +00:00
|
|
|
mod ui;
|
2019-09-12 20:14:41 +00:00
|
|
|
mod event;
|
2019-09-07 11:35:45 +00:00
|
|
|
mod bridge;
|
2019-08-31 14:07:45 +00:00
|
|
|
mod engine;
|
2019-09-01 20:00:31 +00:00
|
|
|
mod config;
|
2019-09-07 11:35:45 +00:00
|
|
|
mod system;
|
2019-09-12 20:14:41 +00:00
|
|
|
mod context;
|
2019-09-07 11:35:45 +00:00
|
|
|
mod matcher;
|
|
|
|
mod keyboard;
|
2019-09-06 22:38:13 +00:00
|
|
|
mod clipboard;
|
2019-08-30 12:33:40 +00:00
|
|
|
|
2019-09-05 21:06:43 +00:00
|
|
|
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
|
|
|
|
2019-08-30 12:33:40 +00:00
|
|
|
fn main() {
|
2019-09-05 21:06:43 +00:00
|
|
|
let matches = App::new("espanso")
|
|
|
|
.version(VERSION)
|
|
|
|
.author("Federico Terzi")
|
|
|
|
.about("Cross-platform Text Expander written in Rust")
|
|
|
|
.arg(Arg::with_name("config")
|
|
|
|
.short("c")
|
|
|
|
.long("config")
|
|
|
|
.value_name("FILE")
|
2019-09-07 11:35:45 +00:00
|
|
|
.help("Sets a custom config directory. If not specified, reads the default $HOME/.espanso/default.yaml file, creating it if not present.")
|
2019-09-05 21:06:43 +00:00
|
|
|
.takes_value(true))
|
|
|
|
.arg(Arg::with_name("dump")
|
|
|
|
.long("dump")
|
|
|
|
.help("Prints all current configuration options."))
|
|
|
|
.arg(Arg::with_name("v")
|
|
|
|
.short("v")
|
|
|
|
.multiple(true)
|
|
|
|
.help("Sets the level of verbosity"))
|
2019-09-13 21:30:34 +00:00
|
|
|
.subcommand(SubCommand::with_name("detect")
|
|
|
|
.about("Tool to detect current window properties, to simplify filters creation"))
|
2019-09-05 21:06:43 +00:00
|
|
|
.get_matches();
|
|
|
|
|
2019-09-07 15:59:34 +00:00
|
|
|
|
|
|
|
// Setup logging
|
|
|
|
let log_level = match matches.occurrences_of("v") {
|
|
|
|
0 => LevelFilter::Warn,
|
|
|
|
1 => LevelFilter::Info,
|
|
|
|
2 | _ => LevelFilter::Debug,
|
|
|
|
};
|
2019-09-13 14:17:47 +00:00
|
|
|
let mut log_outputs: Vec<Box<dyn SharedLogger>> = Vec::new();
|
|
|
|
|
|
|
|
// Initialize terminal output
|
|
|
|
let terminal_out = TermLogger::new(log_level, simplelog::Config::default(), TerminalMode::Mixed);
|
|
|
|
if let Some(terminal_out) = terminal_out {
|
|
|
|
log_outputs.push(terminal_out);
|
|
|
|
}
|
|
|
|
|
|
|
|
//TODO: WriteLogger::new(LevelFilter::Info, Config::default(), File::create("my_rust_binary.log").unwrap()),
|
2019-09-07 15:59:34 +00:00
|
|
|
CombinedLogger::init(
|
2019-09-13 14:17:47 +00:00
|
|
|
log_outputs
|
|
|
|
).expect("Error opening log destination");
|
2019-09-07 15:59:34 +00:00
|
|
|
|
|
|
|
info!("espanso is starting...");
|
|
|
|
|
2019-09-07 11:35:45 +00:00
|
|
|
let config_set = match matches.value_of("config") {
|
2019-09-07 15:59:34 +00:00
|
|
|
None => {
|
|
|
|
info!("loading configuration from default location...");
|
|
|
|
ConfigSet::load_default()
|
|
|
|
},
|
|
|
|
Some(path) => {
|
|
|
|
info!("loading configuration from custom location: {}", path);
|
|
|
|
ConfigSet::load(Path::new(path))
|
|
|
|
},
|
2019-09-10 20:53:45 +00:00
|
|
|
}.unwrap_or_else(|e| {
|
|
|
|
error!("{}", e);
|
|
|
|
exit(1);
|
|
|
|
});
|
2019-09-05 21:06:43 +00:00
|
|
|
|
|
|
|
if matches.is_present("dump") {
|
2019-09-07 11:35:45 +00:00
|
|
|
println!("{:#?}", config_set);
|
2019-09-05 21:06:43 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-09-13 21:30:34 +00:00
|
|
|
if let Some(matches) = matches.subcommand_matches("detect") {
|
|
|
|
detect_main();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
daemon_main(config_set);
|
2019-09-05 21:06:43 +00:00
|
|
|
}
|
2019-08-30 12:33:40 +00:00
|
|
|
|
2019-09-13 21:30:34 +00:00
|
|
|
fn daemon_main(config_set: ConfigSet) {
|
2019-09-12 20:14:41 +00:00
|
|
|
let (send_channel, receive_channel) = mpsc::channel();
|
|
|
|
|
|
|
|
let context = context::new(send_channel);
|
2019-09-07 14:13:13 +00:00
|
|
|
|
|
|
|
thread::spawn(move || {
|
2019-09-13 21:30:34 +00:00
|
|
|
daemon_background(receive_channel, config_set);
|
2019-09-07 14:13:13 +00:00
|
|
|
});
|
|
|
|
|
2019-09-12 20:14:41 +00:00
|
|
|
context.eventloop();
|
2019-09-07 14:13:13 +00:00
|
|
|
}
|
2019-09-06 14:06:41 +00:00
|
|
|
|
2019-09-13 21:30:34 +00:00
|
|
|
fn daemon_background(receive_channel: Receiver<Event>, config_set: ConfigSet) {
|
2019-09-07 11:35:45 +00:00
|
|
|
let system_manager = system::get_manager();
|
2019-09-07 14:13:13 +00:00
|
|
|
let config_manager = RuntimeConfigManager::new(config_set, system_manager);
|
2019-09-07 11:35:45 +00:00
|
|
|
|
2019-09-07 14:13:13 +00:00
|
|
|
let ui_manager = ui::get_uimanager();
|
2019-09-09 13:15:01 +00:00
|
|
|
ui_manager.notify("espanso is running!");
|
2019-09-06 22:38:13 +00:00
|
|
|
|
2019-09-07 14:13:13 +00:00
|
|
|
let clipboard_manager = clipboard::get_manager();
|
2019-08-30 16:32:10 +00:00
|
|
|
|
2019-09-13 13:03:03 +00:00
|
|
|
let manager = keyboard::get_manager();
|
2019-08-31 14:07:45 +00:00
|
|
|
|
2019-09-13 13:03:03 +00:00
|
|
|
let engine = Engine::new(&manager,
|
2019-09-07 14:13:13 +00:00
|
|
|
&clipboard_manager,
|
2019-09-08 11:37:58 +00:00
|
|
|
&config_manager,
|
|
|
|
&ui_manager
|
|
|
|
);
|
2019-08-31 14:07:45 +00:00
|
|
|
|
2019-09-12 20:14:41 +00:00
|
|
|
let matcher = ScrollingMatcher::new(&config_manager, &engine);
|
|
|
|
|
|
|
|
let event_manager = DefaultEventManager::new(
|
|
|
|
receive_channel,
|
2019-09-12 21:53:17 +00:00
|
|
|
vec!(&matcher),
|
2019-09-13 12:43:48 +00:00
|
|
|
vec!(&engine, &matcher),
|
2019-09-12 20:14:41 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
event_manager.eventloop();
|
2019-09-13 21:30:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Cli tool used to analyze active windows to extract useful information
|
|
|
|
/// to create configuration filters.
|
|
|
|
fn detect_main() {
|
|
|
|
let system_manager = system::get_manager();
|
|
|
|
|
|
|
|
println!("Listening for changes, now focus the window you want to analyze.");
|
|
|
|
println!("You can terminate with CTRL+C\n");
|
|
|
|
|
|
|
|
let mut last_title : String = "".to_owned();
|
|
|
|
let mut last_class : String = "".to_owned();
|
|
|
|
let mut last_exec : String = "".to_owned();
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let curr_title = system_manager.get_current_window_title().unwrap_or_default();
|
|
|
|
let curr_class = system_manager.get_current_window_class().unwrap_or_default();
|
|
|
|
let curr_exec = system_manager.get_current_window_executable().unwrap_or_default();
|
|
|
|
|
|
|
|
// Check if a change occurred
|
|
|
|
if curr_title != last_title || curr_class != last_class || curr_exec != last_exec {
|
|
|
|
println!("Detected change, current window has properties:");
|
|
|
|
println!("==> Title: '{}'", curr_title);
|
|
|
|
println!("==> Class: '{}'", curr_class);
|
|
|
|
println!("==> Executable: '{}'", curr_exec);
|
|
|
|
println!("");
|
|
|
|
}
|
|
|
|
|
|
|
|
last_title = curr_title;
|
|
|
|
last_class = curr_class;
|
|
|
|
last_exec = curr_exec;
|
|
|
|
|
|
|
|
thread::sleep(Duration::from_millis(500));
|
|
|
|
}
|
2019-08-30 12:33:40 +00:00
|
|
|
}
|