1use std::fmt::Display;
24
25use base64::Engine;
26use place_macro::place;
27
28#[macro_export]
31macro_rules! seq {
32 ($sq:literal, $i:literal) => {
33 concat!($sq, $i)
34 };
35 ($sq:literal, $i:literal, $f:literal, $($a:literal),*) => {
36 concat!($sq, $f $(, ';', $a)*, $i)
37 };
38 ($sq:literal, $i:literal, $f:expr $(,$a:expr)*) => {
39 $crate::seq!($sq, $i, $f, $(";{}"; $a),*)
40 };
41 ($sq:literal, $i:literal, $f:expr, $($l:literal; $e:expr),*) => {
42 format!(concat!($sq, "{}" $(,$l)*, $i), $f $(,$e)*)
43 }
44}
45
46pub const ESC: char = '\x1b';
50pub const CSI: &str = "\x1b[";
52pub const DCS: &str = "\x1bP";
54pub const OSC: &str = "\x1b]";
56pub const ST: &str = "\x1b\\";
58pub const SS3: &str = "\x1bO";
60
61#[macro_export]
64macro_rules! csi {
65 ($i:literal $(,$a:expr)* $(,)?) => {
66 $crate::seq!("\x1b[", $i $(, $a)*)
67 };
68}
69
70#[macro_export]
72macro_rules! graphic {
73 ($($a:expr),* $(,)?) => {
74 $crate::csi!('m' $(, $a)*)
75 };
76}
77
78#[macro_export]
81macro_rules! osc {
82 ($($a:expr),+) => {
83 $crate::seq!("\x1b]", "\x1b\\", $($a),+)
84 };
85}
86
87#[macro_export]
89macro_rules! enable {
90 ($a:expr) => {
91 $crate::seq!("\x1b[?", 'h', $a)
92 };
93}
94
95#[macro_export]
97macro_rules! disable {
98 ($a:expr) => {
99 $crate::seq!("\x1b[?", 'l', $a)
100 };
101}
102
103pub const BELL: char = '\x07';
107pub const BACKSPACE: char = '\x08';
109pub const HTAB: char = '\t';
111pub const NEWLINE: char = '\n';
113pub const VTAB: char = '\x0b';
115pub const FORMFEED: char = '\x0c';
117pub const CARRIAGE_RETURN: char = '\r';
119pub const DELETE: char = '\x7f';
121
122macro_rules! code_macro {
131 ($code:ident $(
132 $name:ident
133 $(, $($nam:ident)? $($lit:literal)?)+ ;
134 $($i:literal)? $(?$doc:literal)?),+ $(,)?
135 ) => {
136 place! {$(
137 $(#[doc = __repnl__($doc, " ")])?
138 #[macro_export]
139 macro_rules! $name {
140 (__start__($($(__s__ $nam:expr,)?)+) __s__ (,)?) => {
141 __s__ crate::$code!($($i,)? $($(__s__ $nam)? $($lit)?),+)
142 }
143 }
144 pub use $name;
145 )+}
146 };
147 ($code:ident != $ex:literal => $(
148 $name:ident,
149 $nam:ident;
150 $($i:literal)? $(?$doc:literal)?),+ $(,)?
151 ) => {
152 place! {$(
153 $(#[doc = __repnl__($doc, " ")])?
154 #[macro_export]
155 macro_rules! $name {
156 (__start__(__s__ $nam:expr,)) => {{
157 let v = __s__ $nam;
158 if v == $ex {
159 "".into()
160 } else {
161 __s__ crate::$code!($($i,)? v)
162 }
163 }}
164 }
165 pub use $name;
166 )+}
167 };
168}
169
170#[macro_export]
172macro_rules! move_to {
173 ($x:expr, $y:expr) => {
174 $crate::csi!('H', $y, $x)
175 };
176}
177
178pub use move_to;
179
180use crate::Rgb;
181
182code_macro!(csi != 0 =>
183 move_up, n; 'A' ? "Moves cursor up by N positions",
184 move_down, n; 'B' ? "Moves cursor down by N positions",
185 move_right, n; 'C' ? "Moves cursor right by N positions",
186 move_left, n; 'D' ? "Moves cursor left by N positions",
187 insert_lines, n; 'L' ? "Insert n lines at the cursor moving them down.",
188 delete_lines, n; 'M'
189 ? "Delete n lines at the cursor, moving the remaining from bottom.",
190 insert_chars, n; '@' ? "Insert n characters, moving them to the right.",
191 delete_chars, n; 'P' ? "Delete n characters, moving the chars from right.",
192 insert_columns, n; "'}}" ? "Insert n columns, moving them to the right.",
193 delete_columns, n; "'~" ? "Delete n columns, moving them from the right",
194 set_down, n; 'E' ? "Moves cursor to the start of line N lines down",
195 set_up, n; 'F' ? "Moves cursor to the start of line N lines up",
196 repeat_char, n; 'b' ? "Repeat the previous char n times."
197);
198
199code_macro!(csi
200 column, n; 'G' ? "Moves cursor to the given column",
201);
202
203pub const UP_SCRL: &str = "\x1bM";
205pub const CUR_SAVE: &str = "\x1b7";
207pub const CUR_LOAD: &str = "\x1b8";
210
211pub const ERASE_TO_END: &str = csi!('J');
215pub const ERASE_FROM_START: &str = csi!('J', 1);
217pub const ERASE_SCREEN: &str = csi!('J', 2);
219pub const ERASE_ALL: &str = csi!('J', 3);
221pub const ERASE_TO_LN_END: &str = csi!('K');
223pub const ERASE_FROM_LN_START: &str = csi!('K', 1);
225pub const ERASE_LINE: &str = csi!('K', 2);
227
228pub const RESET: &str = graphic!(0);
232
233pub const BOLD: &str = graphic!(1);
235pub const FAINT: &str = graphic!(2);
237pub const ITALIC: &str = graphic!(3);
239pub const UNDERLINE: &str = graphic!(4);
241pub const BLINKING: &str = graphic!(5);
243pub const INVERSE: &str = graphic!(7);
245pub const INVISIBLE: &str = graphic!(8);
247pub const STRIKETROUGH: &str = graphic!(9);
249pub const DOUBLE_UNDERLINE: &str = graphic!(21);
251pub const OVERLINE: &str = graphic!(53);
253
254pub const RESET_BOLD: &str = graphic!(22);
256pub const RESET_ITALIC: &str = graphic!(23);
258pub const RESET_UNDERLINE: &str = graphic!(24);
260pub const RESET_BLINKING: &str = graphic!(25);
262pub const RESET_INVERSE: &str = graphic!(27);
264pub const RESET_INVISIBLE: &str = graphic!(28);
266pub const RESET_STRIKETROUGH: &str = graphic!(29);
268pub const RESET_OVERLINE: &str = graphic!(55);
270
271pub const BLACK_FG: &str = graphic!(30);
273pub const WHITE_FG: &str = graphic!(97);
275pub const GRAY_FG: &str = graphic!(90);
277pub const GRAY_BRIGHT_FG: &str = graphic!(37);
279
280pub const RED_FG: &str = graphic!(91);
282pub const GREEN_FG: &str = graphic!(92);
284pub const YELLOW_FG: &str = graphic!(93);
286pub const BLUE_FG: &str = graphic!(94);
288pub const MAGENTA_FG: &str = graphic!(95);
290pub const CYAN_FG: &str = graphic!(96);
292
293pub const RED_DARK_FG: &str = graphic!(31);
295pub const GREEN_DARK_FG: &str = graphic!(32);
297pub const YELLOW_DARK_FG: &str = graphic!(33);
299pub const BLUE_DARK_FG: &str = graphic!(34);
301pub const MAGENTA_DARK_FG: &str = graphic!(35);
303pub const CYAN_DARK_FG: &str = graphic!(36);
305
306pub const RESET_FG: &str = graphic!(39);
308
309pub const BLACK_BG: &str = graphic!(40);
311pub const WHITE_BG: &str = graphic!(107);
313pub const GRAY_BG: &str = graphic!(100);
315pub const GRAY_BRIGHT_BG: &str = graphic!(47);
317
318pub const RED_BG: &str = graphic!(101);
320pub const GREEN_BG: &str = graphic!(102);
322pub const YELLOW_BG: &str = graphic!(103);
324pub const BLUE_BG: &str = graphic!(104);
326pub const MAGENTA_BG: &str = graphic!(105);
328pub const CYAN_BG: &str = graphic!(106);
330
331pub const RED_DARK_BG: &str = graphic!(41);
333pub const GREEN_DARK_BG: &str = graphic!(42);
335pub const YELLOW_DARK_BG: &str = graphic!(43);
337pub const BLUE_DARK_BG: &str = graphic!(44);
339pub const MAGENTA_DARK_BG: &str = graphic!(45);
341pub const CYAN_DARK_BG: &str = graphic!(46);
343
344pub const RESET_BG: &str = graphic!(49);
346
347code_macro! { graphic
348 fg256, 38, 5, c;
349 ? "creates a foreground color, color is value in range 0..256",
350
351 bg256, 48, 5, c;
352 ? "creates a background color, color is value in range 0..256",
353
354 underline256, 58, 5, c;
355 ? "Set underline color as 256 color.",
356
357 fg, 38, 2, r, g, b;
358 ? "creates a true rgb foreground color. R, G and B must be values in
359 range 0..256",
360
361 bg, 48, 2, r, g, b;
362 ? "creates a true rgb background color. R, G and B must be values in
363 range 0..256",
364
365 underline_rgb, 58, 2, r, g, b;
366 ? "Set underline color as rgb.",
367}
368
369pub const RESET_UNDERLINE_COLOR: &str = graphic!(59);
371
372pub const DOUBLE_CHAR_HEIGHT_DOWN: &str = "\x1b#3";
375pub const DOUBLE_CHAR_HEIGHT_UP: &str = "\x1b#4";
377pub const DOUBLE_CHAR_WIDTH: &str = "\x1b#6";
379pub const RESET_CHAR_SIZE: &str = "\x1b#5";
381
382pub const ENABLE_LINE_WRAP: &str = "\x1b[=7h";
386pub const DISABLE_LINE_WRAP: &str = "\x1b[=7l";
388
389pub const ENABLE_REVERSE_COLOR: &str = enable!(5);
391pub const DISABLE_REVERSE_COLOR: &str = disable!(5);
394
395pub const HIDE_CURSOR: &str = disable!(25);
399pub const SHOW_CURSOR: &str = enable!(25);
401pub const SAVE_SCREEN: &str = disable!(47);
403pub const LOAD_SCREEN: &str = enable!(47);
405pub const ENABLE_ALTERNATIVE_BUFFER: &str = enable!(1049);
407pub const DISABLE_ALTERNATIVE_BUFFER: &str = disable!(1049);
409
410pub const FULL_RESET: &str = "\x1bc";
413
414pub const REQUEST_DEVICE_ATTRIBUTES: &str = csi!('c');
416pub const REQUEST_STATUS_REPORT: &str = csi!('n', 5);
418pub const REQUEST_CURSOR_POSITION: &str = csi!('n', 6);
421pub const REQUEST_CURSOR_POSITION2: &str = "\x1b[?6n";
425pub const REQUEST_TERMINAL_NAME: &str = "\x1b[>0q";
427pub const REQUEST_TEXT_AREA_SIZE_PX: &str = csi!('t', 14);
429pub const REQUEST_CHAR_SIZE: &str = csi!('t', 16);
431pub const REQUEST_TEXT_AREA_SIZE: &str = csi!('t', 18);
433pub const REQUEST_SIXEL_COLORS: &str = "\x1b[?1;1;1S";
435
436pub const ENABLE_MOUSE_XY_TRACKING: &str = enable!(9);
438pub const DISABLE_MOUSE_XY_TRACKING: &str = disable!(9);
440pub const ENABLE_MOUSE_XY_PR_TRACKING: &str = enable!(1000);
442pub const DISABLE_MOUSE_XY_PR_TRACKING: &str = disable!(1000);
444pub const ENABLE_MOUSE_XY_DRAG_TRACKING: &str = enable!(1002);
446pub const DISABLE_MOUSE_XY_DRAG_TRACKING: &str = disable!(1002);
448pub const ENABLE_MOUSE_XY_ALL_TRACKING: &str = enable!(1003);
451pub const DISABLE_MOUSE_XY_ALL_TRACKING: &str = disable!(1003);
454pub const ENABLE_FOCUS_EVENT: &str = enable!(1004);
456pub const DISABLE_FOCUS_EVENT: &str = disable!(1004);
458pub const ENABLE_MOUSE_XY_UTF8_EXT: &str = enable!(1005);
461pub const DISABLE_MOUSE_XY_UTF8_EXT: &str = disable!(1005);
464pub const ENABLE_MOUSE_XY_EXT: &str = enable!(1006);
467pub const DISABLE_MOUSE_XY_EXT: &str = disable!(1006);
470pub const ENABLE_MOUSE_XY_URXVT_EXT: &str = enable!(1015);
473pub const DISABLE_MOUSE_XY_URXVT_EXT: &str = disable!(1015);
475pub const ENABLE_MOUSE_XY_PIX_EXT: &str = enable!(1016);
478pub const DISABLE_MOUSE_XY_PIX_EXT: &str = disable!(1016);
481
482code_macro! { csi
483 scroll_region, t, b; 'r'
484 ? "Set the scroll region in the terminal. Also moves the cursor to the
485 top left."
486}
487
488pub const RESET_SCROLL_REGION: &str = scroll_region!(0, 0);
490pub const DONT_LIMIT_PRINT_TO_SCROLL_REGION: &str = enable!(19);
492pub const LIMIT_PRINT_TO_SCROLL_REGION: &str = disable!(19);
494
495pub const ENABLE_BRACKETED_PASTE_MODE: &str = enable!(2004);
498pub const DISABLE_BRACKETED_PASTE_MODE: &str = disable!(2004);
499
500#[derive(Clone, Debug, Copy, Eq, PartialEq)]
501pub enum CursorStyle {
502 Block(Option<bool>),
507 Underline(bool),
511 Bar(bool),
515}
516
517pub fn set_cursor(style: CursorStyle) -> &'static str {
518 match style {
519 CursorStyle::Block(Some(true)) => csi!(" q", 0),
520 CursorStyle::Block(None) => csi!(" q", 1),
521 CursorStyle::Block(Some(false)) => csi!(" q", 2),
522 CursorStyle::Underline(true) => csi!(" q", 3),
523 CursorStyle::Underline(false) => csi!(" q", 4),
524 CursorStyle::Bar(true) => csi!(" q", 5),
525 CursorStyle::Bar(false) => csi!(" q", 6),
526 }
527}
528
529code_macro! {osc
532 request_color_code, 4, code, "?";
533 ? "Requests the current color assigned to the given color code.",
534
535 reset_color_code, 104, code;
536 ? "Resets the color definition for the given color code.",
537}
538
539pub fn define_color_code<T>(code: u8, color: impl Into<Rgb<T>>) -> String
541where
542 Rgb<T>: Display,
543{
544 osc!(4, code, color.into())
545}
546
547pub fn set_default_fg_color<T>(color: impl Into<Rgb<T>>) -> String
549where
550 Rgb<T>: Display,
551{
552 osc!(10, color.into())
553}
554
555pub fn set_default_bg_color<T>(color: impl Into<Rgb<T>>) -> String
557where
558 Rgb<T>: Display,
559{
560 osc!(11, color.into())
561}
562
563pub fn set_cursor_color<T>(color: impl Into<Rgb<T>>) -> String
565where
566 Rgb<T>: Display,
567{
568 osc!(12, color.into())
569}
570
571pub const RESET_ALL_COLOR_CODES: &str = osc!(104);
573pub const RESET_DEFAULT_FG_COLOR: &str = osc!(110);
575pub const RESET_DEFAULT_BG_COLOR: &str = osc!(111);
577pub const RESET_CURSOR_COLOR: &str = osc!(112);
579
580pub const REQUEST_DEFAULT_FG_COLOR: &str = osc!(10, '?');
582pub const REQUEST_DEFAULT_BG_COLOR: &str = osc!(11, '?');
584pub const REQUEST_CURSOR_COLOR: &str = osc!(12, '?');
586
587pub const REQUEST_SELECTION: &str = osc!(52, "", '?');
589
590#[derive(Clone, Debug, Copy, Eq, PartialEq)]
592pub enum Selection {
593 Clipboard,
594 Primary,
595 Secondary,
596 Select,
598 Cut0,
599 Cut1,
600 Cut2,
601 Cut3,
602 Cut4,
603 Cut5,
604 Cut6,
605 Cut7,
606}
607
608impl Selection {
609 fn get_char(&self) -> char {
610 match self {
611 Selection::Clipboard => 'c',
612 Selection::Primary => 'p',
613 Selection::Secondary => 'q',
614 Selection::Select => 's',
615 Selection::Cut0 => '0',
616 Selection::Cut1 => '1',
617 Selection::Cut2 => '2',
618 Selection::Cut3 => '3',
619 Selection::Cut4 => '4',
620 Selection::Cut5 => '5',
621 Selection::Cut6 => '6',
622 Selection::Cut7 => '7',
623 }
624 }
625}
626
627fn prepare_selection(sel: impl IntoIterator<Item = Selection>) -> String {
628 let mut res = "\x1b]52;".to_string();
629 for b in sel {
630 res.push(b.get_char());
631 }
632 res.push(';');
633 res
634}
635
636pub fn request_selectoin(sel: impl IntoIterator<Item = Selection>) -> String {
639 prepare_selection(sel) + "?\x1b\\"
640}
641
642pub fn set_selection(
645 sel: impl IntoIterator<Item = Selection>,
646 data: impl AsRef<[u8]>,
647) -> String {
648 let mut res = prepare_selection(sel);
649 base64::prelude::BASE64_STANDARD.encode_string(data, &mut res);
650 res + "\x1b\\"
651}
652
653pub const BRACKETED_PASTE_START: &str = "\x1b[200~";
659pub const BRACKETED_PASTE_END: &str = "\x1b[201~";
661
662pub trait GetString {
664 fn get_string(self) -> String;
667}
668
669impl GetString for &str {
670 fn get_string(self) -> String {
671 self.to_owned()
672 }
673}
674
675impl GetString for String {
676 fn get_string(self) -> String {
677 self
678 }
679}
680
681#[cfg(test)]
682mod tests {
683 use std::any::TypeId;
684
685 fn type_id_of<T: 'static>(_: T) -> TypeId {
686 TypeId::of::<T>()
687 }
688
689 #[test]
690 fn test_macros() {
691 assert_eq!(csi!('a', 1, 2, 3, 4, 5), "\x1b[1;2;3;4;5a");
692 assert_eq!(csi!('a', 1 + 0, 2, 3, 4, 5), "\x1b[1;2;3;4;5a");
693 assert_eq!(type_id_of(csi!('a', 1, 2, 3, 4, 5)), TypeId::of::<&str>());
694 assert_eq!(
695 type_id_of(csi!('a', 1 + 0, 2, 3, 4, 5)),
696 TypeId::of::<String>()
697 );
698 }
699}