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 /// Returns the console screen buffer coordinates of the upper-left and
131 /// lower-right corners of the display window.
132 ///
133 /// This corresponds to `srWindow`.
window_rect(&self) -> SmallRect134 pub fn window_rect(&self) -> SmallRect {
135 SmallRect {
136 left: self.0.srWindow.Left,
137 top: self.0.srWindow.Top,
138 right: self.0.srWindow.Right,
139 bottom: self.0.srWindow.Bottom,
140 }
141 }
142 }
143
144 /// Defines the coordinates of the upper left and lower right corners of a rectangle.
145 ///
146 /// This corresponds to [`SMALL_RECT`].
147 ///
148 /// [`SMALL_RECT`]: https://docs.microsoft.com/en-us/windows/console/small-rect-str
149 pub struct SmallRect {
150 pub left: i16,
151 pub top: i16,
152 pub right: i16,
153 pub bottom: i16,
154 }
155
156 /// A Windows console.
157 ///
158 /// This represents a very limited set of functionality available to a Windows
159 /// console. In particular, it can only change text attributes such as color
160 /// and intensity. This may grow over time. If you need more routines, please
161 /// file an issue and/or PR.
162 ///
163 /// There is no way to "write" to this console. Simply write to
164 /// stdout or stderr instead, while interleaving instructions to the console
165 /// to change text attributes.
166 ///
167 /// A common pitfall when using a console is to forget to flush writes to
168 /// stdout before setting new text attributes.
169 ///
170 /// # Example
171 /// ```no_run
172 /// # #[cfg(windows)]
173 /// # {
174 /// use winapi_util::console::{Console, Color, Intense};
175 ///
176 /// let mut con = Console::stdout().unwrap();
177 /// con.fg(Intense::Yes, Color::Cyan).unwrap();
178 /// println!("This text will be intense cyan.");
179 /// con.reset().unwrap();
180 /// println!("This text will be normal.");
181 /// # }
182 /// ```
183 #[derive(Debug)]
184 pub struct Console {
185 kind: HandleKind,
186 start_attr: TextAttributes,
187 cur_attr: TextAttributes,
188 }
189
190 #[derive(Clone, Copy, Debug)]
191 enum HandleKind {
192 Stdout,
193 Stderr,
194 }
195
196 impl HandleKind {
handle(&self) -> HandleRef197 fn handle(&self) -> HandleRef {
198 match *self {
199 HandleKind::Stdout => HandleRef::stdout(),
200 HandleKind::Stderr => HandleRef::stderr(),
201 }
202 }
203 }
204
205 impl Console {
206 /// Get a console for a standard I/O stream.
create_for_stream(kind: HandleKind) -> io::Result<Console>207 fn create_for_stream(kind: HandleKind) -> io::Result<Console> {
208 let h = kind.handle();
209 let info = screen_buffer_info(&h)?;
210 let attr = TextAttributes::from_word(info.attributes());
211 Ok(Console { kind: kind, start_attr: attr, cur_attr: attr })
212 }
213
214 /// Create a new Console to stdout.
215 ///
216 /// If there was a problem creating the console, then an error is returned.
stdout() -> io::Result<Console>217 pub fn stdout() -> io::Result<Console> {
218 Self::create_for_stream(HandleKind::Stdout)
219 }
220
221 /// Create a new Console to stderr.
222 ///
223 /// If there was a problem creating the console, then an error is returned.
stderr() -> io::Result<Console>224 pub fn stderr() -> io::Result<Console> {
225 Self::create_for_stream(HandleKind::Stderr)
226 }
227
228 /// Applies the current text attributes.
set(&mut self) -> io::Result<()>229 fn set(&mut self) -> io::Result<()> {
230 set_text_attributes(self.kind.handle(), self.cur_attr.to_word())
231 }
232
233 /// Apply the given intensity and color attributes to the console
234 /// foreground.
235 ///
236 /// If there was a problem setting attributes on the console, then an error
237 /// is returned.
fg(&mut self, intense: Intense, color: Color) -> io::Result<()>238 pub fn fg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
239 self.cur_attr.fg_color = color;
240 self.cur_attr.fg_intense = intense;
241 self.set()
242 }
243
244 /// Apply the given intensity and color attributes to the console
245 /// background.
246 ///
247 /// If there was a problem setting attributes on the console, then an error
248 /// is returned.
bg(&mut self, intense: Intense, color: Color) -> io::Result<()>249 pub fn bg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
250 self.cur_attr.bg_color = color;
251 self.cur_attr.bg_intense = intense;
252 self.set()
253 }
254
255 /// Reset the console text attributes to their original settings.
256 ///
257 /// The original settings correspond to the text attributes on the console
258 /// when this `Console` value was created.
259 ///
260 /// If there was a problem setting attributes on the console, then an error
261 /// is returned.
reset(&mut self) -> io::Result<()>262 pub fn reset(&mut self) -> io::Result<()> {
263 self.cur_attr = self.start_attr;
264 self.set()
265 }
266
267 /// Toggle virtual terminal processing.
268 ///
269 /// This method attempts to toggle virtual terminal processing for this
270 /// console. If there was a problem toggling it, then an error returned.
271 /// On success, the caller may assume that toggling it was successful.
272 ///
273 /// When virtual terminal processing is enabled, characters emitted to the
274 /// console are parsed for VT100 and similar control character sequences
275 /// that control color and other similar operations.
set_virtual_terminal_processing( &mut self, yes: bool, ) -> io::Result<()>276 pub fn set_virtual_terminal_processing(
277 &mut self,
278 yes: bool,
279 ) -> io::Result<()> {
280 let vt = wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING;
281
282 let handle = self.kind.handle();
283 let old_mode = mode(&handle)?;
284 let new_mode = if yes { old_mode | vt } else { old_mode & !vt };
285 if old_mode == new_mode {
286 return Ok(());
287 }
288 set_mode(&handle, new_mode)
289 }
290 }
291
292 /// A representation of text attributes for the Windows console.
293 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
294 struct TextAttributes {
295 fg_color: Color,
296 fg_intense: Intense,
297 bg_color: Color,
298 bg_intense: Intense,
299 }
300
301 impl TextAttributes {
to_word(&self) -> WORD302 fn to_word(&self) -> WORD {
303 let mut w = 0;
304 w |= self.fg_color.to_fg();
305 w |= self.fg_intense.to_fg();
306 w |= self.bg_color.to_bg();
307 w |= self.bg_intense.to_bg();
308 w
309 }
310
from_word(word: WORD) -> TextAttributes311 fn from_word(word: WORD) -> TextAttributes {
312 TextAttributes {
313 fg_color: Color::from_fg(word),
314 fg_intense: Intense::from_fg(word),
315 bg_color: Color::from_bg(word),
316 bg_intense: Intense::from_bg(word),
317 }
318 }
319 }
320
321 /// Whether to use intense colors or not.
322 #[allow(missing_docs)]
323 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
324 pub enum Intense {
325 Yes,
326 No,
327 }
328
329 impl Intense {
to_bg(&self) -> WORD330 fn to_bg(&self) -> WORD {
331 self.to_fg() << 4
332 }
333
from_bg(word: WORD) -> Intense334 fn from_bg(word: WORD) -> Intense {
335 Intense::from_fg(word >> 4)
336 }
337
to_fg(&self) -> WORD338 fn to_fg(&self) -> WORD {
339 match *self {
340 Intense::No => 0,
341 Intense::Yes => FG_INTENSITY,
342 }
343 }
344
from_fg(word: WORD) -> Intense345 fn from_fg(word: WORD) -> Intense {
346 if word & FG_INTENSITY > 0 {
347 Intense::Yes
348 } else {
349 Intense::No
350 }
351 }
352 }
353
354 /// The set of available colors for use with a Windows console.
355 #[allow(missing_docs)]
356 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
357 pub enum Color {
358 Black,
359 Blue,
360 Green,
361 Red,
362 Cyan,
363 Magenta,
364 Yellow,
365 White,
366 }
367
368 impl Color {
to_bg(&self) -> WORD369 fn to_bg(&self) -> WORD {
370 self.to_fg() << 4
371 }
372
from_bg(word: WORD) -> Color373 fn from_bg(word: WORD) -> Color {
374 Color::from_fg(word >> 4)
375 }
376
to_fg(&self) -> WORD377 fn to_fg(&self) -> WORD {
378 match *self {
379 Color::Black => 0,
380 Color::Blue => FG_BLUE,
381 Color::Green => FG_GREEN,
382 Color::Red => FG_RED,
383 Color::Cyan => FG_CYAN,
384 Color::Magenta => FG_MAGENTA,
385 Color::Yellow => FG_YELLOW,
386 Color::White => FG_WHITE,
387 }
388 }
389
from_fg(word: WORD) -> Color390 fn from_fg(word: WORD) -> Color {
391 match word & 0b111 {
392 FG_BLUE => Color::Blue,
393 FG_GREEN => Color::Green,
394 FG_RED => Color::Red,
395 FG_CYAN => Color::Cyan,
396 FG_MAGENTA => Color::Magenta,
397 FG_YELLOW => Color::Yellow,
398 FG_WHITE => Color::White,
399 _ => Color::Black,
400 }
401 }
402 }
403