use windows::core::Result as WinResult;
use windows::Win32::Foundation::WAIT_OBJECT_0;
use windows::Win32::{
Foundation::HANDLE,
System::{
Console::{
GetConsoleMode, GetStdHandle, SetConsoleMode, CONSOLE_MODE,
DISABLE_NEWLINE_AUTO_RETURN, ENABLE_ECHO_INPUT, ENABLE_EXTENDED_FLAGS,
ENABLE_INSERT_MODE, ENABLE_LINE_INPUT, ENABLE_MOUSE_INPUT, ENABLE_PROCESSED_INPUT,
ENABLE_QUICK_EDIT_MODE, ENABLE_VIRTUAL_TERMINAL_INPUT, STD_ERROR_HANDLE,
STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
},
Threading::WaitForSingleObject,
},
};
use crate::error::Error;
#[derive(Debug, Clone)]
pub struct Console {
stdin: HANDLE,
stdout: HANDLE,
stderr: HANDLE,
stdin_mode: CONSOLE_MODE,
stdout_mode: CONSOLE_MODE,
stderr_mode: CONSOLE_MODE,
}
impl Console {
pub fn current() -> Result<Self, Error> {
let stdin = unsafe { GetStdHandle(STD_INPUT_HANDLE)? };
let stdout = unsafe { GetStdHandle(STD_OUTPUT_HANDLE)? };
let stderr = unsafe { GetStdHandle(STD_ERROR_HANDLE)? };
let stdin_mode = get_console_mode(stdin)?;
let stdout_mode = get_console_mode(stdout)?;
let stderr_mode = get_console_mode(stderr)?;
Ok(Self {
stderr,
stderr_mode,
stdin,
stdin_mode,
stdout,
stdout_mode,
})
}
pub fn set_raw(&self) -> Result<(), Error> {
set_raw_stdin(self.stdin, self.stdin_mode)?;
unsafe {
SetConsoleMode(self.stdout, self.stdout_mode | DISABLE_NEWLINE_AUTO_RETURN)?;
}
unsafe {
SetConsoleMode(self.stderr, self.stderr_mode | DISABLE_NEWLINE_AUTO_RETURN)?;
}
Ok(())
}
pub fn reset(&self) -> Result<(), Error> {
for (handle, mode) in self.streams() {
unsafe { SetConsoleMode(handle, mode)? };
}
Ok(())
}
pub fn is_stdin_empty(&self) -> Result<bool, Error> {
let empty = unsafe { WaitForSingleObject(self.stdin, 0) == WAIT_OBJECT_0 };
Ok(empty)
}
fn streams(&self) -> [(HANDLE, CONSOLE_MODE); 3] {
[
(self.stdin, self.stdin_mode),
(self.stdout, self.stdout_mode),
(self.stderr, self.stderr_mode),
]
}
}
fn get_console_mode(h: HANDLE) -> WinResult<CONSOLE_MODE> {
let mut mode = CONSOLE_MODE::default();
unsafe {
GetConsoleMode(h, &mut mode)?;
}
Ok(mode)
}
fn set_raw_stdin(stdin: HANDLE, mut mode: CONSOLE_MODE) -> WinResult<()> {
mode &= !ENABLE_ECHO_INPUT;
mode &= !ENABLE_LINE_INPUT;
mode &= !ENABLE_MOUSE_INPUT;
mode &= !ENABLE_LINE_INPUT;
mode &= !ENABLE_PROCESSED_INPUT;
mode |= ENABLE_EXTENDED_FLAGS;
mode |= ENABLE_INSERT_MODE;
mode |= ENABLE_QUICK_EDIT_MODE;
let vt_input_supported = true;
if vt_input_supported {
mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
}
unsafe {
SetConsoleMode(stdin, mode)?;
}
Ok(())
}