use std::io::{ Stdout, Write };
const CSI: [u8; 2] = [0x1b, '[' as u8];
#[allow(dead_code)]
#[derive(Copy, Clone)]
enum ClearRegion {
AfterCursor = 0,
BeforeCursor = 1,
All = 2,
}
#[allow(dead_code)]
enum ClearTarget {
Screen,
Line,
}
#[allow(dead_code)]
enum Dir {
Up,
Down,
Right,
Left,
}
#[allow(dead_code)]
enum CsiCommand {
Move(Dir),
ToRowBeg(Dir, u8),
InCurrLineMoveTo(u8),
MoveTo { y: u8, x: u8, },
Clear { target: ClearTarget, region: ClearRegion },
ApplySgrs(Vec<SGR>),
ShowCursor,
HideCursor,
}
impl CsiCommand {
fn to_seq(&self) -> Option<String> {
match self {
CsiCommand::Move(Dir::Up) => Some("A".to_string()),
CsiCommand::Move(Dir::Down) => Some("B".to_string()),
CsiCommand::Move(Dir::Right) => Some("C".to_string()),
CsiCommand::Move(Dir::Left) => Some("D".to_string()),
CsiCommand::ToRowBeg(Dir::Down, n) => Some(format!("{}E", n)),
CsiCommand::ToRowBeg(Dir::Up, n) => Some(format!("{}F", n)),
CsiCommand::ToRowBeg(_, _) => None,
CsiCommand::InCurrLineMoveTo(n) => Some(format!("{}G", n)),
CsiCommand::MoveTo { y, x } if *x >=1 && *y >= 1 => Some(format!("{};{}H", y, x)),
CsiCommand::MoveTo { y: _, x: _ } => None,
CsiCommand::Clear { target, region } => {
let target_str = match target {
ClearTarget::Screen => "J",
ClearTarget::Line => "K",
};
Some(format!("{}{}", *region as u8, target_str))
},
CsiCommand::ApplySgrs(sgrs) => {
let x = sgrs.into_iter().map(|x| x.to_code()).collect::<Vec<_>>().join(";");
Some(x + "m")
},
CsiCommand::ShowCursor => Some("?25h".to_string()),
CsiCommand::HideCursor => Some("?25l".to_string()),
}
}
}
#[allow(dead_code)]
#[derive(Copy, Clone)]
pub enum Color {
Black,
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
White,
}
#[allow(dead_code)]
pub enum SGR {
Reset,
Bold,
Thin,
Italic,
Underline,
Blink,
BlinkFaster,
Reverse,
Hide,
StrikeOut,
DefaultFg,
DefaultBg,
Fg(Color),
Bg(Color),
BrighterFg(Color),
BrighterBg(Color),
}
impl SGR {
fn to_code(&self) -> String {
let n: u8 = match self {
SGR::Reset => 0,
SGR::Bold => 1,
SGR::Thin => 2,
SGR::Italic => 3,
SGR::Underline => 4,
SGR::Blink => 5,
SGR::BlinkFaster => 6,
SGR::Reverse => 7,
SGR::Hide => 8,
SGR::StrikeOut => 9,
SGR::DefaultFg => 39,
SGR::DefaultBg => 49,
SGR::Fg(x) => 30 + *x as u8,
SGR::Bg(x) => 40 + *x as u8,
SGR::BrighterFg(x) => 90 + *x as u8,
SGR::BrighterBg(x) => 100 + *x as u8,
};
n.to_string()
}
}
pub struct EscSeq {
pub stdout: Stdout,
}
impl EscSeq {
pub fn new() -> EscSeq {
EscSeq { stdout: std::io::stdout() }
}
fn write(&mut self, cmd: CsiCommand) {
if let Some(x) = cmd.to_seq() {
self.stdout.write(&CSI).unwrap();
self.stdout.write(x.as_bytes()).unwrap();
}
}
pub fn next_line(&mut self) {
self.write(CsiCommand::ToRowBeg(Dir::Down, 1));
}
#[allow(dead_code)]
pub fn to_line_beg(&mut self) {
self.write(CsiCommand::InCurrLineMoveTo(1));
}
pub fn clear_screen(&mut self) {
self.write(CsiCommand::Clear { target: ClearTarget::Screen, region: ClearRegion::All });
}
pub fn clear_line(&mut self) {
self.write(CsiCommand::Clear { target: ClearTarget::Line, region: ClearRegion::All });
}
pub fn move_to(&mut self, y: u8, x: u8) {
self.write(CsiCommand::MoveTo { y, x });
}
pub fn flush(&mut self) {
self.stdout.flush().unwrap();
}
pub fn set(&mut self, sgrs: Vec<SGR>) {
self.write(CsiCommand::ApplySgrs(sgrs));
}
#[allow(dead_code)]
pub fn hide_cursor(&mut self) {
self.write(CsiCommand::HideCursor);
}
#[allow(dead_code)]
pub fn show_cursor(&mut self) {
self.write(CsiCommand::ShowCursor);
}
}