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::Arc;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering::Release; 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>, pub struct Engine<'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>,
U: UIManager, R: Renderer> { 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, 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{ menu.push(MenuItem{
item_type: MenuItemType::Separator, item_type: MenuItemType::Separator,
item_name: "".to_owned(), 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>{ M: ConfigManager<'a>, U: UIManager, R: Renderer> ActionEventReceiver for Engine<'a, S, C, M, U, R>{
fn on_action_event(&self, e: ActionType) { fn on_action_event(&self, e: ActionType) {
let config = self.config_manager.default_config();
match e { match e {
ActionType::IconClick => { ActionType::IconClick => {
self.ui_manager.show_menu(self.build_menu()); self.ui_manager.show_menu(self.build_menu());
}, },
ActionType::Exit => { ActionType::ExitWorker => {
info!("terminating worker process"); info!("terminating worker process");
self.ui_manager.cleanup(); self.ui_manager.cleanup();
exit(0); 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, Enable = 4,
Disable = 5, Disable = 5,
RestartWorker = 6, RestartWorker = 6,
ExitWorker = 7,
} }
impl From<i32> for ActionType { impl From<i32> for ActionType {
@ -49,6 +50,7 @@ impl From<i32> for ActionType {
4 => ActionType::Enable, 4 => ActionType::Enable,
5 => ActionType::Disable, 5 => ActionType::Disable,
6 => ActionType::RestartWorker, 6 => ActionType::RestartWorker,
7 => ActionType::ExitWorker,
_ => ActionType::Noop, _ => ActionType::Noop,
} }
} }

View File

@ -382,11 +382,7 @@ fn daemon_main(config_set: ConfigSet) {
match event { match event {
Event::Action(ActionType::RestartWorker) => { Event::Action(ActionType::RestartWorker) => {
// Terminate the worker process // Terminate the worker process
let ipc_client = protocol::get_ipc_client(Service::Worker, config_set.default.clone()); send_command_or_warn(Service::Worker, config_set.default.clone(), IPCCommand::exit_worker());
ipc_client.send_command(IPCCommand {
id: "exit".to_owned(),
payload: "".to_owned(),
});
std::thread::sleep(Duration::from_millis(500)); 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())); crate::process::spawn_process(&espanso_path.to_string_lossy().to_string(), &vec!("worker".to_owned(), "--reload".to_owned()));
}, },
Event::Action(ActionType::Exit) => { Event::Action(ActionType::Exit) => {
let ipc_client = protocol::get_ipc_client(Service::Worker, config_set.default.clone()); send_command_or_warn(Service::Worker, config_set.default.clone(), IPCCommand::exit_worker());
ipc_client.send_command(IPCCommand {
id: "exit".to_owned(),
payload: "".to_owned(),
});
std::thread::sleep(Duration::from_millis(200)); std::thread::sleep(Duration::from_millis(200));
@ -408,9 +400,8 @@ fn daemon_main(config_set: ConfigSet) {
_ => { _ => {
// Forward the command to the worker // Forward the command to the worker
let command = IPCCommand::from(event); let command = IPCCommand::from(event);
let ipc_client = protocol::get_ipc_client(Service::Worker, config_set.default.clone());
if let Some(command) = command { 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(); let config_path = crate::context::get_config_dir();
watcher.watch(&config_path, RecursiveMode::Recursive).expect("unable to start watcher"); 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 { loop {
@ -558,7 +549,7 @@ fn worker_background(receive_channel: Receiver<Event>, config_set: ConfigSet, is
vec!(&engine), vec!(&engine),
); );
info!("espanso is running!"); info!("worker is running!");
event_manager.eventloop(); event_manager.eventloop();
} }
@ -742,17 +733,7 @@ fn stop_main(config_set: ConfigSet) {
exit(3); exit(3);
} }
let res = send_command(Service::Daemon, config_set.default, IPCCommand{ send_command_or_warn(Service::Daemon, config_set.default, IPCCommand::exit());
id: "exit".to_owned(),
payload: "".to_owned(),
});
if let Err(e) = res {
println!("{}", e);
exit(1);
}else{
exit(0);
}
} }
/// Kill the daemon if running and start it again /// Kill the daemon if running and start it again
@ -761,10 +742,7 @@ fn restart_main(config_set: ConfigSet) {
let lock_file = acquire_lock(); let lock_file = acquire_lock();
if lock_file.is_none() { if lock_file.is_none() {
// Terminate the current espanso daemon // Terminate the current espanso daemon
send_command(Service::Daemon, config_set.default.clone(), IPCCommand { send_command_or_warn(Service::Daemon, config_set.default.clone(), IPCCommand::exit());
id: "exit".to_owned(),
payload: "".to_owned(),
});
}else{ }else{
release_lock(lock_file.unwrap()); release_lock(lock_file.unwrap());
} }
@ -859,10 +837,7 @@ fn detect_main() {
/// Send the given command to the espanso daemon /// Send the given command to the espanso daemon
fn cmd_main(config_set: ConfigSet, matches: &ArgMatches) { fn cmd_main(config_set: ConfigSet, matches: &ArgMatches) {
let command = if matches.subcommand_matches("exit").is_some() { let command = if matches.subcommand_matches("exit").is_some() {
Some(IPCCommand { Some(IPCCommand::exit())
id: String::from("exit"),
payload: String::from(""),
})
}else if matches.subcommand_matches("toggle").is_some() { }else if matches.subcommand_matches("toggle").is_some() {
Some(IPCCommand { Some(IPCCommand {
id: String::from("toggle"), id: String::from("toggle"),
@ -883,23 +858,12 @@ fn cmd_main(config_set: ConfigSet, matches: &ArgMatches) {
}; };
if let Some(command) = command { if let Some(command) = command {
let res = send_command(Service::Daemon, config_set.default, command); send_command_or_warn(Service::Daemon, config_set.default, command);
if res.is_ok() {
exit(0);
}else{
println!("{}", res.unwrap_err());
}
} }
exit(1); 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() { fn log_main() {
let espanso_dir = context::get_data_dir(); let espanso_dir = context::get_data_dir();
let log_file_path = espanso_dir.join(LOG_FILE); 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>; 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)] #[derive(Serialize, Deserialize, Debug)]
pub struct IPCCommand { pub struct IPCCommand {
pub id: String, pub id: String,
@ -54,6 +62,9 @@ impl IPCCommand {
"exit" => { "exit" => {
Some(Event::Action(ActionType::Exit)) Some(Event::Action(ActionType::Exit))
}, },
"wexit" => {
Some(Event::Action(ActionType::ExitWorker))
},
"toggle" => { "toggle" => {
Some(Event::Action(ActionType::Toggle)) Some(Event::Action(ActionType::Toggle))
}, },
@ -63,6 +74,9 @@ impl IPCCommand {
"disable" => { "disable" => {
Some(Event::Action(ActionType::Disable)) Some(Event::Action(ActionType::Disable))
}, },
"restartworker" => {
Some(Event::Action(ActionType::RestartWorker))
},
"notify" => { "notify" => {
Some(Event::System(SystemEvent::NotifyRequest(self.payload.clone()))) Some(Event::System(SystemEvent::NotifyRequest(self.payload.clone())))
}, },
@ -73,13 +87,36 @@ impl IPCCommand {
pub fn from(event: Event) -> Option<IPCCommand> { pub fn from(event: Event) -> Option<IPCCommand> {
match event { match event {
Event::Action(ActionType::Exit) => Some(IPCCommand{id: "exit".to_owned(), payload: "".to_owned()}), 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::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::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::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}), Event::System(SystemEvent::NotifyRequest(message)) => Some(IPCCommand{id: "notify".to_owned(), payload: message}),
_ => None _ => 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>) { 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 // WINDOWS IMPLEMENTATION
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub fn get_ipc_server(config_set: Configs, event_channel: Sender<Event>) -> impl IPCServer { pub fn get_ipc_server(service: Service, config: Configs, event_channel: Sender<Event>) -> impl IPCServer {
windows::WindowsIPCServer::new(config_set, event_channel) windows::WindowsIPCServer::new(service, config, event_channel)
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub fn get_ipc_client(config_set: Configs) -> impl IPCClient { pub fn get_ipc_client(service: Service, config: Configs) -> impl IPCClient {
windows::WindowsIPCClient::new(config_set) windows::WindowsIPCClient::new(service, config)
} }

View File

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