1use std::io::{ Stdout, Write };
2
3const CSI: [u8; 2] = [0x1b, '[' as u8];
4
5#[allow(dead_code)]
6#[derive(Copy, Clone)]
7enum ClearRegion {
8 AfterCursor = 0,
9 BeforeCursor = 1,
10 All = 2,
11}
12
13#[allow(dead_code)]
14enum ClearTarget {
15 Screen,
16 Line,
17}
18
19#[allow(dead_code)]
20enum Dir {
21 Up,
22 Down,
23 Right,
24 Left,
25}
26
27#[allow(dead_code)]
28enum CsiCommand {
29 Move(Dir),
30 ToRowBeg(Dir, u8),
31 InCurrLineMoveTo(u8),
32 MoveTo { y: u8, x: u8, },
33 Clear { target: ClearTarget, region: ClearRegion },
34 ApplySgrs(Vec<SGR>),
35 ShowCursor,
36 HideCursor,
37}
38
39impl CsiCommand {
40 fn to_seq(&self) -> Option<String> {
41 match self {
42 CsiCommand::Move(Dir::Up) => Some("A".to_string()),
43 CsiCommand::Move(Dir::Down) => Some("B".to_string()),
44 CsiCommand::Move(Dir::Right) => Some("C".to_string()),
45 CsiCommand::Move(Dir::Left) => Some("D".to_string()),
46 CsiCommand::ToRowBeg(Dir::Down, n) => Some(format!("{}E", n)),
47 CsiCommand::ToRowBeg(Dir::Up, n) => Some(format!("{}F", n)),
48 CsiCommand::ToRowBeg(_, _) => None,
49 CsiCommand::InCurrLineMoveTo(n) => Some(format!("{}G", n)),
50 CsiCommand::MoveTo { y, x } if *x >=1 && *y >= 1 => Some(format!("{};{}H", y, x)),
51 CsiCommand::MoveTo { y: _, x: _ } => None,
52 CsiCommand::Clear { target, region } => {
53 let target_str = match target {
54 ClearTarget::Screen => "J",
55 ClearTarget::Line => "K",
56 };
57 Some(format!("{}{}", *region as u8, target_str))
58 },
59 CsiCommand::ApplySgrs(sgrs) => {
60 let x = sgrs.into_iter().map(|x| x.to_code()).collect::<Vec<_>>().join(";");
61 Some(x + "m")
62 },
63 CsiCommand::ShowCursor => Some("?25h".to_string()),
64 CsiCommand::HideCursor => Some("?25l".to_string()),
65 }
66 }
67}
68
69#[allow(dead_code)]
70#[derive(Copy, Clone)]
71pub enum Color {
72 Black,
73 Red,
74 Green,
75 Yellow,
76 Blue,
77 Magenta,
78 Cyan,
79 White,
80}
81
82#[allow(dead_code)]
83pub enum SGR {
84 Reset,
85 Bold,
86 Thin,
87 Italic,
88 Underline,
89 Blink,
90 BlinkFaster,
91 Reverse,
92 Hide,
93 StrikeOut,
94 DefaultFg,
95 DefaultBg,
96 Fg(Color),
97 Bg(Color),
98 BrighterFg(Color),
99 BrighterBg(Color),
100}
101
102impl SGR {
103 fn to_code(&self) -> String {
104 let n: u8 = match self {
105 SGR::Reset => 0,
106 SGR::Bold => 1,
107 SGR::Thin => 2,
108 SGR::Italic => 3,
109 SGR::Underline => 4,
110 SGR::Blink => 5,
111 SGR::BlinkFaster => 6,
112 SGR::Reverse => 7,
113 SGR::Hide => 8,
114 SGR::StrikeOut => 9,
115 SGR::DefaultFg => 39,
116 SGR::DefaultBg => 49,
117 SGR::Fg(x) => 30 + *x as u8,
118 SGR::Bg(x) => 40 + *x as u8,
119 SGR::BrighterFg(x) => 90 + *x as u8,
120 SGR::BrighterBg(x) => 100 + *x as u8,
121 };
122 n.to_string()
123 }
124}
125
126pub struct EscSeq {
127 pub stdout: Stdout,
128}
129
130impl EscSeq {
131 pub fn new() -> EscSeq {
132 EscSeq { stdout: std::io::stdout() }
133 }
134
135 fn write(&mut self, cmd: CsiCommand) {
136 if let Some(x) = cmd.to_seq() {
137 self.stdout.write(&CSI).unwrap();
138 self.stdout.write(x.as_bytes()).unwrap();
139 }
140 }
141
142 pub fn next_line(&mut self) {
143 self.write(CsiCommand::ToRowBeg(Dir::Down, 1));
144 }
145
146 #[allow(dead_code)]
147 pub fn to_line_beg(&mut self) {
148 self.write(CsiCommand::InCurrLineMoveTo(1));
149 }
150
151 pub fn clear_screen(&mut self) {
152 self.write(CsiCommand::Clear { target: ClearTarget::Screen, region: ClearRegion::All });
153 }
154
155 pub fn clear_line(&mut self) {
156 self.write(CsiCommand::Clear { target: ClearTarget::Line, region: ClearRegion::All });
157 }
158
159 pub fn move_to(&mut self, y: u8, x: u8) {
160 self.write(CsiCommand::MoveTo { y, x });
161 }
162
163 pub fn flush(&mut self) {
164 self.stdout.flush().unwrap();
165 }
166
167 pub fn set(&mut self, sgrs: Vec<SGR>) {
168 self.write(CsiCommand::ApplySgrs(sgrs));
169 }
170
171 #[allow(dead_code)]
172 pub fn hide_cursor(&mut self) {
173 self.write(CsiCommand::HideCursor);
174 }
175
176 #[allow(dead_code)]
177 pub fn show_cursor(&mut self) {
178 self.write(CsiCommand::ShowCursor);
179 }
180}