use windows::core as win;
use windows::Win32::System::Console::{
GetConsoleMode, GetStdHandle, SetConsoleMode, 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,
};
use windows::Win32::System::Threading::{WaitForSingleObject, WAIT_OBJECT_0};
use windows::Win32::{Foundation::HANDLE, System::Console::CONSOLE_MODE};
use crate::error::Error;
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).ok()?;
}
unsafe {
SetConsoleMode(self.stderr, self.stderr_mode | DISABLE_NEWLINE_AUTO_RETURN).ok()?;
}
Ok(())
}
pub fn reset(&self) -> Result<(), Error> {
for (handle, mode) in self.streams() {
unsafe { SetConsoleMode(handle, mode).ok()? };
}
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) -> win::Result<CONSOLE_MODE> {
let mut mode = CONSOLE_MODE::default();
unsafe {
GetConsoleMode(h, &mut mode).ok()?;
}
Ok(mode)
}
fn set_raw_stdin(stdin: HANDLE, mut mode: CONSOLE_MODE) -> win::Result<()> {
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 vtInputSupported = true;
if vtInputSupported {
mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
}
unsafe {
SetConsoleMode(stdin, mode).ok()?;
}
Ok(())
}