1use std::io;
2
3use winapi::shared::minwindef::WORD;
4use winapi::um::wincon::{
5 self, FOREGROUND_BLUE as FG_BLUE, FOREGROUND_GREEN as FG_GREEN,
6 FOREGROUND_INTENSITY as FG_INTENSITY, FOREGROUND_RED as FG_RED,
7};
8use winapi_util as winutil;
9
10const FG_CYAN: WORD = FG_BLUE | FG_GREEN;
11const FG_MAGENTA: WORD = FG_BLUE | FG_RED;
12const FG_YELLOW: WORD = FG_GREEN | FG_RED;
13const FG_WHITE: WORD = FG_BLUE | FG_GREEN | FG_RED;
14
15#[derive(Debug)]
28pub struct Console {
29 kind: HandleKind,
30 start_attr: TextAttributes,
31 cur_attr: TextAttributes,
32}
33
34#[derive(Clone, Copy, Debug)]
35enum HandleKind {
36 Stdout,
37 Stderr,
38}
39
40impl HandleKind {
41 fn handle(&self) -> winutil::HandleRef {
42 match *self {
43 HandleKind::Stdout => winutil::HandleRef::stdout(),
44 HandleKind::Stderr => winutil::HandleRef::stderr(),
45 }
46 }
47}
48
49impl Console {
50 fn create_for_stream(kind: HandleKind) -> io::Result<Console> {
52 let h = kind.handle();
53 let info = winutil::console::screen_buffer_info(&h)?;
54 let attr = TextAttributes::from_word(info.attributes());
55 Ok(Console { kind: kind, start_attr: attr, cur_attr: attr })
56 }
57
58 pub fn stdout() -> io::Result<Console> {
62 Self::create_for_stream(HandleKind::Stdout)
63 }
64
65 pub fn stderr() -> io::Result<Console> {
69 Self::create_for_stream(HandleKind::Stderr)
70 }
71
72 fn set(&mut self) -> io::Result<()> {
74 winutil::console::set_text_attributes(
75 self.kind.handle(),
76 self.cur_attr.to_word(),
77 )
78 }
79
80 pub fn fg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
86 self.cur_attr.fg_color = color;
87 self.cur_attr.fg_intense = intense;
88 self.set()
89 }
90
91 pub fn bg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
97 self.cur_attr.bg_color = color;
98 self.cur_attr.bg_intense = intense;
99 self.set()
100 }
101
102 pub fn reset(&mut self) -> io::Result<()> {
110 self.cur_attr = self.start_attr;
111 self.set()
112 }
113
114 pub fn set_virtual_terminal_processing(
124 &mut self,
125 yes: bool,
126 ) -> io::Result<()> {
127 let vt = wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING;
128
129 let handle = self.kind.handle();
130 let old_mode = winutil::console::mode(&handle)?;
131 let new_mode = if yes { old_mode | vt } else { old_mode & !vt };
132 if old_mode == new_mode {
133 return Ok(());
134 }
135 winutil::console::set_mode(&handle, new_mode)
136 }
137}
138
139#[derive(Copy, Clone, Debug, Eq, PartialEq)]
141struct TextAttributes {
142 fg_color: Color,
143 fg_intense: Intense,
144 bg_color: Color,
145 bg_intense: Intense,
146}
147
148impl TextAttributes {
149 fn to_word(&self) -> WORD {
150 let mut w = 0;
151 w |= self.fg_color.to_fg();
152 w |= self.fg_intense.to_fg();
153 w |= self.bg_color.to_bg();
154 w |= self.bg_intense.to_bg();
155 w
156 }
157
158 fn from_word(word: WORD) -> TextAttributes {
159 TextAttributes {
160 fg_color: Color::from_fg(word),
161 fg_intense: Intense::from_fg(word),
162 bg_color: Color::from_bg(word),
163 bg_intense: Intense::from_bg(word),
164 }
165 }
166}
167
168#[allow(missing_docs)]
170#[derive(Clone, Copy, Debug, Eq, PartialEq)]
171pub enum Intense {
172 Yes,
173 No,
174}
175
176impl Intense {
177 fn to_bg(&self) -> WORD {
178 self.to_fg() << 4
179 }
180
181 fn from_bg(word: WORD) -> Intense {
182 Intense::from_fg(word >> 4)
183 }
184
185 fn to_fg(&self) -> WORD {
186 match *self {
187 Intense::No => 0,
188 Intense::Yes => FG_INTENSITY,
189 }
190 }
191
192 fn from_fg(word: WORD) -> Intense {
193 if word & FG_INTENSITY > 0 {
194 Intense::Yes
195 } else {
196 Intense::No
197 }
198 }
199}
200
201#[allow(missing_docs)]
203#[derive(Clone, Copy, Debug, Eq, PartialEq)]
204pub enum Color {
205 Black,
206 Blue,
207 Green,
208 Red,
209 Cyan,
210 Magenta,
211 Yellow,
212 White,
213}
214
215impl Color {
216 fn to_bg(&self) -> WORD {
217 self.to_fg() << 4
218 }
219
220 fn from_bg(word: WORD) -> Color {
221 Color::from_fg(word >> 4)
222 }
223
224 fn to_fg(&self) -> WORD {
225 match *self {
226 Color::Black => 0,
227 Color::Blue => FG_BLUE,
228 Color::Green => FG_GREEN,
229 Color::Red => FG_RED,
230 Color::Cyan => FG_CYAN,
231 Color::Magenta => FG_MAGENTA,
232 Color::Yellow => FG_YELLOW,
233 Color::White => FG_WHITE,
234 }
235 }
236
237 fn from_fg(word: WORD) -> Color {
238 match word & 0b111 {
239 FG_BLUE => Color::Blue,
240 FG_GREEN => Color::Green,
241 FG_RED => Color::Red,
242 FG_CYAN => Color::Cyan,
243 FG_MAGENTA => Color::Magenta,
244 FG_YELLOW => Color::Yellow,
245 FG_WHITE => Color::White,
246 _ => Color::Black,
247 }
248 }
249}