1use std::io;
6use std::io::prelude::*;
7
8use super::{Terminal, color};
9
10pub(crate) struct WinConsole<T> {
12 buf: T,
13 def_foreground: color::Color,
14 def_background: color::Color,
15 foreground: color::Color,
16 background: color::Color,
17}
18
19type SHORT = i16;
20type WORD = u16;
21type DWORD = u32;
22type BOOL = i32;
23type HANDLE = *mut u8;
24const STD_OUTPUT_HANDLE: DWORD = -11 as _;
26
27#[allow(non_snake_case)]
28#[repr(C)]
29struct SMALL_RECT {
30 Left: SHORT,
31 Top: SHORT,
32 Right: SHORT,
33 Bottom: SHORT,
34}
35
36#[allow(non_snake_case)]
37#[repr(C)]
38struct COORD {
39 X: SHORT,
40 Y: SHORT,
41}
42
43#[allow(non_snake_case)]
44#[repr(C)]
45struct CONSOLE_SCREEN_BUFFER_INFO {
46 dwSize: COORD,
47 dwCursorPosition: COORD,
48 wAttributes: WORD,
49 srWindow: SMALL_RECT,
50 dwMaximumWindowSize: COORD,
51}
52
53#[allow(non_snake_case)]
54#[link(name = "kernel32")]
55unsafe extern "system" {
56 fn SetConsoleTextAttribute(handle: HANDLE, attr: WORD) -> BOOL;
57 fn GetStdHandle(which: DWORD) -> HANDLE;
58 fn GetConsoleScreenBufferInfo(handle: HANDLE, info: *mut CONSOLE_SCREEN_BUFFER_INFO) -> BOOL;
59}
60
61fn color_to_bits(color: color::Color) -> u16 {
62 let bits = match color % 8 {
65 color::BLACK => 0,
66 color::BLUE => 0x1,
67 color::GREEN => 0x2,
68 color::RED => 0x4,
69 color::YELLOW => 0x2 | 0x4,
70 color::MAGENTA => 0x1 | 0x4,
71 color::CYAN => 0x1 | 0x2,
72 color::WHITE => 0x1 | 0x2 | 0x4,
73 _ => unreachable!(),
74 };
75
76 if color >= 8 { bits | 0x8 } else { bits }
77}
78
79fn bits_to_color(bits: u16) -> color::Color {
80 let color = match bits & 0x7 {
81 0 => color::BLACK,
82 0x1 => color::BLUE,
83 0x2 => color::GREEN,
84 0x4 => color::RED,
85 0x6 => color::YELLOW,
86 0x5 => color::MAGENTA,
87 0x3 => color::CYAN,
88 0x7 => color::WHITE,
89 _ => unreachable!(),
90 };
91
92 color | (u32::from(bits) & 0x8) }
94
95impl<T: Write + Send + 'static> WinConsole<T> {
96 fn apply(&mut self) {
97 let _unused = self.buf.flush();
98 let mut accum: WORD = 0;
99 accum |= color_to_bits(self.foreground);
100 accum |= color_to_bits(self.background) << 4;
101
102 unsafe {
103 let out = GetStdHandle(STD_OUTPUT_HANDLE);
110 SetConsoleTextAttribute(out, accum);
111 }
112 }
113
114 pub(crate) fn new(out: T) -> WinConsole<T> {
115 use std::mem::MaybeUninit;
116
117 let fg;
118 let bg;
119 unsafe {
120 let mut buffer_info = MaybeUninit::<CONSOLE_SCREEN_BUFFER_INFO>::uninit();
121 let handle = GetStdHandle(STD_OUTPUT_HANDLE);
122 if GetConsoleScreenBufferInfo(handle, buffer_info.as_mut_ptr()) != 0 {
123 let buffer_info = buffer_info.assume_init();
124 fg = bits_to_color(buffer_info.wAttributes);
125 bg = bits_to_color(buffer_info.wAttributes >> 4);
126 } else {
127 fg = color::WHITE;
128 bg = color::BLACK;
129 }
130 }
131 WinConsole {
132 buf: out,
133 def_foreground: fg,
134 def_background: bg,
135 foreground: fg,
136 background: bg,
137 }
138 }
139}
140
141impl<T: Write> Write for WinConsole<T> {
142 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
143 self.buf.write(buf)
144 }
145
146 fn flush(&mut self) -> io::Result<()> {
147 self.buf.flush()
148 }
149}
150
151impl<T: Write + Send + 'static> Terminal for WinConsole<T> {
152 fn fg(&mut self, color: color::Color) -> io::Result<bool> {
153 self.foreground = color;
154 self.apply();
155
156 Ok(true)
157 }
158
159 fn reset(&mut self) -> io::Result<bool> {
160 self.foreground = self.def_foreground;
161 self.background = self.def_background;
162 self.apply();
163
164 Ok(true)
165 }
166}