use std::{io, mem};
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::System::Console::{GetConsoleMode, SetConsoleMode};
use windows_sys::Win32::System::Console::{
GetConsoleScreenBufferInfo, SetConsoleTextAttribute,
CONSOLE_SCREEN_BUFFER_INFO, ENABLE_VIRTUAL_TERMINAL_PROCESSING,
FOREGROUND_BLUE, FOREGROUND_GREEN, FOREGROUND_INTENSITY, FOREGROUND_RED,
};
use crate::{AsHandleRef, HandleRef};
use FOREGROUND_BLUE as FG_BLUE;
use FOREGROUND_GREEN as FG_GREEN;
use FOREGROUND_INTENSITY as FG_INTENSITY;
use FOREGROUND_RED as FG_RED;
const FG_CYAN: u16 = FG_BLUE | FG_GREEN;
const FG_MAGENTA: u16 = FG_BLUE | FG_RED;
const FG_YELLOW: u16 = FG_GREEN | FG_RED;
const FG_WHITE: u16 = FG_BLUE | FG_GREEN | FG_RED;
pub fn screen_buffer_info<H: AsHandleRef>(
h: H,
) -> io::Result<ScreenBufferInfo> {
unsafe {
let mut info: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
let rc = GetConsoleScreenBufferInfo(h.as_raw() as HANDLE, &mut info);
if rc == 0 {
return Err(io::Error::last_os_error());
}
Ok(ScreenBufferInfo(info))
}
}
pub fn set_text_attributes<H: AsHandleRef>(
h: H,
attributes: u16,
) -> io::Result<()> {
if unsafe { SetConsoleTextAttribute(h.as_raw() as HANDLE, attributes) }
== 0
{
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn mode<H: AsHandleRef>(h: H) -> io::Result<u32> {
let mut mode = 0;
if unsafe { GetConsoleMode(h.as_raw() as HANDLE, &mut mode) } == 0 {
Err(io::Error::last_os_error())
} else {
Ok(mode)
}
}
pub fn set_mode<H: AsHandleRef>(h: H, mode: u32) -> io::Result<()> {
if unsafe { SetConsoleMode(h.as_raw() as HANDLE, mode) } == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
#[derive(Clone)]
pub struct ScreenBufferInfo(CONSOLE_SCREEN_BUFFER_INFO);
impl ScreenBufferInfo {
pub fn size(&self) -> (i16, i16) {
(self.0.dwSize.X, self.0.dwSize.Y)
}
pub fn cursor_position(&self) -> (i16, i16) {
(self.0.dwCursorPosition.X, self.0.dwCursorPosition.Y)
}
pub fn attributes(&self) -> u16 {
self.0.wAttributes
}
pub fn max_window_size(&self) -> (i16, i16) {
(self.0.dwMaximumWindowSize.X, self.0.dwMaximumWindowSize.Y)
}
pub fn window_rect(&self) -> SmallRect {
SmallRect {
left: self.0.srWindow.Left,
top: self.0.srWindow.Top,
right: self.0.srWindow.Right,
bottom: self.0.srWindow.Bottom,
}
}
}
pub struct SmallRect {
pub left: i16,
pub top: i16,
pub right: i16,
pub bottom: i16,
}
#[derive(Debug)]
pub struct Console {
kind: HandleKind,
start_attr: TextAttributes,
cur_attr: TextAttributes,
}
#[derive(Clone, Copy, Debug)]
enum HandleKind {
Stdout,
Stderr,
}
impl HandleKind {
fn handle(&self) -> HandleRef {
match *self {
HandleKind::Stdout => HandleRef::stdout(),
HandleKind::Stderr => HandleRef::stderr(),
}
}
}
impl Console {
fn create_for_stream(kind: HandleKind) -> io::Result<Console> {
let h = kind.handle();
let info = screen_buffer_info(&h)?;
let attr = TextAttributes::from_word(info.attributes());
Ok(Console { kind, start_attr: attr, cur_attr: attr })
}
pub fn stdout() -> io::Result<Console> {
Self::create_for_stream(HandleKind::Stdout)
}
pub fn stderr() -> io::Result<Console> {
Self::create_for_stream(HandleKind::Stderr)
}
fn set(&mut self) -> io::Result<()> {
set_text_attributes(self.kind.handle(), self.cur_attr.to_word())
}
pub fn fg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
self.cur_attr.fg_color = color;
self.cur_attr.fg_intense = intense;
self.set()
}
pub fn bg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
self.cur_attr.bg_color = color;
self.cur_attr.bg_intense = intense;
self.set()
}
pub fn reset(&mut self) -> io::Result<()> {
self.cur_attr = self.start_attr;
self.set()
}
pub fn set_virtual_terminal_processing(
&mut self,
yes: bool,
) -> io::Result<()> {
let vt = ENABLE_VIRTUAL_TERMINAL_PROCESSING;
let handle = self.kind.handle();
let old_mode = mode(&handle)?;
let new_mode = if yes { old_mode | vt } else { old_mode & !vt };
if old_mode == new_mode {
return Ok(());
}
set_mode(&handle, new_mode)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct TextAttributes {
fg_color: Color,
fg_intense: Intense,
bg_color: Color,
bg_intense: Intense,
}
impl TextAttributes {
fn to_word(&self) -> u16 {
let mut w = 0;
w |= self.fg_color.to_fg();
w |= self.fg_intense.to_fg();
w |= self.bg_color.to_bg();
w |= self.bg_intense.to_bg();
w
}
fn from_word(word: u16) -> TextAttributes {
TextAttributes {
fg_color: Color::from_fg(word),
fg_intense: Intense::from_fg(word),
bg_color: Color::from_bg(word),
bg_intense: Intense::from_bg(word),
}
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Intense {
Yes,
No,
}
impl Intense {
fn to_bg(&self) -> u16 {
self.to_fg() << 4
}
fn from_bg(word: u16) -> Intense {
Intense::from_fg(word >> 4)
}
fn to_fg(&self) -> u16 {
match *self {
Intense::No => 0,
Intense::Yes => FG_INTENSITY,
}
}
fn from_fg(word: u16) -> Intense {
if word & FG_INTENSITY > 0 {
Intense::Yes
} else {
Intense::No
}
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Color {
Black,
Blue,
Green,
Red,
Cyan,
Magenta,
Yellow,
White,
}
impl Color {
fn to_bg(&self) -> u16 {
self.to_fg() << 4
}
fn from_bg(word: u16) -> Color {
Color::from_fg(word >> 4)
}
fn to_fg(&self) -> u16 {
match *self {
Color::Black => 0,
Color::Blue => FG_BLUE,
Color::Green => FG_GREEN,
Color::Red => FG_RED,
Color::Cyan => FG_CYAN,
Color::Magenta => FG_MAGENTA,
Color::Yellow => FG_YELLOW,
Color::White => FG_WHITE,
}
}
fn from_fg(word: u16) -> Color {
match word & 0b111 {
FG_BLUE => Color::Blue,
FG_GREEN => Color::Green,
FG_RED => Color::Red,
FG_CYAN => Color::Cyan,
FG_MAGENTA => Color::Magenta,
FG_YELLOW => Color::Yellow,
FG_WHITE => Color::White,
_ => Color::Black,
}
}
}