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