]> git.lizzy.rs Git - rust.git/blob - src/libterm/win.rs
e93b956dc7c835eaee3f11af0606a8bcf7437ba2
[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::old_io::IoResult;
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
89 impl<T: Writer+Send+'static> WinConsole<T> {
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> {
133     fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
134         self.buf.write_all(buf)
135     }
136
137     fn flush(&mut self) -> IoResult<()> {
138         self.buf.flush()
139     }
140 }
141
142 impl<T: Writer+Send+'static> Terminal<T> for WinConsole<T> {
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
195 impl<T: Writer+Send+'static> UnwrappableTerminal<T> for WinConsole<T> {
196     fn unwrap(self) -> T { self.buf }
197 }