use super::registry::{DeadOnDrop, ListenerRegistry};
use std::sync::OnceLock;
use windows_sys::Win32::Foundation::{FALSE, TRUE};
use windows_sys::Win32::System::Console::{
CTRL_BREAK_EVENT, CTRL_C_EVENT, CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT,
SetConsoleCtrlHandler,
};
type Bool = i32;
static CTRLC: OnceLock<ListenerRegistry> = OnceLock::new();
static CTRLBREAK: OnceLock<ListenerRegistry> = OnceLock::new();
static CTRLCLOSE: OnceLock<ListenerRegistry> = OnceLock::new();
static CTRLLOGOFF: OnceLock<ListenerRegistry> = OnceLock::new();
static CTRLSHUTDOWN: OnceLock<ListenerRegistry> = OnceLock::new();
static HANDLER_INSTALLED: OnceLock<()> = OnceLock::new();
fn ctrlc_registry() -> &'static ListenerRegistry {
CTRLC.get_or_init(ListenerRegistry::new)
}
fn ctrlbreak_registry() -> &'static ListenerRegistry {
CTRLBREAK.get_or_init(ListenerRegistry::new)
}
fn ctrlclose_registry() -> &'static ListenerRegistry {
CTRLCLOSE.get_or_init(ListenerRegistry::new)
}
fn ctrllogoff_registry() -> &'static ListenerRegistry {
CTRLLOGOFF.get_or_init(ListenerRegistry::new)
}
fn ctrlshutdown_registry() -> &'static ListenerRegistry {
CTRLSHUTDOWN.get_or_init(ListenerRegistry::new)
}
unsafe extern "system" fn handler(ctrl_type: u32) -> Bool {
match ctrl_type {
CTRL_C_EVENT => {
ctrlc_registry().broadcast();
TRUE
}
CTRL_BREAK_EVENT => {
ctrlbreak_registry().broadcast();
TRUE
}
CTRL_CLOSE_EVENT => {
ctrlclose_registry().broadcast();
TRUE
}
CTRL_LOGOFF_EVENT => {
ctrllogoff_registry().broadcast();
TRUE
}
CTRL_SHUTDOWN_EVENT => {
ctrlshutdown_registry().broadcast();
TRUE
}
_ => FALSE,
}
}
fn ensure_handler_installed() {
HANDLER_INSTALLED.get_or_init(|| {
let ok = unsafe { SetConsoleCtrlHandler(Some(handler), TRUE) };
assert!(ok != 0, "dtact-signal: SetConsoleCtrlHandler failed");
});
}
pub struct DtactSignalStream {
state: std::sync::Arc<super::registry::ListenerState>,
_dead_on_drop: DeadOnDrop,
}
impl DtactSignalStream {
fn from_registry(reg: &'static ListenerRegistry) -> Self {
ensure_handler_installed();
let state = reg.register();
Self {
state: std::sync::Arc::clone(&state),
_dead_on_drop: DeadOnDrop(state),
}
}
pub async fn recv(&self) {
std::future::poll_fn(|cx| self.state.poll_recv(cx)).await;
}
}
#[must_use]
pub fn ctrl_c() -> DtactSignalStream {
DtactSignalStream::from_registry(ctrlc_registry())
}
#[must_use]
pub fn ctrl_break() -> DtactSignalStream {
DtactSignalStream::from_registry(ctrlbreak_registry())
}
#[must_use]
pub fn ctrl_close() -> DtactSignalStream {
DtactSignalStream::from_registry(ctrlclose_registry())
}
#[must_use]
pub fn ctrl_logoff() -> DtactSignalStream {
DtactSignalStream::from_registry(ctrllogoff_registry())
}
#[must_use]
pub fn ctrl_shutdown() -> DtactSignalStream {
DtactSignalStream::from_registry(ctrlshutdown_registry())
}