]> git.proxmox.com Git - rustc.git/blame - vendor/term/src/win.rs
Merge tag 'debian/1.52.1+dfsg1-1_exp2' into proxmox/buster
[rustc.git] / vendor / term / src / win.rs
CommitLineData
f20569fa
XL
1// Copyright 2013-2019 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
15use crate::color;
16use crate::Attr;
17use crate::Error;
18use crate::Result;
19use crate::Terminal;
20use std::io;
21use std::io::prelude::*;
22use std::ops::Deref;
23use std::ptr;
24
25use winapi::shared::minwindef::{DWORD, WORD};
26use winapi::um::consoleapi::{GetConsoleMode, SetConsoleMode};
27use winapi::um::fileapi::{CreateFileA, OPEN_EXISTING};
28use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
29use winapi::um::wincon::FillConsoleOutputAttribute;
30use winapi::um::wincon::{
31 FillConsoleOutputCharacterW, GetConsoleScreenBufferInfo, CONSOLE_SCREEN_BUFFER_INFO, COORD,
32};
33use winapi::um::wincon::{SetConsoleCursorPosition, SetConsoleTextAttribute};
34use winapi::um::wincon::{BACKGROUND_INTENSITY, ENABLE_VIRTUAL_TERMINAL_PROCESSING};
35use winapi::um::winnt::{FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE, HANDLE};
36
37/// Console info which can be used by a Terminal implementation
38/// which uses the Win32 Console API.
39pub struct WinConsoleInfo {
40 def_foreground: color::Color,
41 def_background: color::Color,
42 foreground: color::Color,
43 background: color::Color,
44 reverse: bool,
45 secure: bool,
46 standout: bool,
47}
48
49/// A Terminal implementation which uses the Win32 Console API.
50pub struct WinConsole<T> {
51 buf: T,
52 info: WinConsoleInfo,
53}
54
55fn color_to_bits(color: color::Color) -> u16 {
56 // magic numbers from mingw-w64's wincon.h
57
58 let bits = match color % 8 {
59 color::BLACK => 0,
60 color::BLUE => 0x1,
61 color::GREEN => 0x2,
62 color::RED => 0x4,
63 color::YELLOW => 0x2 | 0x4,
64 color::MAGENTA => 0x1 | 0x4,
65 color::CYAN => 0x1 | 0x2,
66 color::WHITE => 0x1 | 0x2 | 0x4,
67 _ => unreachable!(),
68 };
69
70 if color >= 8 {
71 bits | 0x8
72 } else {
73 bits
74 }
75}
76
77fn bits_to_color(bits: u16) -> color::Color {
78 let color = match bits & 0x7 {
79 0 => color::BLACK,
80 0x1 => color::BLUE,
81 0x2 => color::GREEN,
82 0x4 => color::RED,
83 0x6 => color::YELLOW,
84 0x5 => color::MAGENTA,
85 0x3 => color::CYAN,
86 0x7 => color::WHITE,
87 _ => unreachable!(),
88 };
89
90 color | (bits as u32 & 0x8) // copy the hi-intensity bit
91}
92
93struct HandleWrapper {
94 inner: HANDLE,
95}
96
97impl HandleWrapper {
98 fn new(h: HANDLE) -> HandleWrapper {
99 HandleWrapper { inner: h }
100 }
101}
102
103impl Drop for HandleWrapper {
104 fn drop(&mut self) {
105 if self.inner != INVALID_HANDLE_VALUE {
106 unsafe {
107 CloseHandle(self.inner);
108 }
109 }
110 }
111}
112
113impl Deref for HandleWrapper {
114 type Target = HANDLE;
115 fn deref(&self) -> &HANDLE {
116 &self.inner
117 }
118}
119
120/// Just get a handle to the current console buffer whatever it is
121fn conout() -> io::Result<HandleWrapper> {
122 let name = b"CONOUT$\0";
123 let handle = unsafe {
124 CreateFileA(
125 name.as_ptr() as *const i8,
126 GENERIC_READ | GENERIC_WRITE,
127 FILE_SHARE_WRITE,
128 ptr::null_mut(),
129 OPEN_EXISTING,
130 0,
131 ptr::null_mut(),
132 )
133 };
134 if handle == INVALID_HANDLE_VALUE {
135 Err(io::Error::last_os_error())
136 } else {
137 Ok(HandleWrapper::new(handle))
138 }
139}
140
141unsafe fn set_flag(handle: HANDLE, flag: DWORD) -> io::Result<()> {
142 let mut curr_mode: DWORD = 0;
143 if GetConsoleMode(handle, &mut curr_mode) == 0 {
144 return Err(io::Error::last_os_error());
145 }
146
147 if SetConsoleMode(handle, curr_mode | flag) == 0 {
148 return Err(io::Error::last_os_error());
149 }
150 return Ok(());
151}
152
153/// Check if console supports ansi codes (should succeed on Windows 10)
154pub fn supports_ansi() -> bool {
155 conout()
156 .and_then(|handle| unsafe { set_flag(*handle, ENABLE_VIRTUAL_TERMINAL_PROCESSING) })
157 .is_ok()
158}
159
160// This test will only pass if it is running in an actual console, probably
161#[test]
162fn test_conout() {
163 assert!(conout().is_ok())
164}
165
166#[rustversion::before(1.36)]
167unsafe fn get_console_screen_buffer_info(handle: HANDLE) -> io::Result<CONSOLE_SCREEN_BUFFER_INFO> {
168 let mut buffer_info = ::std::mem::uninitialized();
169 if GetConsoleScreenBufferInfo(handle, &mut buffer_info) == 0 {
170 Err(io::Error::last_os_error())
171 } else {
172 Ok(buffer_info)
173 }
174}
175#[rustversion::since(1.36)]
176unsafe fn get_console_screen_buffer_info(handle: HANDLE) -> io::Result<CONSOLE_SCREEN_BUFFER_INFO> {
177 let mut buffer_info = ::std::mem::MaybeUninit::uninit();
178 if GetConsoleScreenBufferInfo(handle, buffer_info.as_mut_ptr()) == 0 {
179 Err(io::Error::last_os_error())
180 } else {
181 Ok(buffer_info.assume_init())
182 }
183}
184
185// This test will only pass if it is running in an actual console, probably
186#[test]
187fn test_get_console_screen_buffer_info() {
188 let handle = conout().unwrap();
189 unsafe {
190 let buffer_info = get_console_screen_buffer_info(*handle);
191 assert!(buffer_info.is_ok());
192 }
193}
194
195impl WinConsoleInfo {
196 /// Returns `Err` whenever console info cannot be retrieved for some
197 /// reason.
198 pub fn from_env() -> io::Result<WinConsoleInfo> {
199 let fg;
200 let bg;
201 let handle = conout()?;
202 unsafe {
203 let buffer_info = get_console_screen_buffer_info(*handle)?;
204 fg = bits_to_color(buffer_info.wAttributes);
205 bg = bits_to_color(buffer_info.wAttributes >> 4);
206 }
207 Ok(WinConsoleInfo {
208 def_foreground: fg,
209 def_background: bg,
210 foreground: fg,
211 background: bg,
212 reverse: false,
213 secure: false,
214 standout: false,
215 })
216 }
217}
218
219impl<T: Write + Send> WinConsole<T> {
220 fn apply(&mut self) -> io::Result<()> {
221 let out = conout()?;
222 let _unused = self.buf.flush();
223
224 let (mut fg, bg) = if self.info.reverse {
225 (self.info.background, self.info.foreground)
226 } else {
227 (self.info.foreground, self.info.background)
228 };
229
230 if self.info.secure {
231 fg = bg;
232 }
233
234 let mut accum: WORD = 0;
235
236 accum |= color_to_bits(fg);
237 accum |= color_to_bits(bg) << 4;
238
239 if self.info.standout {
240 accum |= BACKGROUND_INTENSITY;
241 } else {
242 accum &= BACKGROUND_INTENSITY ^ 0xFF;
243 }
244
245 unsafe {
246 SetConsoleTextAttribute(*out, accum);
247 }
248 Ok(())
249 }
250
251 /// Create a new WinConsole with the given WinConsoleInfo and out
252 pub fn new_with_consoleinfo(out: T, info: WinConsoleInfo) -> WinConsole<T> {
253 WinConsole { buf: out, info }
254 }
255
256 /// Returns `Err` whenever the terminal cannot be created for some
257 /// reason.
258 pub fn new(out: T) -> io::Result<WinConsole<T>> {
259 let info = WinConsoleInfo::from_env()?;
260 Ok(Self::new_with_consoleinfo(out, info))
261 }
262}
263
264impl<T: Write> Write for WinConsole<T> {
265 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
266 self.buf.write(buf)
267 }
268
269 fn flush(&mut self) -> io::Result<()> {
270 self.buf.flush()
271 }
272}
273
274impl<T: Write + Send> Terminal for WinConsole<T> {
275 type Output = T;
276
277 fn fg(&mut self, color: color::Color) -> Result<()> {
278 self.info.foreground = color;
279 self.apply()?;
280
281 Ok(())
282 }
283
284 fn bg(&mut self, color: color::Color) -> Result<()> {
285 self.info.background = color;
286 self.apply()?;
287
288 Ok(())
289 }
290
291 fn attr(&mut self, attr: Attr) -> Result<()> {
292 match attr {
293 Attr::ForegroundColor(f) => {
294 self.info.foreground = f;
295 self.apply()?;
296 Ok(())
297 }
298 Attr::BackgroundColor(b) => {
299 self.info.background = b;
300 self.apply()?;
301 Ok(())
302 }
303 Attr::Reverse => {
304 self.info.reverse = true;
305 self.apply()?;
306 Ok(())
307 }
308 Attr::Secure => {
309 self.info.secure = true;
310 self.apply()?;
311 Ok(())
312 }
313 Attr::Standout(v) => {
314 self.info.standout = v;
315 self.apply()?;
316 Ok(())
317 }
318 _ => Err(Error::NotSupported),
319 }
320 }
321
322 fn supports_attr(&self, attr: Attr) -> bool {
323 match attr {
324 Attr::ForegroundColor(_)
325 | Attr::BackgroundColor(_)
326 | Attr::Standout(_)
327 | Attr::Reverse
328 | Attr::Secure => true,
329 _ => false,
330 }
331 }
332
333 fn reset(&mut self) -> Result<()> {
334 self.info.foreground = self.info.def_foreground;
335 self.info.background = self.info.def_background;
336 self.info.reverse = false;
337 self.info.secure = false;
338 self.info.standout = false;
339 self.apply()?;
340
341 Ok(())
342 }
343
344 fn supports_reset(&self) -> bool {
345 true
346 }
347
348 fn supports_color(&self) -> bool {
349 true
350 }
351
352 fn cursor_up(&mut self) -> Result<()> {
353 let _unused = self.buf.flush();
354 let handle = conout()?;
355 unsafe {
356 let buffer_info = get_console_screen_buffer_info(*handle)?;
357 let (x, y) = (
358 buffer_info.dwCursorPosition.X,
359 buffer_info.dwCursorPosition.Y,
360 );
361 if y == 0 {
362 // Even though this might want to be a CursorPositionInvalid, on Unix there
363 // is no checking to see if the cursor is already on the first line.
364 // I'm not sure what the ideal behavior is, but I think it'd be silly to have
365 // cursor_up fail in this case.
366 Ok(())
367 } else {
368 let pos = COORD { X: x, Y: y - 1 };
369 if SetConsoleCursorPosition(*handle, pos) != 0 {
370 Ok(())
371 } else {
372 Err(io::Error::last_os_error().into())
373 }
374 }
375 }
376 }
377
378 fn delete_line(&mut self) -> Result<()> {
379 let _unused = self.buf.flush();
380 let handle = conout()?;
381 unsafe {
382 let buffer_info = get_console_screen_buffer_info(*handle)?;
383 let pos = buffer_info.dwCursorPosition;
384 let size = buffer_info.dwSize;
385 let num = (size.X - pos.X) as DWORD;
386 let mut written = 0;
387 // 0x0020u16 is ' ' (space) in UTF-16 (same as ascii)
388 if FillConsoleOutputCharacterW(*handle, 0x0020, num, pos, &mut written) == 0 {
389 return Err(io::Error::last_os_error().into());
390 }
391 if FillConsoleOutputAttribute(*handle, 0, num, pos, &mut written) == 0 {
392 return Err(io::Error::last_os_error().into());
393 }
394 // Similar reasoning for not failing as in cursor_up -- it doesn't even make
395 // sense to
396 // me that these APIs could have written 0, unless the terminal is width zero.
397 Ok(())
398 }
399 }
400
401 fn carriage_return(&mut self) -> Result<()> {
402 let _unused = self.buf.flush();
403 let handle = conout()?;
404 unsafe {
405 let buffer_info = get_console_screen_buffer_info(*handle)?;
406 let COORD { X: x, Y: y } = buffer_info.dwCursorPosition;
407 if x == 0 {
408 Err(Error::CursorDestinationInvalid)
409 } else {
410 let pos = COORD { X: 0, Y: y };
411 if SetConsoleCursorPosition(*handle, pos) != 0 {
412 Ok(())
413 } else {
414 Err(io::Error::last_os_error().into())
415 }
416 }
417 }
418 }
419
420 fn get_ref<'a>(&'a self) -> &'a T {
421 &self.buf
422 }
423
424 fn get_mut<'a>(&'a mut self) -> &'a mut T {
425 &mut self.buf
426 }
427
428 fn into_inner(self) -> T
429 where
430 Self: Sized,
431 {
432 self.buf
433 }
434}