#![expect(unsafe_code)]
#[cfg(windows)]
pub fn enable_vt_and_utf8() {
use winapi::um::consoleapi;
use winapi::um::processenv;
use winapi::um::winbase;
use winapi::um::wincon;
use winapi::um::winnls;
unsafe {
let conout = processenv::GetStdHandle(winbase::STD_OUTPUT_HANDLE);
let mut mode = 0;
if consoleapi::GetConsoleMode(conout, &mut mode) != 0 {
if mode & wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0 {
consoleapi::SetConsoleMode(
conout,
mode | wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING,
);
}
wincon::SetConsoleOutputCP(winnls::CP_UTF8);
}
}
}
#[cfg(not(windows))]
pub fn enable_vt_and_utf8() {}
#[cfg(windows)]
pub fn set_raw_console(enable: bool) {
use winapi::shared::minwindef;
use winapi::um::consoleapi;
use winapi::um::processenv;
use winapi::um::winbase;
use winapi::um::wincon;
use winapi::um::winnls;
unsafe {
let conin = processenv::GetStdHandle(winbase::STD_INPUT_HANDLE);
let mut mode: minwindef::DWORD = 0;
if consoleapi::GetConsoleMode(conin, &mut mode) != 0 {
let on = wincon::ENABLE_VIRTUAL_TERMINAL_INPUT;
let off = wincon::ENABLE_LINE_INPUT
| wincon::ENABLE_ECHO_INPUT
| wincon::ENABLE_PROCESSED_INPUT;
if enable {
mode |= on;
mode &= !off;
} else {
mode &= !on;
mode |= off;
}
consoleapi::SetConsoleMode(conin, mode);
}
let conout = processenv::GetStdHandle(winbase::STD_OUTPUT_HANDLE);
if consoleapi::GetConsoleMode(conout, &mut mode) != 0 {
let on =
wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING | wincon::DISABLE_NEWLINE_AUTO_RETURN;
let off = 0;
if enable {
mode |= on;
mode &= !off;
} else {
mode &= !on;
mode |= off;
}
consoleapi::SetConsoleMode(conout, mode);
wincon::SetConsoleOutputCP(winnls::CP_UTF8);
}
}
}
#[cfg(not(windows))]
pub fn set_raw_console(enable: bool) {
if enable {
crossterm::terminal::enable_raw_mode().unwrap();
} else {
crossterm::terminal::disable_raw_mode().unwrap();
}
}
#[cfg(windows)]
fn clone_file(file: impl std::os::windows::io::AsHandle) -> std::fs::File {
file.as_handle().try_clone_to_owned().unwrap().into()
}
#[cfg(unix)]
fn clone_file(file: impl std::os::unix::io::AsFd) -> std::fs::File {
file.as_fd().try_clone_to_owned().unwrap().into()
}
pub fn raw_stdout() -> std::fs::File {
clone_file(std::io::stdout())
}
pub fn raw_stderr() -> std::fs::File {
clone_file(std::io::stderr())
}
#[cfg(unix)]
pub fn revert_terminal_on_panic() {
let orig_termios = get_termios();
let base_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
eprintln!("restoring terminal attributes on panic...");
set_termios(orig_termios);
base_hook(info)
}));
}
#[cfg(unix)]
#[derive(Copy, Clone)]
pub struct Termios(libc::termios);
#[cfg(unix)]
pub fn get_termios() -> Termios {
let mut orig_termios = std::mem::MaybeUninit::<libc::termios>::uninit();
let ret = unsafe { libc::tcgetattr(libc::STDERR_FILENO, orig_termios.as_mut_ptr()) };
if ret != 0 {
panic!(
"error: could not save term attributes: {}",
std::io::Error::last_os_error()
);
}
let orig_termios = unsafe { orig_termios.assume_init() };
Termios(orig_termios)
}
#[cfg(unix)]
pub fn set_termios(termios: Termios) {
let ret = unsafe { libc::tcsetattr(libc::STDERR_FILENO, libc::TCSAFLUSH, &termios.0) };
if ret != 0 {
panic!(
"error: could not restore term attributes via tcsetattr: {}",
std::io::Error::last_os_error()
);
}
}