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