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 |
|
45 | use std::fmt;
|
46 |
|
47 | use crate::{csi, impl_display, Command};
|
48 |
|
49 | pub(crate) mod sys;
|
50 |
|
51 | #[cfg (feature = "events" )]
|
52 | pub 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)]
|
60 | pub struct MoveTo(pub u16, pub u16);
|
61 |
|
62 | impl 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)]
|
81 | pub struct MoveToNextLine(pub u16);
|
82 |
|
83 | impl 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)]
|
106 | pub struct MoveToPreviousLine(pub u16);
|
107 |
|
108 | impl 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)]
|
129 | pub struct MoveToColumn(pub u16);
|
130 |
|
131 | impl 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)]
|
149 | pub struct MoveToRow(pub u16);
|
150 |
|
151 | impl 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)]
|
170 | pub struct MoveUp(pub u16);
|
171 |
|
172 | impl 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)]
|
191 | pub struct MoveRight(pub u16);
|
192 |
|
193 | impl 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)]
|
212 | pub struct MoveDown(pub u16);
|
213 |
|
214 | impl 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)]
|
233 | pub struct MoveLeft(pub u16);
|
234 |
|
235 | impl 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)]
|
256 | pub struct SavePosition;
|
257 |
|
258 | impl 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)]
|
278 | pub struct RestorePosition;
|
279 |
|
280 | impl 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)]
|
297 | pub struct Hide;
|
298 |
|
299 | impl 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)]
|
316 | pub struct Show;
|
317 |
|
318 | impl 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)]
|
337 | pub struct EnableBlinking;
|
338 | impl 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)]
|
356 | pub struct DisableBlinking;
|
357 | impl 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)]
|
374 | pub 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 |
|
391 | impl 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 |
|
410 | impl_display!(for MoveTo);
|
411 | impl_display!(for MoveToColumn);
|
412 | impl_display!(for MoveToRow);
|
413 | impl_display!(for MoveToNextLine);
|
414 | impl_display!(for MoveToPreviousLine);
|
415 | impl_display!(for MoveUp);
|
416 | impl_display!(for MoveDown);
|
417 | impl_display!(for MoveLeft);
|
418 | impl_display!(for MoveRight);
|
419 | impl_display!(for SavePosition);
|
420 | impl_display!(for RestorePosition);
|
421 | impl_display!(for Hide);
|
422 | impl_display!(for Show);
|
423 | impl_display!(for EnableBlinking);
|
424 | impl_display!(for DisableBlinking);
|
425 | impl_display!(for SetCursorStyle);
|
426 |
|
427 | #[cfg (test)]
|
428 | #[cfg (feature = "events" )]
|
429 | mod 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 | |