use console::Term;
use signal_hook::{
consts::SIGINT,
iterator::{Handle, Signals},
};
use std::{
io::Error,
sync::{LazyLock, RwLock},
thread,
};
static HANDLE: LazyLock<RwLock<CtrlcHandle>> = LazyLock::new(|| RwLock::new(CtrlcHandle(None)));
#[derive(Clone)]
pub struct CtrlcHandle(Option<Handle>);
impl CtrlcHandle {
pub fn close(&self) {
if let Some(handle) = &self.0 {
handle.close();
let mut handle_guard = HANDLE.write().unwrap();
if handle_guard.0.is_some() {
handle_guard.0 = None;
}
}
}
}
pub fn show_cursor_after_ctrlc(term: &Term) -> Result<CtrlcHandle, Error> {
let t = term.clone();
set_ctrlc_handler(move || {
let _ = t.show_cursor();
})
}
pub fn set_ctrlc_handler<F>(handler: F) -> Result<CtrlcHandle, Error>
where
F: FnMut() + 'static + Send,
{
let mut handle_guard = HANDLE.write().unwrap();
if handle_guard.0.is_some() {
return Ok(handle_guard.clone());
}
let handle = Some(set_ctrlc_handler_internal(handler).unwrap());
if let Some(h) = handle {
*handle_guard = CtrlcHandle(Some(h.clone()));
Ok(CtrlcHandle(Some(h)))
} else {
Err(Error::other("Failed to set Ctrl+C handler"))
}
}
fn set_ctrlc_handler_internal<F>(mut handler: F) -> Result<Handle, Error>
where
F: FnMut() + 'static + Send,
{
let mut signals = Signals::new([SIGINT])?;
let handle = signals.handle();
thread::Builder::new()
.name("ctrl-c".into())
.spawn(move || {
for _ in signals.forever() {
handler();
}
})?;
Ok(handle)
}