diff --git a/Cargo.lock b/Cargo.lock index 039cf6b1..2cb57041 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1985,6 +1985,7 @@ dependencies = [ "serde_cbor", "serde_json", "signal-hook", + "signal-hook-tokio", "strum", "strum_macros", "tokio", @@ -3267,6 +3268,18 @@ dependencies = [ "libc", ] +[[package]] +name = "signal-hook-tokio" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213241f76fb1e37e27de3b6aa1b068a2c333233b59cca6634f634b80a27ecf1e" +dependencies = [ + "futures-core", + "libc", + "signal-hook", + "tokio", +] + [[package]] name = "slab" version = "0.4.9" diff --git a/Cargo.toml b/Cargo.toml index 2f3d6214..de850fbc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,6 +66,7 @@ wl-clipboard-rs = {version = "0.7", optional = true} [target.'cfg(unix)'.dependencies] signal-hook = "0.3.0" +signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"] } [dependencies.rspotify] default-features = false diff --git a/src/application.rs b/src/application.rs index 86223a8e..7c947ec3 100644 --- a/src/application.rs +++ b/src/application.rs @@ -3,11 +3,15 @@ use std::rc::Rc; use std::sync::{Arc, OnceLock}; use cursive::traits::Nameable; -use cursive::{Cursive, CursiveRunner}; +use cursive::{CbSink, Cursive, CursiveRunner}; use log::{error, info, trace}; #[cfg(unix)] -use signal_hook::{consts::SIGHUP, consts::SIGTERM, iterator::Signals}; +use futures::stream::StreamExt; +#[cfg(unix)] +use signal_hook::{consts::SIGHUP, consts::SIGTERM}; +#[cfg(unix)] +use signal_hook_tokio::Signals; use crate::command::Command; use crate::commands::CommandManager; @@ -50,6 +54,27 @@ pub fn setup_logging(filename: &Path) -> Result<(), fern::InitError> { Ok(()) } +#[cfg(unix)] +async fn handle_signals(cursive_callback_sink: CbSink) { + let mut signals = Signals::new([SIGTERM, SIGHUP]).expect("could not register signal handler"); + + while let Some(signal) = signals.next().await { + info!("Caught {}, cleaning up and closing", signal); + match signal { + SIGTERM => { + cursive_callback_sink + .send(Box::new(|cursive| { + if let Some(data) = cursive.user_data::().cloned() { + data.cmd.handle(cursive, Command::Quit); + } + })) + .expect("can't send callback to cursive"); + } + _ => unreachable!(), + } + } +} + pub type UserData = Rc; pub struct UserDataInner { pub cmd: CommandManager, @@ -193,6 +218,14 @@ impl Application { cursive.add_fullscreen_layer(layout.with_name("main")); + #[cfg(unix)] + let cursive_callback_sink = cursive.cb_sink().clone(); + + #[cfg(unix)] + ASYNC_RUNTIME.get().unwrap().spawn(async { + handle_signals(cursive_callback_sink).await; + }); + Ok(Self { queue, spotify, @@ -207,22 +240,9 @@ impl Application { /// Start the application and run the event loop. pub fn run(&mut self) -> Result<(), String> { - #[cfg(unix)] - let mut signals = - Signals::new([SIGTERM, SIGHUP]).expect("could not register signal handler"); - // cursive event loop while self.cursive.is_running() { self.cursive.step(); - #[cfg(unix)] - for signal in signals.pending() { - if signal == SIGTERM || signal == SIGHUP { - info!("Caught {}, cleaning up and closing", signal); - if let Some(data) = self.cursive.user_data::().cloned() { - data.cmd.handle(&mut self.cursive, Command::Quit); - } - } - } for event in self.event_manager.msg_iter() { match event { Event::Player(state) => {