]> git.lizzy.rs Git - rust.git/blob - library/test/src/term/win.rs
Rollup merge of #87346 - rylev:rename-force-warn, r=nikomatsakis
[rust.git] / library / test / src / term / win.rs
1 //! Windows console handling
2
3 // FIXME (#13400): this is only a tiny fraction of the Windows console api
4
5 use std::io;
6 use std::io::prelude::*;
7
8 use super::color;
9 use super::Terminal;
10
11 /// A Terminal implementation that uses the Win32 Console API.
12 pub(crate) struct WinConsole<T> {
13     buf: T,
14     def_foreground: color::Color,
15     def_background: color::Color,
16     foreground: color::Color,
17     background: color::Color,
18 }
19
20 type SHORT = i16;
21 type WORD = u16;
22 type DWORD = u32;
23 type BOOL = i32;
24 type HANDLE = *mut u8;
25
26 #[allow(non_snake_case)]
27 #[repr(C)]
28 struct SMALL_RECT {
29     Left: SHORT,
30     Top: SHORT,
31     Right: SHORT,
32     Bottom: SHORT,
33 }
34
35 #[allow(non_snake_case)]
36 #[repr(C)]
37 struct COORD {
38     X: SHORT,
39     Y: SHORT,
40 }
41
42 #[allow(non_snake_case)]
43 #[repr(C)]
44 struct CONSOLE_SCREEN_BUFFER_INFO {
45     dwSize: COORD,
46     dwCursorPosition: COORD,
47     wAttributes: WORD,
48     srWindow: SMALL_RECT,
49     dwMaximumWindowSize: COORD,
50 }
51
52 #[allow(non_snake_case)]
53 #[link(name = "kernel32")]
54 extern "system" {
55     fn SetConsoleTextAttribute(handle: HANDLE, attr: WORD) -> BOOL;
56     fn GetStdHandle(which: DWORD) -> HANDLE;
57     fn GetConsoleScreenBufferInfo(handle: HANDLE, info: *mut CONSOLE_SCREEN_BUFFER_INFO) -> BOOL;
58 }
59
60 fn color_to_bits(color: color::Color) -> u16 {
61     // magic numbers from mingw-w64's wincon.h
62
63     let bits = match color % 8 {
64         color::BLACK => 0,
65         color::BLUE => 0x1,
66         color::GREEN => 0x2,
67         color::RED => 0x4,
68         color::YELLOW => 0x2 | 0x4,
69         color::MAGENTA => 0x1 | 0x4,
70         color::CYAN => 0x1 | 0x2,
71         color::WHITE => 0x1 | 0x2 | 0x4,
72         _ => unreachable!(),
73     };
74
75     if color >= 8 { bits | 0x8 } else { bits }
76 }
77
78 fn bits_to_color(bits: u16) -> color::Color {
79     let color = match bits & 0x7 {
80         0 => color::BLACK,
81         0x1 => color::BLUE,
82         0x2 => color::GREEN,
83         0x4 => color::RED,
84         0x6 => color::YELLOW,
85         0x5 => color::MAGENTA,
86         0x3 => color::CYAN,
87         0x7 => color::WHITE,
88         _ => unreachable!(),
89     };
90
91     color | (u32::from(bits) & 0x8) // copy the hi-intensity bit
92 }
93
94 impl<T: Write + Send + 'static> WinConsole<T> {
95     fn apply(&mut self) {
96         let _unused = self.buf.flush();
97         let mut accum: WORD = 0;
98         accum |= color_to_bits(self.foreground);
99         accum |= color_to_bits(self.background) << 4;
100
101         unsafe {
102             // Magic -11 means stdout, from
103             // https://docs.microsoft.com/en-us/windows/console/getstdhandle
104             //
105             // You may be wondering, "but what about stderr?", and the answer
106             // to that is that setting terminal attributes on the stdout
107             // handle also sets them for stderr, since they go to the same
108             // terminal! Admittedly, this is fragile, since stderr could be
109             // redirected to a different console. This is good enough for
110             // rustc though. See #13400.
111             let out = GetStdHandle(-11i32 as DWORD);
112             SetConsoleTextAttribute(out, accum);
113         }
114     }
115
116     /// Returns `None` whenever the terminal cannot be created for some reason.
117     pub(crate) fn new(out: T) -> io::Result<WinConsole<T>> {
118         use std::mem::MaybeUninit;
119
120         let fg;
121         let bg;
122         unsafe {
123             let mut buffer_info = MaybeUninit::<CONSOLE_SCREEN_BUFFER_INFO>::uninit();
124             if GetConsoleScreenBufferInfo(GetStdHandle(-11i32 as DWORD), buffer_info.as_mut_ptr())
125                 != 0
126             {
127                 let buffer_info = buffer_info.assume_init();
128                 fg = bits_to_color(buffer_info.wAttributes);
129                 bg = bits_to_color(buffer_info.wAttributes >> 4);
130             } else {
131                 fg = color::WHITE;
132                 bg = color::BLACK;
133             }
134         }
135         Ok(WinConsole {
136             buf: out,
137             def_foreground: fg,
138             def_background: bg,
139             foreground: fg,
140             background: bg,
141         })
142     }
143 }
144
145 impl<T: Write> Write for WinConsole<T> {
146     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
147         self.buf.write(buf)
148     }
149
150     fn flush(&mut self) -> io::Result<()> {
151         self.buf.flush()
152     }
153 }
154
155 impl<T: Write + Send + 'static> Terminal for WinConsole<T> {
156     fn fg(&mut self, color: color::Color) -> io::Result<bool> {
157         self.foreground = color;
158         self.apply();
159
160         Ok(true)
161     }
162
163     fn reset(&mut self) -> io::Result<bool> {
164         self.foreground = self.def_foreground;
165         self.background = self.def_background;
166         self.apply();
167
168         Ok(true)
169     }
170 }