1 //! Windows console handling
2
3 // FIXME (#13400): this is only a tiny fraction of the Windows console api
4
5 use std::io;
6 use std::io::prelude::*;
7
8 use super::color;
9 use super::Terminal;
10
11 /// A Terminal implementation that uses the Win32 Console API.
12 pub(crate) struct WinConsole<T> {
13 buf: T,
14 def_foreground: color::Color,
15 def_background: color::Color,
16 foreground: color::Color,
17 background: color::Color,
18 }
19
20 type SHORT = i16;
21 type WORD = u16;
22 type DWORD = u32;
23 type BOOL = i32;
24 type HANDLE = *mut u8;
25
26 #[allow(non_snake_case)]
27 #[repr(C)]
28 struct SMALL_RECT {
29 Left: SHORT,
30 Top: SHORT,
31 Right: SHORT,
32 Bottom: SHORT,
33 }
34
35 #[allow(non_snake_case)]
36 #[repr(C)]
37 struct COORD {
38 X: SHORT,
39 Y: SHORT,
40 }
41
42 #[allow(non_snake_case)]
43 #[repr(C)]
44 struct CONSOLE_SCREEN_BUFFER_INFO {
45 dwSize: COORD,
46 dwCursorPosition: COORD,
47 wAttributes: WORD,
48 srWindow: SMALL_RECT,
49 dwMaximumWindowSize: COORD,
50 }
51
52 #[allow(non_snake_case)]
53 #[link(name = "kernel32")]
54 extern "system" {
SetConsoleTextAttribute(handle: HANDLE, attr: WORD) -> BOOL55 fn SetConsoleTextAttribute(handle: HANDLE, attr: WORD) -> BOOL;
GetStdHandle(which: DWORD) -> HANDLE56 fn GetStdHandle(which: DWORD) -> HANDLE;
GetConsoleScreenBufferInfo(handle: HANDLE, info: *mut CONSOLE_SCREEN_BUFFER_INFO) -> BOOL57 fn GetConsoleScreenBufferInfo(handle: HANDLE, info: *mut CONSOLE_SCREEN_BUFFER_INFO) -> BOOL;
58 }
59
color_to_bits(color: color::Color) -> u1660 fn color_to_bits(color: color::Color) -> u16 {
61 // magic numbers from mingw-w64's wincon.h
62
63 let bits = match color % 8 {
64 color::BLACK => 0,
65 color::BLUE => 0x1,
66 color::GREEN => 0x2,
67 color::RED => 0x4,
68 color::YELLOW => 0x2 | 0x4,
69 color::MAGENTA => 0x1 | 0x4,
70 color::CYAN => 0x1 | 0x2,
71 color::WHITE => 0x1 | 0x2 | 0x4,
72 _ => unreachable!(),
73 };
74
75 if color >= 8 { bits | 0x8 } else { bits }
76 }
77
bits_to_color(bits: u16) -> color::Color78 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 | (u32::from(bits) & 0x8) // copy the hi-intensity bit
92 }
93
94 impl<T: Write + Send + 'static> WinConsole<T> {
apply(&mut self)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 // https://docs.microsoft.com/en-us/windows/console/getstdhandle
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
new(out: T) -> WinConsole<T>116 pub(crate) fn new(out: T) -> WinConsole<T> {
117 use std::mem::MaybeUninit;
118
119 let fg;
120 let bg;
121 unsafe {
122 let mut buffer_info = MaybeUninit::<CONSOLE_SCREEN_BUFFER_INFO>::uninit();
123 if GetConsoleScreenBufferInfo(GetStdHandle(-11i32 as DWORD), buffer_info.as_mut_ptr())
124 != 0
125 {
126 let buffer_info = buffer_info.assume_init();
127 fg = bits_to_color(buffer_info.wAttributes);
128 bg = bits_to_color(buffer_info.wAttributes >> 4);
129 } else {
130 fg = color::WHITE;
131 bg = color::BLACK;
132 }
133 }
134 WinConsole {
135 buf: out,
136 def_foreground: fg,
137 def_background: bg,
138 foreground: fg,
139 background: bg,
140 }
141 }
142 }
143
144 impl<T: Write> Write for WinConsole<T> {
write(&mut self, buf: &[u8]) -> io::Result<usize>145 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
146 self.buf.write(buf)
147 }
148
flush(&mut self) -> io::Result<()>149 fn flush(&mut self) -> io::Result<()> {
150 self.buf.flush()
151 }
152 }
153
154 impl<T: Write + Send + 'static> Terminal for WinConsole<T> {
fg(&mut self, color: color::Color) -> io::Result<bool>155 fn fg(&mut self, color: color::Color) -> io::Result<bool> {
156 self.foreground = color;
157 self.apply();
158
159 Ok(true)
160 }
161
reset(&mut self) -> io::Result<bool>162 fn reset(&mut self) -> io::Result<bool> {
163 self.foreground = self.def_foreground;
164 self.background = self.def_background;
165 self.apply();
166
167 Ok(true)
168 }
169 }
170