2019-10-23 01:33:38 +11:00
|
|
|
//! # Cursor
|
|
|
|
//!
|
2019-10-28 00:33:47 +11:00
|
|
|
//! The `cursor` module provides functionality to work with the terminal cursor.
|
2019-10-23 01:33:38 +11:00
|
|
|
//!
|
|
|
|
//! This documentation does not contain a lot of examples. The reason is that it's fairly
|
|
|
|
//! obvious how to use this crate. Although, we do provide
|
|
|
|
//! [examples](https://github.com/crossterm-rs/examples) repository
|
|
|
|
//! to demonstrate the capabilities.
|
|
|
|
//!
|
|
|
|
//! ## Examples
|
|
|
|
//!
|
2019-10-28 00:33:47 +11:00
|
|
|
//! Cursor actions can be performed with commands.
|
|
|
|
//! Please have a look at [command documention](../index.html#command-api) for a more detailed documentation.
|
2019-10-23 01:33:38 +11:00
|
|
|
//!
|
|
|
|
//! ```no_run
|
|
|
|
//! use std::io::{stdout, Write};
|
|
|
|
//!
|
2019-10-28 00:33:47 +11:00
|
|
|
//! use crossterm::{
|
|
|
|
//! ExecutableCommand, execute, Result,
|
|
|
|
//! cursor::{DisableBlinking, EnableBlinking, MoveTo, RestorePosition, SavePosition}
|
|
|
|
//! };
|
2019-10-23 01:33:38 +11:00
|
|
|
//!
|
|
|
|
//! fn main() -> Result<()> {
|
2019-10-28 00:33:47 +11:00
|
|
|
//! // with macro
|
2019-10-23 01:33:38 +11:00
|
|
|
//! execute!(
|
|
|
|
//! stdout(),
|
2019-10-28 00:33:47 +11:00
|
|
|
//! SavePosition,
|
|
|
|
//! MoveTo(10, 10),
|
|
|
|
//! EnableBlinking,
|
|
|
|
//! DisableBlinking,
|
|
|
|
//! RestorePosition
|
|
|
|
//! );
|
|
|
|
//!
|
|
|
|
//! // with function
|
|
|
|
//! stdout()
|
|
|
|
//! .execute(MoveTo(11,11))?
|
|
|
|
//! .execute(RestorePosition);
|
|
|
|
//!
|
|
|
|
//! Ok(())
|
2019-10-23 01:33:38 +11:00
|
|
|
//! }
|
|
|
|
//! ```
|
2019-10-28 00:33:47 +11:00
|
|
|
//!
|
|
|
|
//! For manual execution control check out [crossterm::queue](../macro.queue.html).
|
|
|
|
|
|
|
|
pub use sys::position;
|
2019-10-23 01:33:38 +11:00
|
|
|
|
|
|
|
#[cfg(windows)]
|
2019-12-12 03:10:34 +11:00
|
|
|
use crate::Result;
|
|
|
|
use crate::{impl_display, Command};
|
2019-10-23 01:33:38 +11:00
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
mod ansi;
|
|
|
|
pub(crate) mod sys;
|
2019-10-23 01:33:38 +11:00
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
/// A command that moves the terminal cursor to the given position (column, row).
|
2019-10-23 01:33:38 +11:00
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
///
|
2019-10-28 00:33:47 +11:00
|
|
|
/// * Top left cell is represented as `0,0`.
|
|
|
|
/// * Commands must be executed/queued for execution otherwise they do nothing.
|
|
|
|
pub struct MoveTo(pub u16, pub u16);
|
2019-10-23 01:33:38 +11:00
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
impl Command for MoveTo {
|
2019-10-23 01:33:38 +11:00
|
|
|
type AnsiType = String;
|
|
|
|
|
|
|
|
fn ansi_code(&self) -> Self::AnsiType {
|
2019-10-28 00:33:47 +11:00
|
|
|
ansi::move_to_csi_sequence(self.0, self.1)
|
2019-10-23 01:33:38 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
fn execute_winapi(&self) -> Result<()> {
|
2019-10-28 00:33:47 +11:00
|
|
|
sys::move_to(self.0, self.1)
|
2019-10-23 01:33:38 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-30 03:46:50 +11:00
|
|
|
/// A command that moves the terminal cursor up the given number of lines,
|
|
|
|
/// and moves it to the first column.
|
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
///
|
|
|
|
/// Commands must be executed/queued for execution otherwise they do nothing.
|
2019-12-04 17:22:38 +11:00
|
|
|
pub struct MoveToNextLine(pub u16);
|
2019-11-30 03:46:50 +11:00
|
|
|
|
|
|
|
impl Command for MoveToNextLine {
|
|
|
|
type AnsiType = String;
|
|
|
|
|
|
|
|
fn ansi_code(&self) -> Self::AnsiType {
|
|
|
|
ansi::move_to_next_line_csi_sequence(self.0)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
fn execute_winapi(&self) -> Result<()> {
|
|
|
|
sys::move_to_next_line(self.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A command that moves the terminal cursor down the given number of lines,
|
|
|
|
/// and moves it to the first column.
|
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
///
|
|
|
|
/// Commands must be executed/queued for execution otherwise they do nothing.
|
2019-12-04 17:22:38 +11:00
|
|
|
pub struct MoveToPreviousLine(pub u16);
|
2019-11-30 03:46:50 +11:00
|
|
|
|
|
|
|
impl Command for MoveToPreviousLine {
|
|
|
|
type AnsiType = String;
|
|
|
|
|
|
|
|
fn ansi_code(&self) -> Self::AnsiType {
|
|
|
|
ansi::move_to_previous_line_csi_sequence(self.0)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
fn execute_winapi(&self) -> Result<()> {
|
|
|
|
sys::move_to_previous_line(self.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A command that moves the terminal cursor to the given column on the current row.
|
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
///
|
|
|
|
/// Commands must be executed/queued for execution otherwise they do nothing.
|
2019-12-04 17:22:38 +11:00
|
|
|
pub struct MoveToColumn(pub u16);
|
2019-11-30 03:46:50 +11:00
|
|
|
|
|
|
|
impl Command for MoveToColumn {
|
|
|
|
type AnsiType = String;
|
|
|
|
|
|
|
|
fn ansi_code(&self) -> Self::AnsiType {
|
|
|
|
ansi::move_to_column_csi_sequence(self.0)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
fn execute_winapi(&self) -> Result<()> {
|
|
|
|
sys::move_to_column(self.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
/// A command that moves the terminal cursor a given number of rows up.
|
2019-10-23 01:33:38 +11:00
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
///
|
|
|
|
/// Commands must be executed/queued for execution otherwise they do nothing.
|
2019-10-28 00:33:47 +11:00
|
|
|
pub struct MoveUp(pub u16);
|
2019-10-23 01:33:38 +11:00
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
impl Command for MoveUp {
|
2019-10-23 01:33:38 +11:00
|
|
|
type AnsiType = String;
|
|
|
|
|
|
|
|
fn ansi_code(&self) -> Self::AnsiType {
|
|
|
|
ansi::move_up_csi_sequence(self.0)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
fn execute_winapi(&self) -> Result<()> {
|
2019-10-28 00:33:47 +11:00
|
|
|
sys::move_up(self.0)
|
2019-10-23 01:33:38 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-30 03:46:50 +11:00
|
|
|
/// A command that moves the terminal cursor a given number of columns to the right.
|
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
///
|
|
|
|
/// Commands must be executed/queued for execution otherwise they do nothing.
|
|
|
|
pub struct MoveRight(pub u16);
|
|
|
|
|
|
|
|
impl Command for MoveRight {
|
|
|
|
type AnsiType = String;
|
|
|
|
|
|
|
|
fn ansi_code(&self) -> Self::AnsiType {
|
|
|
|
ansi::move_right_csi_sequence(self.0)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
fn execute_winapi(&self) -> Result<()> {
|
|
|
|
sys::move_right(self.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
/// A command that moves the terminal cursor a given number of rows down.
|
2019-10-23 01:33:38 +11:00
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
///
|
|
|
|
/// Commands must be executed/queued for execution otherwise they do nothing.
|
2019-10-28 00:33:47 +11:00
|
|
|
pub struct MoveDown(pub u16);
|
2019-10-23 01:33:38 +11:00
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
impl Command for MoveDown {
|
2019-10-23 01:33:38 +11:00
|
|
|
type AnsiType = String;
|
|
|
|
|
|
|
|
fn ansi_code(&self) -> Self::AnsiType {
|
|
|
|
ansi::move_down_csi_sequence(self.0)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
fn execute_winapi(&self) -> Result<()> {
|
2019-10-28 00:33:47 +11:00
|
|
|
sys::move_down(self.0)
|
2019-10-23 01:33:38 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
/// A command that moves the terminal cursor a given number of columns to the left.
|
2019-10-23 01:33:38 +11:00
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
///
|
|
|
|
/// Commands must be executed/queued for execution otherwise they do nothing.
|
2019-10-28 00:33:47 +11:00
|
|
|
pub struct MoveLeft(pub u16);
|
2019-10-23 01:33:38 +11:00
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
impl Command for MoveLeft {
|
2019-10-23 01:33:38 +11:00
|
|
|
type AnsiType = String;
|
|
|
|
|
|
|
|
fn ansi_code(&self) -> Self::AnsiType {
|
|
|
|
ansi::move_left_csi_sequence(self.0)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
fn execute_winapi(&self) -> Result<()> {
|
2019-10-28 00:33:47 +11:00
|
|
|
sys::move_left(self.0)
|
2019-10-23 01:33:38 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
/// A command that saves the current terminal cursor position.
|
|
|
|
///
|
|
|
|
/// See the [RestorePosition](./struct.RestorePosition.html) command.
|
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
///
|
|
|
|
/// - The cursor position is stored globally.
|
|
|
|
/// - Commands must be executed/queued for execution otherwise they do nothing.
|
|
|
|
pub struct SavePosition;
|
2019-10-23 01:33:38 +11:00
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
impl Command for SavePosition {
|
2019-10-23 01:33:38 +11:00
|
|
|
type AnsiType = &'static str;
|
|
|
|
|
|
|
|
fn ansi_code(&self) -> Self::AnsiType {
|
|
|
|
ansi::SAVE_POSITION_CSI_SEQUENCE
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
fn execute_winapi(&self) -> Result<()> {
|
2019-10-28 00:33:47 +11:00
|
|
|
sys::save_position()
|
2019-10-23 01:33:38 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
/// A command that restores the saved terminal cursor position.
|
|
|
|
///
|
|
|
|
/// See the [SavePosition](./struct.SavePosition.html) command.
|
2019-10-23 01:33:38 +11:00
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
///
|
2019-10-28 00:33:47 +11:00
|
|
|
/// - The cursor position is stored globally.
|
|
|
|
/// - Commands must be executed/queued for execution otherwise they do nothing.
|
|
|
|
pub struct RestorePosition;
|
2019-10-23 01:33:38 +11:00
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
impl Command for RestorePosition {
|
2019-10-23 01:33:38 +11:00
|
|
|
type AnsiType = &'static str;
|
|
|
|
|
|
|
|
fn ansi_code(&self) -> Self::AnsiType {
|
|
|
|
ansi::RESTORE_POSITION_CSI_SEQUENCE
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
fn execute_winapi(&self) -> Result<()> {
|
2019-10-28 00:33:47 +11:00
|
|
|
sys::restore_position()
|
2019-10-23 01:33:38 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
/// A command that hides the terminal cursor.
|
2019-10-23 01:33:38 +11:00
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
///
|
2019-10-28 00:33:47 +11:00
|
|
|
/// - Commands must be executed/queued for execution otherwise they do nothing.
|
2019-10-23 01:33:38 +11:00
|
|
|
pub struct Hide;
|
|
|
|
|
|
|
|
impl Command for Hide {
|
|
|
|
type AnsiType = &'static str;
|
|
|
|
|
|
|
|
fn ansi_code(&self) -> Self::AnsiType {
|
|
|
|
ansi::HIDE_CSI_SEQUENCE
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
fn execute_winapi(&self) -> Result<()> {
|
2019-10-28 00:33:47 +11:00
|
|
|
sys::show_cursor(false)
|
2019-10-23 01:33:38 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
/// A command that shows the terminal cursor.
|
2019-10-23 01:33:38 +11:00
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
///
|
2019-10-28 00:33:47 +11:00
|
|
|
/// - Commands must be executed/queued for execution otherwise they do nothing.
|
2019-10-23 01:33:38 +11:00
|
|
|
pub struct Show;
|
|
|
|
|
|
|
|
impl Command for Show {
|
|
|
|
type AnsiType = &'static str;
|
|
|
|
|
|
|
|
fn ansi_code(&self) -> Self::AnsiType {
|
|
|
|
ansi::SHOW_CSI_SEQUENCE
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
fn execute_winapi(&self) -> Result<()> {
|
2019-10-28 00:33:47 +11:00
|
|
|
sys::show_cursor(true)
|
2019-10-23 01:33:38 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
/// A command that enables blinking of the terminal cursor.
|
2019-10-23 01:33:38 +11:00
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
///
|
2019-10-28 00:33:47 +11:00
|
|
|
/// - Windows versions lower than Windows 10 do not support this functionality.
|
|
|
|
/// - Commands must be executed/queued for execution otherwise they do nothing.
|
|
|
|
pub struct EnableBlinking;
|
2019-10-23 01:33:38 +11:00
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
impl Command for EnableBlinking {
|
2019-10-23 01:33:38 +11:00
|
|
|
type AnsiType = &'static str;
|
|
|
|
|
|
|
|
fn ansi_code(&self) -> Self::AnsiType {
|
2019-10-28 00:33:47 +11:00
|
|
|
ansi::ENABLE_BLINKING_CSI_SEQUENCE
|
2019-10-23 01:33:38 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
fn execute_winapi(&self) -> Result<()> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
/// A command that disables blinking of the terminal cursor.
|
2019-10-23 01:33:38 +11:00
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
///
|
2019-10-28 00:33:47 +11:00
|
|
|
/// - Windows versions lower than Windows 10 do not support this functionality.
|
|
|
|
/// - Commands must be executed/queued for execution otherwise they do nothing.
|
|
|
|
pub struct DisableBlinking;
|
2019-10-23 01:33:38 +11:00
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
impl Command for DisableBlinking {
|
2019-10-23 01:33:38 +11:00
|
|
|
type AnsiType = &'static str;
|
|
|
|
|
|
|
|
fn ansi_code(&self) -> Self::AnsiType {
|
2019-10-28 00:33:47 +11:00
|
|
|
ansi::DISABLE_BLINKING_CSI_SEQUENCE
|
2019-10-23 01:33:38 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
fn execute_winapi(&self) -> Result<()> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
impl_display!(for MoveTo);
|
|
|
|
impl_display!(for MoveUp);
|
|
|
|
impl_display!(for MoveDown);
|
|
|
|
impl_display!(for MoveLeft);
|
|
|
|
impl_display!(for MoveRight);
|
|
|
|
impl_display!(for SavePosition);
|
|
|
|
impl_display!(for RestorePosition);
|
2019-10-23 01:33:38 +11:00
|
|
|
impl_display!(for Hide);
|
|
|
|
impl_display!(for Show);
|
2019-10-28 00:33:47 +11:00
|
|
|
impl_display!(for EnableBlinking);
|
|
|
|
impl_display!(for DisableBlinking);
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2019-10-29 19:14:47 +11:00
|
|
|
use std::io::{self, stdout, Write};
|
|
|
|
|
|
|
|
use crate::execute;
|
|
|
|
|
2019-10-28 00:33:47 +11:00
|
|
|
use super::{
|
|
|
|
position, MoveDown, MoveLeft, MoveRight, MoveTo, MoveUp, RestorePosition, SavePosition,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Test is disabled, because it's failing on Travis
|
|
|
|
#[test]
|
|
|
|
#[ignore]
|
|
|
|
fn test_move_to() {
|
|
|
|
let (saved_x, saved_y) = position().unwrap();
|
|
|
|
|
|
|
|
execute!(stdout(), MoveTo(saved_x + 1, saved_y + 1)).unwrap();
|
|
|
|
assert_eq!(position().unwrap(), (saved_x + 1, saved_y + 1));
|
|
|
|
|
|
|
|
execute!(stdout(), MoveTo(saved_x, saved_y)).unwrap();
|
|
|
|
assert_eq!(position().unwrap(), (saved_x, saved_y));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test is disabled, because it's failing on Travis
|
|
|
|
#[test]
|
|
|
|
#[ignore]
|
|
|
|
fn test_move_right() {
|
|
|
|
let (saved_x, saved_y) = position().unwrap();
|
|
|
|
execute!(io::stdout(), MoveRight(1)).unwrap();
|
|
|
|
assert_eq!(position().unwrap(), (saved_x + 1, saved_y));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test is disabled, because it's failing on Travis
|
|
|
|
#[test]
|
|
|
|
#[ignore]
|
|
|
|
fn test_move_left() {
|
|
|
|
execute!(stdout(), MoveTo(2, 0), MoveLeft(2)).unwrap();
|
|
|
|
assert_eq!(position().unwrap(), (0, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test is disabled, because it's failing on Travis
|
|
|
|
#[test]
|
|
|
|
#[ignore]
|
|
|
|
fn test_move_up() {
|
|
|
|
execute!(stdout(), MoveTo(0, 2), MoveUp(2)).unwrap();
|
|
|
|
assert_eq!(position().unwrap(), (0, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test is disabled, because it's failing on Travis
|
|
|
|
#[test]
|
|
|
|
#[ignore]
|
|
|
|
fn test_move_down() {
|
|
|
|
execute!(stdout(), MoveTo(0, 0), MoveDown(2)).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(position().unwrap(), (0, 2));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test is disabled, because it's failing on Travis
|
|
|
|
#[test]
|
|
|
|
#[ignore]
|
|
|
|
fn test_save_restore_position() {
|
|
|
|
let (saved_x, saved_y) = position().unwrap();
|
|
|
|
|
|
|
|
execute!(
|
|
|
|
stdout(),
|
|
|
|
SavePosition,
|
|
|
|
MoveTo(saved_x + 1, saved_y + 1),
|
|
|
|
RestorePosition
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let (x, y) = position().unwrap();
|
|
|
|
|
|
|
|
assert_eq!(x, saved_x);
|
|
|
|
assert_eq!(y, saved_y);
|
|
|
|
}
|
|
|
|
}
|