]> git.lizzy.rs Git - rust.git/blob - src/libterm/win.rs
Rollup merge of #67363 - alexcrichton:wasm-import-modules, r=eddyb
[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::Attr;
9 use crate::color;
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 {
77         bits | 0x8
78     } else {
79         bits
80     }
81 }
82
83 fn bits_to_color(bits: u16) -> color::Color {
84     let color = match bits & 0x7 {
85         0 => color::BLACK,
86         0x1 => color::BLUE,
87         0x2 => color::GREEN,
88         0x4 => color::RED,
89         0x6 => color::YELLOW,
90         0x5 => color::MAGENTA,
91         0x3 => color::CYAN,
92         0x7 => color::WHITE,
93         _ => unreachable!(),
94     };
95
96     color | (bits & 0x8) // copy the hi-intensity bit
97 }
98
99 impl<T: Write + Send + 'static> WinConsole<T> {
100     fn apply(&mut self) {
101         let _unused = self.buf.flush();
102         let mut accum: WORD = 0;
103         accum |= color_to_bits(self.foreground);
104         accum |= color_to_bits(self.background) << 4;
105
106         unsafe {
107             // Magic -11 means stdout, from
108             // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx
109             //
110             // You may be wondering, "but what about stderr?", and the answer
111             // to that is that setting terminal attributes on the stdout
112             // handle also sets them for stderr, since they go to the same
113             // terminal! Admittedly, this is fragile, since stderr could be
114             // redirected to a different console. This is good enough for
115             // rustc though. See #13400.
116             let out = GetStdHandle(-11i32 as DWORD);
117             SetConsoleTextAttribute(out, accum);
118         }
119     }
120
121     /// Returns `None` whenever the terminal cannot be created for some reason.
122     pub fn new(out: T) -> io::Result<WinConsole<T>> {
123         use std::mem::MaybeUninit;
124
125         let fg;
126         let bg;
127         unsafe {
128             let mut buffer_info = MaybeUninit::<CONSOLE_SCREEN_BUFFER_INFO>::uninit();
129             if GetConsoleScreenBufferInfo(
130                 GetStdHandle(-11i32 as DWORD),
131                 buffer_info.as_mut_ptr()
132             ) != 0 {
133                 let buffer_info = buffer_info.assume_init() ;
134                 fg = bits_to_color(buffer_info.wAttributes);
135                 bg = bits_to_color(buffer_info.wAttributes >> 4);
136             } else {
137                 fg = color::WHITE;
138                 bg = color::BLACK;
139             }
140         }
141         Ok(WinConsole {
142             buf: out,
143             def_foreground: fg,
144             def_background: bg,
145             foreground: fg,
146             background: bg,
147         })
148     }
149 }
150
151 impl<T: Write> Write for WinConsole<T> {
152     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
153         self.buf.write(buf)
154     }
155
156     fn flush(&mut self) -> io::Result<()> {
157         self.buf.flush()
158     }
159 }
160
161 impl<T: Write + Send + 'static> Terminal for WinConsole<T> {
162     type Output = T;
163
164     fn fg(&mut self, color: color::Color) -> io::Result<bool> {
165         self.foreground = color;
166         self.apply();
167
168         Ok(true)
169     }
170
171     fn bg(&mut self, color: color::Color) -> io::Result<bool> {
172         self.background = color;
173         self.apply();
174
175         Ok(true)
176     }
177
178     fn attr(&mut self, attr: Attr) -> io::Result<bool> {
179         match attr {
180             Attr::ForegroundColor(f) => {
181                 self.foreground = f;
182                 self.apply();
183                 Ok(true)
184             }
185             Attr::BackgroundColor(b) => {
186                 self.background = b;
187                 self.apply();
188                 Ok(true)
189             }
190             _ => Ok(false),
191         }
192     }
193
194     fn supports_attr(&self, attr: Attr) -> bool {
195         // it claims support for underscore and reverse video, but I can't get
196         // it to do anything -cmr
197         match attr {
198             Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => true,
199             _ => false,
200         }
201     }
202
203     fn reset(&mut self) -> io::Result<bool> {
204         self.foreground = self.def_foreground;
205         self.background = self.def_background;
206         self.apply();
207
208         Ok(true)
209     }
210
211     fn get_ref(&self) -> &T {
212         &self.buf
213     }
214
215     fn get_mut(&mut self) -> &mut T {
216         &mut self.buf
217     }
218
219     fn into_inner(self) -> T
220         where Self: Sized
221     {
222         self.buf
223     }
224 }