use core::pin::Pin;
use core::task::{Context, Poll};
use std::io;
use std::sync::OnceLock;
use super::Listener;
use crate::syscore::windows::{BOOL, SetConsoleCtrlHandler};
const CTRL_C_EVENT: u32 = 0;
const CTRL_BREAK_EVENT: u32 = 1;
const CTRL_CLOSE_EVENT: u32 = 2;
const CTRL_LOGOFF_EVENT: u32 = 5;
const CTRL_SHUTDOWN_EVENT: u32 = 6;
#[derive(Debug)]
pub struct CtrlEvent {
listener: Listener,
}
impl CtrlEvent {
pub async fn recv(&mut self) -> Option<()> {
core::future::poll_fn(|cx| self.poll_recv(cx)).await
}
pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> {
self.listener.poll_recv(cx, || {})
}
}
impl futures_core::Stream for CtrlEvent {
type Item = ();
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<()>> {
self.get_mut().poll_recv(cx)
}
}
pub fn ctrl_c() -> io::Result<CtrlEvent> {
listen(CTRL_C_EVENT)
}
pub fn ctrl_break() -> io::Result<CtrlEvent> {
listen(CTRL_BREAK_EVENT)
}
pub fn ctrl_close() -> io::Result<CtrlEvent> {
listen(CTRL_CLOSE_EVENT)
}
pub fn ctrl_logoff() -> io::Result<CtrlEvent> {
listen(CTRL_LOGOFF_EVENT)
}
pub fn ctrl_shutdown() -> io::Result<CtrlEvent> {
listen(CTRL_SHUTDOWN_EVENT)
}
fn listen(event: u32) -> io::Result<CtrlEvent> {
install()?;
Ok(CtrlEvent {
listener: Listener::new(event as usize),
})
}
fn install() -> io::Result<()> {
static INSTALLED: OnceLock<Result<(), i32>> = OnceLock::new();
INSTALLED
.get_or_init(|| {
if unsafe { SetConsoleCtrlHandler(Some(on_event), 1) } != 0 {
Ok(())
} else {
Err(io::Error::last_os_error().raw_os_error().unwrap_or(0))
}
})
.map_err(io::Error::from_raw_os_error)
}
unsafe extern "system" fn on_event(event: u32) -> BOOL {
let signum = event as usize;
if !super::listened(signum) {
return 0;
}
super::delivered(signum);
super::dispatch();
1
}
#[cfg(test)]
pub(super) fn deliver_for_test(event: u32) -> bool {
unsafe { on_event(event) != 0 }
}