summaryrefslogtreecommitdiffstats
path: root/library/test/src/term/win.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/test/src/term/win.rs')
-rw-r--r--library/test/src/term/win.rs170
1 files changed, 170 insertions, 0 deletions
diff --git a/library/test/src/term/win.rs b/library/test/src/term/win.rs
new file mode 100644
index 000000000..4bdbd6ee7
--- /dev/null
+++ b/library/test/src/term/win.rs
@@ -0,0 +1,170 @@
+//! Windows console handling
+
+// FIXME (#13400): this is only a tiny fraction of the Windows console api
+
+use std::io;
+use std::io::prelude::*;
+
+use super::color;
+use super::Terminal;
+
+/// A Terminal implementation that uses the Win32 Console API.
+pub(crate) struct WinConsole<T> {
+ 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<T: Write + Send + 'static> WinConsole<T> {
+ 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(crate) fn new(out: T) -> io::Result<WinConsole<T>> {
+ use std::mem::MaybeUninit;
+
+ let fg;
+ let bg;
+ unsafe {
+ let mut buffer_info = MaybeUninit::<CONSOLE_SCREEN_BUFFER_INFO>::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<T: Write> Write for WinConsole<T> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.buf.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.buf.flush()
+ }
+}
+
+impl<T: Write + Send + 'static> Terminal for WinConsole<T> {
+ fn fg(&mut self, color: color::Color) -> io::Result<bool> {
+ self.foreground = color;
+ self.apply();
+
+ Ok(true)
+ }
+
+ fn reset(&mut self) -> io::Result<bool> {
+ self.foreground = self.def_foreground;
+ self.background = self.def_background;
+ self.apply();
+
+ Ok(true)
+ }
+}