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::{impl_display, Command};
50 
51 pub use sys::position;
52 
53 mod ansi;
54 pub(crate) mod sys;
55 
56 /// A command that moves the terminal cursor to the given position (column, row).
57 ///
58 /// # Notes
59 ///
60 /// * Top left cell is represented as `0,0`.
61 /// * Commands must be executed/queued for execution otherwise they do nothing.
62 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
63 pub struct MoveTo(pub u16, pub u16);
64 
65 impl Command for MoveTo {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result66     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
67         ansi::move_to_csi_sequence(f, self.0, self.1)
68     }
69 
70     #[cfg(windows)]
execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()>71     fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> {
72         sys::move_to(self.0, self.1)
73     }
74 }
75 
76 /// A command that moves the terminal cursor up the given number of lines,
77 /// and moves it to the first column.
78 ///
79 /// # Notes
80 ///
81 /// Commands must be executed/queued for execution otherwise they do nothing.
82 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
83 pub struct MoveToNextLine(pub u16);
84 
85 impl Command for MoveToNextLine {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result86     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
87         ansi::move_to_next_line_csi_sequence(f, self.0)
88     }
89 
90     #[cfg(windows)]
execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()>91     fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> {
92         sys::move_to_next_line(self.0)
93     }
94 }
95 
96 /// A command that moves the terminal cursor down the given number of lines,
97 /// and moves it to the first column.
98 ///
99 /// # Notes
100 ///
101 /// Commands must be executed/queued for execution otherwise they do nothing.
102 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
103 pub struct MoveToPreviousLine(pub u16);
104 
105 impl Command for MoveToPreviousLine {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result106     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
107         ansi::move_to_previous_line_csi_sequence(f, self.0)
108     }
109 
110     #[cfg(windows)]
execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()>111     fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> {
112         sys::move_to_previous_line(self.0)
113     }
114 }
115 
116 /// A command that moves the terminal cursor to the given column on the current row.
117 ///
118 /// # Notes
119 ///
120 /// Commands must be executed/queued for execution otherwise they do nothing.
121 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
122 pub struct MoveToColumn(pub u16);
123 
124 impl Command for MoveToColumn {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result125     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
126         ansi::move_to_column_csi_sequence(f, self.0)
127     }
128 
129     #[cfg(windows)]
execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()>130     fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> {
131         sys::move_to_column(self.0)
132     }
133 }
134 
135 /// A command that moves the terminal cursor a given number of rows up.
136 ///
137 /// # Notes
138 ///
139 /// Commands must be executed/queued for execution otherwise they do nothing.
140 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
141 pub struct MoveUp(pub u16);
142 
143 impl Command for MoveUp {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result144     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
145         ansi::move_up_csi_sequence(f, self.0)
146     }
147 
148     #[cfg(windows)]
execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()>149     fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> {
150         sys::move_up(self.0)
151     }
152 }
153 
154 /// A command that moves the terminal cursor a given number of columns to the right.
155 ///
156 /// # Notes
157 ///
158 /// Commands must be executed/queued for execution otherwise they do nothing.
159 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
160 pub struct MoveRight(pub u16);
161 
162 impl Command for MoveRight {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result163     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
164         ansi::move_right_csi_sequence(f, self.0)
165     }
166 
167     #[cfg(windows)]
execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()>168     fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> {
169         sys::move_right(self.0)
170     }
171 }
172 
173 /// A command that moves the terminal cursor a given number of rows down.
174 ///
175 /// # Notes
176 ///
177 /// Commands must be executed/queued for execution otherwise they do nothing.
178 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
179 pub struct MoveDown(pub u16);
180 
181 impl Command for MoveDown {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result182     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
183         ansi::move_down_csi_sequence(f, self.0)
184     }
185 
186     #[cfg(windows)]
execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()>187     fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> {
188         sys::move_down(self.0)
189     }
190 }
191 
192 /// A command that moves the terminal cursor a given number of columns to the left.
193 ///
194 /// # Notes
195 ///
196 /// Commands must be executed/queued for execution otherwise they do nothing.
197 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
198 pub struct MoveLeft(pub u16);
199 
200 impl Command for MoveLeft {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result201     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
202         ansi::move_left_csi_sequence(f, self.0)
203     }
204 
205     #[cfg(windows)]
execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()>206     fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> {
207         sys::move_left(self.0)
208     }
209 }
210 
211 /// A command that saves the current terminal cursor position.
212 ///
213 /// See the [RestorePosition](./struct.RestorePosition.html) command.
214 ///
215 /// # Notes
216 ///
217 /// - The cursor position is stored globally.
218 /// - Commands must be executed/queued for execution otherwise they do nothing.
219 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
220 pub struct SavePosition;
221 
222 impl Command for SavePosition {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result223     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
224         f.write_str(ansi::SAVE_POSITION_CSI_SEQUENCE)
225     }
226 
227     #[cfg(windows)]
execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()>228     fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> {
229         sys::save_position()
230     }
231 }
232 
233 /// A command that restores the saved terminal cursor position.
234 ///
235 /// See the [SavePosition](./struct.SavePosition.html) command.
236 ///
237 /// # Notes
238 ///
239 /// - The cursor position is stored globally.
240 /// - Commands must be executed/queued for execution otherwise they do nothing.
241 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
242 pub struct RestorePosition;
243 
244 impl Command for RestorePosition {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result245     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
246         f.write_str(ansi::RESTORE_POSITION_CSI_SEQUENCE)
247     }
248 
249     #[cfg(windows)]
execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()>250     fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> {
251         sys::restore_position()
252     }
253 }
254 
255 /// A command that hides the terminal cursor.
256 ///
257 /// # Notes
258 ///
259 /// - Commands must be executed/queued for execution otherwise they do nothing.
260 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
261 pub struct Hide;
262 
263 impl Command for Hide {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result264     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
265         f.write_str(ansi::HIDE_CSI_SEQUENCE)
266     }
267 
268     #[cfg(windows)]
execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()>269     fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> {
270         sys::show_cursor(false)
271     }
272 }
273 
274 /// A command that shows the terminal cursor.
275 ///
276 /// # Notes
277 ///
278 /// - Commands must be executed/queued for execution otherwise they do nothing.
279 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
280 pub struct Show;
281 
282 impl Command for Show {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result283     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
284         f.write_str(ansi::SHOW_CSI_SEQUENCE)
285     }
286 
287     #[cfg(windows)]
execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()>288     fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> {
289         sys::show_cursor(true)
290     }
291 }
292 
293 /// A command that enables blinking of the terminal cursor.
294 ///
295 /// # Notes
296 ///
297 /// - Windows versions lower than Windows 10 do not support this functionality.
298 /// - Commands must be executed/queued for execution otherwise they do nothing.
299 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
300 pub struct EnableBlinking;
301 
302 impl Command for EnableBlinking {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result303     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
304         f.write_str(ansi::ENABLE_BLINKING_CSI_SEQUENCE)
305     }
306 
307     #[cfg(windows)]
execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()>308     fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> {
309         Ok(())
310     }
311 }
312 
313 /// A command that disables blinking of the terminal cursor.
314 ///
315 /// # Notes
316 ///
317 /// - Windows versions lower than Windows 10 do not support this functionality.
318 /// - Commands must be executed/queued for execution otherwise they do nothing.
319 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
320 pub struct DisableBlinking;
321 
322 impl Command for DisableBlinking {
write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result323     fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
324         f.write_str(ansi::DISABLE_BLINKING_CSI_SEQUENCE)
325     }
326 
327     #[cfg(windows)]
execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()>328     fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> {
329         Ok(())
330     }
331 }
332 
333 impl_display!(for MoveTo);
334 impl_display!(for MoveToColumn);
335 impl_display!(for MoveToNextLine);
336 impl_display!(for MoveToPreviousLine);
337 impl_display!(for MoveUp);
338 impl_display!(for MoveDown);
339 impl_display!(for MoveLeft);
340 impl_display!(for MoveRight);
341 impl_display!(for SavePosition);
342 impl_display!(for RestorePosition);
343 impl_display!(for Hide);
344 impl_display!(for Show);
345 impl_display!(for EnableBlinking);
346 impl_display!(for DisableBlinking);
347 
348 #[cfg(test)]
349 mod tests {
350     use std::io::{self, stdout};
351 
352     use crate::execute;
353 
354     use super::{
355         position, MoveDown, MoveLeft, MoveRight, MoveTo, MoveUp, RestorePosition, SavePosition,
356     };
357 
358     // Test is disabled, because it's failing on Travis
359     #[test]
360     #[ignore]
test_move_to()361     fn test_move_to() {
362         let (saved_x, saved_y) = position().unwrap();
363 
364         execute!(stdout(), MoveTo(saved_x + 1, saved_y + 1)).unwrap();
365         assert_eq!(position().unwrap(), (saved_x + 1, saved_y + 1));
366 
367         execute!(stdout(), MoveTo(saved_x, saved_y)).unwrap();
368         assert_eq!(position().unwrap(), (saved_x, saved_y));
369     }
370 
371     // Test is disabled, because it's failing on Travis
372     #[test]
373     #[ignore]
test_move_right()374     fn test_move_right() {
375         let (saved_x, saved_y) = position().unwrap();
376         execute!(io::stdout(), MoveRight(1)).unwrap();
377         assert_eq!(position().unwrap(), (saved_x + 1, saved_y));
378     }
379 
380     // Test is disabled, because it's failing on Travis
381     #[test]
382     #[ignore]
test_move_left()383     fn test_move_left() {
384         execute!(stdout(), MoveTo(2, 0), MoveLeft(2)).unwrap();
385         assert_eq!(position().unwrap(), (0, 0));
386     }
387 
388     // Test is disabled, because it's failing on Travis
389     #[test]
390     #[ignore]
test_move_up()391     fn test_move_up() {
392         execute!(stdout(), MoveTo(0, 2), MoveUp(2)).unwrap();
393         assert_eq!(position().unwrap(), (0, 0));
394     }
395 
396     // Test is disabled, because it's failing on Travis
397     #[test]
398     #[ignore]
test_move_down()399     fn test_move_down() {
400         execute!(stdout(), MoveTo(0, 0), MoveDown(2)).unwrap();
401 
402         assert_eq!(position().unwrap(), (0, 2));
403     }
404 
405     // Test is disabled, because it's failing on Travis
406     #[test]
407     #[ignore]
test_save_restore_position()408     fn test_save_restore_position() {
409         let (saved_x, saved_y) = position().unwrap();
410 
411         execute!(
412             stdout(),
413             SavePosition,
414             MoveTo(saved_x + 1, saved_y + 1),
415             RestorePosition
416         )
417         .unwrap();
418 
419         let (x, y) = position().unwrap();
420 
421         assert_eq!(x, saved_x);
422         assert_eq!(y, saved_y);
423     }
424 }
425