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