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