]> git.lizzy.rs Git - rust.git/blob - src/libterm/win.rs
Auto merge of #38138 - rkruppe:no_std-no_loop, r=steveklabnik
[rust.git] / src / libterm / win.rs
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
17 use std::io;
18 use std::io::prelude::*;
19
20 use Attr;
21 use color;
22 use Terminal;
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
33 type WORD = u16;
34 type DWORD = u32;
35 type BOOL = i32;
36 type HANDLE = *mut u8;
37
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],
43     wAttributes: WORD,
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" {
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;
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 {
60         color::BLACK => 0,
61         color::BLUE => 0x1,
62         color::GREEN => 0x2,
63         color::RED => 0x4,
64         color::YELLOW => 0x2 | 0x4,
65         color::MAGENTA => 0x1 | 0x4,
66         color::CYAN => 0x1 | 0x2,
67         color::WHITE => 0x1 | 0x2 | 0x4,
68         _ => unreachable!(),
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,
88         _ => unreachable!(),
89     };
90
91     color | (bits & 0x8) // copy the hi-intensity bit
92 }
93
94 impl<T: Write + Send + 'static> WinConsole<T> {
95     fn apply(&mut self) {
96         let _unused = self.buf.flush();
97         let mut accum: WORD = 0;
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.
111             let out = GetStdHandle(-11i32 as DWORD);
112             SetConsoleTextAttribute(out, accum);
113         }
114     }
115
116     /// Returns `None` whenever the terminal cannot be created for some
117     /// reason.
118     pub fn new(out: T) -> io::Result<WinConsole<T>> {
119         let fg;
120         let bg;
121         unsafe {
122             let mut buffer_info = ::std::mem::uninitialized();
123             if GetConsoleScreenBufferInfo(GetStdHandle(-11i32 as DWORD), &mut buffer_info) != 0 {
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         }
131         Ok(WinConsole {
132             buf: out,
133             def_foreground: fg,
134             def_background: bg,
135             foreground: fg,
136             background: bg,
137         })
138     }
139 }
140
141 impl<T: Write> Write for WinConsole<T> {
142     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
143         self.buf.write(buf)
144     }
145
146     fn flush(&mut self) -> io::Result<()> {
147         self.buf.flush()
148     }
149 }
150
151 impl<T: Write + Send + 'static> Terminal for WinConsole<T> {
152     type Output = T;
153
154     fn fg(&mut self, color: color::Color) -> io::Result<bool> {
155         self.foreground = color;
156         self.apply();
157
158         Ok(true)
159     }
160
161     fn bg(&mut self, color: color::Color) -> io::Result<bool> {
162         self.background = color;
163         self.apply();
164
165         Ok(true)
166     }
167
168     fn attr(&mut self, attr: Attr) -> io::Result<bool> {
169         match attr {
170             Attr::ForegroundColor(f) => {
171                 self.foreground = f;
172                 self.apply();
173                 Ok(true)
174             }
175             Attr::BackgroundColor(b) => {
176                 self.background = b;
177                 self.apply();
178                 Ok(true)
179             }
180             _ => Ok(false),
181         }
182     }
183
184     fn supports_attr(&self, attr: Attr) -> bool {
185         // it claims support for underscore and reverse video, but I can't get
186         // it to do anything -cmr
187         match attr {
188             Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => true,
189             _ => false,
190         }
191     }
192
193     fn reset(&mut self) -> io::Result<bool> {
194         self.foreground = self.def_foreground;
195         self.background = self.def_background;
196         self.apply();
197
198         Ok(true)
199     }
200
201     fn get_ref<'a>(&'a self) -> &'a T {
202         &self.buf
203     }
204
205     fn get_mut<'a>(&'a mut self) -> &'a mut T {
206         &mut self.buf
207     }
208
209     fn into_inner(self) -> T
210         where Self: Sized
211     {
212         self.buf
213     }
214 }