use crate::Session; use libc::c_int; use nix::sys::signal::*; use std::{sync::*, time::Instant}; static SESSION: Mutex>>> = Mutex::new(None); fn handle() { let Ok(mut se) = SESSION.lock() else { return }; let Some(se) = se.as_mut() else { return }; let Ok(mut se) = se.lock() else { return }; se.ctrlc.last_press = Instant::now(); } extern "C" fn c_handle(_signal: c_int) { // cannot propagate panic into C-land let _ = std::panic::catch_unwind(|| { if let Err(e) = std::panic::catch_unwind(handle) { eprintln!("{e:?}"); // might panic } }); } pub struct CtrlC { last_press: Instant, } impl Default for CtrlC { fn default() -> Self { Self { last_press: Instant::now(), } } } struct Teardown; impl Drop for Teardown { fn drop(&mut self) { teardown(); } } fn teardown() { unsafe { let _ = signal(Signal::SIGINT, SigHandler::SigDfl); } if let Ok(mut se) = SESSION.lock() { *se = None; } } #[must_use] pub fn setup(session: Arc>) -> impl Drop { *SESSION.lock().unwrap() = Some(session); unsafe { signal(Signal::SIGINT, SigHandler::Handler(c_handle)) .expect("failed to set ctrl+c signal handler"); } Teardown } pub fn pressed_since(session: &Session, instant: Instant) -> bool { session.ctrlc.last_press > instant }