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