use super::{ConsoleControl, HandlerContract};
use std::io;
use std::sync::atomic::{AtomicUsize, Ordering};
use windows_sys::core::BOOL;
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::System::Console::{
GetConsoleMode, GetStdHandle, SetConsoleCtrlHandler, SetConsoleMode, WriteConsoleA,
CONSOLE_MODE, CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT, CTRL_C_EVENT, CTRL_LOGOFF_EVENT,
CTRL_SHUTDOWN_EVENT, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
};
#[derive(Debug, Clone, Copy, Default)]
pub struct WindowsConsoleControl;
impl WindowsConsoleControl {
pub const fn new() -> Self {
Self
}
}
impl crate::sealed::Sealed for WindowsConsoleControl {}
#[derive(Clone, Copy)]
pub struct WindowsModes {
input: CONSOLE_MODE,
output: CONSOLE_MODE,
}
static ON_INTERRUPT: AtomicUsize = AtomicUsize::new(0);
const CLOSE_EVENT_BUDGET_MS: u32 = 5000;
unsafe extern "system" fn on_ctrl(event: u32) -> BOOL {
if !matches!(
event,
CTRL_C_EVENT
| CTRL_BREAK_EVENT
| CTRL_CLOSE_EVENT
| CTRL_LOGOFF_EVENT
| CTRL_SHUTDOWN_EVENT
) {
return 0; }
let f = ON_INTERRUPT.load(Ordering::SeqCst);
if f != 0 {
let f: fn() = unsafe { std::mem::transmute::<usize, fn()>(f) };
f();
}
std::process::exit(130);
}
fn std_handle(which: u32) -> io::Result<HANDLE> {
let h = unsafe { GetStdHandle(which) };
if h.is_null() || h as isize == -1 {
return Err(io::Error::last_os_error());
}
Ok(h)
}
impl ConsoleControl for WindowsConsoleControl {
type Saved = WindowsModes;
const HANDLER_CONTRACT: HandlerContract = HandlerContract::SeparateThreadWithBudget {
millis: CLOSE_EVENT_BUDGET_MS,
};
fn capture(&self) -> io::Result<Self::Saved> {
let (i, o) = (
std_handle(STD_INPUT_HANDLE)?,
std_handle(STD_OUTPUT_HANDLE)?,
);
let mut input: CONSOLE_MODE = 0;
let mut output: CONSOLE_MODE = 0;
if unsafe { GetConsoleMode(i, &mut input) } == 0 {
return Err(io::Error::last_os_error());
}
if unsafe { GetConsoleMode(o, &mut output) } == 0 {
return Err(io::Error::last_os_error());
}
Ok(WindowsModes { input, output })
}
fn restore(&self, saved: &Self::Saved) -> io::Result<()> {
let (i, o) = (
std_handle(STD_INPUT_HANDLE)?,
std_handle(STD_OUTPUT_HANDLE)?,
);
if unsafe { SetConsoleMode(i, saved.input) } == 0 {
return Err(io::Error::last_os_error());
}
if unsafe { SetConsoleMode(o, saved.output) } == 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
fn write_raw(&self, bytes: &[u8]) {
let Ok(h) = std_handle(STD_OUTPUT_HANDLE) else {
return;
};
let mut written = 0u32;
unsafe {
WriteConsoleA(
h,
bytes.as_ptr().cast(),
bytes.len() as u32,
&mut written,
std::ptr::null(),
);
}
}
fn trap(&self, on_interrupt: fn()) -> io::Result<()> {
ON_INTERRUPT.store(on_interrupt as usize, Ordering::SeqCst);
if unsafe { SetConsoleCtrlHandler(Some(on_ctrl), 1) } == 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
fn interrupt_exit_code(&self) -> i32 {
130
}
}