feat(core): add restart option in context menu and improve exit handling
This commit is contained in:
parent
39758e2b9a
commit
4ab040da3c
|
@ -27,7 +27,14 @@ use espanso_ipc::IPCClient;
|
||||||
use espanso_path::Paths;
|
use espanso_path::Paths;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
|
|
||||||
use crate::{exit_code::{DAEMON_ALREADY_RUNNING, DAEMON_GENERAL_ERROR, DAEMON_SUCCESS, WORKER_EXIT_ALL_PROCESSES, WORKER_SUCCESS}, ipc::{create_ipc_client_to_worker, IPCEvent}, lock::{acquire_daemon_lock, acquire_worker_lock}};
|
use crate::{
|
||||||
|
exit_code::{
|
||||||
|
DAEMON_ALREADY_RUNNING, DAEMON_GENERAL_ERROR, DAEMON_SUCCESS, WORKER_EXIT_ALL_PROCESSES,
|
||||||
|
WORKER_RESTART, WORKER_SUCCESS,
|
||||||
|
},
|
||||||
|
ipc::{create_ipc_client_to_worker, IPCEvent},
|
||||||
|
lock::{acquire_daemon_lock, acquire_worker_lock},
|
||||||
|
};
|
||||||
|
|
||||||
use super::{CliModule, CliModuleArgs};
|
use super::{CliModule, CliModuleArgs};
|
||||||
|
|
||||||
|
@ -74,13 +81,11 @@ fn daemon_main(args: CliModuleArgs) -> i32 {
|
||||||
|
|
||||||
spawn_worker(&paths, exit_notify.clone());
|
spawn_worker(&paths, exit_notify.clone());
|
||||||
|
|
||||||
ipc::initialize_and_spawn(&paths.runtime, exit_notify)
|
ipc::initialize_and_spawn(&paths.runtime, exit_notify.clone())
|
||||||
.expect("unable to initialize ipc server for daemon");
|
.expect("unable to initialize ipc server for daemon");
|
||||||
|
|
||||||
// TODO: start file watcher thread
|
// TODO: start file watcher thread
|
||||||
|
|
||||||
let mut exit_code: i32 = DAEMON_SUCCESS;
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
select! {
|
select! {
|
||||||
recv(exit_signal) -> code => {
|
recv(exit_signal) -> code => {
|
||||||
|
@ -89,24 +94,28 @@ fn daemon_main(args: CliModuleArgs) -> i32 {
|
||||||
match code {
|
match code {
|
||||||
WORKER_EXIT_ALL_PROCESSES => {
|
WORKER_EXIT_ALL_PROCESSES => {
|
||||||
info!("worker requested a general exit, quitting the daemon");
|
info!("worker requested a general exit, quitting the daemon");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
WORKER_RESTART => {
|
||||||
|
info!("worker requested a restart, spawning a new one...");
|
||||||
|
spawn_worker(&paths, exit_notify.clone());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
error!("received unexpected exit code from worker {}, exiting", code);
|
error!("received unexpected exit code from worker {}, exiting", code);
|
||||||
exit_code = code
|
return code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("received error when unwrapping exit_code: {}", err);
|
error!("received error when unwrapping exit_code: {}", err);
|
||||||
exit_code = DAEMON_GENERAL_ERROR;
|
return DAEMON_GENERAL_ERROR;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exit_code
|
DAEMON_SUCCESS
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminate_worker_if_already_running(runtime_dir: &Path, worker_ipc: impl IPCClient<IPCEvent>) {
|
fn terminate_worker_if_already_running(runtime_dir: &Path, worker_ipc: impl IPCClient<IPCEvent>) {
|
||||||
|
|
|
@ -27,7 +27,7 @@ use espanso_ui::{UIRemote, event::UIEvent};
|
||||||
use log::info;
|
use log::info;
|
||||||
use ui::selector::MatchSelectorAdapter;
|
use ui::selector::MatchSelectorAdapter;
|
||||||
|
|
||||||
use crate::cli::worker::engine::{matcher::regex::RegexMatcherAdapterOptions, path::PathProviderAdapter};
|
use crate::{cli::worker::engine::{matcher::regex::RegexMatcherAdapterOptions, path::PathProviderAdapter}, engine::event::ExitMode};
|
||||||
|
|
||||||
use super::ui::icon::IconPaths;
|
use super::ui::icon::IconPaths;
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ pub fn initialize_and_spawn(
|
||||||
ui_remote: Box<dyn UIRemote>,
|
ui_remote: Box<dyn UIRemote>,
|
||||||
exit_signal: Receiver<()>,
|
exit_signal: Receiver<()>,
|
||||||
ui_event_receiver: Receiver<UIEvent>,
|
ui_event_receiver: Receiver<UIEvent>,
|
||||||
) -> Result<JoinHandle<bool>> {
|
) -> Result<JoinHandle<ExitMode>> {
|
||||||
let handle = std::thread::Builder::new()
|
let handle = std::thread::Builder::new()
|
||||||
.name("engine thread".to_string())
|
.name("engine thread".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
|
@ -154,12 +154,12 @@ pub fn initialize_and_spawn(
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut engine = crate::engine::Engine::new(&funnel, &mut processor, &dispatcher);
|
let mut engine = crate::engine::Engine::new(&funnel, &mut processor, &dispatcher);
|
||||||
let exit_all_processes = engine.run();
|
let exit_mode = engine.run();
|
||||||
|
|
||||||
info!("engine eventloop has terminated, propagating exit event...");
|
info!("engine eventloop has terminated, propagating exit event...");
|
||||||
ui_remote.exit();
|
ui_remote.exit();
|
||||||
|
|
||||||
exit_all_processes
|
exit_mode
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(handle)
|
Ok(handle)
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
use crossbeam::channel::{Receiver, Select, SelectedOperation};
|
use crossbeam::channel::{Receiver, Select, SelectedOperation};
|
||||||
|
|
||||||
use crate::engine::{event::{Event, EventType}, funnel};
|
use crate::engine::{event::{Event, EventType, ExitMode}, funnel};
|
||||||
|
|
||||||
use super::sequencer::Sequencer;
|
use super::sequencer::Sequencer;
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ impl<'a> funnel::Source<'a> for ExitSource<'a> {
|
||||||
.expect("unable to select data from ExitSource receiver");
|
.expect("unable to select data from ExitSource receiver");
|
||||||
Event {
|
Event {
|
||||||
source_id: self.sequencer.next_id(),
|
source_id: self.sequencer.next_id(),
|
||||||
etype: EventType::ExitRequested(false),
|
etype: EventType::ExitRequested(ExitMode::Exit),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -20,7 +20,14 @@
|
||||||
use crossbeam::channel::unbounded;
|
use crossbeam::channel::unbounded;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
|
|
||||||
use crate::{exit_code::{WORKER_ALREADY_RUNNING, WORKER_EXIT_ALL_PROCESSES, WORKER_GENERAL_ERROR, WORKER_SUCCESS}, lock::acquire_worker_lock};
|
use crate::{
|
||||||
|
engine::event::ExitMode,
|
||||||
|
exit_code::{
|
||||||
|
WORKER_ALREADY_RUNNING, WORKER_EXIT_ALL_PROCESSES, WORKER_GENERAL_ERROR, WORKER_RESTART,
|
||||||
|
WORKER_SUCCESS,
|
||||||
|
},
|
||||||
|
lock::acquire_worker_lock,
|
||||||
|
};
|
||||||
|
|
||||||
use self::ui::util::convert_icon_paths_to_tray_vec;
|
use self::ui::util::convert_icon_paths_to_tray_vec;
|
||||||
|
|
||||||
|
@ -108,15 +115,20 @@ fn worker_main(args: CliModuleArgs) -> i32 {
|
||||||
|
|
||||||
info!("waiting for engine exit mode...");
|
info!("waiting for engine exit mode...");
|
||||||
match engine_handle.join() {
|
match engine_handle.join() {
|
||||||
Ok(exit_all_processes) => {
|
Ok(mode) => match mode {
|
||||||
if exit_all_processes {
|
ExitMode::Exit => {
|
||||||
info!("exiting worker process and daemon...");
|
|
||||||
return WORKER_EXIT_ALL_PROCESSES;
|
|
||||||
} else {
|
|
||||||
info!("exiting worker process...");
|
info!("exiting worker process...");
|
||||||
return WORKER_SUCCESS;
|
return WORKER_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
ExitMode::ExitAllProcesses => {
|
||||||
|
info!("exiting worker process and daemon...");
|
||||||
|
return WORKER_EXIT_ALL_PROCESSES;
|
||||||
|
}
|
||||||
|
ExitMode::RestartWorker => {
|
||||||
|
info!("exiting worker (to be restarted)");
|
||||||
|
return WORKER_RESTART;
|
||||||
|
}
|
||||||
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("unable to read engine exit mode: {:?}", err);
|
error!("unable to read engine exit mode: {:?}", err);
|
||||||
return WORKER_GENERAL_ERROR;
|
return WORKER_GENERAL_ERROR;
|
||||||
|
|
|
@ -48,8 +48,8 @@ impl Event {
|
||||||
pub enum EventType {
|
pub enum EventType {
|
||||||
NOOP,
|
NOOP,
|
||||||
ProcessingError(String),
|
ProcessingError(String),
|
||||||
ExitRequested(bool), // If true, exit also the daemon process and not just the worker
|
ExitRequested(ExitMode),
|
||||||
Exit(bool),
|
Exit(ExitMode),
|
||||||
|
|
||||||
// Inputs
|
// Inputs
|
||||||
Keyboard(input::KeyboardEvent),
|
Keyboard(input::KeyboardEvent),
|
||||||
|
@ -82,4 +82,11 @@ pub enum EventType {
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
ShowContextMenu(ui::ShowContextMenuEvent),
|
ShowContextMenu(ui::ShowContextMenuEvent),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ExitMode {
|
||||||
|
Exit,
|
||||||
|
ExitAllProcesses,
|
||||||
|
RestartWorker,
|
||||||
}
|
}
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
use log::{debug};
|
use log::{debug};
|
||||||
|
|
||||||
use self::{dispatch::Dispatcher, event::{Event, EventType}, funnel::{Funnel, FunnelResult}, process::Processor};
|
use self::{dispatch::Dispatcher, event::{Event, EventType, ExitMode}, funnel::{Funnel, FunnelResult}, process::Processor};
|
||||||
|
|
||||||
pub mod dispatch;
|
pub mod dispatch;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
|
@ -41,15 +41,15 @@ impl <'a> Engine<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) -> bool {
|
pub fn run(&mut self) -> ExitMode {
|
||||||
loop {
|
loop {
|
||||||
match self.funnel.receive() {
|
match self.funnel.receive() {
|
||||||
FunnelResult::Event(event) => {
|
FunnelResult::Event(event) => {
|
||||||
let processed_events = self.processor.process(event);
|
let processed_events = self.processor.process(event);
|
||||||
for event in processed_events {
|
for event in processed_events {
|
||||||
if let EventType::Exit(exit_all_processes) = &event.etype {
|
if let EventType::Exit(mode) = &event.etype {
|
||||||
debug!("exit event received, exiting engine");
|
debug!("exit event received with mode {:?}, exiting engine", mode);
|
||||||
return *exit_all_processes;
|
return mode.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dispatcher.dispatch(event);
|
self.dispatcher.dispatch(event);
|
||||||
|
@ -57,7 +57,7 @@ impl <'a> Engine<'a> {
|
||||||
}
|
}
|
||||||
FunnelResult::EndOfStream => {
|
FunnelResult::EndOfStream => {
|
||||||
debug!("end of stream received");
|
debug!("end of stream received");
|
||||||
return false;
|
return ExitMode::Exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,11 @@
|
||||||
use super::super::Middleware;
|
use super::super::Middleware;
|
||||||
use crate::engine::event::{
|
use crate::engine::event::{
|
||||||
ui::{MenuItem, ShowContextMenuEvent, SimpleMenuItem},
|
ui::{MenuItem, ShowContextMenuEvent, SimpleMenuItem},
|
||||||
Event, EventType,
|
Event, EventType, ExitMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
const CONTEXT_ITEM_EXIT: u32 = 0;
|
const CONTEXT_ITEM_EXIT: u32 = 0;
|
||||||
|
const CONTEXT_ITEM_RELOAD: u32 = 1;
|
||||||
|
|
||||||
pub struct ContextMenuMiddleware {}
|
pub struct ContextMenuMiddleware {}
|
||||||
|
|
||||||
|
@ -51,18 +52,30 @@ impl Middleware for ContextMenuMiddleware {
|
||||||
event.source_id,
|
event.source_id,
|
||||||
EventType::ShowContextMenu(ShowContextMenuEvent {
|
EventType::ShowContextMenu(ShowContextMenuEvent {
|
||||||
// TODO: add actual entries
|
// TODO: add actual entries
|
||||||
items: vec![MenuItem::Simple(SimpleMenuItem {
|
items: vec![
|
||||||
id: CONTEXT_ITEM_EXIT,
|
MenuItem::Simple(SimpleMenuItem {
|
||||||
label: "Exit espanso".to_string(),
|
id: CONTEXT_ITEM_RELOAD,
|
||||||
})],
|
label: "Reload config".to_string(),
|
||||||
|
}),
|
||||||
|
MenuItem::Separator,
|
||||||
|
MenuItem::Simple(SimpleMenuItem {
|
||||||
|
id: CONTEXT_ITEM_EXIT,
|
||||||
|
label: "Exit espanso".to_string(),
|
||||||
|
}),
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
EventType::ContextMenuClicked(context_click_event) => {
|
EventType::ContextMenuClicked(context_click_event) => {
|
||||||
match context_click_event.context_item_id {
|
match context_click_event.context_item_id {
|
||||||
CONTEXT_ITEM_EXIT => {
|
CONTEXT_ITEM_EXIT => Event::caused_by(
|
||||||
Event::caused_by(event.source_id, EventType::ExitRequested(true))
|
event.source_id,
|
||||||
}
|
EventType::ExitRequested(ExitMode::ExitAllProcesses),
|
||||||
|
),
|
||||||
|
CONTEXT_ITEM_RELOAD => Event::caused_by(
|
||||||
|
event.source_id,
|
||||||
|
EventType::ExitRequested(ExitMode::RestartWorker),
|
||||||
|
),
|
||||||
custom => {
|
custom => {
|
||||||
// TODO: handle dynamic items
|
// TODO: handle dynamic items
|
||||||
todo!()
|
todo!()
|
||||||
|
|
|
@ -36,9 +36,9 @@ impl Middleware for ExitMiddleware {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
||||||
if let EventType::ExitRequested(exit_all_processes) = &event.etype {
|
if let EventType::ExitRequested(mode) = &event.etype {
|
||||||
debug!("received ExitRequested event with 'exit_all_processes: {}', dispatching exit", exit_all_processes);
|
debug!("received ExitRequested event with mode: {:?}, dispatching exit", mode);
|
||||||
return Event::caused_by(event.source_id, EventType::Exit(*exit_all_processes));
|
return Event::caused_by(event.source_id, EventType::Exit(mode.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
event
|
event
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub const WORKER_SUCCESS: i32 = 0;
|
||||||
pub const WORKER_ALREADY_RUNNING: i32 = 1;
|
pub const WORKER_ALREADY_RUNNING: i32 = 1;
|
||||||
pub const WORKER_GENERAL_ERROR: i32 = 2;
|
pub const WORKER_GENERAL_ERROR: i32 = 2;
|
||||||
pub const WORKER_EXIT_ALL_PROCESSES: i32 = 101;
|
pub const WORKER_EXIT_ALL_PROCESSES: i32 = 101;
|
||||||
|
pub const WORKER_RESTART: i32 = 102;
|
||||||
|
|
||||||
pub const DAEMON_SUCCESS: i32 = 0;
|
pub const DAEMON_SUCCESS: i32 = 0;
|
||||||
pub const DAEMON_ALREADY_RUNNING: i32 = 1;
|
pub const DAEMON_ALREADY_RUNNING: i32 = 1;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user