Implement Auto-reload on Windows. #239
This commit is contained in:
parent
d41366b7c3
commit
56b89432d3
|
@ -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());
|
||||||
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
54
src/main.rs
54
src/main.rs
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user