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