]> git.lizzy.rs Git - rust.git/blob - src/libterm/win.rs
Auto merge of #68037 - msizanoen1:riscv-ci, r=alexcrichton
[rust.git] / src / libterm / 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 crate::color;
9 use crate::Attr;
10 use crate::Terminal;
11
12 /// A Terminal implementation that uses the Win32 Console API.
13 pub struct WinConsole<T> {
14     buf: T,
15     def_foreground: color::Color,
16     def_background: color::Color,
17     foreground: color::Color,
18     background: color::Color,
19 }
20
21 type SHORT = i16;
22 type WORD = u16;
23 type DWORD = u32;
24 type BOOL = i32;
25 type HANDLE = *mut u8;
26
27 #[allow(non_snake_case)]
28 #[repr(C)]
29 struct SMALL_RECT {
30     Left: SHORT,
31     Top: SHORT,
32     Right: SHORT,
33     Bottom: SHORT,
34 }
35
36 #[allow(non_snake_case)]
37 #[repr(C)]
38 struct COORD {
39     X: SHORT,
40     Y: SHORT,
41 }
42
43 #[allow(non_snake_case)]
44 #[repr(C)]
45 struct CONSOLE_SCREEN_BUFFER_INFO {
46     dwSize: COORD,
47     dwCursorPosition: COORD,
48     wAttributes: WORD,
49     srWindow: SMALL_RECT,
50     dwMaximumWindowSize: COORD,
51 }
52
53 #[allow(non_snake_case)]
54 #[link(name = "kernel32")]
55 extern "system" {
56     fn SetConsoleTextAttribute(handle: HANDLE, attr: WORD) -> BOOL;
57     fn GetStdHandle(which: DWORD) -> HANDLE;
58     fn GetConsoleScreenBufferInfo(handle: HANDLE, info: *mut CONSOLE_SCREEN_BUFFER_INFO) -> BOOL;
59 }
60
61 fn color_to_bits(color: color::Color) -> u16 {
62     // magic numbers from mingw-w64's wincon.h
63
64     let bits = match color % 8 {
65         color::BLACK => 0,
66         color::BLUE => 0x1,
67         color::GREEN => 0x2,
68         color::RED => 0x4,
69         color::YELLOW => 0x2 | 0x4,
70         color::MAGENTA => 0x1 | 0x4,
71         color::CYAN => 0x1 | 0x2,
72         color::WHITE => 0x1 | 0x2 | 0x4,
73         _ => unreachable!(),
74     };
75
76     if color >= 8 { bits | 0x8 } else { bits }
77 }
78
79 fn bits_to_color(bits: u16) -> color::Color {
80     let color = match bits & 0x7 {
81         0 => color::BLACK,
82         0x1 => color::BLUE,
83         0x2 => color::GREEN,
84         0x4 => color::RED,
85         0x6 => color::YELLOW,
86         0x5 => color::MAGENTA,
87         0x3 => color::CYAN,
88         0x7 => color::WHITE,
89         _ => unreachable!(),
90     };
91
92     color | (u32::from(bits) & 0x8) // copy the hi-intensity bit
93 }
94
95 impl<T: Write + Send + 'static> WinConsole<T> {
96     fn apply(&mut self) {
97         let _unused = self.buf.flush();
98         let mut accum: WORD = 0;
99         accum |= color_to_bits(self.foreground);
100         accum |= color_to_bits(self.background) << 4;
101
102         unsafe {
103             // Magic -11 means stdout, from
104             // https://docs.microsoft.com/en-us/windows/console/getstdhandle
105             //
106             // You may be wondering, "but what about stderr?", and the answer
107             // to that is that setting terminal attributes on the stdout
108             // handle also sets them for stderr, since they go to the same
109             // terminal! Admittedly, this is fragile, since stderr could be
110             // redirected to a different console. This is good enough for
111             // rustc though. See #13400.
112             let out = GetStdHandle(-11i32 as DWORD);
113             SetConsoleTextAttribute(out, accum);
114         }
115     }
116
117     /// Returns `None` whenever the terminal cannot be created for some reason.
118     pub fn new(out: T) -> io::Result<WinConsole<T>> {
119         use std::mem::MaybeUninit;
120
121         let fg;
122         let bg;
123         unsafe {
124             let mut buffer_info = MaybeUninit::<CONSOLE_SCREEN_BUFFER_INFO>::uninit();
125             if GetConsoleScreenBufferInfo(GetStdHandle(-11i32 as DWORD), buffer_info.as_mut_ptr())
126                 != 0
127             {
128                 let buffer_info = buffer_info.assume_init();
129                 fg = bits_to_color(buffer_info.wAttributes);
130                 bg = bits_to_color(buffer_info.wAttributes >> 4);
131             } else {
132                 fg = color::WHITE;
133                 bg = color::BLACK;
134             }
135         }
136         Ok(WinConsole {
137             buf: out,
138             def_foreground: fg,
139             def_background: bg,
140             foreground: fg,
141             background: bg,
142         })
143     }
144 }
145
146 impl<T: Write> Write for WinConsole<T> {
147     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
148         self.buf.write(buf)
149     }
150
151     fn flush(&mut self) -> io::Result<()> {
152         self.buf.flush()
153     }
154 }
155
156 impl<T: Write + Send + 'static> Terminal for WinConsole<T> {
157     type Output = T;
158
159     fn fg(&mut self, color: color::Color) -> io::Result<bool> {
160         self.foreground = color;
161         self.apply();
162
163         Ok(true)
164     }
165
166     fn bg(&mut self, color: color::Color) -> io::Result<bool> {
167         self.background = color;
168         self.apply();
169
170         Ok(true)
171     }
172
173     fn attr(&mut self, attr: Attr) -> io::Result<bool> {
174         match attr {
175             Attr::ForegroundColor(f) => {
176                 self.foreground = f;
177                 self.apply();
178                 Ok(true)
179             }
180             Attr::BackgroundColor(b) => {
181                 self.background = b;
182                 self.apply();
183                 Ok(true)
184             }
185             _ => Ok(false),
186         }
187     }
188
189     fn supports_attr(&self, attr: Attr) -> bool {
190         // it claims support for underscore and reverse video, but I can't get
191         // it to do anything -cmr
192         match attr {
193             Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => true,
194             _ => false,
195         }
196     }
197
198     fn reset(&mut self) -> io::Result<bool> {
199         self.foreground = self.def_foreground;
200         self.background = self.def_background;
201         self.apply();
202
203         Ok(true)
204     }
205
206     fn get_ref(&self) -> &T {
207         &self.buf
208     }
209
210     fn get_mut(&mut self) -> &mut T {
211         &mut self.buf
212     }
213
214     fn into_inner(self) -> T
215     where
216         Self: Sized,
217     {
218         self.buf
219     }
220 }