use std::sync::LazyLock as Lazy;
use std::sync::atomic::{AtomicBool, Ordering};
use console::Term;
use tokio::sync::Notify;
use windows_sys::Win32::Foundation::{FALSE, TRUE};
use windows_sys::Win32::System::Console::{CTRL_BREAK_EVENT, CTRL_C_EVENT, SetConsoleCtrlHandler};
use windows_sys::core::BOOL;
use crate::cmd::CmdLineRunner;
static EXIT: AtomicBool = AtomicBool::new(true);
static SHOW_CURSOR: AtomicBool = AtomicBool::new(false);
static CANCELLED: AtomicBool = AtomicBool::new(false);
static SHOULD_EXIT: AtomicBool = AtomicBool::new(false);
static INSTALLED: AtomicBool = AtomicBool::new(false);
static INTERRUPTED: Lazy<Notify> = Lazy::new(Notify::new);
unsafe extern "system" fn handler(ctrl_type: u32) -> BOOL {
match ctrl_type {
CTRL_C_EVENT | CTRL_BREAK_EVENT => {
if EXIT.load(Ordering::Relaxed) || CANCELLED.swap(true, Ordering::Relaxed) {
SHOULD_EXIT.store(true, Ordering::Relaxed);
}
INTERRUPTED.notify_one();
TRUE
}
_ => FALSE,
}
}
fn install_handler() {
if INSTALLED.swap(true, Ordering::Relaxed) {
return;
}
Lazy::force(&INTERRUPTED);
if unsafe { SetConsoleCtrlHandler(Some(handler), TRUE) } == FALSE {
debug!(
"failed to install console ctrl handler: {}",
std::io::Error::last_os_error()
);
}
}
pub(crate) async fn exit_signal() -> i32 {
loop {
INTERRUPTED.notified().await;
if SHOW_CURSOR.load(Ordering::Relaxed) {
let _ = Term::stderr().show_cursor();
}
vfox::cancel_http_requests();
if SHOULD_EXIT.load(Ordering::Relaxed) {
debug!("Ctrl-C pressed, exiting...");
CmdLineRunner::kill_all();
return 1;
}
info!(
"interrupted, waiting for running commands to exit (press Ctrl-C again to stop them)"
);
}
}
pub(crate) fn exit_on_ctrl_c(do_exit: bool) {
EXIT.store(do_exit, Ordering::Relaxed);
CANCELLED.store(false, Ordering::Relaxed);
install_handler();
}
pub(crate) fn is_cancelled() -> bool {
CANCELLED.load(Ordering::Relaxed)
}
pub(crate) fn show_cursor_after_ctrl_c() {
SHOW_CURSOR.store(true, Ordering::Relaxed);
}