diff options
author | Valentin Popov <valentin@popov.link> | 2024-07-19 15:37:58 +0300 |
---|---|---|
committer | Valentin Popov <valentin@popov.link> | 2024-07-19 15:37:58 +0300 |
commit | a990de90fe41456a23e58bd087d2f107d321f3a1 (patch) | |
tree | 15afc392522a9e85dc3332235e311b7d39352ea9 /vendor/console/src/windows_term | |
parent | 3d48cd3f81164bbfc1a755dc1d4a9a02f98c8ddd (diff) | |
download | fparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.tar.xz fparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.zip |
Deleted vendor folder
Diffstat (limited to 'vendor/console/src/windows_term')
-rw-r--r-- | vendor/console/src/windows_term/colors.rs | 451 | ||||
-rw-r--r-- | vendor/console/src/windows_term/mod.rs | 563 |
2 files changed, 0 insertions, 1014 deletions
diff --git a/vendor/console/src/windows_term/colors.rs b/vendor/console/src/windows_term/colors.rs deleted file mode 100644 index dc8209d..0000000 --- a/vendor/console/src/windows_term/colors.rs +++ /dev/null @@ -1,451 +0,0 @@ -use std::io; -use std::mem; -use std::os::windows::io::AsRawHandle; -use std::str::Bytes; - -use windows_sys::Win32::Foundation::HANDLE; -use windows_sys::Win32::System::Console::{ - GetConsoleScreenBufferInfo, SetConsoleTextAttribute, CONSOLE_SCREEN_BUFFER_INFO, - FOREGROUND_BLUE as FG_BLUE, FOREGROUND_GREEN as FG_GREEN, FOREGROUND_INTENSITY as FG_INTENSITY, - FOREGROUND_RED as FG_RED, -}; - -use crate::Term; - -type WORD = u16; - -const FG_CYAN: WORD = FG_BLUE | FG_GREEN; -const FG_MAGENTA: WORD = FG_BLUE | FG_RED; -const FG_YELLOW: WORD = FG_GREEN | FG_RED; -const FG_WHITE: WORD = FG_BLUE | FG_GREEN | FG_RED; - -/// Query the given handle for information about the console's screen buffer. -/// -/// The given handle should represent a console. Otherwise, an error is -/// returned. -/// -/// This corresponds to calling [`GetConsoleScreenBufferInfo`]. -/// -/// [`GetConsoleScreenBufferInfo`]: https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo -pub fn screen_buffer_info(h: HANDLE) -> io::Result<ScreenBufferInfo> { - unsafe { - let mut info: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed(); - let rc = GetConsoleScreenBufferInfo(h, &mut info); - if rc == 0 { - return Err(io::Error::last_os_error()); - } - Ok(ScreenBufferInfo(info)) - } -} - -/// Set the text attributes of the console represented by the given handle. -/// -/// This corresponds to calling [`SetConsoleTextAttribute`]. -/// -/// [`SetConsoleTextAttribute`]: https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute -pub fn set_text_attributes(h: HANDLE, attributes: u16) -> io::Result<()> { - if unsafe { SetConsoleTextAttribute(h, attributes) } == 0 { - Err(io::Error::last_os_error()) - } else { - Ok(()) - } -} - -/// Represents console screen buffer information such as size, cursor position -/// and styling attributes. -/// -/// This wraps a [`CONSOLE_SCREEN_BUFFER_INFO`]. -/// -/// [`CONSOLE_SCREEN_BUFFER_INFO`]: https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str -#[derive(Clone)] -pub struct ScreenBufferInfo(CONSOLE_SCREEN_BUFFER_INFO); - -impl ScreenBufferInfo { - /// Returns the character attributes associated with this console. - /// - /// This corresponds to `wAttributes`. - /// - /// See [`char info`] for more details. - /// - /// [`char info`]: https://docs.microsoft.com/en-us/windows/console/char-info-str - pub fn attributes(&self) -> u16 { - self.0.wAttributes - } -} - -/// A Windows console. -/// -/// This represents a very limited set of functionality available to a Windows -/// console. In particular, it can only change text attributes such as color -/// and intensity. This may grow over time. If you need more routines, please -/// file an issue and/or PR. -/// -/// There is no way to "write" to this console. Simply write to -/// stdout or stderr instead, while interleaving instructions to the console -/// to change text attributes. -/// -/// A common pitfall when using a console is to forget to flush writes to -/// stdout before setting new text attributes. -#[derive(Debug)] -pub struct Console { - kind: HandleKind, - start_attr: TextAttributes, - cur_attr: TextAttributes, -} - -#[derive(Clone, Copy, Debug)] -enum HandleKind { - Stdout, - Stderr, -} - -impl HandleKind { - fn handle(&self) -> HANDLE { - match *self { - HandleKind::Stdout => io::stdout().as_raw_handle() as HANDLE, - HandleKind::Stderr => io::stderr().as_raw_handle() as HANDLE, - } - } -} - -impl Console { - /// Get a console for a standard I/O stream. - fn create_for_stream(kind: HandleKind) -> io::Result<Console> { - let h = kind.handle(); - let info = screen_buffer_info(h)?; - let attr = TextAttributes::from_word(info.attributes()); - Ok(Console { - kind: kind, - start_attr: attr, - cur_attr: attr, - }) - } - - /// Create a new Console to stdout. - /// - /// If there was a problem creating the console, then an error is returned. - pub fn stdout() -> io::Result<Console> { - Self::create_for_stream(HandleKind::Stdout) - } - - /// Create a new Console to stderr. - /// - /// If there was a problem creating the console, then an error is returned. - pub fn stderr() -> io::Result<Console> { - Self::create_for_stream(HandleKind::Stderr) - } - - /// Applies the current text attributes. - fn set(&mut self) -> io::Result<()> { - set_text_attributes(self.kind.handle(), self.cur_attr.to_word()) - } - - /// Apply the given intensity and color attributes to the console - /// foreground. - /// - /// If there was a problem setting attributes on the console, then an error - /// is returned. - pub fn fg(&mut self, intense: Intense, color: Color) -> io::Result<()> { - self.cur_attr.fg_color = color; - self.cur_attr.fg_intense = intense; - self.set() - } - - /// Apply the given intensity and color attributes to the console - /// background. - /// - /// If there was a problem setting attributes on the console, then an error - /// is returned. - pub fn bg(&mut self, intense: Intense, color: Color) -> io::Result<()> { - self.cur_attr.bg_color = color; - self.cur_attr.bg_intense = intense; - self.set() - } - - /// Reset the console text attributes to their original settings. - /// - /// The original settings correspond to the text attributes on the console - /// when this `Console` value was created. - /// - /// If there was a problem setting attributes on the console, then an error - /// is returned. - pub fn reset(&mut self) -> io::Result<()> { - self.cur_attr = self.start_attr; - self.set() - } -} - -/// A representation of text attributes for the Windows console. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -struct TextAttributes { - fg_color: Color, - fg_intense: Intense, - bg_color: Color, - bg_intense: Intense, -} - -impl TextAttributes { - fn to_word(&self) -> WORD { - let mut w = 0; - w |= self.fg_color.to_fg(); - w |= self.fg_intense.to_fg(); - w |= self.bg_color.to_bg(); - w |= self.bg_intense.to_bg(); - w - } - - fn from_word(word: WORD) -> TextAttributes { - TextAttributes { - fg_color: Color::from_fg(word), - fg_intense: Intense::from_fg(word), - bg_color: Color::from_bg(word), - bg_intense: Intense::from_bg(word), - } - } -} - -/// Whether to use intense colors or not. -#[allow(missing_docs)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum Intense { - Yes, - No, -} - -impl Intense { - fn to_bg(&self) -> WORD { - self.to_fg() << 4 - } - - fn from_bg(word: WORD) -> Intense { - Intense::from_fg(word >> 4) - } - - fn to_fg(&self) -> WORD { - match *self { - Intense::No => 0, - Intense::Yes => FG_INTENSITY, - } - } - - fn from_fg(word: WORD) -> Intense { - if word & FG_INTENSITY > 0 { - Intense::Yes - } else { - Intense::No - } - } -} - -/// The set of available colors for use with a Windows console. -#[allow(missing_docs)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum Color { - Black, - Blue, - Green, - Red, - Cyan, - Magenta, - Yellow, - White, -} - -impl Color { - fn to_bg(&self) -> WORD { - self.to_fg() << 4 - } - - fn from_bg(word: WORD) -> Color { - Color::from_fg(word >> 4) - } - - fn to_fg(&self) -> WORD { - match *self { - Color::Black => 0, - Color::Blue => FG_BLUE, - Color::Green => FG_GREEN, - Color::Red => FG_RED, - Color::Cyan => FG_CYAN, - Color::Magenta => FG_MAGENTA, - Color::Yellow => FG_YELLOW, - Color::White => FG_WHITE, - } - } - - fn from_fg(word: WORD) -> Color { - match word & 0b111 { - FG_BLUE => Color::Blue, - FG_GREEN => Color::Green, - FG_RED => Color::Red, - FG_CYAN => Color::Cyan, - FG_MAGENTA => Color::Magenta, - FG_YELLOW => Color::Yellow, - FG_WHITE => Color::White, - _ => Color::Black, - } - } -} - -pub fn console_colors(out: &Term, mut con: Console, bytes: &[u8]) -> io::Result<()> { - use crate::ansi::AnsiCodeIterator; - use std::str::from_utf8; - - let s = from_utf8(bytes).expect("data to be printed is not an ansi string"); - let mut iter = AnsiCodeIterator::new(s); - - while !iter.rest_slice().is_empty() { - if let Some((part, is_esc)) = iter.next() { - if !is_esc { - out.write_through_common(part.as_bytes())?; - } else if part == "\x1b[0m" { - con.reset()?; - } else if let Some((intense, color, fg_bg)) = driver(parse_color, part) { - match fg_bg { - FgBg::Foreground => con.fg(intense, color), - FgBg::Background => con.bg(intense, color), - }?; - } else if driver(parse_attr, part).is_none() { - out.write_through_common(part.as_bytes())?; - } - } - } - - Ok(()) -} - -#[derive(Debug, PartialEq, Eq)] -enum FgBg { - Foreground, - Background, -} - -impl FgBg { - fn new(byte: u8) -> Option<Self> { - match byte { - b'3' => Some(Self::Foreground), - b'4' => Some(Self::Background), - _ => None, - } - } -} - -fn driver<Out>(parse: fn(Bytes<'_>) -> Option<Out>, part: &str) -> Option<Out> { - let mut bytes = part.bytes(); - - loop { - while bytes.next()? != b'\x1b' {} - - if let ret @ Some(_) = (parse)(bytes.clone()) { - return ret; - } - } -} - -// `driver(parse_color, s)` parses the equivalent of the regex -// \x1b\[(3|4)8;5;(8|9|1[0-5])m -// for intense or -// \x1b\[(3|4)([0-7])m -// for normal -fn parse_color(mut bytes: Bytes<'_>) -> Option<(Intense, Color, FgBg)> { - parse_prefix(&mut bytes)?; - - let fg_bg = FgBg::new(bytes.next()?)?; - let (intense, color) = match bytes.next()? { - b @ b'0'..=b'7' => (Intense::No, normal_color_ansi_from_byte(b)?), - b'8' => { - if &[bytes.next()?, bytes.next()?, bytes.next()?] != b";5;" { - return None; - } - (Intense::Yes, parse_intense_color_ansi(&mut bytes)?) - } - _ => return None, - }; - - parse_suffix(&mut bytes)?; - Some((intense, color, fg_bg)) -} - -// `driver(parse_attr, s)` parses the equivalent of the regex -// \x1b\[([1-8])m -fn parse_attr(mut bytes: Bytes<'_>) -> Option<u8> { - parse_prefix(&mut bytes)?; - let attr = match bytes.next()? { - attr @ b'1'..=b'8' => attr, - _ => return None, - }; - parse_suffix(&mut bytes)?; - Some(attr) -} - -fn parse_prefix(bytes: &mut Bytes<'_>) -> Option<()> { - if bytes.next()? == b'[' { - Some(()) - } else { - None - } -} - -fn parse_intense_color_ansi(bytes: &mut Bytes<'_>) -> Option<Color> { - let color = match bytes.next()? { - b'8' => Color::Black, - b'9' => Color::Red, - b'1' => match bytes.next()? { - b'0' => Color::Green, - b'1' => Color::Yellow, - b'2' => Color::Blue, - b'3' => Color::Magenta, - b'4' => Color::Cyan, - b'5' => Color::White, - _ => return None, - }, - _ => return None, - }; - Some(color) -} - -fn normal_color_ansi_from_byte(b: u8) -> Option<Color> { - let color = match b { - b'0' => Color::Black, - b'1' => Color::Red, - b'2' => Color::Green, - b'3' => Color::Yellow, - b'4' => Color::Blue, - b'5' => Color::Magenta, - b'6' => Color::Cyan, - b'7' => Color::White, - _ => return None, - }; - Some(color) -} - -fn parse_suffix(bytes: &mut Bytes<'_>) -> Option<()> { - if bytes.next()? == b'm' { - Some(()) - } else { - None - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn color_parsing() { - let intense_color = "leading bytes \x1b[38;5;10m trailing bytes"; - let parsed = driver(parse_color, intense_color).unwrap(); - assert_eq!(parsed, (Intense::Yes, Color::Green, FgBg::Foreground)); - - let normal_color = "leading bytes \x1b[40m trailing bytes"; - let parsed = driver(parse_color, normal_color).unwrap(); - assert_eq!(parsed, (Intense::No, Color::Black, FgBg::Background)); - } - - #[test] - fn attr_parsing() { - let attr = "leading bytes \x1b[1m trailing bytes"; - let parsed = driver(parse_attr, attr).unwrap(); - assert_eq!(parsed, b'1'); - } -} diff --git a/vendor/console/src/windows_term/mod.rs b/vendor/console/src/windows_term/mod.rs deleted file mode 100644 index c4ec193..0000000 --- a/vendor/console/src/windows_term/mod.rs +++ /dev/null @@ -1,563 +0,0 @@ -use std::cmp; -use std::env; -use std::ffi::OsStr; -use std::fmt::Display; -use std::io; -use std::iter::once; -use std::mem; -use std::os::raw::c_void; -use std::os::windows::ffi::OsStrExt; -use std::os::windows::io::AsRawHandle; -use std::slice; -use std::{char, mem::MaybeUninit}; - -use encode_unicode::error::InvalidUtf16Tuple; -use encode_unicode::CharExt; -use windows_sys::Win32::Foundation::{CHAR, HANDLE, INVALID_HANDLE_VALUE, MAX_PATH}; -use windows_sys::Win32::Storage::FileSystem::{ - FileNameInfo, GetFileInformationByHandleEx, FILE_NAME_INFO, -}; -use windows_sys::Win32::System::Console::{ - FillConsoleOutputAttribute, FillConsoleOutputCharacterA, GetConsoleCursorInfo, GetConsoleMode, - GetConsoleScreenBufferInfo, GetNumberOfConsoleInputEvents, GetStdHandle, ReadConsoleInputW, - SetConsoleCursorInfo, SetConsoleCursorPosition, SetConsoleMode, SetConsoleTitleW, - CONSOLE_CURSOR_INFO, CONSOLE_SCREEN_BUFFER_INFO, COORD, INPUT_RECORD, KEY_EVENT, - KEY_EVENT_RECORD, STD_ERROR_HANDLE, STD_HANDLE, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, -}; -use windows_sys::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY; - -use crate::common_term; -use crate::kb::Key; -use crate::term::{Term, TermTarget}; - -#[cfg(feature = "windows-console-colors")] -mod colors; - -#[cfg(feature = "windows-console-colors")] -pub use self::colors::*; - -const ENABLE_VIRTUAL_TERMINAL_PROCESSING: u32 = 0x4; -pub const DEFAULT_WIDTH: u16 = 79; - -pub fn as_handle(term: &Term) -> HANDLE { - // convert between windows_sys::Win32::Foundation::HANDLE and std::os::windows::raw::HANDLE - term.as_raw_handle() as HANDLE -} - -pub fn is_a_terminal(out: &Term) -> bool { - let (fd, others) = match out.target() { - TermTarget::Stdout => (STD_OUTPUT_HANDLE, [STD_INPUT_HANDLE, STD_ERROR_HANDLE]), - TermTarget::Stderr => (STD_ERROR_HANDLE, [STD_INPUT_HANDLE, STD_OUTPUT_HANDLE]), - }; - - if unsafe { console_on_any(&[fd]) } { - // False positives aren't possible. If we got a console then - // we definitely have a tty on stdin. - return true; - } - - // At this point, we *could* have a false negative. We can determine that - // this is true negative if we can detect the presence of a console on - // any of the other streams. If another stream has a console, then we know - // we're in a Windows console and can therefore trust the negative. - if unsafe { console_on_any(&others) } { - return false; - } - - msys_tty_on(out) -} - -pub fn is_a_color_terminal(out: &Term) -> bool { - if !is_a_terminal(out) { - return false; - } - if msys_tty_on(out) { - return match env::var("TERM") { - Ok(term) => term != "dumb", - Err(_) => true, - }; - } - enable_ansi_on(out) -} - -fn enable_ansi_on(out: &Term) -> bool { - unsafe { - let handle = as_handle(out); - - let mut dw_mode = 0; - if GetConsoleMode(handle, &mut dw_mode) == 0 { - return false; - } - - dw_mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - if SetConsoleMode(handle, dw_mode) == 0 { - return false; - } - - true - } -} - -unsafe fn console_on_any(fds: &[STD_HANDLE]) -> bool { - for &fd in fds { - let mut out = 0; - let handle = GetStdHandle(fd); - if GetConsoleMode(handle, &mut out) != 0 { - return true; - } - } - false -} - -pub fn terminal_size(out: &Term) -> Option<(u16, u16)> { - use windows_sys::Win32::System::Console::SMALL_RECT; - - // convert between windows_sys::Win32::Foundation::HANDLE and std::os::windows::raw::HANDLE - let handle = out.as_raw_handle(); - let hand = handle as windows_sys::Win32::Foundation::HANDLE; - - if hand == INVALID_HANDLE_VALUE { - return None; - } - - let zc = COORD { X: 0, Y: 0 }; - let mut csbi = CONSOLE_SCREEN_BUFFER_INFO { - dwSize: zc, - dwCursorPosition: zc, - wAttributes: 0, - srWindow: SMALL_RECT { - Left: 0, - Top: 0, - Right: 0, - Bottom: 0, - }, - dwMaximumWindowSize: zc, - }; - if unsafe { GetConsoleScreenBufferInfo(hand, &mut csbi) } == 0 { - return None; - } - - let rows = (csbi.srWindow.Bottom - csbi.srWindow.Top + 1) as u16; - let columns = (csbi.srWindow.Right - csbi.srWindow.Left + 1) as u16; - - Some((rows, columns)) -} - -pub fn move_cursor_to(out: &Term, x: usize, y: usize) -> io::Result<()> { - if out.is_msys_tty { - return common_term::move_cursor_to(out, x, y); - } - if let Some((hand, _)) = get_console_screen_buffer_info(as_handle(out)) { - unsafe { - SetConsoleCursorPosition( - hand, - COORD { - X: x as i16, - Y: y as i16, - }, - ); - } - } - Ok(()) -} - -pub fn move_cursor_up(out: &Term, n: usize) -> io::Result<()> { - if out.is_msys_tty { - return common_term::move_cursor_up(out, n); - } - - if let Some((_, csbi)) = get_console_screen_buffer_info(as_handle(out)) { - move_cursor_to(out, 0, csbi.dwCursorPosition.Y as usize - n)?; - } - Ok(()) -} - -pub fn move_cursor_down(out: &Term, n: usize) -> io::Result<()> { - if out.is_msys_tty { - return common_term::move_cursor_down(out, n); - } - - if let Some((_, csbi)) = get_console_screen_buffer_info(as_handle(out)) { - move_cursor_to(out, 0, csbi.dwCursorPosition.Y as usize + n)?; - } - Ok(()) -} - -pub fn move_cursor_left(out: &Term, n: usize) -> io::Result<()> { - if out.is_msys_tty { - return common_term::move_cursor_left(out, n); - } - - if let Some((_, csbi)) = get_console_screen_buffer_info(as_handle(out)) { - move_cursor_to( - out, - csbi.dwCursorPosition.X as usize - n, - csbi.dwCursorPosition.Y as usize, - )?; - } - Ok(()) -} - -pub fn move_cursor_right(out: &Term, n: usize) -> io::Result<()> { - if out.is_msys_tty { - return common_term::move_cursor_right(out, n); - } - - if let Some((_, csbi)) = get_console_screen_buffer_info(as_handle(out)) { - move_cursor_to( - out, - csbi.dwCursorPosition.X as usize + n, - csbi.dwCursorPosition.Y as usize, - )?; - } - Ok(()) -} - -pub fn clear_line(out: &Term) -> io::Result<()> { - if out.is_msys_tty { - return common_term::clear_line(out); - } - if let Some((hand, csbi)) = get_console_screen_buffer_info(as_handle(out)) { - unsafe { - let width = csbi.srWindow.Right - csbi.srWindow.Left; - let pos = COORD { - X: 0, - Y: csbi.dwCursorPosition.Y, - }; - let mut written = 0; - FillConsoleOutputCharacterA(hand, b' ' as CHAR, width as u32, pos, &mut written); - FillConsoleOutputAttribute(hand, csbi.wAttributes, width as u32, pos, &mut written); - SetConsoleCursorPosition(hand, pos); - } - } - Ok(()) -} - -pub fn clear_chars(out: &Term, n: usize) -> io::Result<()> { - if out.is_msys_tty { - return common_term::clear_chars(out, n); - } - if let Some((hand, csbi)) = get_console_screen_buffer_info(as_handle(out)) { - unsafe { - let width = cmp::min(csbi.dwCursorPosition.X, n as i16); - let pos = COORD { - X: csbi.dwCursorPosition.X - width, - Y: csbi.dwCursorPosition.Y, - }; - let mut written = 0; - FillConsoleOutputCharacterA(hand, b' ' as CHAR, width as u32, pos, &mut written); - FillConsoleOutputAttribute(hand, csbi.wAttributes, width as u32, pos, &mut written); - SetConsoleCursorPosition(hand, pos); - } - } - Ok(()) -} - -pub fn clear_screen(out: &Term) -> io::Result<()> { - if out.is_msys_tty { - return common_term::clear_screen(out); - } - if let Some((hand, csbi)) = get_console_screen_buffer_info(as_handle(out)) { - unsafe { - let cells = csbi.dwSize.X as u32 * csbi.dwSize.Y as u32; // as u32, or else this causes stack overflows. - let pos = COORD { X: 0, Y: 0 }; - let mut written = 0; - FillConsoleOutputCharacterA(hand, b' ' as CHAR, cells, pos, &mut written); // cells as u32 no longer needed. - FillConsoleOutputAttribute(hand, csbi.wAttributes, cells, pos, &mut written); - SetConsoleCursorPosition(hand, pos); - } - } - Ok(()) -} - -pub fn clear_to_end_of_screen(out: &Term) -> io::Result<()> { - if out.is_msys_tty { - return common_term::clear_to_end_of_screen(out); - } - if let Some((hand, csbi)) = get_console_screen_buffer_info(as_handle(out)) { - unsafe { - let bottom = csbi.srWindow.Right as u32 * csbi.srWindow.Bottom as u32; - let cells = bottom - (csbi.dwCursorPosition.X as u32 * csbi.dwCursorPosition.Y as u32); // as u32, or else this causes stack overflows. - let pos = COORD { - X: 0, - Y: csbi.dwCursorPosition.Y, - }; - let mut written = 0; - FillConsoleOutputCharacterA(hand, b' ' as CHAR, cells, pos, &mut written); // cells as u32 no longer needed. - FillConsoleOutputAttribute(hand, csbi.wAttributes, cells, pos, &mut written); - SetConsoleCursorPosition(hand, pos); - } - } - Ok(()) -} - -pub fn show_cursor(out: &Term) -> io::Result<()> { - if out.is_msys_tty { - return common_term::show_cursor(out); - } - if let Some((hand, mut cci)) = get_console_cursor_info(as_handle(out)) { - unsafe { - cci.bVisible = 1; - SetConsoleCursorInfo(hand, &cci); - } - } - Ok(()) -} - -pub fn hide_cursor(out: &Term) -> io::Result<()> { - if out.is_msys_tty { - return common_term::hide_cursor(out); - } - if let Some((hand, mut cci)) = get_console_cursor_info(as_handle(out)) { - unsafe { - cci.bVisible = 0; - SetConsoleCursorInfo(hand, &cci); - } - } - Ok(()) -} - -fn get_console_screen_buffer_info(hand: HANDLE) -> Option<(HANDLE, CONSOLE_SCREEN_BUFFER_INFO)> { - let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = unsafe { mem::zeroed() }; - match unsafe { GetConsoleScreenBufferInfo(hand, &mut csbi) } { - 0 => None, - _ => Some((hand, csbi)), - } -} - -fn get_console_cursor_info(hand: HANDLE) -> Option<(HANDLE, CONSOLE_CURSOR_INFO)> { - let mut cci: CONSOLE_CURSOR_INFO = unsafe { mem::zeroed() }; - match unsafe { GetConsoleCursorInfo(hand, &mut cci) } { - 0 => None, - _ => Some((hand, cci)), - } -} - -pub fn key_from_key_code(code: VIRTUAL_KEY) -> Key { - use windows_sys::Win32::UI::Input::KeyboardAndMouse; - - match code { - KeyboardAndMouse::VK_LEFT => Key::ArrowLeft, - KeyboardAndMouse::VK_RIGHT => Key::ArrowRight, - KeyboardAndMouse::VK_UP => Key::ArrowUp, - KeyboardAndMouse::VK_DOWN => Key::ArrowDown, - KeyboardAndMouse::VK_RETURN => Key::Enter, - KeyboardAndMouse::VK_ESCAPE => Key::Escape, - KeyboardAndMouse::VK_BACK => Key::Backspace, - KeyboardAndMouse::VK_TAB => Key::Tab, - KeyboardAndMouse::VK_HOME => Key::Home, - KeyboardAndMouse::VK_END => Key::End, - KeyboardAndMouse::VK_DELETE => Key::Del, - KeyboardAndMouse::VK_SHIFT => Key::Shift, - KeyboardAndMouse::VK_MENU => Key::Alt, - _ => Key::Unknown, - } -} - -pub fn read_secure() -> io::Result<String> { - let mut rv = String::new(); - loop { - match read_single_key()? { - Key::Enter => { - break; - } - Key::Char('\x08') => { - if !rv.is_empty() { - let new_len = rv.len() - 1; - rv.truncate(new_len); - } - } - Key::Char(c) => { - rv.push(c); - } - _ => {} - } - } - Ok(rv) -} - -pub fn read_single_key() -> io::Result<Key> { - let key_event = read_key_event()?; - - let unicode_char = unsafe { key_event.uChar.UnicodeChar }; - if unicode_char == 0 { - Ok(key_from_key_code(key_event.wVirtualKeyCode)) - } else { - // This is a unicode character, in utf-16. Try to decode it by itself. - match char::from_utf16_tuple((unicode_char, None)) { - Ok(c) => { - // Maintain backward compatibility. The previous implementation (_getwch()) would return - // a special keycode for `Enter`, while ReadConsoleInputW() prefers to use '\r'. - if c == '\r' { - Ok(Key::Enter) - } else if c == '\x08' { - Ok(Key::Backspace) - } else if c == '\x1B' { - Ok(Key::Escape) - } else { - Ok(Key::Char(c)) - } - } - // This is part of a surrogate pair. Try to read the second half. - Err(InvalidUtf16Tuple::MissingSecond) => { - // Confirm that there is a next character to read. - if get_key_event_count()? == 0 { - let message = format!( - "Read invlid utf16 {}: {}", - unicode_char, - InvalidUtf16Tuple::MissingSecond - ); - return Err(io::Error::new(io::ErrorKind::InvalidData, message)); - } - - // Read the next character. - let next_event = read_key_event()?; - let next_surrogate = unsafe { next_event.uChar.UnicodeChar }; - - // Attempt to decode it. - match char::from_utf16_tuple((unicode_char, Some(next_surrogate))) { - Ok(c) => Ok(Key::Char(c)), - - // Return an InvalidData error. This is the recommended value for UTF-related I/O errors. - // (This error is given when reading a non-UTF8 file into a String, for example.) - Err(e) => { - let message = format!( - "Read invalid surrogate pair ({}, {}): {}", - unicode_char, next_surrogate, e - ); - Err(io::Error::new(io::ErrorKind::InvalidData, message)) - } - } - } - - // Return an InvalidData error. This is the recommended value for UTF-related I/O errors. - // (This error is given when reading a non-UTF8 file into a String, for example.) - Err(e) => { - let message = format!("Read invalid utf16 {}: {}", unicode_char, e); - Err(io::Error::new(io::ErrorKind::InvalidData, message)) - } - } - } -} - -fn get_stdin_handle() -> io::Result<HANDLE> { - let handle = unsafe { GetStdHandle(STD_INPUT_HANDLE) }; - if handle == INVALID_HANDLE_VALUE { - Err(io::Error::last_os_error()) - } else { - Ok(handle) - } -} - -/// Get the number of pending events in the ReadConsoleInput queue. Note that while -/// these aren't necessarily key events, the only way that multiple events can be -/// put into the queue simultaneously is if a unicode character spanning multiple u16's -/// is read. -/// -/// Therefore, this is accurate as long as at least one KEY_EVENT has already been read. -fn get_key_event_count() -> io::Result<u32> { - let handle = get_stdin_handle()?; - let mut event_count: u32 = unsafe { mem::zeroed() }; - - let success = unsafe { GetNumberOfConsoleInputEvents(handle, &mut event_count) }; - if success == 0 { - Err(io::Error::last_os_error()) - } else { - Ok(event_count) - } -} - -fn read_key_event() -> io::Result<KEY_EVENT_RECORD> { - let handle = get_stdin_handle()?; - let mut buffer: INPUT_RECORD = unsafe { mem::zeroed() }; - - let mut events_read: u32 = unsafe { mem::zeroed() }; - - let mut key_event: KEY_EVENT_RECORD; - loop { - let success = unsafe { ReadConsoleInputW(handle, &mut buffer, 1, &mut events_read) }; - if success == 0 { - return Err(io::Error::last_os_error()); - } - if events_read == 0 { - return Err(io::Error::new( - io::ErrorKind::Other, - "ReadConsoleInput returned no events, instead of waiting for an event", - )); - } - - if events_read == 1 && buffer.EventType != KEY_EVENT as u16 { - // This isn't a key event; ignore it. - continue; - } - - key_event = unsafe { mem::transmute(buffer.Event) }; - - if key_event.bKeyDown == 0 { - // This is a key being released; ignore it. - continue; - } - - return Ok(key_event); - } -} - -pub fn wants_emoji() -> bool { - // If WT_SESSION is set, we can assume we're running in the nne - // Windows Terminal. The correct way to detect this is not available - // yet. See https://github.com/microsoft/terminal/issues/1040 - env::var("WT_SESSION").is_ok() -} - -/// Returns true if there is an MSYS tty on the given handle. -pub fn msys_tty_on(term: &Term) -> bool { - let handle = term.as_raw_handle(); - unsafe { - // Check whether the Windows 10 native pty is enabled - { - let mut out = MaybeUninit::uninit(); - let res = GetConsoleMode(handle as HANDLE, out.as_mut_ptr()); - if res != 0 // If res is true then out was initialized. - && (out.assume_init() & ENABLE_VIRTUAL_TERMINAL_PROCESSING) - == ENABLE_VIRTUAL_TERMINAL_PROCESSING - { - return true; - } - } - - let size = mem::size_of::<FILE_NAME_INFO>(); - let mut name_info_bytes = vec![0u8; size + MAX_PATH as usize * mem::size_of::<u16>()]; - let res = GetFileInformationByHandleEx( - handle as HANDLE, - FileNameInfo, - &mut *name_info_bytes as *mut _ as *mut c_void, - name_info_bytes.len() as u32, - ); - if res == 0 { - return false; - } - let name_info: &FILE_NAME_INFO = &*(name_info_bytes.as_ptr() as *const FILE_NAME_INFO); - let s = slice::from_raw_parts( - name_info.FileName.as_ptr(), - name_info.FileNameLength as usize / 2, - ); - let name = String::from_utf16_lossy(s); - // This checks whether 'pty' exists in the file name, which indicates that - // a pseudo-terminal is attached. To mitigate against false positives - // (e.g., an actual file name that contains 'pty'), we also require that - // either the strings 'msys-' or 'cygwin-' are in the file name as well.) - let is_msys = name.contains("msys-") || name.contains("cygwin-"); - let is_pty = name.contains("-pty"); - is_msys && is_pty - } -} - -pub fn set_title<T: Display>(title: T) { - let buffer: Vec<u16> = OsStr::new(&format!("{}", title)) - .encode_wide() - .chain(once(0)) - .collect(); - unsafe { - SetConsoleTitleW(buffer.as_ptr()); - } -} |