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}
505