1 // Copyright 2013-2019 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10 
11 //! Windows console handling
12 
13 // FIXME (#13400): this is only a tiny fraction of the Windows console api
14 
15 use crate::color;
16 use crate::Attr;
17 use crate::Error;
18 use crate::Result;
19 use crate::Terminal;
20 use std::io;
21 use std::io::prelude::*;
22 use std::ops::Deref;
23 use std::ptr;
24 
25 use winapi::shared::minwindef::{DWORD, WORD};
26 use winapi::um::consoleapi::{GetConsoleMode, SetConsoleMode};
27 use winapi::um::fileapi::{CreateFileA, OPEN_EXISTING};
28 use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
29 use winapi::um::wincon::FillConsoleOutputAttribute;
30 use winapi::um::wincon::{
31     FillConsoleOutputCharacterW, GetConsoleScreenBufferInfo, CONSOLE_SCREEN_BUFFER_INFO, COORD,
32 };
33 use winapi::um::wincon::{SetConsoleCursorPosition, SetConsoleTextAttribute};
34 use winapi::um::wincon::{BACKGROUND_INTENSITY, ENABLE_VIRTUAL_TERMINAL_PROCESSING};
35 use winapi::um::winnt::{FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE, HANDLE};
36 
37 /// Console info which can be used by a Terminal implementation
38 /// which uses the Win32 Console API.
39 pub struct WinConsoleInfo {
40     def_foreground: color::Color,
41     def_background: color::Color,
42     foreground: color::Color,
43     background: color::Color,
44     reverse: bool,
45     secure: bool,
46     standout: bool,
47 }
48 
49 /// A Terminal implementation which uses the Win32 Console API.
50 pub struct WinConsole<T> {
51     buf: T,
52     info: WinConsoleInfo,
53 }
54 
color_to_bits(color: color::Color) -> u1655 fn color_to_bits(color: color::Color) -> u16 {
56     // magic numbers from mingw-w64's wincon.h
57 
58     let bits = match color % 8 {
59         color::BLACK => 0,
60         color::BLUE => 0x1,
61         color::GREEN => 0x2,
62         color::RED => 0x4,
63         color::YELLOW => 0x2 | 0x4,
64         color::MAGENTA => 0x1 | 0x4,
65         color::CYAN => 0x1 | 0x2,
66         color::WHITE => 0x1 | 0x2 | 0x4,
67         _ => unreachable!(),
68     };
69 
70     if color >= 8 {
71         bits | 0x8
72     } else {
73         bits
74     }
75 }
76 
bits_to_color(bits: u16) -> color::Color77 fn bits_to_color(bits: u16) -> color::Color {
78     let color = match bits & 0x7 {
79         0 => color::BLACK,
80         0x1 => color::BLUE,
81         0x2 => color::GREEN,
82         0x4 => color::RED,
83         0x6 => color::YELLOW,
84         0x5 => color::MAGENTA,
85         0x3 => color::CYAN,
86         0x7 => color::WHITE,
87         _ => unreachable!(),
88     };
89 
90     color | (bits as u32 & 0x8) // copy the hi-intensity bit
91 }
92 
93 struct HandleWrapper {
94     inner: HANDLE,
95 }
96 
97 impl HandleWrapper {
new(h: HANDLE) -> HandleWrapper98     fn new(h: HANDLE) -> HandleWrapper {
99         HandleWrapper { inner: h }
100     }
101 }
102 
103 impl Drop for HandleWrapper {
drop(&mut self)104     fn drop(&mut self) {
105         if self.inner != INVALID_HANDLE_VALUE {
106             unsafe {
107                 CloseHandle(self.inner);
108             }
109         }
110     }
111 }
112 
113 impl Deref for HandleWrapper {
114     type Target = HANDLE;
deref(&self) -> &HANDLE115     fn deref(&self) -> &HANDLE {
116         &self.inner
117     }
118 }
119 
120 /// Just get a handle to the current console buffer whatever it is
conout() -> io::Result<HandleWrapper>121 fn conout() -> io::Result<HandleWrapper> {
122     let name = b"CONOUT$\0";
123     let handle = unsafe {
124         CreateFileA(
125             name.as_ptr() as *const i8,
126             GENERIC_READ | GENERIC_WRITE,
127             FILE_SHARE_WRITE,
128             ptr::null_mut(),
129             OPEN_EXISTING,
130             0,
131             ptr::null_mut(),
132         )
133     };
134     if handle == INVALID_HANDLE_VALUE {
135         Err(io::Error::last_os_error())
136     } else {
137         Ok(HandleWrapper::new(handle))
138     }
139 }
140 
set_flag(handle: HANDLE, flag: DWORD) -> io::Result<()>141 unsafe fn set_flag(handle: HANDLE, flag: DWORD) -> io::Result<()> {
142     let mut curr_mode: DWORD = 0;
143     if GetConsoleMode(handle, &mut curr_mode) == 0 {
144         return Err(io::Error::last_os_error());
145     }
146 
147     if SetConsoleMode(handle, curr_mode | flag) == 0 {
148         return Err(io::Error::last_os_error());
149     }
150     return Ok(());
151 }
152 
153 /// Check if console supports ansi codes (should succeed on Windows 10)
supports_ansi() -> bool154 pub fn supports_ansi() -> bool {
155     conout()
156         .and_then(|handle| unsafe { set_flag(*handle, ENABLE_VIRTUAL_TERMINAL_PROCESSING) })
157         .is_ok()
158 }
159 
160 // This test will only pass if it is running in an actual console, probably
161 #[test]
test_conout()162 fn test_conout() {
163     assert!(conout().is_ok())
164 }
165 
166 #[rustversion::before(1.36)]
get_console_screen_buffer_info(handle: HANDLE) -> io::Result<CONSOLE_SCREEN_BUFFER_INFO>167 unsafe fn get_console_screen_buffer_info(handle: HANDLE) -> io::Result<CONSOLE_SCREEN_BUFFER_INFO> {
168     let mut buffer_info = ::std::mem::uninitialized();
169     if GetConsoleScreenBufferInfo(handle, &mut buffer_info) == 0 {
170         Err(io::Error::last_os_error())
171     } else {
172         Ok(buffer_info)
173     }
174 }
175 #[rustversion::since(1.36)]
get_console_screen_buffer_info(handle: HANDLE) -> io::Result<CONSOLE_SCREEN_BUFFER_INFO>176 unsafe fn get_console_screen_buffer_info(handle: HANDLE) -> io::Result<CONSOLE_SCREEN_BUFFER_INFO> {
177     let mut buffer_info = ::std::mem::MaybeUninit::uninit();
178     if GetConsoleScreenBufferInfo(handle, buffer_info.as_mut_ptr()) == 0 {
179         Err(io::Error::last_os_error())
180     } else {
181         Ok(buffer_info.assume_init())
182     }
183 }
184 
185 // This test will only pass if it is running in an actual console, probably
186 #[test]
test_get_console_screen_buffer_info()187 fn test_get_console_screen_buffer_info() {
188     let handle = conout().unwrap();
189     unsafe {
190         let buffer_info = get_console_screen_buffer_info(*handle);
191         assert!(buffer_info.is_ok());
192     }
193 }
194 
195 impl WinConsoleInfo {
196     /// Returns `Err` whenever console info cannot be retrieved for some
197     /// reason.
from_env() -> io::Result<WinConsoleInfo>198     pub fn from_env() -> io::Result<WinConsoleInfo> {
199         let fg;
200         let bg;
201         let handle = conout()?;
202         unsafe {
203             let buffer_info = get_console_screen_buffer_info(*handle)?;
204             fg = bits_to_color(buffer_info.wAttributes);
205             bg = bits_to_color(buffer_info.wAttributes >> 4);
206         }
207         Ok(WinConsoleInfo {
208             def_foreground: fg,
209             def_background: bg,
210             foreground: fg,
211             background: bg,
212             reverse: false,
213             secure: false,
214             standout: false,
215         })
216     }
217 }
218 
219 impl<T: Write + Send> WinConsole<T> {
apply(&mut self) -> io::Result<()>220     fn apply(&mut self) -> io::Result<()> {
221         let out = conout()?;
222         let _unused = self.buf.flush();
223 
224         let (mut fg, bg) = if self.info.reverse {
225             (self.info.background, self.info.foreground)
226         } else {
227             (self.info.foreground, self.info.background)
228         };
229 
230         if self.info.secure {
231             fg = bg;
232         }
233 
234         let mut accum: WORD = 0;
235 
236         accum |= color_to_bits(fg);
237         accum |= color_to_bits(bg) << 4;
238 
239         if self.info.standout {
240             accum |= BACKGROUND_INTENSITY;
241         } else {
242             accum &= BACKGROUND_INTENSITY ^ 0xFF;
243         }
244 
245         unsafe {
246             SetConsoleTextAttribute(*out, accum);
247         }
248         Ok(())
249     }
250 
251     /// Create a new WinConsole with the given WinConsoleInfo and out
new_with_consoleinfo(out: T, info: WinConsoleInfo) -> WinConsole<T>252     pub fn new_with_consoleinfo(out: T, info: WinConsoleInfo) -> WinConsole<T> {
253         WinConsole { buf: out, info }
254     }
255 
256     /// Returns `Err` whenever the terminal cannot be created for some
257     /// reason.
new(out: T) -> io::Result<WinConsole<T>>258     pub fn new(out: T) -> io::Result<WinConsole<T>> {
259         let info = WinConsoleInfo::from_env()?;
260         Ok(Self::new_with_consoleinfo(out, info))
261     }
262 }
263 
264 impl<T: Write> Write for WinConsole<T> {
write(&mut self, buf: &[u8]) -> io::Result<usize>265     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
266         self.buf.write(buf)
267     }
268 
flush(&mut self) -> io::Result<()>269     fn flush(&mut self) -> io::Result<()> {
270         self.buf.flush()
271     }
272 }
273 
274 impl<T: Write + Send> Terminal for WinConsole<T> {
275     type Output = T;
276 
fg(&mut self, color: color::Color) -> Result<()>277     fn fg(&mut self, color: color::Color) -> Result<()> {
278         self.info.foreground = color;
279         self.apply()?;
280 
281         Ok(())
282     }
283 
bg(&mut self, color: color::Color) -> Result<()>284     fn bg(&mut self, color: color::Color) -> Result<()> {
285         self.info.background = color;
286         self.apply()?;
287 
288         Ok(())
289     }
290 
attr(&mut self, attr: Attr) -> Result<()>291     fn attr(&mut self, attr: Attr) -> Result<()> {
292         match attr {
293             Attr::ForegroundColor(f) => {
294                 self.info.foreground = f;
295                 self.apply()?;
296                 Ok(())
297             }
298             Attr::BackgroundColor(b) => {
299                 self.info.background = b;
300                 self.apply()?;
301                 Ok(())
302             }
303             Attr::Reverse => {
304                 self.info.reverse = true;
305                 self.apply()?;
306                 Ok(())
307             }
308             Attr::Secure => {
309                 self.info.secure = true;
310                 self.apply()?;
311                 Ok(())
312             }
313             Attr::Standout(v) => {
314                 self.info.standout = v;
315                 self.apply()?;
316                 Ok(())
317             }
318             _ => Err(Error::NotSupported),
319         }
320     }
321 
supports_attr(&self, attr: Attr) -> bool322     fn supports_attr(&self, attr: Attr) -> bool {
323         match attr {
324             Attr::ForegroundColor(_)
325             | Attr::BackgroundColor(_)
326             | Attr::Standout(_)
327             | Attr::Reverse
328             | Attr::Secure => true,
329             _ => false,
330         }
331     }
332 
reset(&mut self) -> Result<()>333     fn reset(&mut self) -> Result<()> {
334         self.info.foreground = self.info.def_foreground;
335         self.info.background = self.info.def_background;
336         self.info.reverse = false;
337         self.info.secure = false;
338         self.info.standout = false;
339         self.apply()?;
340 
341         Ok(())
342     }
343 
supports_reset(&self) -> bool344     fn supports_reset(&self) -> bool {
345         true
346     }
347 
supports_color(&self) -> bool348     fn supports_color(&self) -> bool {
349         true
350     }
351 
cursor_up(&mut self) -> Result<()>352     fn cursor_up(&mut self) -> Result<()> {
353         let _unused = self.buf.flush();
354         let handle = conout()?;
355         unsafe {
356             let buffer_info = get_console_screen_buffer_info(*handle)?;
357             let (x, y) = (
358                 buffer_info.dwCursorPosition.X,
359                 buffer_info.dwCursorPosition.Y,
360             );
361             if y == 0 {
362                 // Even though this might want to be a CursorPositionInvalid, on Unix there
363                 // is no checking to see if the cursor is already on the first line.
364                 // I'm not sure what the ideal behavior is, but I think it'd be silly to have
365                 // cursor_up fail in this case.
366                 Ok(())
367             } else {
368                 let pos = COORD { X: x, Y: y - 1 };
369                 if SetConsoleCursorPosition(*handle, pos) != 0 {
370                     Ok(())
371                 } else {
372                     Err(io::Error::last_os_error().into())
373                 }
374             }
375         }
376     }
377 
delete_line(&mut self) -> Result<()>378     fn delete_line(&mut self) -> Result<()> {
379         let _unused = self.buf.flush();
380         let handle = conout()?;
381         unsafe {
382             let buffer_info = get_console_screen_buffer_info(*handle)?;
383             let pos = buffer_info.dwCursorPosition;
384             let size = buffer_info.dwSize;
385             let num = (size.X - pos.X) as DWORD;
386             let mut written = 0;
387             // 0x0020u16 is ' ' (space) in UTF-16 (same as ascii)
388             if FillConsoleOutputCharacterW(*handle, 0x0020, num, pos, &mut written) == 0 {
389                 return Err(io::Error::last_os_error().into());
390             }
391             if FillConsoleOutputAttribute(*handle, 0, num, pos, &mut written) == 0 {
392                 return Err(io::Error::last_os_error().into());
393             }
394             // Similar reasoning for not failing as in cursor_up -- it doesn't even make
395             // sense to
396             // me that these APIs could have written 0, unless the terminal is width zero.
397             Ok(())
398         }
399     }
400 
carriage_return(&mut self) -> Result<()>401     fn carriage_return(&mut self) -> Result<()> {
402         let _unused = self.buf.flush();
403         let handle = conout()?;
404         unsafe {
405             let buffer_info = get_console_screen_buffer_info(*handle)?;
406             let COORD { X: x, Y: y } = buffer_info.dwCursorPosition;
407             if x == 0 {
408                 Err(Error::CursorDestinationInvalid)
409             } else {
410                 let pos = COORD { X: 0, Y: y };
411                 if SetConsoleCursorPosition(*handle, pos) != 0 {
412                     Ok(())
413                 } else {
414                     Err(io::Error::last_os_error().into())
415                 }
416             }
417         }
418     }
419 
get_ref<'a>(&'a self) -> &'a T420     fn get_ref<'a>(&'a self) -> &'a T {
421         &self.buf
422     }
423 
get_mut<'a>(&'a mut self) -> &'a mut T424     fn get_mut<'a>(&'a mut self) -> &'a mut T {
425         &mut self.buf
426     }
427 
into_inner(self) -> T where Self: Sized,428     fn into_inner(self) -> T
429     where
430         Self: Sized,
431     {
432         self.buf
433     }
434 }
435