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/crossterm/tree/master/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 documentation](../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 use std::fmt;
46 
47 #[cfg(windows)]
48 use crate::Result;
49 use crate::{csi, impl_display, Command};
50 
51 pub use sys::position;
52 
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 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
62 pub struct MoveTo(pub u16, pub u16);
63 
64 impl Command for MoveTo {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result65     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
66         write!(f, csi!("{};{}H"), self.1 + 1, self.0 + 1)
67     }
68 
69     #[cfg(windows)]
execute_winapi(&self) -> Result<()>70     fn execute_winapi(&self) -> Result<()> {
71         sys::move_to(self.0, self.1)
72     }
73 }
74 
75 /// A command that moves the terminal cursor down the given number of lines,
76 /// and moves it to the first column.
77 ///
78 /// # Notes
79 ///
80 /// Commands must be executed/queued for execution otherwise they do nothing.
81 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
82 pub struct MoveToNextLine(pub u16);
83 
84 impl Command for MoveToNextLine {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result85     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
86         write!(f, csi!("{}E"), self.0)
87     }
88 
89     #[cfg(windows)]
execute_winapi(&self) -> Result<()>90     fn execute_winapi(&self) -> Result<()> {
91         sys::move_to_next_line(self.0)
92     }
93 }
94 
95 /// A command that moves the terminal cursor up the given number of lines,
96 /// and moves it to the first column.
97 ///
98 /// # Notes
99 ///
100 /// Commands must be executed/queued for execution otherwise they do nothing.
101 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
102 pub struct MoveToPreviousLine(pub u16);
103 
104 impl Command for MoveToPreviousLine {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result105     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
106         write!(f, csi!("{}F"), self.0)
107     }
108 
109     #[cfg(windows)]
execute_winapi(&self) -> Result<()>110     fn execute_winapi(&self) -> Result<()> {
111         sys::move_to_previous_line(self.0)
112     }
113 }
114 
115 /// A command that moves the terminal cursor to the given column on the current row.
116 ///
117 /// # Notes
118 ///
119 /// Commands must be executed/queued for execution otherwise they do nothing.
120 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
121 pub struct MoveToColumn(pub u16);
122 
123 impl Command for MoveToColumn {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result124     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
125         write!(f, csi!("{}G"), self.0)
126     }
127 
128     #[cfg(windows)]
execute_winapi(&self) -> Result<()>129     fn execute_winapi(&self) -> Result<()> {
130         sys::move_to_column(self.0)
131     }
132 }
133 
134 /// A command that moves the terminal cursor to the given row on the current column.
135 ///
136 /// # Notes
137 ///
138 /// Commands must be executed/queued for execution otherwise they do nothing.
139 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
140 pub struct MoveToRow(pub u16);
141 
142 impl Command for MoveToRow {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result143     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
144         write!(f, csi!("{}d"), self.0)
145     }
146 
147     #[cfg(windows)]
execute_winapi(&self) -> Result<()>148     fn execute_winapi(&self) -> Result<()> {
149         sys::move_to_row(self.0)
150     }
151 }
152 
153 /// A command that moves the terminal cursor a given number of rows up.
154 ///
155 /// # Notes
156 ///
157 /// Commands must be executed/queued for execution otherwise they do nothing.
158 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
159 pub struct MoveUp(pub u16);
160 
161 impl Command for MoveUp {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result162     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
163         if self.0 != 0 {
164             write!(f, csi!("{}A"), self.0)?;
165         }
166         Ok(())
167     }
168 
169     #[cfg(windows)]
execute_winapi(&self) -> Result<()>170     fn execute_winapi(&self) -> Result<()> {
171         sys::move_up(self.0)
172     }
173 }
174 
175 /// A command that moves the terminal cursor a given number of columns to the right.
176 ///
177 /// # Notes
178 ///
179 /// Commands must be executed/queued for execution otherwise they do nothing.
180 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
181 pub struct MoveRight(pub u16);
182 
183 impl Command for MoveRight {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result184     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
185         if self.0 != 0 {
186             write!(f, csi!("{}C"), self.0)?;
187         }
188         Ok(())
189     }
190 
191     #[cfg(windows)]
execute_winapi(&self) -> Result<()>192     fn execute_winapi(&self) -> Result<()> {
193         sys::move_right(self.0)
194     }
195 }
196 
197 /// A command that moves the terminal cursor a given number of rows down.
198 ///
199 /// # Notes
200 ///
201 /// Commands must be executed/queued for execution otherwise they do nothing.
202 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
203 pub struct MoveDown(pub u16);
204 
205 impl Command for MoveDown {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result206     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
207         if self.0 != 0 {
208             write!(f, csi!("{}B"), self.0)?;
209         }
210         Ok(())
211     }
212 
213     #[cfg(windows)]
execute_winapi(&self) -> Result<()>214     fn execute_winapi(&self) -> Result<()> {
215         sys::move_down(self.0)
216     }
217 }
218 
219 /// A command that moves the terminal cursor a given number of columns to the left.
220 ///
221 /// # Notes
222 ///
223 /// Commands must be executed/queued for execution otherwise they do nothing.
224 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
225 pub struct MoveLeft(pub u16);
226 
227 impl Command for MoveLeft {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result228     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
229         if self.0 != 0 {
230             write!(f, csi!("{}D"), self.0)?;
231         }
232         Ok(())
233     }
234 
235     #[cfg(windows)]
execute_winapi(&self) -> Result<()>236     fn execute_winapi(&self) -> Result<()> {
237         sys::move_left(self.0)
238     }
239 }
240 
241 /// A command that saves the current terminal cursor position.
242 ///
243 /// See the [RestorePosition](./struct.RestorePosition.html) command.
244 ///
245 /// # Notes
246 ///
247 /// - The cursor position is stored globally.
248 /// - Commands must be executed/queued for execution otherwise they do nothing.
249 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
250 pub struct SavePosition;
251 
252 impl Command for SavePosition {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result253     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
254         f.write_str("\x1B7")
255     }
256 
257     #[cfg(windows)]
execute_winapi(&self) -> Result<()>258     fn execute_winapi(&self) -> Result<()> {
259         sys::save_position()
260     }
261 }
262 
263 /// A command that restores the saved terminal cursor position.
264 ///
265 /// See the [SavePosition](./struct.SavePosition.html) command.
266 ///
267 /// # Notes
268 ///
269 /// - The cursor position is stored globally.
270 /// - Commands must be executed/queued for execution otherwise they do nothing.
271 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
272 pub struct RestorePosition;
273 
274 impl Command for RestorePosition {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result275     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
276         f.write_str("\x1B8")
277     }
278 
279     #[cfg(windows)]
execute_winapi(&self) -> Result<()>280     fn execute_winapi(&self) -> Result<()> {
281         sys::restore_position()
282     }
283 }
284 
285 /// A command that hides the terminal cursor.
286 ///
287 /// # Notes
288 ///
289 /// - Commands must be executed/queued for execution otherwise they do nothing.
290 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
291 pub struct Hide;
292 
293 impl Command for Hide {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result294     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
295         f.write_str(csi!("?25l"))
296     }
297 
298     #[cfg(windows)]
execute_winapi(&self) -> Result<()>299     fn execute_winapi(&self) -> Result<()> {
300         sys::show_cursor(false)
301     }
302 }
303 
304 /// A command that shows the terminal cursor.
305 ///
306 /// # Notes
307 ///
308 /// - Commands must be executed/queued for execution otherwise they do nothing.
309 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
310 pub struct Show;
311 
312 impl Command for Show {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result313     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
314         f.write_str(csi!("?25h"))
315     }
316 
317     #[cfg(windows)]
execute_winapi(&self) -> Result<()>318     fn execute_winapi(&self) -> Result<()> {
319         sys::show_cursor(true)
320     }
321 }
322 
323 /// A command that enables blinking of the terminal cursor.
324 ///
325 /// # Notes
326 ///
327 /// - Windows versions lower than Windows 10 do not support this functionality.
328 /// - Commands must be executed/queued for execution otherwise they do nothing.
329 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
330 pub struct EnableBlinking;
331 
332 impl Command for EnableBlinking {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result333     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
334         f.write_str(csi!("?12h"))
335     }
336 
337     #[cfg(windows)]
execute_winapi(&self) -> Result<()>338     fn execute_winapi(&self) -> Result<()> {
339         Ok(())
340     }
341 }
342 
343 /// A command that disables blinking of the terminal cursor.
344 ///
345 /// # Notes
346 ///
347 /// - Windows versions lower than Windows 10 do not support this functionality.
348 /// - Commands must be executed/queued for execution otherwise they do nothing.
349 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
350 pub struct DisableBlinking;
351 
352 impl Command for DisableBlinking {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result353     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
354         f.write_str(csi!("?12l"))
355     }
356 
357     #[cfg(windows)]
execute_winapi(&self) -> Result<()>358     fn execute_winapi(&self) -> Result<()> {
359         Ok(())
360     }
361 }
362 
363 /// All supported cursor shapes
364 ///
365 /// # Note
366 ///
367 /// - Used with SetCursorShape
368 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
369 pub enum CursorShape {
370     UnderScore,
371     Line,
372     Block,
373 }
374 
375 /// A command that sets the shape of the cursor
376 ///
377 /// # Note
378 ///
379 /// - Commands must be executed/queued for execution otherwise they do nothing.
380 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
381 pub struct SetCursorShape(pub CursorShape);
382 
383 impl Command for SetCursorShape {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result384     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
385         use CursorShape::*;
386         match self.0 {
387             UnderScore => f.write_str("\x1b[3 q"),
388             Line => f.write_str("\x1b[5 q"),
389             Block => f.write_str("\x1b[2 q"),
390         }
391     }
392 
393     #[cfg(windows)]
execute_winapi(&self) -> Result<()>394     fn execute_winapi(&self) -> Result<()> {
395         Ok(())
396     }
397 }
398 
399 impl_display!(for MoveTo);
400 impl_display!(for MoveToColumn);
401 impl_display!(for MoveToRow);
402 impl_display!(for MoveToNextLine);
403 impl_display!(for MoveToPreviousLine);
404 impl_display!(for MoveUp);
405 impl_display!(for MoveDown);
406 impl_display!(for MoveLeft);
407 impl_display!(for MoveRight);
408 impl_display!(for SavePosition);
409 impl_display!(for RestorePosition);
410 impl_display!(for Hide);
411 impl_display!(for Show);
412 impl_display!(for EnableBlinking);
413 impl_display!(for DisableBlinking);
414 impl_display!(for SetCursorShape);
415 
416 #[cfg(test)]
417 mod tests {
418     use std::io::{self, stdout};
419 
420     use crate::execute;
421 
422     use super::{
423         position, MoveDown, MoveLeft, MoveRight, MoveTo, MoveUp, RestorePosition, SavePosition,
424     };
425 
426     // Test is disabled, because it's failing on Travis
427     #[test]
428     #[ignore]
test_move_to()429     fn test_move_to() {
430         let (saved_x, saved_y) = position().unwrap();
431 
432         execute!(stdout(), MoveTo(saved_x + 1, saved_y + 1)).unwrap();
433         assert_eq!(position().unwrap(), (saved_x + 1, saved_y + 1));
434 
435         execute!(stdout(), MoveTo(saved_x, saved_y)).unwrap();
436         assert_eq!(position().unwrap(), (saved_x, saved_y));
437     }
438 
439     // Test is disabled, because it's failing on Travis
440     #[test]
441     #[ignore]
test_move_right()442     fn test_move_right() {
443         let (saved_x, saved_y) = position().unwrap();
444         execute!(io::stdout(), MoveRight(1)).unwrap();
445         assert_eq!(position().unwrap(), (saved_x + 1, saved_y));
446     }
447 
448     // Test is disabled, because it's failing on Travis
449     #[test]
450     #[ignore]
test_move_left()451     fn test_move_left() {
452         execute!(stdout(), MoveTo(2, 0), MoveLeft(2)).unwrap();
453         assert_eq!(position().unwrap(), (0, 0));
454     }
455 
456     // Test is disabled, because it's failing on Travis
457     #[test]
458     #[ignore]
test_move_up()459     fn test_move_up() {
460         execute!(stdout(), MoveTo(0, 2), MoveUp(2)).unwrap();
461         assert_eq!(position().unwrap(), (0, 0));
462     }
463 
464     // Test is disabled, because it's failing on Travis
465     #[test]
466     #[ignore]
test_move_down()467     fn test_move_down() {
468         execute!(stdout(), MoveTo(0, 0), MoveDown(2)).unwrap();
469 
470         assert_eq!(position().unwrap(), (0, 2));
471     }
472 
473     // Test is disabled, because it's failing on Travis
474     #[test]
475     #[ignore]
test_save_restore_position()476     fn test_save_restore_position() {
477         let (saved_x, saved_y) = position().unwrap();
478 
479         execute!(
480             stdout(),
481             SavePosition,
482             MoveTo(saved_x + 1, saved_y + 1),
483             RestorePosition
484         )
485         .unwrap();
486 
487         let (x, y) = position().unwrap();
488 
489         assert_eq!(x, saved_x);
490         assert_eq!(y, saved_y);
491     }
492 }
493