• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::io;
2 
3 use winapi::shared::minwindef::WORD;
4 use 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 };
8 use winapi_util as winutil;
9 
10 const FG_CYAN: WORD = FG_BLUE | FG_GREEN;
11 const FG_MAGENTA: WORD = FG_BLUE | FG_RED;
12 const FG_YELLOW: WORD = FG_GREEN | FG_RED;
13 const FG_WHITE: WORD = FG_BLUE | FG_GREEN | FG_RED;
14 
15 /// A Windows console.
16 ///
17 /// This represents a very limited set of functionality available to a Windows
18 /// console. In particular, it can only change text attributes such as color
19 /// and intensity.
20 ///
21 /// There is no way to "write" to this console. Simply write to
22 /// stdout or stderr instead, while interleaving instructions to the console
23 /// to change text attributes.
24 ///
25 /// A common pitfall when using a console is to forget to flush writes to
26 /// stdout before setting new text attributes.
27 #[derive(Debug)]
28 pub struct Console {
29     kind: HandleKind,
30     start_attr: TextAttributes,
31     cur_attr: TextAttributes,
32 }
33 
34 #[derive(Clone, Copy, Debug)]
35 enum HandleKind {
36     Stdout,
37     Stderr,
38 }
39 
40 impl HandleKind {
handle(&self) -> winutil::HandleRef41     fn handle(&self) -> winutil::HandleRef {
42         match *self {
43             HandleKind::Stdout => winutil::HandleRef::stdout(),
44             HandleKind::Stderr => winutil::HandleRef::stderr(),
45         }
46     }
47 }
48 
49 impl Console {
50     /// Get a console for a standard I/O stream.
create_for_stream(kind: HandleKind) -> io::Result<Console>51     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     /// Create a new Console to stdout.
59     ///
60     /// If there was a problem creating the console, then an error is returned.
stdout() -> io::Result<Console>61     pub fn stdout() -> io::Result<Console> {
62         Self::create_for_stream(HandleKind::Stdout)
63     }
64 
65     /// Create a new Console to stderr.
66     ///
67     /// If there was a problem creating the console, then an error is returned.
stderr() -> io::Result<Console>68     pub fn stderr() -> io::Result<Console> {
69         Self::create_for_stream(HandleKind::Stderr)
70     }
71 
72     /// Applies the current text attributes.
set(&mut self) -> io::Result<()>73     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     /// Apply the given intensity and color attributes to the console
81     /// foreground.
82     ///
83     /// If there was a problem setting attributes on the console, then an error
84     /// is returned.
fg(&mut self, intense: Intense, color: Color) -> io::Result<()>85     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     /// Apply the given intensity and color attributes to the console
92     /// background.
93     ///
94     /// If there was a problem setting attributes on the console, then an error
95     /// is returned.
bg(&mut self, intense: Intense, color: Color) -> io::Result<()>96     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     /// Reset the console text attributes to their original settings.
103     ///
104     /// The original settings correspond to the text attributes on the console
105     /// when this `Console` value was created.
106     ///
107     /// If there was a problem setting attributes on the console, then an error
108     /// is returned.
reset(&mut self) -> io::Result<()>109     pub fn reset(&mut self) -> io::Result<()> {
110         self.cur_attr = self.start_attr;
111         self.set()
112     }
113 
114     /// Toggle virtual terminal processing.
115     ///
116     /// This method attempts to toggle virtual terminal processing for this
117     /// console. If there was a problem toggling it, then an error returned.
118     /// On success, the caller may assume that toggling it was successful.
119     ///
120     /// When virtual terminal processing is enabled, characters emitted to the
121     /// console are parsed for VT100 and similar control character sequences
122     /// that control color and other similar operations.
set_virtual_terminal_processing( &mut self, yes: bool, ) -> io::Result<()>123     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 /// A representation of text attributes for the Windows console.
140 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
141 struct TextAttributes {
142     fg_color: Color,
143     fg_intense: Intense,
144     bg_color: Color,
145     bg_intense: Intense,
146 }
147 
148 impl TextAttributes {
to_word(&self) -> WORD149     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 
from_word(word: WORD) -> TextAttributes158     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 /// Whether to use intense colors or not.
169 #[allow(missing_docs)]
170 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
171 pub enum Intense {
172     Yes,
173     No,
174 }
175 
176 impl Intense {
to_bg(&self) -> WORD177     fn to_bg(&self) -> WORD {
178         self.to_fg() << 4
179     }
180 
from_bg(word: WORD) -> Intense181     fn from_bg(word: WORD) -> Intense {
182         Intense::from_fg(word >> 4)
183     }
184 
to_fg(&self) -> WORD185     fn to_fg(&self) -> WORD {
186         match *self {
187             Intense::No => 0,
188             Intense::Yes => FG_INTENSITY,
189         }
190     }
191 
from_fg(word: WORD) -> Intense192     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 /// The set of available colors for use with a Windows console.
202 #[allow(missing_docs)]
203 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
204 pub enum Color {
205     Black,
206     Blue,
207     Green,
208     Red,
209     Cyan,
210     Magenta,
211     Yellow,
212     White,
213 }
214 
215 impl Color {
to_bg(&self) -> WORD216     fn to_bg(&self) -> WORD {
217         self.to_fg() << 4
218     }
219 
from_bg(word: WORD) -> Color220     fn from_bg(word: WORD) -> Color {
221         Color::from_fg(word >> 4)
222     }
223 
to_fg(&self) -> WORD224     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 
from_fg(word: WORD) -> Color237     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 }
250