//! Windows console handling // FIXME (#13400): this is only a tiny fraction of the Windows console api use std::io; use std::io::prelude::*; use crate::color; use crate::Attr; use crate::Terminal; /// A Terminal implementation that uses the Win32 Console API. pub struct WinConsole { buf: T, def_foreground: color::Color, def_background: color::Color, foreground: color::Color, background: color::Color, } type SHORT = i16; type WORD = u16; type DWORD = u32; type BOOL = i32; type HANDLE = *mut u8; #[allow(non_snake_case)] #[repr(C)] struct SMALL_RECT { Left: SHORT, Top: SHORT, Right: SHORT, Bottom: SHORT, } #[allow(non_snake_case)] #[repr(C)] struct COORD { X: SHORT, Y: SHORT, } #[allow(non_snake_case)] #[repr(C)] struct CONSOLE_SCREEN_BUFFER_INFO { dwSize: COORD, dwCursorPosition: COORD, wAttributes: WORD, srWindow: SMALL_RECT, dwMaximumWindowSize: COORD, } #[allow(non_snake_case)] #[link(name = "kernel32")] extern "system" { fn SetConsoleTextAttribute(handle: HANDLE, attr: WORD) -> BOOL; fn GetStdHandle(which: DWORD) -> HANDLE; fn GetConsoleScreenBufferInfo(handle: HANDLE, info: *mut CONSOLE_SCREEN_BUFFER_INFO) -> BOOL; } fn color_to_bits(color: color::Color) -> u16 { // magic numbers from mingw-w64's wincon.h let bits = match color % 8 { color::BLACK => 0, color::BLUE => 0x1, color::GREEN => 0x2, color::RED => 0x4, color::YELLOW => 0x2 | 0x4, color::MAGENTA => 0x1 | 0x4, color::CYAN => 0x1 | 0x2, color::WHITE => 0x1 | 0x2 | 0x4, _ => unreachable!(), }; if color >= 8 { bits | 0x8 } else { bits } } fn bits_to_color(bits: u16) -> color::Color { let color = match bits & 0x7 { 0 => color::BLACK, 0x1 => color::BLUE, 0x2 => color::GREEN, 0x4 => color::RED, 0x6 => color::YELLOW, 0x5 => color::MAGENTA, 0x3 => color::CYAN, 0x7 => color::WHITE, _ => unreachable!(), }; color | (u32::from(bits) & 0x8) // copy the hi-intensity bit } impl WinConsole { fn apply(&mut self) { let _unused = self.buf.flush(); let mut accum: WORD = 0; accum |= color_to_bits(self.foreground); accum |= color_to_bits(self.background) << 4; unsafe { // Magic -11 means stdout, from // https://docs.microsoft.com/en-us/windows/console/getstdhandle // // You may be wondering, "but what about stderr?", and the answer // to that is that setting terminal attributes on the stdout // handle also sets them for stderr, since they go to the same // terminal! Admittedly, this is fragile, since stderr could be // redirected to a different console. This is good enough for // rustc though. See #13400. let out = GetStdHandle(-11i32 as DWORD); SetConsoleTextAttribute(out, accum); } } /// Returns `None` whenever the terminal cannot be created for some reason. pub fn new(out: T) -> io::Result> { use std::mem::MaybeUninit; let fg; let bg; unsafe { let mut buffer_info = MaybeUninit::::uninit(); if GetConsoleScreenBufferInfo(GetStdHandle(-11i32 as DWORD), buffer_info.as_mut_ptr()) != 0 { let buffer_info = buffer_info.assume_init(); fg = bits_to_color(buffer_info.wAttributes); bg = bits_to_color(buffer_info.wAttributes >> 4); } else { fg = color::WHITE; bg = color::BLACK; } } Ok(WinConsole { buf: out, def_foreground: fg, def_background: bg, foreground: fg, background: bg, }) } } impl Write for WinConsole { fn write(&mut self, buf: &[u8]) -> io::Result { self.buf.write(buf) } fn flush(&mut self) -> io::Result<()> { self.buf.flush() } } impl Terminal for WinConsole { type Output = T; fn fg(&mut self, color: color::Color) -> io::Result { self.foreground = color; self.apply(); Ok(true) } fn bg(&mut self, color: color::Color) -> io::Result { self.background = color; self.apply(); Ok(true) } fn attr(&mut self, attr: Attr) -> io::Result { match attr { Attr::ForegroundColor(f) => { self.foreground = f; self.apply(); Ok(true) } Attr::BackgroundColor(b) => { self.background = b; self.apply(); Ok(true) } _ => Ok(false), } } fn supports_attr(&self, attr: Attr) -> bool { // it claims support for underscore and reverse video, but I can't get // it to do anything -cmr match attr { Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => true, _ => false, } } fn reset(&mut self) -> io::Result { self.foreground = self.def_foreground; self.background = self.def_background; self.apply(); Ok(true) } fn get_ref(&self) -> &T { &self.buf } fn get_mut(&mut self) -> &mut T { &mut self.buf } fn into_inner(self) -> T where Self: Sized, { self.buf } }