Implement Auto-reload on Windows. #239

This commit is contained in:
Federico Terzi 2020-05-10 14:01:23 +02:00
parent d41366b7c3
commit 56b89432d3
5 changed files with 94 additions and 60 deletions

View File

@ -32,6 +32,7 @@ use regex::{Regex};
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering::Release;
use crate::protocol::{Service, IPCCommand, send_command_or_warn};
pub struct Engine<'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>,
U: UIManager, R: Renderer> {
@ -77,6 +78,18 @@ impl <'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>, U: UIMa
item_id: ActionType::Toggle as i32,
});
menu.push(MenuItem{
item_type: MenuItemType::Separator,
item_name: "".to_owned(),
item_id: 998,
});
menu.push(MenuItem{
item_type: MenuItemType::Button,
item_name: "Reload configs".to_owned(),
item_id: ActionType::RestartWorker as i32,
});
menu.push(MenuItem{
item_type: MenuItemType::Separator,
item_name: "".to_owned(),
@ -332,15 +345,22 @@ impl <'a, S: KeyboardManager, C: ClipboardManager,
M: ConfigManager<'a>, U: UIManager, R: Renderer> ActionEventReceiver for Engine<'a, S, C, M, U, R>{
fn on_action_event(&self, e: ActionType) {
let config = self.config_manager.default_config();
match e {
ActionType::IconClick => {
self.ui_manager.show_menu(self.build_menu());
},
ActionType::Exit => {
ActionType::ExitWorker => {
info!("terminating worker process");
self.ui_manager.cleanup();
exit(0);
},
ActionType::Exit => {
send_command_or_warn(Service::Daemon, config.clone(), IPCCommand::exit());
},
ActionType::RestartWorker => {
send_command_or_warn(Service::Daemon, config.clone(), IPCCommand::restart_worker());
},
_ => {}
}
}

View File

@ -38,6 +38,7 @@ pub enum ActionType {
Enable = 4,
Disable = 5,
RestartWorker = 6,
ExitWorker = 7,
}
impl From<i32> for ActionType {
@ -49,6 +50,7 @@ impl From<i32> for ActionType {
4 => ActionType::Enable,
5 => ActionType::Disable,
6 => ActionType::RestartWorker,
7 => ActionType::ExitWorker,
_ => ActionType::Noop,
}
}

View File

@ -382,11 +382,7 @@ fn daemon_main(config_set: ConfigSet) {
match event {
Event::Action(ActionType::RestartWorker) => {
// Terminate the worker process
let ipc_client = protocol::get_ipc_client(Service::Worker, config_set.default.clone());
ipc_client.send_command(IPCCommand {
id: "exit".to_owned(),
payload: "".to_owned(),
});
send_command_or_warn(Service::Worker, config_set.default.clone(), IPCCommand::exit_worker());
std::thread::sleep(Duration::from_millis(500));
@ -394,11 +390,7 @@ fn daemon_main(config_set: ConfigSet) {
crate::process::spawn_process(&espanso_path.to_string_lossy().to_string(), &vec!("worker".to_owned(), "--reload".to_owned()));
},
Event::Action(ActionType::Exit) => {
let ipc_client = protocol::get_ipc_client(Service::Worker, config_set.default.clone());
ipc_client.send_command(IPCCommand {
id: "exit".to_owned(),
payload: "".to_owned(),
});
send_command_or_warn(Service::Worker, config_set.default.clone(), IPCCommand::exit_worker());
std::thread::sleep(Duration::from_millis(200));
@ -408,9 +400,8 @@ fn daemon_main(config_set: ConfigSet) {
_ => {
// Forward the command to the worker
let command = IPCCommand::from(event);
let ipc_client = protocol::get_ipc_client(Service::Worker, config_set.default.clone());
if let Some(command) = command {
ipc_client.send_command(command);
send_command_or_warn(Service::Worker, config_set.default.clone(), command);
}
}
}
@ -431,7 +422,7 @@ fn watcher_background(sender: Sender<Event>) {
let config_path = crate::context::get_config_dir();
watcher.watch(&config_path, RecursiveMode::Recursive).expect("unable to start watcher");
info!("watching for changes in path: {:?}", config_path);
info!("watching for changes in path: {}", config_path.to_string_lossy());
loop {
@ -558,7 +549,7 @@ fn worker_background(receive_channel: Receiver<Event>, config_set: ConfigSet, is
vec!(&engine),
);
info!("espanso is running!");
info!("worker is running!");
event_manager.eventloop();
}
@ -742,17 +733,7 @@ fn stop_main(config_set: ConfigSet) {
exit(3);
}
let res = send_command(Service::Daemon, config_set.default, IPCCommand{
id: "exit".to_owned(),
payload: "".to_owned(),
});
if let Err(e) = res {
println!("{}", e);
exit(1);
}else{
exit(0);
}
send_command_or_warn(Service::Daemon, config_set.default, IPCCommand::exit());
}
/// Kill the daemon if running and start it again
@ -761,10 +742,7 @@ fn restart_main(config_set: ConfigSet) {
let lock_file = acquire_lock();
if lock_file.is_none() {
// Terminate the current espanso daemon
send_command(Service::Daemon, config_set.default.clone(), IPCCommand {
id: "exit".to_owned(),
payload: "".to_owned(),
});
send_command_or_warn(Service::Daemon, config_set.default.clone(), IPCCommand::exit());
}else{
release_lock(lock_file.unwrap());
}
@ -859,10 +837,7 @@ fn detect_main() {
/// Send the given command to the espanso daemon
fn cmd_main(config_set: ConfigSet, matches: &ArgMatches) {
let command = if matches.subcommand_matches("exit").is_some() {
Some(IPCCommand {
id: String::from("exit"),
payload: String::from(""),
})
Some(IPCCommand::exit())
}else if matches.subcommand_matches("toggle").is_some() {
Some(IPCCommand {
id: String::from("toggle"),
@ -883,23 +858,12 @@ fn cmd_main(config_set: ConfigSet, matches: &ArgMatches) {
};
if let Some(command) = command {
let res = send_command(Service::Daemon, config_set.default, command);
if res.is_ok() {
exit(0);
}else{
println!("{}", res.unwrap_err());
}
send_command_or_warn(Service::Daemon, config_set.default, command);
}
exit(1);
}
fn send_command(service: Service, config: Configs, command: IPCCommand) -> Result<(), String> {
let ipc_client = protocol::get_ipc_client(service, config);
ipc_client.send_command(command)
}
fn log_main() {
let espanso_dir = context::get_data_dir();
let log_file_path = espanso_dir.join(LOG_FILE);

View File

@ -40,6 +40,14 @@ pub trait IPCClient {
fn send_command(&self, command: IPCCommand) -> Result<(), String>;
}
pub fn send_command_or_warn(service: Service, configs: Configs, command: IPCCommand) {
let ipc_client = get_ipc_client(service, configs);
if let Err(e) = ipc_client.send_command(command) {
error!("unable to send command to IPC server");
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct IPCCommand {
pub id: String,
@ -54,6 +62,9 @@ impl IPCCommand {
"exit" => {
Some(Event::Action(ActionType::Exit))
},
"wexit" => {
Some(Event::Action(ActionType::ExitWorker))
},
"toggle" => {
Some(Event::Action(ActionType::Toggle))
},
@ -63,6 +74,9 @@ impl IPCCommand {
"disable" => {
Some(Event::Action(ActionType::Disable))
},
"restartworker" => {
Some(Event::Action(ActionType::RestartWorker))
},
"notify" => {
Some(Event::System(SystemEvent::NotifyRequest(self.payload.clone())))
},
@ -73,13 +87,36 @@ impl IPCCommand {
pub fn from(event: Event) -> Option<IPCCommand> {
match event {
Event::Action(ActionType::Exit) => Some(IPCCommand{id: "exit".to_owned(), payload: "".to_owned()}),
Event::Action(ActionType::ExitWorker) => Some(IPCCommand{id: "wexit".to_owned(), payload: "".to_owned()}),
Event::Action(ActionType::Toggle) => Some(IPCCommand{id: "toggle".to_owned(), payload: "".to_owned()}),
Event::Action(ActionType::Enable) => Some(IPCCommand{id: "enable".to_owned(), payload: "".to_owned()}),
Event::Action(ActionType::Disable) => Some(IPCCommand{id: "disable".to_owned(), payload: "".to_owned()}),
Event::Action(ActionType::RestartWorker) => Some(IPCCommand{id: "restartworker".to_owned(), payload: "".to_owned()}),
Event::System(SystemEvent::NotifyRequest(message)) => Some(IPCCommand{id: "notify".to_owned(), payload: message}),
_ => None
}
}
pub fn exit() -> IPCCommand {
Self {
id: "exit".to_owned(),
payload: "".to_owned(),
}
}
pub fn exit_worker() -> IPCCommand {
Self {
id: "wexit".to_owned(),
payload: "".to_owned(),
}
}
pub fn restart_worker() -> IPCCommand {
Self {
id: "restartworker".to_owned(),
payload: "".to_owned(),
}
}
}
fn process_event<R: Read, E: Error>(event_channel: &Sender<Event>, stream: Result<R, E>) {
@ -147,11 +184,11 @@ pub fn get_ipc_client(service: Service, _: Configs) -> impl IPCClient {
// WINDOWS IMPLEMENTATION
#[cfg(target_os = "windows")]
pub fn get_ipc_server(config_set: Configs, event_channel: Sender<Event>) -> impl IPCServer {
windows::WindowsIPCServer::new(config_set, event_channel)
pub fn get_ipc_server(service: Service, config: Configs, event_channel: Sender<Event>) -> impl IPCServer {
windows::WindowsIPCServer::new(service, config, event_channel)
}
#[cfg(target_os = "windows")]
pub fn get_ipc_client(config_set: Configs) -> impl IPCClient {
windows::WindowsIPCClient::new(config_set)
pub fn get_ipc_client(service: Service, config: Configs) -> impl IPCClient {
windows::WindowsIPCClient::new(service, config)
}

View File

@ -23,24 +23,33 @@ use std::net::{TcpListener, TcpStream};
use super::IPCCommand;
use crate::event::*;
use crate::protocol::{process_event, send_command};
use crate::config::ConfigSet;
use crate::protocol::{process_event, send_command, Service};
use crate::config::{Configs};
pub struct WindowsIPCServer {
config_set: ConfigSet,
service: Service,
config: Configs,
event_channel: Sender<Event>,
}
fn to_port(config: &Configs, service: &Service) -> u16 {
let port = match service {
Service::Daemon => {config.ipc_server_port},
Service::Worker => {config.worker_ipc_server_port},
};
port as u16
}
impl WindowsIPCServer {
pub fn new(config_set: ConfigSet, event_channel: Sender<Event>) -> WindowsIPCServer {
WindowsIPCServer {config_set, event_channel}
pub fn new(service: Service, config: Configs, event_channel: Sender<Event>) -> WindowsIPCServer {
WindowsIPCServer {service, config, event_channel}
}
}
impl super::IPCServer for WindowsIPCServer {
fn start(&self) {
let event_channel = self.event_channel.clone();
let server_port = self.config_set.default.ipc_server_port;
let server_port = to_port(&self.config, &self.service);
std::thread::Builder::new().name("ipc_server".to_string()).spawn(move || {
let listener = TcpListener::bind(
format!("127.0.0.1:{}", server_port)
@ -56,19 +65,21 @@ impl super::IPCServer for WindowsIPCServer {
}
pub struct WindowsIPCClient {
config_set: ConfigSet,
service: Service,
config: Configs,
}
impl WindowsIPCClient {
pub fn new(config_set: ConfigSet) -> WindowsIPCClient {
WindowsIPCClient{config_set}
pub fn new(service: Service, config: Configs) -> WindowsIPCClient {
WindowsIPCClient{service, config}
}
}
impl super::IPCClient for WindowsIPCClient {
fn send_command(&self, command: IPCCommand) -> Result<(), String> {
let port = to_port(&self.config, &self.service);
let stream = TcpStream::connect(
("127.0.0.1", self.config_set.default.ipc_server_port as u16)
("127.0.0.1", port)
);
send_command(command, stream)