mirror of
https://github.com/Xyverle/neutuino.git
synced 2026-06-26 22:23:14 -04:00
Rewrite & Document ANSI module
This commit is contained in:
368
src/ansi.rs
368
src/ansi.rs
@@ -1,189 +1,195 @@
|
|||||||
use std::fmt;
|
//! Collection of ANSI escape code consts/functions
|
||||||
|
//!
|
||||||
|
//! These should work on *most* terminals (i.e. Xterm compatible terminals)
|
||||||
|
//!
|
||||||
|
//! For these to work on Windows you need to run the enable_ansi function in the os module
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
/// Sets the terminal to an arbitrary 12-bit/truecolor color
|
||||||
pub enum CursorMovement {
|
pub fn rgb_color_code(red: u8, green: u8, blue: u8) -> String {
|
||||||
Move(u16, u16),
|
format!("\x1b[38;2;{red};{green};{blue}m")
|
||||||
Up(u16),
|
|
||||||
Down(u16),
|
|
||||||
Right(u16),
|
|
||||||
Left(u16),
|
|
||||||
Save,
|
|
||||||
Restore,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CursorMovement {
|
/// Sets the title of the window
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
///
|
||||||
let str = match *self {
|
/// The title must be only in ASCII characters or **weird** things will happen
|
||||||
Self::Move(x, y) => &format!("[{x};{y}H"),
|
///
|
||||||
Self::Up(n) => &format!("[{n}A"),
|
/// Panics:
|
||||||
Self::Down(n) => &format!("[{n}B"),
|
///
|
||||||
Self::Left(n) => &format!("[{n}D"),
|
/// When the title is more than 255 characters
|
||||||
Self::Right(n) => &format!("[{n}C"),
|
pub fn set_window_title(title: String) -> String {
|
||||||
Self::Save => "7",
|
|
||||||
Self::Restore => "8",
|
|
||||||
};
|
|
||||||
write!(f, "\x1b{str}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub enum CursorShape {
|
|
||||||
Reset,
|
|
||||||
BlockBlinking,
|
|
||||||
BlockSteady,
|
|
||||||
UnderlineBlinking,
|
|
||||||
UnderlineSteady,
|
|
||||||
BarBlinking,
|
|
||||||
BarSteady,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for CursorShape {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let str = match *self {
|
|
||||||
Self::Reset => "0",
|
|
||||||
Self::BlockBlinking => "1",
|
|
||||||
Self::BlockSteady => "2",
|
|
||||||
Self::UnderlineBlinking => "3",
|
|
||||||
Self::UnderlineSteady => "4",
|
|
||||||
Self::BarBlinking => "5",
|
|
||||||
Self::BarSteady => "6",
|
|
||||||
};
|
|
||||||
write!(f, "\x1b[{str} q")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub enum Erase {
|
|
||||||
Screen,
|
|
||||||
Line,
|
|
||||||
CursorToScreenStart,
|
|
||||||
CursorToScreenEnd,
|
|
||||||
CursorToLineStart,
|
|
||||||
CursorToLineEnd,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Erase {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let str = match *self {
|
|
||||||
Self::Screen => "2J",
|
|
||||||
Self::Line => "2K",
|
|
||||||
Self::CursorToScreenStart => "1J",
|
|
||||||
Self::CursorToScreenEnd => "0J",
|
|
||||||
Self::CursorToLineStart => "1K",
|
|
||||||
Self::CursorToLineEnd => "0K",
|
|
||||||
};
|
|
||||||
write!(f, "\x1b[{str}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub enum Style {
|
|
||||||
ResetAll,
|
|
||||||
Bold,
|
|
||||||
Dim,
|
|
||||||
Italic,
|
|
||||||
Underline,
|
|
||||||
Blinking,
|
|
||||||
Reverse,
|
|
||||||
Hidden,
|
|
||||||
Strikethrough,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Style {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let str = match *self {
|
|
||||||
Self::ResetAll => "0",
|
|
||||||
Self::Bold => "1",
|
|
||||||
Self::Dim => "2",
|
|
||||||
Self::Italic => "3",
|
|
||||||
Self::Underline => "4",
|
|
||||||
Self::Blinking => "5",
|
|
||||||
Self::Reverse => "7",
|
|
||||||
Self::Hidden => "8",
|
|
||||||
Self::Strikethrough => "9",
|
|
||||||
};
|
|
||||||
write!(f, "\x1b[{str}m")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::LowerHex for Style {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let str = match *self {
|
|
||||||
Self::ResetAll => "0",
|
|
||||||
Self::Bold | Self::Dim => "22",
|
|
||||||
Self::Italic => "23",
|
|
||||||
Self::Underline => "24",
|
|
||||||
Self::Blinking => "25",
|
|
||||||
Self::Reverse => "27",
|
|
||||||
Self::Hidden => "28",
|
|
||||||
Self::Strikethrough => "29",
|
|
||||||
};
|
|
||||||
write!(f, "\x1b[{str}m")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub enum Color {
|
|
||||||
Rgb(u8, u8, u8),
|
|
||||||
Black,
|
|
||||||
Red,
|
|
||||||
Green,
|
|
||||||
Yellow,
|
|
||||||
Blue,
|
|
||||||
Magenta,
|
|
||||||
Cyan,
|
|
||||||
White,
|
|
||||||
Default,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Color {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let str = match *self {
|
|
||||||
Self::Rgb(r, g, b) => &format!("38;2;{r};{g};{b}"),
|
|
||||||
Self::Black => "30",
|
|
||||||
Self::Red => "31",
|
|
||||||
Self::Green => "32",
|
|
||||||
Self::Yellow => "33",
|
|
||||||
Self::Blue => "34",
|
|
||||||
Self::Magenta => "35",
|
|
||||||
Self::Cyan => "36",
|
|
||||||
Self::White => "37",
|
|
||||||
Self::Default => "39",
|
|
||||||
};
|
|
||||||
write!(f, "\x1b[{str}m")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Binary for Color {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let str = match *self {
|
|
||||||
Self::Rgb(r, g, b) => &format!("48;2;{r};{g};{b}"),
|
|
||||||
Self::Black => "40",
|
|
||||||
Self::Red => "41",
|
|
||||||
Self::Green => "42",
|
|
||||||
Self::Yellow => "43",
|
|
||||||
Self::Blue => "44",
|
|
||||||
Self::Magenta => "45",
|
|
||||||
Self::Cyan => "46",
|
|
||||||
Self::White => "47",
|
|
||||||
Self::Default => "49",
|
|
||||||
};
|
|
||||||
write!(f, "\x1b[{str}m")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enter_alternate_screen() {
|
|
||||||
println!("\x1b[?1049h");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exit_alternate_screen() {
|
|
||||||
println!("\x1b[?1049l");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_window_title(title: &str) {
|
|
||||||
assert!(
|
assert!(
|
||||||
title.len() <= 255,
|
title.len() <= 255,
|
||||||
"Title length longer than maximum of 255"
|
"Title length longer than maximum of 255"
|
||||||
);
|
);
|
||||||
println!("\x1b]0;{title}\x1b\x5c");
|
format!("\x1b]0;{title}\x1b\x5c")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Moves the cursor up {num} characters
|
||||||
|
pub fn move_cursor_up(num: u16) -> String {
|
||||||
|
format!("\x1b[{num}A")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves the cursor down {num} characters
|
||||||
|
pub fn move_cursor_down(num: u16) -> String {
|
||||||
|
format!("\x1b[{num}B")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves the cursor right {num} characters
|
||||||
|
pub fn move_cursor_right(num: u16) -> String {
|
||||||
|
format!("\x1b[{num}C")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves the cursor left {num} characters
|
||||||
|
pub fn move_cursor_left(num: u16) -> String {
|
||||||
|
format!("\x1b[{num}A")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves the cursor to {row}
|
||||||
|
///
|
||||||
|
/// Origin is 1, 1
|
||||||
|
pub fn move_cursor_to_row(row: u16) -> String {
|
||||||
|
format!("\x1b[{row}d")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves the cursor to {column}
|
||||||
|
///
|
||||||
|
/// Origin is 1, 1
|
||||||
|
pub fn move_cursor_to_column(column: u16) -> String {
|
||||||
|
format!("\x1b[{column}G")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves the cursor to Position {x}, {y}
|
||||||
|
///
|
||||||
|
/// Origin is 1, 1
|
||||||
|
pub fn move_cursor_to_position(column: u16, line: u16) -> String {
|
||||||
|
format!("\x1b[{line};{column}H")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Saves the current cursor position
|
||||||
|
pub const CURSOR_POSITION_SAVE: &str = "\x1b7";
|
||||||
|
/// Restores the saved cursor position
|
||||||
|
pub const CURSOR_POSITION_RESTORE: &str = "\x1b8";
|
||||||
|
|
||||||
|
/// Enters the alternate screen
|
||||||
|
///
|
||||||
|
/// The alternate screen is a blank screen that won't interrupt the main screen (e.g. vi)
|
||||||
|
pub const ALT_SCREEN_ENTER: &str = "\x1b[?1049h";
|
||||||
|
/// Exits the alternate screen
|
||||||
|
///
|
||||||
|
/// The alternate screen is a blank screen that won't interrupt the main screen (e.g. vi)
|
||||||
|
pub const ALT_SCREEN_EXIT: &str = "\x1b[?1049l";
|
||||||
|
|
||||||
|
/// Sets the cursor shape to the user-specified default
|
||||||
|
pub const SHAPE_RESET: &str = "\x1b[0q";
|
||||||
|
/// Sets the cursor shape to a blinking block
|
||||||
|
pub const SHAPE_BLOCK_BLINKING: &str = "\x1b[1q";
|
||||||
|
/// Sets the cursor shape to a steady block
|
||||||
|
pub const SHAPE_BLOCK_STEADY: &str = "\x1b[2q";
|
||||||
|
/// Sets the cursor shape to a blinking underline
|
||||||
|
pub const SHAPE_UNDERLINE_BLINKING: &str = "\x1b[3q";
|
||||||
|
/// Sets the cursor shape to a steady underline
|
||||||
|
pub const SHAPE_UNDERLINE_STEADY: &str = "\x1b[4q";
|
||||||
|
/// Sets the cursor shape to a blinking bar
|
||||||
|
pub const SHAPE_BAR_BLINKING: &str = "\x1b[5q";
|
||||||
|
/// Sets the cursor shape to a steady bar
|
||||||
|
pub const SHAPE_BAR_STEADY: &str = "\x1b[6q";
|
||||||
|
|
||||||
|
/// Erases the entire screen while leaving cursor in place
|
||||||
|
pub const ERASE_SCREEN: &str = "\x1b[2J";
|
||||||
|
/// Erases the line the cursor is on while leaving cursor in place
|
||||||
|
pub const ERASE_LINE: &str = "\x1b[2K";
|
||||||
|
/// Erases from the screen start to the cursor while leaving cursor in place
|
||||||
|
pub const ERASE_CURSOR_TO_SCREEN_START: &str = "\x1b[1J";
|
||||||
|
/// Erases from the cursor to the screen end while leaving cursor in place
|
||||||
|
pub const ERASE_CURSOR_TO_SCREEN_END: &str = "\x1b[0J";
|
||||||
|
/// Erases from the line start to the cursor while leaving cursor in place
|
||||||
|
pub const ERASE_CURSOR_TO_LINE_START: &str = "\x1b[1K";
|
||||||
|
/// Erases from the cursor to the line end while leaving cursor in place
|
||||||
|
pub const ERASE_CURSOR_TO_LINE_END: &str = "\x1b[0K";
|
||||||
|
|
||||||
|
/// Makes characters sent to the screen bold
|
||||||
|
pub const STYLE_BOLD: &str = "\x1b[1m";
|
||||||
|
/// Makes characters sent to the screen dim
|
||||||
|
pub const STYLE_DIM: &str = "\x1b[2m";
|
||||||
|
/// Makes characters sent to the screen italic
|
||||||
|
pub const STYLE_ITALIC: &str = "\x1b[3m";
|
||||||
|
/// Makes characters sent to the screen underlined
|
||||||
|
///
|
||||||
|
/// This is less commonly supported than other styles
|
||||||
|
pub const STYLE_UNDERLINE: &str = "\x1b[4m";
|
||||||
|
/// Makes characters sent to the screen blinking
|
||||||
|
///
|
||||||
|
/// This is less commonly supported than other styles
|
||||||
|
pub const STYLE_BLINKING: &str = "\x1b[5m";
|
||||||
|
/// Makes characters sent to the screen reversed
|
||||||
|
///
|
||||||
|
/// This is less commonly supported than other styles
|
||||||
|
pub const STYLE_REVERSE: &str = "\x1b[7m";
|
||||||
|
/// Makes characters sent to the screen hidden
|
||||||
|
///
|
||||||
|
/// This is less commonly supported than other styles
|
||||||
|
pub const STYLE_HIDDEN: &str = "\x1b[8m";
|
||||||
|
/// Makes characters sent to the screen struckthrough
|
||||||
|
///
|
||||||
|
/// This is less commonly supported than other styles
|
||||||
|
pub const STYLE_STRIKETHROUGH: &str = "\x1b[9m";
|
||||||
|
|
||||||
|
/// Resets all styles and colors
|
||||||
|
pub const STYLE_RESET: &str = "\x1b[0m";
|
||||||
|
/// Resets bold
|
||||||
|
///
|
||||||
|
/// Often bold & dim's implementations are overlapping and will likely unset both
|
||||||
|
pub const STYLE_RESET_BOLD: &str = "\x1b[21m";
|
||||||
|
/// Resets dim
|
||||||
|
///
|
||||||
|
/// Often bold & dim's implementations are overlapping and will likely unset both
|
||||||
|
pub const STYLE_RESET_DIM: &str = "\x1b[22m";
|
||||||
|
/// Reset italic
|
||||||
|
pub const STYLE_RESET_ITALIC: &str = "\x1b[23m";
|
||||||
|
/// Reset underline
|
||||||
|
pub const STYLE_RESET_UNDERLINE: &str = "\x1b[24m";
|
||||||
|
/// Reset blinking
|
||||||
|
pub const STYLE_RESET_BLINKING: &str = "\x1b[25m";
|
||||||
|
/// Reset reverse
|
||||||
|
pub const STYLE_RESET_REVERSE: &str = "\x1b[27m";
|
||||||
|
/// Reset hidden
|
||||||
|
pub const STYLE_RESET_HIDDEN: &str = "\x1b[28m";
|
||||||
|
/// Reset strikethrough
|
||||||
|
pub const STYLE_RESET_STRIKETHROUGH: &str = "\x1b[29m";
|
||||||
|
|
||||||
|
/// Makes characters sent to the screen have a black foreground
|
||||||
|
pub const COLOR_BLACK_FG: &str = "\x1b[30m";
|
||||||
|
/// Makes characters sent to the screen have a black background
|
||||||
|
pub const COLOR_BLACK_BG: &str = "\x1b[40m";
|
||||||
|
/// Makes characters sent to the screen have a red foreground
|
||||||
|
pub const COLOR_RED_FG: &str = "\x1b[31m";
|
||||||
|
/// Makes characters sent to the screen have a red background
|
||||||
|
pub const COLOR_RED_BG: &str = "\x1b[41m";
|
||||||
|
/// Makes characters sent to the screen have a green foreground
|
||||||
|
pub const COLOR_GREEN_FG: &str = "\x1b[32m";
|
||||||
|
/// Makes characters sent to the screen have a green background
|
||||||
|
pub const COLOR_GREEN_BG: &str = "\x1b[42m";
|
||||||
|
/// Makes characters sent to the screen have a yellow foreground
|
||||||
|
pub const COLOR_YELLOW_FG: &str = "\x1b[33m";
|
||||||
|
/// Makes characters sent to the screen have a yellow background
|
||||||
|
pub const COLOR_YELLOW_BG: &str = "\x1b[43m";
|
||||||
|
/// Makes characters sent to the screen have a blue foreground
|
||||||
|
pub const COLOR_BLUE_FG: &str = "\x1b[34m";
|
||||||
|
/// Makes characters sent to the screen have a blue background
|
||||||
|
pub const COLOR_BLUE_BG: &str = "\x1b[44m";
|
||||||
|
/// Makes characters sent to the screen have a magenta foreground
|
||||||
|
pub const COLOR_MAGENTA_FG: &str = "\x1b[35m";
|
||||||
|
/// Makes characters sent to the screen have a magenta background
|
||||||
|
pub const COLOR_MAGENTA_BG: &str = "\x1b[45m";
|
||||||
|
/// Makes characters sent to the screen have a cyan foreground
|
||||||
|
pub const COLOR_CYAN_FG: &str = "\x1b[36m";
|
||||||
|
/// Makes characters sent to the screen have a cyan background
|
||||||
|
pub const COLOR_CYAN_BG: &str = "\x1b[46m";
|
||||||
|
/// Makes characters sent to the screen have a white foreground
|
||||||
|
pub const COLOR_WHITE_FG: &str = "\x1b[37m";
|
||||||
|
/// Makes characters sent to the screen have a white background
|
||||||
|
pub const COLOR_WHITE_BG: &str = "\x1b[47m";
|
||||||
|
/// Makes characters sent to the screen have a default foreground
|
||||||
|
pub const COLOR_DEFAULT_FG: &str = "\x1b[39m";
|
||||||
|
/// Makes characters sent to the screen have a default background
|
||||||
|
pub const COLOR_DEFAULT_BG: &str = "\x1b[49m";
|
||||||
|
|||||||
10
src/os.rs
10
src/os.rs
@@ -1,11 +1,13 @@
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
#[path = "unix.rs"] mod unix;
|
#[path = "unix.rs"]
|
||||||
|
mod unix;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use unix as os;
|
use unix as os;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
#[path = "windows.rs"] mod windows;
|
#[path = "windows.rs"]
|
||||||
|
mod windows;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use windows as os;
|
use windows as os;
|
||||||
@@ -15,10 +17,12 @@ pub use os::is_terminal;
|
|||||||
|
|
||||||
/// Enables ANSI support on Windows terminals
|
/// Enables ANSI support on Windows terminals
|
||||||
///
|
///
|
||||||
/// ANSI is on by default on *nix machines but still exists for ease of use
|
/// ANSI is on by default on *nix machines but still exists on them for simpler usage
|
||||||
pub use os::enable_ansi;
|
pub use os::enable_ansi;
|
||||||
|
|
||||||
/// Gets the size of the terminal
|
/// Gets the size of the terminal
|
||||||
|
///
|
||||||
|
/// Returns in (width, height) format
|
||||||
pub use os::get_terminal_size;
|
pub use os::get_terminal_size;
|
||||||
|
|
||||||
/// Enables raw mode
|
/// Enables raw mode
|
||||||
|
|||||||
@@ -57,12 +57,12 @@ pub fn enable_ansi() -> io::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_terminal_size() -> io::Result<(c_ushort, c_ushort)> {
|
pub fn get_terminal_size() -> io::Result<(u16, u16)> {
|
||||||
let mut winsize = unsafe { std::mem::zeroed::<Winsize>() };
|
let mut winsize = unsafe { std::mem::zeroed::<Winsize>() };
|
||||||
let ioctl_result = unsafe { ioctl(STDOUT_FILENO, TIOCGWINSZ, (&raw mut winsize).cast::<u8>()) };
|
let ioctl_result = unsafe { ioctl(STDOUT_FILENO, TIOCGWINSZ, (&raw mut winsize).cast::<u8>()) };
|
||||||
|
|
||||||
if ioctl_result == 0 {
|
if ioctl_result == 0 {
|
||||||
Ok((winsize.col, winsize.row))
|
Ok((winsize.col as u16, winsize.row as u16))
|
||||||
} else {
|
} else {
|
||||||
Err(io::Error::last_os_error())
|
Err(io::Error::last_os_error())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ struct ConsoleScreenBufferInfo {
|
|||||||
dwMaximumWindowSizeY: u16,
|
dwMaximumWindowSizeY: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use] pub fn is_terminal() -> bool {
|
#[must_use]
|
||||||
|
pub fn is_terminal() -> bool {
|
||||||
let handle = get_std_handle(STD_OUTPUT_HANDLE);
|
let handle = get_std_handle(STD_OUTPUT_HANDLE);
|
||||||
match handle {
|
match handle {
|
||||||
Ok(handle) => {
|
Ok(handle) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user