1 use std::io;
2 use std::mem;
3
4 use winapi::shared::minwindef::WORD;
5 use winapi::um::consoleapi::{GetConsoleMode, SetConsoleMode};
6 use winapi::um::wincon::{
7 self, GetConsoleScreenBufferInfo, SetConsoleTextAttribute,
8 CONSOLE_SCREEN_BUFFER_INFO, FOREGROUND_BLUE as FG_BLUE,
9 FOREGROUND_GREEN as FG_GREEN, FOREGROUND_INTENSITY as FG_INTENSITY,
10 FOREGROUND_RED as FG_RED,
11 };
12
13 use crate::{AsHandleRef, HandleRef};
14
15 const FG_CYAN: WORD = FG_BLUE | FG_GREEN;
16 const FG_MAGENTA: WORD = FG_BLUE | FG_RED;
17 const FG_YELLOW: WORD = FG_GREEN | FG_RED;
18 const FG_WHITE: WORD = FG_BLUE | FG_GREEN | FG_RED;
19
20 /// Query the given handle for information about the console's screen buffer.
21 ///
22 /// The given handle should represent a console. Otherwise, an error is
23 /// returned.
24 ///
25 /// This corresponds to calling [`GetConsoleScreenBufferInfo`].
26 ///
27 /// [`GetConsoleScreenBufferInfo`]: https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
screen_buffer_info<H: AsHandleRef>( h: H, ) -> io::Result<ScreenBufferInfo>28 pub fn screen_buffer_info<H: AsHandleRef>(
29 h: H,
30 ) -> io::Result<ScreenBufferInfo> {
31 unsafe {
32 let mut info: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
33 let rc = GetConsoleScreenBufferInfo(h.as_raw(), &mut info);
34 if rc == 0 {
35 return Err(io::Error::last_os_error());
36 }
37 Ok(ScreenBufferInfo(info))
38 }
39 }
40
41 /// Set the text attributes of the console represented by the given handle.
42 ///
43 /// This corresponds to calling [`SetConsoleTextAttribute`].
44 ///
45 /// [`SetConsoleTextAttribute`]: https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute
set_text_attributes<H: AsHandleRef>( h: H, attributes: u16, ) -> io::Result<()>46 pub fn set_text_attributes<H: AsHandleRef>(
47 h: H,
48 attributes: u16,
49 ) -> io::Result<()> {
50 if unsafe { SetConsoleTextAttribute(h.as_raw(), attributes) } == 0 {
51 Err(io::Error::last_os_error())
52 } else {
53 Ok(())
54 }
55 }
56
57 /// Query the mode of the console represented by the given handle.
58 ///
59 /// This corresponds to calling [`GetConsoleMode`], which describes the return
60 /// value.
61 ///
62 /// [`GetConsoleMode`]: https://docs.microsoft.com/en-us/windows/console/getconsolemode
mode<H: AsHandleRef>(h: H) -> io::Result<u32>63 pub fn mode<H: AsHandleRef>(h: H) -> io::Result<u32> {
64 let mut mode = 0;
65 if unsafe { GetConsoleMode(h.as_raw(), &mut mode) } == 0 {
66 Err(io::Error::last_os_error())
67 } else {
68 Ok(mode)
69 }
70 }
71
72 /// Set the mode of the console represented by the given handle.
73 ///
74 /// This corresponds to calling [`SetConsoleMode`], which describes the format
75 /// of the mode parameter.
76 ///
77 /// [`SetConsoleMode`]: https://docs.microsoft.com/en-us/windows/console/setconsolemode
set_mode<H: AsHandleRef>(h: H, mode: u32) -> io::Result<()>78 pub fn set_mode<H: AsHandleRef>(h: H, mode: u32) -> io::Result<()> {
79 if unsafe { SetConsoleMode(h.as_raw(), mode) } == 0 {
80 Err(io::Error::last_os_error())
81 } else {
82 Ok(())
83 }
84 }
85
86 /// Represents console screen buffer information such as size, cursor position
87 /// and styling attributes.
88 ///
89 /// This wraps a [`CONSOLE_SCREEN_BUFFER_INFO`].
90 ///
91 /// [`CONSOLE_SCREEN_BUFFER_INFO`]: https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
92 #[derive(Clone)]
93 pub struct ScreenBufferInfo(CONSOLE_SCREEN_BUFFER_INFO);
94
95 impl ScreenBufferInfo {
96 /// Returns the size of the console screen buffer, in character columns and
97 /// rows.
98 ///
99 /// This corresponds to `dwSize`.
size(&self) -> (i16, i16)100 pub fn size(&self) -> (i16, i16) {
101 (self.0.dwSize.X, self.0.dwSize.Y)
102 }
103
104 /// Returns the position of the cursor in terms of column and row
105 /// coordinates of the console screen buffer.
106 ///
107 /// This corresponds to `dwCursorPosition`.
cursor_position(&self) -> (i16, i16)108 pub fn cursor_position(&self) -> (i16, i16) {
109 (self.0.dwCursorPosition.X, self.0.dwCursorPosition.Y)
110 }
111
112 /// Returns the character attributes associated with this console.
113 ///
114 /// This corresponds to `wAttributes`.
115 ///
116 /// See [`char info`] for more details.
117 ///
118 /// [`char info`]: https://docs.microsoft.com/en-us/windows/console/char-info-str
attributes(&self) -> u16119 pub fn attributes(&self) -> u16 {
120 self.0.wAttributes
121 }
122
123 /// Returns the maximum size of the console window, in character columns
124 /// and rows, given the current screen buffer size and font and the screen
125 /// size.
max_window_size(&self) -> (i16, i16)126 pub fn max_window_size(&self) -> (i16, i16) {
127 (self.0.dwMaximumWindowSize.X, self.0.dwMaximumWindowSize.Y)
128 }
129 }
130
131 /// A Windows console.
132 ///
133 /// This represents a very limited set of functionality available to a Windows
134 /// console. In particular, it can only change text attributes such as color
135 /// and intensity. This may grow over time. If you need more routines, please
136 /// file an issue and/or PR.
137 ///
138 /// There is no way to "write" to this console. Simply write to
139 /// stdout or stderr instead, while interleaving instructions to the console
140 /// to change text attributes.
141 ///
142 /// A common pitfall when using a console is to forget to flush writes to
143 /// stdout before setting new text attributes.
144 ///
145 /// # Example
146 /// ```no_run
147 /// # #[cfg(windows)]
148 /// # {
149 /// use winapi_util::console::{Console, Color, Intense};
150 ///
151 /// let mut con = Console::stdout().unwrap();
152 /// con.fg(Intense::Yes, Color::Cyan).unwrap();
153 /// println!("This text will be intense cyan.");
154 /// con.reset().unwrap();
155 /// println!("This text will be normal.");
156 /// # }
157 /// ```
158 #[derive(Debug)]
159 pub struct Console {
160 kind: HandleKind,
161 start_attr: TextAttributes,
162 cur_attr: TextAttributes,
163 }
164
165 #[derive(Clone, Copy, Debug)]
166 enum HandleKind {
167 Stdout,
168 Stderr,
169 }
170
171 impl HandleKind {
handle(&self) -> HandleRef172 fn handle(&self) -> HandleRef {
173 match *self {
174 HandleKind::Stdout => HandleRef::stdout(),
175 HandleKind::Stderr => HandleRef::stderr(),
176 }
177 }
178 }
179
180 impl Console {
181 /// Get a console for a standard I/O stream.
create_for_stream(kind: HandleKind) -> io::Result<Console>182 fn create_for_stream(kind: HandleKind) -> io::Result<Console> {
183 let h = kind.handle();
184 let info = screen_buffer_info(&h)?;
185 let attr = TextAttributes::from_word(info.attributes());
186 Ok(Console { kind: kind, start_attr: attr, cur_attr: attr })
187 }
188
189 /// Create a new Console to stdout.
190 ///
191 /// If there was a problem creating the console, then an error is returned.
stdout() -> io::Result<Console>192 pub fn stdout() -> io::Result<Console> {
193 Self::create_for_stream(HandleKind::Stdout)
194 }
195
196 /// Create a new Console to stderr.
197 ///
198 /// If there was a problem creating the console, then an error is returned.
stderr() -> io::Result<Console>199 pub fn stderr() -> io::Result<Console> {
200 Self::create_for_stream(HandleKind::Stderr)
201 }
202
203 /// Applies the current text attributes.
set(&mut self) -> io::Result<()>204 fn set(&mut self) -> io::Result<()> {
205 set_text_attributes(self.kind.handle(), self.cur_attr.to_word())
206 }
207
208 /// Apply the given intensity and color attributes to the console
209 /// foreground.
210 ///
211 /// If there was a problem setting attributes on the console, then an error
212 /// is returned.
fg(&mut self, intense: Intense, color: Color) -> io::Result<()>213 pub fn fg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
214 self.cur_attr.fg_color = color;
215 self.cur_attr.fg_intense = intense;
216 self.set()
217 }
218
219 /// Apply the given intensity and color attributes to the console
220 /// background.
221 ///
222 /// If there was a problem setting attributes on the console, then an error
223 /// is returned.
bg(&mut self, intense: Intense, color: Color) -> io::Result<()>224 pub fn bg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
225 self.cur_attr.bg_color = color;
226 self.cur_attr.bg_intense = intense;
227 self.set()
228 }
229
230 /// Reset the console text attributes to their original settings.
231 ///
232 /// The original settings correspond to the text attributes on the console
233 /// when this `Console` value was created.
234 ///
235 /// If there was a problem setting attributes on the console, then an error
236 /// is returned.
reset(&mut self) -> io::Result<()>237 pub fn reset(&mut self) -> io::Result<()> {
238 self.cur_attr = self.start_attr;
239 self.set()
240 }
241
242 /// Toggle virtual terminal processing.
243 ///
244 /// This method attempts to toggle virtual terminal processing for this
245 /// console. If there was a problem toggling it, then an error returned.
246 /// On success, the caller may assume that toggling it was successful.
247 ///
248 /// When virtual terminal processing is enabled, characters emitted to the
249 /// console are parsed for VT100 and similar control character sequences
250 /// that control color and other similar operations.
set_virtual_terminal_processing( &mut self, yes: bool, ) -> io::Result<()>251 pub fn set_virtual_terminal_processing(
252 &mut self,
253 yes: bool,
254 ) -> io::Result<()> {
255 let vt = wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING;
256
257 let handle = self.kind.handle();
258 let old_mode = mode(&handle)?;
259 let new_mode = if yes { old_mode | vt } else { old_mode & !vt };
260 if old_mode == new_mode {
261 return Ok(());
262 }
263 set_mode(&handle, new_mode)
264 }
265 }
266
267 /// A representation of text attributes for the Windows console.
268 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
269 struct TextAttributes {
270 fg_color: Color,
271 fg_intense: Intense,
272 bg_color: Color,
273 bg_intense: Intense,
274 }
275
276 impl TextAttributes {
to_word(&self) -> WORD277 fn to_word(&self) -> WORD {
278 let mut w = 0;
279 w |= self.fg_color.to_fg();
280 w |= self.fg_intense.to_fg();
281 w |= self.bg_color.to_bg();
282 w |= self.bg_intense.to_bg();
283 w
284 }
285
from_word(word: WORD) -> TextAttributes286 fn from_word(word: WORD) -> TextAttributes {
287 TextAttributes {
288 fg_color: Color::from_fg(word),
289 fg_intense: Intense::from_fg(word),
290 bg_color: Color::from_bg(word),
291 bg_intense: Intense::from_bg(word),
292 }
293 }
294 }
295
296 /// Whether to use intense colors or not.
297 #[allow(missing_docs)]
298 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
299 pub enum Intense {
300 Yes,
301 No,
302 }
303
304 impl Intense {
to_bg(&self) -> WORD305 fn to_bg(&self) -> WORD {
306 self.to_fg() << 4
307 }
308
from_bg(word: WORD) -> Intense309 fn from_bg(word: WORD) -> Intense {
310 Intense::from_fg(word >> 4)
311 }
312
to_fg(&self) -> WORD313 fn to_fg(&self) -> WORD {
314 match *self {
315 Intense::No => 0,
316 Intense::Yes => FG_INTENSITY,
317 }
318 }
319
from_fg(word: WORD) -> Intense320 fn from_fg(word: WORD) -> Intense {
321 if word & FG_INTENSITY > 0 {
322 Intense::Yes
323 } else {
324 Intense::No
325 }
326 }
327 }
328
329 /// The set of available colors for use with a Windows console.
330 #[allow(missing_docs)]
331 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
332 pub enum Color {
333 Black,
334 Blue,
335 Green,
336 Red,
337 Cyan,
338 Magenta,
339 Yellow,
340 White,
341 }
342
343 impl Color {
to_bg(&self) -> WORD344 fn to_bg(&self) -> WORD {
345 self.to_fg() << 4
346 }
347
from_bg(word: WORD) -> Color348 fn from_bg(word: WORD) -> Color {
349 Color::from_fg(word >> 4)
350 }
351
to_fg(&self) -> WORD352 fn to_fg(&self) -> WORD {
353 match *self {
354 Color::Black => 0,
355 Color::Blue => FG_BLUE,
356 Color::Green => FG_GREEN,
357 Color::Red => FG_RED,
358 Color::Cyan => FG_CYAN,
359 Color::Magenta => FG_MAGENTA,
360 Color::Yellow => FG_YELLOW,
361 Color::White => FG_WHITE,
362 }
363 }
364
from_fg(word: WORD) -> Color365 fn from_fg(word: WORD) -> Color {
366 match word & 0b111 {
367 FG_BLUE => Color::Blue,
368 FG_GREEN => Color::Green,
369 FG_RED => Color::Red,
370 FG_CYAN => Color::Cyan,
371 FG_MAGENTA => Color::Magenta,
372 FG_YELLOW => Color::Yellow,
373 FG_WHITE => Color::White,
374 _ => Color::Black,
375 }
376 }
377 }
378