crossterm/
cursor.rs

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