You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
clyde/src/output.rs

168 lines
6.8 KiB
Rust

use crate::{error::Error, log::*};
use anyhow::{Context, Result};
use std::io::{self, Write};
use windows::Win32::{
Foundation::{CloseHandle, HANDLE},
System::Console,
};
#[allow(dead_code)]
fn log_output_mode(mode: Console::CONSOLE_MODE) {
// Characters written by the WriteFile or WriteConsole function or echoed by the ReadFile or
// ReadConsole function are parsed for ASCII control sequences, and the correct action is
// performed. Backspace, tab, bell, carriage return, and line feed characters are processed. It
// should be enabled when using control sequences or when ENABLE_VIRTUAL_TERMINAL_PROCESSING is
// set.
if (mode & Console::ENABLE_PROCESSED_OUTPUT).0 > 0 {
debug!("Processed Output: Enabled");
} else {
debug!("Processed Output: Disabled");
}
// When writing with WriteFile or WriteConsole or echoing with ReadFile or ReadConsole, the
// cursor moves to the beginning of the next row when it reaches the end of the current row.
// This causes the rows displayed in the console window to scroll up automatically when the
// cursor advances beyond the last row in the window. It also causes the contents of the
// console screen buffer to scroll up (../discarding the top row of the console screen buffer)
// when the cursor advances beyond the last row in the console screen buffer. If this mode is
// disabled, the last character in the row is overwritten with any subsequent characters.
if (mode & Console::ENABLE_WRAP_AT_EOL_OUTPUT).0 > 0 {
debug!("Wrap at EOL: Enabled");
} else {
debug!("Wrap at EOL: Disabled");
}
// When writing with WriteFile or WriteConsole, characters are parsed for VT100 and similar
// control character sequences that control cursor movement, color/font mode, and other
// operations that can also be performed via the existing Console APIs. For more information,
// see Console Virtual Terminal Sequences.
//
// Ensure ENABLE_PROCESSED_OUTPUT is set when using this flag.
if (mode & Console::ENABLE_VIRTUAL_TERMINAL_PROCESSING).0 > 0 {
debug!("Terminal Processing: Enabled");
} else {
debug!("Terminal Processing: Disabled");
}
// When writing with WriteFile or WriteConsole, this adds an additional state to end-of-line
// wrapping that can delay the cursor move and buffer scroll operations.
//
// Normally when ENABLE_WRAP_AT_EOL_OUTPUT is set and text reaches the end of the line, the
// cursor will immediately move to the next line and the contents of the buffer will scroll up
// by one line. In contrast with this flag set, the cursor does not move to the next line, and
// the scroll operation is not performed. The written character will be printed in the final
// position on the line and the cursor will remain above this character as if
// ENABLE_WRAP_AT_EOL_OUTPUT was off, but the next printable character will be printed as if
// ENABLE_WRAP_AT_EOL_OUTPUT is on. No overwrite will occur. Specifically, the cursor quickly
// advances down to the following line, a scroll is performed if necessary, the character is
// printed, and the cursor advances one more position.
//
// The typical usage of this flag is intended in conjunction with setting
// ENABLE_VIRTUAL_TERMINAL_PROCESSING to better emulate a terminal emulator where writing the
// final character on the screen (../in the bottom right corner) without triggering an
// immediate scroll is the desired behavior.
if (mode & Console::DISABLE_NEWLINE_AUTO_RETURN).0 > 0 {
debug!("Newline Auto Return: Enabled");
} else {
debug!("Newline Auto Return: Disabled");
}
// The APIs for writing character attributes including WriteConsoleOutput and
// WriteConsoleOutputAttribute allow the usage of flags from character attributes to adjust the
// color of the foreground and background of text. Additionally, a range of DBCS flags was
// specified with the COMMON_LVB prefix. Historically, these flags only functioned in DBCS code
// pages for Chinese, Japanese, and Korean languages.
//
// With exception of the leading byte and trailing byte flags, the remaining flags describing
// line drawing and reverse video (../swap foreground and background colors) can be useful for
// other languages to emphasize portions of output.
//
// Setting this console mode flag will allow these attributes to be used in every code page on
// every language.
//
// It is off by default to maintain compatibility with known applications that have
// historically taken advantage of the console ignoring these flags on non-CJK machines to
// store bits in these fields for their own purposes or by accident.
//
// Note that using the ENABLE_VIRTUAL_TERMINAL_PROCESSING mode can result in LVB grid and
// reverse video flags being set while this flag is still off if the attached application
// requests underlining or inverse video via Console Virtual Terminal Sequences.
if (mode & Console::ENABLE_LVB_GRID_WORLDWIDE).0 > 0 {
debug!("LVB Grid: Enabled");
} else {
debug!("LVB Grid: Disabled");
}
}
pub struct Writer {
output: HANDLE,
}
impl Writer {
pub fn stdout() -> Result<Self> {
unsafe {
let handle = Console::GetStdHandle(Console::STD_OUTPUT_HANDLE)
.context("unable to get stdout handle")?;
let mut stdout = Self{output: handle};
stdout.reset()?;
Ok(stdout)
}
}
pub fn close(&mut self) -> Result<()> {
unsafe {
CloseHandle(self.output);
}
Ok(())
}
pub fn reset(&mut self) -> Result<()> {
unsafe {
Console::SetConsoleOutputCP(65001);
}
Ok(())
}
pub fn newline(&mut self) -> Result<()> {
self.write(b"\r\n")?;
Ok(())
}
pub fn back(&mut self, n: usize) -> Result<()> {
if n > 0 {
let text = format!("\x1b[{}D", n);
self.write(text.as_bytes())?;
}
Ok(())
}
pub fn forward(&mut self, n: usize) -> Result<()> {
let text = format!("\x1b[{}C", n);
self.write(text.as_bytes())?;
Ok(())
}
pub fn clear(&mut self) -> Result<()> {
self.write(b"\x1b[2J\x1b[0;0H")?;
Ok(())
}
// pub fn hide_cursor(&mut self) -> Result<()> {
// self.write(b"\x1b[?25l")?;
// Ok(())
// }
}
impl Write for Writer {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unsafe {
Error::check(Console::WriteConsoleA(self.output, buf, None, None))?;
}
Ok(0)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}