1 //! # Cursor
2 //!
3 //! The `cursor` module provides functionality to work with the terminal cursor.
4 //!
5 //! This documentation does not contain a lot of examples. The reason is that it's fairly
6 //! obvious how to use this crate. Although, we do provide
7 //! [examples](https://github.com/crossterm-rs/examples) repository
8 //! to demonstrate the capabilities.
9 //!
10 //! ## Examples
11 //!
12 //! Cursor actions can be performed with commands.
13 //! Please have a look at [command documention](../index.html#command-api) for a more detailed documentation.
14 //!
15 //! ```no_run
16 //! use std::io::{stdout, Write};
17 //!
18 //! use crossterm::{
19 //!     ExecutableCommand, execute, Result,
20 //!     cursor::{DisableBlinking, EnableBlinking, MoveTo, RestorePosition, SavePosition}
21 //! };
22 //!
23 //! fn main() -> Result<()> {
24 //!     // with macro
25 //!     execute!(
26 //!         stdout(),
27 //!         SavePosition,
28 //!         MoveTo(10, 10),
29 //!         EnableBlinking,
30 //!         DisableBlinking,
31 //!         RestorePosition
32 //!     );
33 //!
34 //!   // with function
35 //!   stdout()
36 //!     .execute(MoveTo(11,11))?
37 //!     .execute(RestorePosition);
38 //!
39 //!  Ok(())
40 //! }
41 //! ```
42 //!
43 //! For manual execution control check out [crossterm::queue](../macro.queue.html).
44 
45 pub use sys::position;
46 
47 use crate::impl_display;
48 use crate::utils::Command;
49 #[cfg(windows)]
50 use crate::utils::Result;
51 
52 mod ansi;
53 pub(crate) mod sys;
54 
55 /// A command that moves the terminal cursor to the given position (column, row).
56 ///
57 /// # Notes
58 ///
59 /// * Top left cell is represented as `0,0`.
60 /// * Commands must be executed/queued for execution otherwise they do nothing.
61 pub struct MoveTo(pub u16, pub u16);
62 
63 impl Command for MoveTo {
64     type AnsiType = String;
65 
ansi_code(&self) -> Self::AnsiType66     fn ansi_code(&self) -> Self::AnsiType {
67         ansi::move_to_csi_sequence(self.0, self.1)
68     }
69 
70     #[cfg(windows)]
execute_winapi(&self) -> Result<()>71     fn execute_winapi(&self) -> Result<()> {
72         sys::move_to(self.0, self.1)
73     }
74 }
75 
76 /// A command that moves the terminal cursor a given number of rows up.
77 ///
78 /// # Notes
79 ///
80 /// Commands must be executed/queued for execution otherwise they do nothing.
81 pub struct MoveUp(pub u16);
82 
83 impl Command for MoveUp {
84     type AnsiType = String;
85 
ansi_code(&self) -> Self::AnsiType86     fn ansi_code(&self) -> Self::AnsiType {
87         ansi::move_up_csi_sequence(self.0)
88     }
89 
90     #[cfg(windows)]
execute_winapi(&self) -> Result<()>91     fn execute_winapi(&self) -> Result<()> {
92         sys::move_up(self.0)
93     }
94 }
95 
96 /// A command that moves the terminal cursor a given number of rows down.
97 ///
98 /// # Notes
99 ///
100 /// Commands must be executed/queued for execution otherwise they do nothing.
101 pub struct MoveDown(pub u16);
102 
103 impl Command for MoveDown {
104     type AnsiType = String;
105 
ansi_code(&self) -> Self::AnsiType106     fn ansi_code(&self) -> Self::AnsiType {
107         ansi::move_down_csi_sequence(self.0)
108     }
109 
110     #[cfg(windows)]
execute_winapi(&self) -> Result<()>111     fn execute_winapi(&self) -> Result<()> {
112         sys::move_down(self.0)
113     }
114 }
115 
116 /// A command that moves the terminal cursor a given number of columns to the left.
117 ///
118 /// # Notes
119 ///
120 /// Commands must be executed/queued for execution otherwise they do nothing.
121 pub struct MoveLeft(pub u16);
122 
123 impl Command for MoveLeft {
124     type AnsiType = String;
125 
ansi_code(&self) -> Self::AnsiType126     fn ansi_code(&self) -> Self::AnsiType {
127         ansi::move_left_csi_sequence(self.0)
128     }
129 
130     #[cfg(windows)]
execute_winapi(&self) -> Result<()>131     fn execute_winapi(&self) -> Result<()> {
132         sys::move_left(self.0)
133     }
134 }
135 
136 /// A command that moves the terminal cursor a given number of columns to the right.
137 ///
138 /// # Notes
139 ///
140 /// Commands must be executed/queued for execution otherwise they do nothing.
141 pub struct MoveRight(pub u16);
142 
143 /// A command that saves the current terminal cursor position.
144 ///
145 /// See the [RestorePosition](./struct.RestorePosition.html) command.
146 ///
147 /// # Notes
148 ///
149 /// - The cursor position is stored globally.
150 /// - Commands must be executed/queued for execution otherwise they do nothing.
151 pub struct SavePosition;
152 
153 impl Command for MoveRight {
154     type AnsiType = String;
155 
ansi_code(&self) -> Self::AnsiType156     fn ansi_code(&self) -> Self::AnsiType {
157         ansi::move_right_csi_sequence(self.0)
158     }
159 
160     #[cfg(windows)]
execute_winapi(&self) -> Result<()>161     fn execute_winapi(&self) -> Result<()> {
162         sys::move_right(self.0)
163     }
164 }
165 
166 impl Command for SavePosition {
167     type AnsiType = &'static str;
168 
ansi_code(&self) -> Self::AnsiType169     fn ansi_code(&self) -> Self::AnsiType {
170         ansi::SAVE_POSITION_CSI_SEQUENCE
171     }
172 
173     #[cfg(windows)]
execute_winapi(&self) -> Result<()>174     fn execute_winapi(&self) -> Result<()> {
175         sys::save_position()
176     }
177 }
178 
179 /// A command that restores the saved terminal cursor position.
180 ///
181 /// See the [SavePosition](./struct.SavePosition.html) command.
182 ///
183 /// # Notes
184 ///
185 /// - The cursor position is stored globally.
186 /// - Commands must be executed/queued for execution otherwise they do nothing.
187 pub struct RestorePosition;
188 
189 impl Command for RestorePosition {
190     type AnsiType = &'static str;
191 
ansi_code(&self) -> Self::AnsiType192     fn ansi_code(&self) -> Self::AnsiType {
193         ansi::RESTORE_POSITION_CSI_SEQUENCE
194     }
195 
196     #[cfg(windows)]
execute_winapi(&self) -> Result<()>197     fn execute_winapi(&self) -> Result<()> {
198         sys::restore_position()
199     }
200 }
201 
202 /// A command that hides the terminal cursor.
203 ///
204 /// # Notes
205 ///
206 /// - Commands must be executed/queued for execution otherwise they do nothing.
207 pub struct Hide;
208 
209 impl Command for Hide {
210     type AnsiType = &'static str;
211 
ansi_code(&self) -> Self::AnsiType212     fn ansi_code(&self) -> Self::AnsiType {
213         ansi::HIDE_CSI_SEQUENCE
214     }
215 
216     #[cfg(windows)]
execute_winapi(&self) -> Result<()>217     fn execute_winapi(&self) -> Result<()> {
218         sys::show_cursor(false)
219     }
220 }
221 
222 /// A command that shows the terminal cursor.
223 ///
224 /// # Notes
225 ///
226 /// - Commands must be executed/queued for execution otherwise they do nothing.
227 pub struct Show;
228 
229 impl Command for Show {
230     type AnsiType = &'static str;
231 
ansi_code(&self) -> Self::AnsiType232     fn ansi_code(&self) -> Self::AnsiType {
233         ansi::SHOW_CSI_SEQUENCE
234     }
235 
236     #[cfg(windows)]
execute_winapi(&self) -> Result<()>237     fn execute_winapi(&self) -> Result<()> {
238         sys::show_cursor(true)
239     }
240 }
241 
242 /// A command that enables blinking of the terminal cursor.
243 ///
244 /// # Notes
245 ///
246 /// - Windows versions lower than Windows 10 do not support this functionality.
247 /// - Commands must be executed/queued for execution otherwise they do nothing.
248 pub struct EnableBlinking;
249 
250 impl Command for EnableBlinking {
251     type AnsiType = &'static str;
252 
ansi_code(&self) -> Self::AnsiType253     fn ansi_code(&self) -> Self::AnsiType {
254         ansi::ENABLE_BLINKING_CSI_SEQUENCE
255     }
256 
257     #[cfg(windows)]
execute_winapi(&self) -> Result<()>258     fn execute_winapi(&self) -> Result<()> {
259         Ok(())
260     }
261 }
262 
263 /// A command that disables blinking of the terminal cursor.
264 ///
265 /// # Notes
266 ///
267 /// - Windows versions lower than Windows 10 do not support this functionality.
268 /// - Commands must be executed/queued for execution otherwise they do nothing.
269 pub struct DisableBlinking;
270 
271 impl Command for DisableBlinking {
272     type AnsiType = &'static str;
273 
ansi_code(&self) -> Self::AnsiType274     fn ansi_code(&self) -> Self::AnsiType {
275         ansi::DISABLE_BLINKING_CSI_SEQUENCE
276     }
277 
278     #[cfg(windows)]
execute_winapi(&self) -> Result<()>279     fn execute_winapi(&self) -> Result<()> {
280         Ok(())
281     }
282 }
283 
284 impl_display!(for MoveTo);
285 impl_display!(for MoveUp);
286 impl_display!(for MoveDown);
287 impl_display!(for MoveLeft);
288 impl_display!(for MoveRight);
289 impl_display!(for SavePosition);
290 impl_display!(for RestorePosition);
291 impl_display!(for Hide);
292 impl_display!(for Show);
293 impl_display!(for EnableBlinking);
294 impl_display!(for DisableBlinking);
295 
296 #[cfg(test)]
297 mod tests {
298     use std::io::{self, stdout, Write};
299 
300     use crate::execute;
301 
302     use super::{
303         position, MoveDown, MoveLeft, MoveRight, MoveTo, MoveUp, RestorePosition, SavePosition,
304     };
305 
306     // Test is disabled, because it's failing on Travis
307     #[test]
308     #[ignore]
test_move_to()309     fn test_move_to() {
310         let (saved_x, saved_y) = position().unwrap();
311 
312         execute!(stdout(), MoveTo(saved_x + 1, saved_y + 1)).unwrap();
313         assert_eq!(position().unwrap(), (saved_x + 1, saved_y + 1));
314 
315         execute!(stdout(), MoveTo(saved_x, saved_y)).unwrap();
316         assert_eq!(position().unwrap(), (saved_x, saved_y));
317     }
318 
319     // Test is disabled, because it's failing on Travis
320     #[test]
321     #[ignore]
test_move_right()322     fn test_move_right() {
323         let (saved_x, saved_y) = position().unwrap();
324         execute!(io::stdout(), MoveRight(1)).unwrap();
325         assert_eq!(position().unwrap(), (saved_x + 1, saved_y));
326     }
327 
328     // Test is disabled, because it's failing on Travis
329     #[test]
330     #[ignore]
test_move_left()331     fn test_move_left() {
332         execute!(stdout(), MoveTo(2, 0), MoveLeft(2)).unwrap();
333         assert_eq!(position().unwrap(), (0, 0));
334     }
335 
336     // Test is disabled, because it's failing on Travis
337     #[test]
338     #[ignore]
test_move_up()339     fn test_move_up() {
340         execute!(stdout(), MoveTo(0, 2), MoveUp(2)).unwrap();
341         assert_eq!(position().unwrap(), (0, 0));
342     }
343 
344     // Test is disabled, because it's failing on Travis
345     #[test]
346     #[ignore]
test_move_down()347     fn test_move_down() {
348         execute!(stdout(), MoveTo(0, 0), MoveDown(2)).unwrap();
349 
350         assert_eq!(position().unwrap(), (0, 2));
351     }
352 
353     // Test is disabled, because it's failing on Travis
354     #[test]
355     #[ignore]
test_save_restore_position()356     fn test_save_restore_position() {
357         let (saved_x, saved_y) = position().unwrap();
358 
359         execute!(
360             stdout(),
361             SavePosition,
362             MoveTo(saved_x + 1, saved_y + 1),
363             RestorePosition
364         )
365         .unwrap();
366 
367         let (x, y) = position().unwrap();
368 
369         assert_eq!(x, saved_x);
370         assert_eq!(y, saved_y);
371     }
372 }
373