use std::cell::Cell;
use std::mem::MaybeUninit;
use windows_sys::Win32::System::Console::{
GetConsoleMode, GetConsoleScreenBufferInfo, GetStdHandle, SetConsoleMode, WriteConsoleW,
CONSOLE_MODE, CONSOLE_SCREEN_BUFFER_INFO, ENABLE_VIRTUAL_TERMINAL_PROCESSING,
STD_OUTPUT_HANDLE,
};
pub struct Terminal {
attrs: CONSOLE_MODE,
}
impl Terminal {
pub fn new() -> Self {
let mut attrs = MaybeUninit::<CONSOLE_MODE>::uninit();
unsafe {
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
if GetConsoleMode(handle, attrs.as_mut_ptr()) != 1 {
panic!("Failed to initialize a windows console");
}
let mut attrs2 = attrs.assume_init();
attrs2 |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(handle, attrs2);
Terminal {
attrs: attrs.assume_init(),
}
}
}
}
impl Drop for Terminal {
fn drop(&mut self) {
unsafe {
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleMode(handle, self.attrs);
}
}
}
pub fn write(str: &str) {
unsafe {
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
let mut encoded = str.encode_utf16().collect::<Vec<_>>();
encoded.push(0);
WriteConsoleW(
handle,
encoded.as_ptr(),
(encoded.len() - 1) as _,
std::ptr::null_mut(),
std::ptr::null(),
);
}
}
pub fn get_window_size() -> (i32, i32) {
unsafe {
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
let mut info = MaybeUninit::<CONSOLE_SCREEN_BUFFER_INFO>::uninit();
GetConsoleScreenBufferInfo(handle, info.as_mut_ptr());
let info = info.assume_init();
let columns = info.srWindow.Right - info.srWindow.Left + 1;
let rows = info.srWindow.Bottom - info.srWindow.Top + 1;
(columns as _, rows as _)
}
}
thread_local! {
static HEIGHT: Cell<i32> = Cell::new(-1);
}
pub fn get_window_height_amortized() -> i32 {
if HEIGHT.get() == -1 {
let (_, rows) = get_window_size();
HEIGHT.set(rows);
}
HEIGHT.get()
}