]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! Windows console handling | |
12 | ||
13 | // FIXME (#13400): this is only a tiny fraction of the Windows console api | |
14 | ||
15 | extern crate libc; | |
16 | ||
c34b1796 AL |
17 | use std::io; |
18 | use std::io::prelude::*; | |
1a4d82fc | 19 | |
92a42be0 | 20 | use Attr; |
1a4d82fc | 21 | use color; |
92a42be0 | 22 | use Terminal; |
1a4d82fc JJ |
23 | |
24 | /// A Terminal implementation which uses the Win32 Console API. | |
25 | pub struct WinConsole<T> { | |
26 | buf: T, | |
27 | def_foreground: color::Color, | |
28 | def_background: color::Color, | |
29 | foreground: color::Color, | |
30 | background: color::Color, | |
31 | } | |
32 | ||
92a42be0 SL |
33 | type WORD = u16; |
34 | type DWORD = u32; | |
35 | type BOOL = i32; | |
36 | type HANDLE = *mut u8; | |
37 | ||
1a4d82fc JJ |
38 | #[allow(non_snake_case)] |
39 | #[repr(C)] | |
40 | struct CONSOLE_SCREEN_BUFFER_INFO { | |
41 | dwSize: [libc::c_short; 2], | |
42 | dwCursorPosition: [libc::c_short; 2], | |
92a42be0 | 43 | wAttributes: WORD, |
1a4d82fc JJ |
44 | srWindow: [libc::c_short; 4], |
45 | dwMaximumWindowSize: [libc::c_short; 2], | |
46 | } | |
47 | ||
48 | #[allow(non_snake_case)] | |
49 | #[link(name = "kernel32")] | |
50 | extern "system" { | |
92a42be0 SL |
51 | fn SetConsoleTextAttribute(handle: HANDLE, attr: WORD) -> BOOL; |
52 | fn GetStdHandle(which: DWORD) -> HANDLE; | |
53 | fn GetConsoleScreenBufferInfo(handle: HANDLE, info: *mut CONSOLE_SCREEN_BUFFER_INFO) -> BOOL; | |
1a4d82fc JJ |
54 | } |
55 | ||
56 | fn color_to_bits(color: color::Color) -> u16 { | |
57 | // magic numbers from mingw-w64's wincon.h | |
58 | ||
59 | let bits = match color % 8 { | |
92a42be0 SL |
60 | color::BLACK => 0, |
61 | color::BLUE => 0x1, | |
62 | color::GREEN => 0x2, | |
63 | color::RED => 0x4, | |
64 | color::YELLOW => 0x2 | 0x4, | |
1a4d82fc | 65 | color::MAGENTA => 0x1 | 0x4, |
92a42be0 SL |
66 | color::CYAN => 0x1 | 0x2, |
67 | color::WHITE => 0x1 | 0x2 | 0x4, | |
68 | _ => unreachable!(), | |
1a4d82fc JJ |
69 | }; |
70 | ||
71 | if color >= 8 { | |
72 | bits | 0x8 | |
73 | } else { | |
74 | bits | |
75 | } | |
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, | |
92a42be0 | 88 | _ => unreachable!(), |
1a4d82fc JJ |
89 | }; |
90 | ||
91 | color | (bits & 0x8) // copy the hi-intensity bit | |
92 | } | |
93 | ||
c34b1796 | 94 | impl<T: Write+Send+'static> WinConsole<T> { |
1a4d82fc JJ |
95 | fn apply(&mut self) { |
96 | let _unused = self.buf.flush(); | |
92a42be0 | 97 | let mut accum: WORD = 0; |
1a4d82fc JJ |
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 | // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx | |
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. | |
92a42be0 | 111 | let out = GetStdHandle(-11i32 as DWORD); |
1a4d82fc JJ |
112 | SetConsoleTextAttribute(out, accum); |
113 | } | |
114 | } | |
115 | ||
116 | /// Returns `None` whenever the terminal cannot be created for some | |
117 | /// reason. | |
92a42be0 | 118 | pub fn new(out: T) -> io::Result<WinConsole<T>> { |
1a4d82fc JJ |
119 | let fg; |
120 | let bg; | |
121 | unsafe { | |
122 | let mut buffer_info = ::std::mem::uninitialized(); | |
92a42be0 | 123 | if GetConsoleScreenBufferInfo(GetStdHandle(-11i32 as DWORD), &mut buffer_info) != 0 { |
1a4d82fc JJ |
124 | fg = bits_to_color(buffer_info.wAttributes); |
125 | bg = bits_to_color(buffer_info.wAttributes >> 4); | |
126 | } else { | |
127 | fg = color::WHITE; | |
128 | bg = color::BLACK; | |
129 | } | |
130 | } | |
92a42be0 SL |
131 | Ok(WinConsole { |
132 | buf: out, | |
133 | def_foreground: fg, | |
134 | def_background: bg, | |
135 | foreground: fg, | |
136 | background: bg, | |
137 | }) | |
1a4d82fc JJ |
138 | } |
139 | } | |
140 | ||
c34b1796 AL |
141 | impl<T: Write> Write for WinConsole<T> { |
142 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | |
143 | self.buf.write(buf) | |
1a4d82fc JJ |
144 | } |
145 | ||
c34b1796 | 146 | fn flush(&mut self) -> io::Result<()> { |
1a4d82fc JJ |
147 | self.buf.flush() |
148 | } | |
149 | } | |
150 | ||
92a42be0 SL |
151 | impl<T: Write+Send+'static> Terminal for WinConsole<T> { |
152 | type Output = T; | |
153 | ||
c34b1796 | 154 | fn fg(&mut self, color: color::Color) -> io::Result<bool> { |
1a4d82fc JJ |
155 | self.foreground = color; |
156 | self.apply(); | |
157 | ||
158 | Ok(true) | |
159 | } | |
160 | ||
c34b1796 | 161 | fn bg(&mut self, color: color::Color) -> io::Result<bool> { |
1a4d82fc JJ |
162 | self.background = color; |
163 | self.apply(); | |
164 | ||
165 | Ok(true) | |
166 | } | |
167 | ||
92a42be0 | 168 | fn attr(&mut self, attr: Attr) -> io::Result<bool> { |
1a4d82fc | 169 | match attr { |
92a42be0 | 170 | Attr::ForegroundColor(f) => { |
1a4d82fc JJ |
171 | self.foreground = f; |
172 | self.apply(); | |
173 | Ok(true) | |
92a42be0 SL |
174 | } |
175 | Attr::BackgroundColor(b) => { | |
1a4d82fc JJ |
176 | self.background = b; |
177 | self.apply(); | |
178 | Ok(true) | |
92a42be0 SL |
179 | } |
180 | _ => Ok(false), | |
1a4d82fc JJ |
181 | } |
182 | } | |
183 | ||
92a42be0 | 184 | fn supports_attr(&self, attr: Attr) -> bool { |
1a4d82fc JJ |
185 | // it claims support for underscore and reverse video, but I can't get |
186 | // it to do anything -cmr | |
187 | match attr { | |
92a42be0 SL |
188 | Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => true, |
189 | _ => false, | |
1a4d82fc JJ |
190 | } |
191 | } | |
192 | ||
92a42be0 | 193 | fn reset(&mut self) -> io::Result<bool> { |
1a4d82fc JJ |
194 | self.foreground = self.def_foreground; |
195 | self.background = self.def_background; | |
196 | self.apply(); | |
197 | ||
92a42be0 | 198 | Ok(true) |
1a4d82fc JJ |
199 | } |
200 | ||
92a42be0 SL |
201 | fn get_ref<'a>(&'a self) -> &'a T { |
202 | &self.buf | |
203 | } | |
1a4d82fc | 204 | |
92a42be0 SL |
205 | fn get_mut<'a>(&'a mut self) -> &'a mut T { |
206 | &mut self.buf | |
207 | } | |
1a4d82fc | 208 | |
92a42be0 SL |
209 | fn into_inner(self) -> T |
210 | where Self: Sized | |
211 | { | |
212 | self.buf | |
213 | } | |
1a4d82fc | 214 | } |