crossterm_cursor/cursor/
ansi.rs1use crossterm_utils::{csi, write_cout, Result};
6
7use crate::sys::{get_cursor_position, show_cursor};
8
9use super::Cursor;
10
11pub(crate) fn goto_csi_sequence(x: u16, y: u16) -> String {
12 format!(csi!("{};{}H"), y + 1, x + 1)
13}
14
15pub(crate) fn move_up_csi_sequence(count: u16) -> String {
16 format!(csi!("{}A"), count)
17}
18
19pub(crate) fn move_right_csi_sequence(count: u16) -> String {
20 format!(csi!("{}C"), count)
21}
22
23pub(crate) fn move_down_csi_sequence(count: u16) -> String {
24 format!(csi!("{}B"), count)
25}
26
27pub(crate) fn move_left_csi_sequence(count: u16) -> String {
28 format!(csi!("{}D"), count)
29}
30
31pub(crate) static SAVE_POSITION_CSI_SEQUENCE: &'static str = csi!("s");
32pub(crate) static RESTORE_POSITION_CSI_SEQUENCE: &'static str = csi!("u");
33pub(crate) static HIDE_CSI_SEQUENCE: &'static str = csi!("?25l");
34pub(crate) static SHOW_CSI_SEQUENCE: &'static str = csi!("?25h");
35pub(crate) static BLINKING_ON_CSI_SEQUENCE: &'static str = csi!("?12h");
36pub(crate) static BLINKING_OFF_CSI_SEQUENCE: &'static str = csi!("?12l");
37
38pub(crate) struct AnsiCursor;
40
41impl AnsiCursor {
42 pub(crate) fn new() -> AnsiCursor {
43 AnsiCursor
44 }
45}
46
47impl Cursor for AnsiCursor {
48 fn goto(&self, x: u16, y: u16) -> Result<()> {
49 write_cout!(goto_csi_sequence(x, y))?;
50 Ok(())
51 }
52
53 fn pos(&self) -> Result<(u16, u16)> {
54 get_cursor_position()
55 }
56
57 fn move_up(&self, count: u16) -> Result<()> {
58 write_cout!(move_up_csi_sequence(count))?;
59 Ok(())
60 }
61
62 fn move_right(&self, count: u16) -> Result<()> {
63 write_cout!(move_right_csi_sequence(count))?;
64 Ok(())
65 }
66
67 fn move_down(&self, count: u16) -> Result<()> {
68 write_cout!(move_down_csi_sequence(count))?;
69 Ok(())
70 }
71
72 fn move_left(&self, count: u16) -> Result<()> {
73 write_cout!(move_left_csi_sequence(count))?;
74 Ok(())
75 }
76
77 fn save_position(&self) -> Result<()> {
78 write_cout!(SAVE_POSITION_CSI_SEQUENCE)?;
79 Ok(())
80 }
81
82 fn restore_position(&self) -> Result<()> {
83 write_cout!(RESTORE_POSITION_CSI_SEQUENCE)?;
84 Ok(())
85 }
86
87 fn hide(&self) -> Result<()> {
88 show_cursor(false)?;
89 Ok(())
90 }
91
92 fn show(&self) -> Result<()> {
93 show_cursor(true)?;
94 Ok(())
95 }
96
97 fn blink(&self, blink: bool) -> Result<()> {
98 if blink {
99 write_cout!(BLINKING_ON_CSI_SEQUENCE)?;
100 } else {
101 write_cout!(BLINKING_OFF_CSI_SEQUENCE)?;
102 }
103 Ok(())
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::{AnsiCursor, Cursor};
110
111 #[test]
113 #[ignore]
114 fn test_save_restore_position() {
115 if try_enable_ansi() {
116 let cursor = AnsiCursor::new();
117
118 let (saved_x, saved_y) = cursor.pos().unwrap();
119
120 cursor.save_position().unwrap();
121 cursor.goto(saved_x + 1, saved_y + 1).unwrap();
122 cursor.restore_position().unwrap();
123
124 let (x, y) = cursor.pos().unwrap();
125
126 assert_eq!(x, saved_x);
127 assert_eq!(y, saved_y);
128 }
129 }
130
131 #[test]
133 #[ignore]
134 fn test_goto() {
135 if try_enable_ansi() {
136 let cursor = AnsiCursor::new();
137
138 let (saved_x, saved_y) = cursor.pos().unwrap();
139
140 cursor.goto(saved_x + 1, saved_y + 1).unwrap();
141 assert_eq!(cursor.pos().unwrap(), (saved_x + 1, saved_y + 1));
142
143 cursor.goto(saved_x, saved_y).unwrap();
144 assert_eq!(cursor.pos().unwrap(), (saved_x, saved_y));
145 }
146 }
147
148 fn try_enable_ansi() -> bool {
149 #[cfg(windows)]
150 {
151 if cfg!(target_os = "windows") {
152 use crossterm_utils::sys::winapi::ansi::set_virtual_terminal_processing;
153
154 match set_virtual_terminal_processing(true) {
156 Ok(_) => return true,
157 Err(_) => return false,
158 }
159 }
160 }
161
162 true
163 }
164}