]>
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 | ||
85aaf69f | 17 | use std::old_io::IoResult; |
1a4d82fc JJ |
18 | |
19 | use attr; | |
20 | use color; | |
21 | use {Terminal,UnwrappableTerminal}; | |
22 | ||
23 | /// A Terminal implementation which uses the Win32 Console API. | |
24 | pub struct WinConsole<T> { | |
25 | buf: T, | |
26 | def_foreground: color::Color, | |
27 | def_background: color::Color, | |
28 | foreground: color::Color, | |
29 | background: color::Color, | |
30 | } | |
31 | ||
32 | #[allow(non_snake_case)] | |
33 | #[repr(C)] | |
34 | struct CONSOLE_SCREEN_BUFFER_INFO { | |
35 | dwSize: [libc::c_short; 2], | |
36 | dwCursorPosition: [libc::c_short; 2], | |
37 | wAttributes: libc::WORD, | |
38 | srWindow: [libc::c_short; 4], | |
39 | dwMaximumWindowSize: [libc::c_short; 2], | |
40 | } | |
41 | ||
42 | #[allow(non_snake_case)] | |
43 | #[link(name = "kernel32")] | |
44 | extern "system" { | |
45 | fn SetConsoleTextAttribute(handle: libc::HANDLE, attr: libc::WORD) -> libc::BOOL; | |
46 | fn GetStdHandle(which: libc::DWORD) -> libc::HANDLE; | |
47 | fn GetConsoleScreenBufferInfo(handle: libc::HANDLE, | |
48 | info: *mut CONSOLE_SCREEN_BUFFER_INFO) -> libc::BOOL; | |
49 | } | |
50 | ||
51 | fn color_to_bits(color: color::Color) -> u16 { | |
52 | // magic numbers from mingw-w64's wincon.h | |
53 | ||
54 | let bits = match color % 8 { | |
55 | color::BLACK => 0, | |
56 | color::BLUE => 0x1, | |
57 | color::GREEN => 0x2, | |
58 | color::RED => 0x4, | |
59 | color::YELLOW => 0x2 | 0x4, | |
60 | color::MAGENTA => 0x1 | 0x4, | |
61 | color::CYAN => 0x1 | 0x2, | |
62 | color::WHITE => 0x1 | 0x2 | 0x4, | |
63 | _ => unreachable!() | |
64 | }; | |
65 | ||
66 | if color >= 8 { | |
67 | bits | 0x8 | |
68 | } else { | |
69 | bits | |
70 | } | |
71 | } | |
72 | ||
73 | fn bits_to_color(bits: u16) -> color::Color { | |
74 | let color = match bits & 0x7 { | |
75 | 0 => color::BLACK, | |
76 | 0x1 => color::BLUE, | |
77 | 0x2 => color::GREEN, | |
78 | 0x4 => color::RED, | |
79 | 0x6 => color::YELLOW, | |
80 | 0x5 => color::MAGENTA, | |
81 | 0x3 => color::CYAN, | |
82 | 0x7 => color::WHITE, | |
83 | _ => unreachable!() | |
84 | }; | |
85 | ||
86 | color | (bits & 0x8) // copy the hi-intensity bit | |
87 | } | |
88 | ||
85aaf69f | 89 | impl<T: Writer+Send+'static> WinConsole<T> { |
1a4d82fc JJ |
90 | fn apply(&mut self) { |
91 | let _unused = self.buf.flush(); | |
92 | let mut accum: libc::WORD = 0; | |
93 | accum |= color_to_bits(self.foreground); | |
94 | accum |= color_to_bits(self.background) << 4; | |
95 | ||
96 | unsafe { | |
97 | // Magic -11 means stdout, from | |
98 | // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx | |
99 | // | |
100 | // You may be wondering, "but what about stderr?", and the answer | |
101 | // to that is that setting terminal attributes on the stdout | |
102 | // handle also sets them for stderr, since they go to the same | |
103 | // terminal! Admittedly, this is fragile, since stderr could be | |
104 | // redirected to a different console. This is good enough for | |
105 | // rustc though. See #13400. | |
106 | let out = GetStdHandle(-11); | |
107 | SetConsoleTextAttribute(out, accum); | |
108 | } | |
109 | } | |
110 | ||
111 | /// Returns `None` whenever the terminal cannot be created for some | |
112 | /// reason. | |
113 | pub fn new(out: T) -> Option<Box<Terminal<T>+Send+'static>> { | |
114 | let fg; | |
115 | let bg; | |
116 | unsafe { | |
117 | let mut buffer_info = ::std::mem::uninitialized(); | |
118 | if GetConsoleScreenBufferInfo(GetStdHandle(-11), &mut buffer_info) != 0 { | |
119 | fg = bits_to_color(buffer_info.wAttributes); | |
120 | bg = bits_to_color(buffer_info.wAttributes >> 4); | |
121 | } else { | |
122 | fg = color::WHITE; | |
123 | bg = color::BLACK; | |
124 | } | |
125 | } | |
126 | Some(box WinConsole { buf: out, | |
127 | def_foreground: fg, def_background: bg, | |
128 | foreground: fg, background: bg } as Box<Terminal<T>+Send>) | |
129 | } | |
130 | } | |
131 | ||
132 | impl<T: Writer> Writer for WinConsole<T> { | |
85aaf69f SL |
133 | fn write_all(&mut self, buf: &[u8]) -> IoResult<()> { |
134 | self.buf.write_all(buf) | |
1a4d82fc JJ |
135 | } |
136 | ||
137 | fn flush(&mut self) -> IoResult<()> { | |
138 | self.buf.flush() | |
139 | } | |
140 | } | |
141 | ||
85aaf69f | 142 | impl<T: Writer+Send+'static> Terminal<T> for WinConsole<T> { |
1a4d82fc JJ |
143 | fn fg(&mut self, color: color::Color) -> IoResult<bool> { |
144 | self.foreground = color; | |
145 | self.apply(); | |
146 | ||
147 | Ok(true) | |
148 | } | |
149 | ||
150 | fn bg(&mut self, color: color::Color) -> IoResult<bool> { | |
151 | self.background = color; | |
152 | self.apply(); | |
153 | ||
154 | Ok(true) | |
155 | } | |
156 | ||
157 | fn attr(&mut self, attr: attr::Attr) -> IoResult<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::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) -> IoResult<()> { | |
183 | self.foreground = self.def_foreground; | |
184 | self.background = self.def_background; | |
185 | self.apply(); | |
186 | ||
187 | Ok(()) | |
188 | } | |
189 | ||
190 | fn get_ref<'a>(&'a self) -> &'a T { &self.buf } | |
191 | ||
192 | fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.buf } | |
193 | } | |
194 | ||
85aaf69f | 195 | impl<T: Writer+Send+'static> UnwrappableTerminal<T> for WinConsole<T> { |
1a4d82fc JJ |
196 | fn unwrap(self) -> T { self.buf } |
197 | } |