Removed old Cursor Api (#289)

This commit is contained in:
Timon 2019-10-27 14:33:47 +01:00 committed by GitHub
parent 1587d10dc8
commit 0479d68f50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 394 additions and 671 deletions

View File

@ -1,6 +1,19 @@
# Next Version
# Master
- Derived 'Copy' for 'KeyEvent'
- `input` module
- Derive 'Copy' for 'KeyEvent'
- `cursor` module
- Remove `TerminalCursor`, `cursor`, `Crossterm::cursor()`
- Introduce static function `crossterm::cursor::position` in place of `TerminalCursor::pos`
- Rename `Goto` to `MoveTo`
- Rename `Up` to `MoveLeft`
- Rename `Right` to `MoveRight`
- Rename `Down` to `MoveDown`
- Rename `BlinkOn` to `EnableBlinking`
- Rename `BlinkOff` to `DisableBlinking`
- Rename `ResetPos` to `ResetPosition`
- Rename `SavePos` to `SavePosition`
- Remove re-export cursor module types at root level, are now accessible from `crossterm::cursor`
# Version 0.12.1

View File

@ -10,12 +10,6 @@ impl Crossterm {
Crossterm
}
/// Crates a new `TerminalCursor`.
#[cfg(feature = "cursor")]
pub fn cursor(&self) -> crate::cursor::TerminalCursor {
crate::cursor::TerminalCursor::new()
}
/// Creates a new `TerminalInput`.
#[cfg(feature = "input")]
pub fn input(&self) -> crate::input::TerminalInput {

View File

@ -1,6 +1,6 @@
//! # Cursor
//!
//! The `cursor` module provides a functionality to work with the terminal cursor.
//! The `cursor` module provides functionality to work with the terminal cursor.
//!
//! 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
@ -9,239 +9,79 @@
//!
//! ## Examples
//!
//! Basic usage:
//!
//! ```no_run
//! // You can replace the following line with `use crossterm::TerminalCursor;`
//! // if you're using the `crossterm` crate with the `cursor` feature enabled.
//! use crossterm::{Result, TerminalCursor};
//!
//! fn main() -> Result<()> {
//! // Get a cursor, save position
//! let cursor = TerminalCursor::new();
//! cursor.save_position()?;
//!
//! // Do something with the cursor
//! cursor.goto(10, 10)?;
//! cursor.blink(true)?;
//!
//! // Be a good citizen, cleanup
//! cursor.blink(false)?;
//! cursor.restore_position()
//! }
//! ```
//!
//! Commands:
//! Cursor actions can be performed with commands.
//! Please have a look at [command documention](../index.html#command-api) for a more detailed documentation.
//!
//! ```no_run
//! use std::io::{stdout, Write};
//!
//! use crossterm::{BlinkOff, BlinkOn, execute, Goto, ResetPos, Result, SavePos};
//!
//! use crossterm::{
//! ExecutableCommand, execute, Result,
//! cursor::{DisableBlinking, EnableBlinking, MoveTo, RestorePosition, SavePosition}
//! };
//!
//! fn main() -> Result<()> {
//! // with macro
//! execute!(
//! stdout(),
//! SavePos,
//! Goto(10, 10),
//! BlinkOn,
//! BlinkOff,
//! ResetPos
//! )
//! SavePosition,
//! MoveTo(10, 10),
//! EnableBlinking,
//! DisableBlinking,
//! RestorePosition
//! );
//!
//! // with function
//! stdout()
//! .execute(MoveTo(11,11))?
//! .execute(RestorePosition);
//!
//! Ok(())
//! }
//! ```
use cursor::ansi::{self, AnsiCursor};
#[cfg(windows)]
use cursor::windows::WinApiCursor;
use cursor::Cursor;
//!
//! For manual execution control check out [crossterm::queue](../macro.queue.html).
pub use sys::position;
use crate::impl_display;
#[cfg(windows)]
use crate::utils::supports_ansi;
use crate::utils::{Command, Result};
use crate::utils::Result;
mod cursor;
mod sys;
use crate::utils::Command;
/// A terminal cursor.
///
/// The `TerminalCursor` instance is stateless and does not hold any data.
/// You can create as many instances as you want and they will always refer to the
/// same terminal cursor.
///
/// The cursor position is 0 based. For example `0` means first column/row, `1`
/// second column/row, etc.
///
/// # Examples
///
/// Basic usage:
///
/// ```no_run
/// use crossterm::{Result, TerminalCursor};
///
/// fn main() -> Result<()> {
/// let cursor = TerminalCursor::new();
/// cursor.save_position()?;
///
/// cursor.goto(10, 10)?;
/// cursor.blink(true)?;
///
/// cursor.blink(false)?;
/// cursor.restore_position()
/// }
/// ```
pub struct TerminalCursor {
#[cfg(windows)]
cursor: Box<(dyn Cursor + Sync + Send)>,
#[cfg(unix)]
cursor: AnsiCursor,
}
mod ansi;
pub(crate) mod sys;
impl TerminalCursor {
/// Creates a new `TerminalCursor`.
pub fn new() -> TerminalCursor {
#[cfg(windows)]
let cursor = if supports_ansi() {
Box::new(AnsiCursor::new()) as Box<(dyn Cursor + Sync + Send)>
} else {
Box::new(WinApiCursor::new()) as Box<(dyn Cursor + Sync + Send)>
};
#[cfg(unix)]
let cursor = AnsiCursor::new();
TerminalCursor { cursor }
}
/// Moves the cursor to the given position.
pub fn goto(&self, column: u16, row: u16) -> Result<()> {
self.cursor.goto(column, row)
}
/// Returns the cursor position (`(column, row)` tuple).
pub fn pos(&self) -> Result<(u16, u16)> {
self.cursor.pos()
}
/// Moves the cursor `row_count` times up.
pub fn move_up(&mut self, row_count: u16) -> Result<&mut TerminalCursor> {
self.cursor.move_up(row_count)?;
Ok(self)
}
/// Moves the cursor `col_count` times right.
pub fn move_right(&mut self, col_count: u16) -> Result<&mut TerminalCursor> {
self.cursor.move_right(col_count)?;
Ok(self)
}
/// Moves the cursor `row_count` times down.
pub fn move_down(&mut self, row_count: u16) -> Result<&mut TerminalCursor> {
self.cursor.move_down(row_count)?;
Ok(self)
}
/// Moves the cursor `col_count` times left.
pub fn move_left(&mut self, col_count: u16) -> Result<&mut TerminalCursor> {
self.cursor.move_left(col_count)?;
Ok(self)
}
/// Saves the cursor position.
///
/// See the [restore_position](struct.TerminalCursor.html#method.restore_position) method.
///
/// # Notes
///
/// The cursor position is stored globally and is not related to the current/any
/// `TerminalCursor` instance.
pub fn save_position(&self) -> Result<()> {
self.cursor.save_position()
}
/// Restores the saved cursor position.
///
/// See the [save_position](struct.TerminalCursor.html#method.save_position) method.
pub fn restore_position(&self) -> Result<()> {
self.cursor.restore_position()
}
/// Hides the cursor.
///
/// See the [show](struct.TerminalCursor.html#method.show) method.
pub fn hide(&self) -> Result<()> {
self.cursor.hide()
}
/// Shows the cursor.
///
/// See the [hide](struct.TerminalCursor.html#method.hide) method.
pub fn show(&self) -> Result<()> {
self.cursor.show()
}
/// Enables or disables the cursor blinking.
///
/// # Notes
///
/// Windows versions lower than Windows 10 do not support this functionality.
pub fn blink(&self, blink: bool) -> Result<()> {
self.cursor.blink(blink)
}
}
/// Creates a new `TerminalCursor`.
///
/// # Examples
///
/// Basic usage:
///
/// ```no_run
/// use crossterm::{cursor, Result};
///
/// fn main() -> Result<()> {
/// let cursor = cursor();
/// cursor.save_position()?;
///
/// cursor.goto(10, 10)?;
/// cursor.blink(true)?;
///
/// cursor.blink(false)?;
/// cursor.restore_position()
/// }
/// ```
pub fn cursor() -> TerminalCursor {
TerminalCursor::new()
}
/// A command to move the cursor to the given position.
/// A command that moves the terminal cursor to the given position (column, row).
///
/// # Notes
///
/// Commands must be executed/queued for execution otherwise they do nothing.
pub struct Goto(pub u16, pub u16);
/// * 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);
impl Command for Goto {
impl Command for MoveTo {
type AnsiType = String;
fn ansi_code(&self) -> Self::AnsiType {
ansi::goto_csi_sequence(self.0, self.1)
ansi::move_to_csi_sequence(self.0, self.1)
}
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
WinApiCursor::new().goto(self.0, self.1)
sys::move_to(self.0, self.1)
}
}
/// A command to move the cursor given rows up.
/// A command that moves the terminal cursor a given number of rows up.
///
/// # Notes
///
/// Commands must be executed/queued for execution otherwise they do nothing.
pub struct Up(pub u16);
pub struct MoveUp(pub u16);
impl Command for Up {
impl Command for MoveUp {
type AnsiType = String;
fn ansi_code(&self) -> Self::AnsiType {
@ -250,18 +90,18 @@ impl Command for Up {
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
WinApiCursor::new().move_up(self.0)
sys::move_up(self.0)
}
}
/// A command to move the cursor given rows down.
/// A command that moves the terminal cursor a given number of rows down.
///
/// # Notes
///
/// Commands must be executed/queued for execution otherwise they do nothing.
pub struct Down(pub u16);
pub struct MoveDown(pub u16);
impl Command for Down {
impl Command for MoveDown {
type AnsiType = String;
fn ansi_code(&self) -> Self::AnsiType {
@ -270,18 +110,18 @@ impl Command for Down {
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
WinApiCursor::new().move_down(self.0)
sys::move_down(self.0)
}
}
/// A command to move the cursor given columns left.
/// A command that moves the terminal cursor a given number of columns to the left.
///
/// # Notes
///
/// Commands must be executed/queued for execution otherwise they do nothing.
pub struct Left(pub u16);
pub struct MoveLeft(pub u16);
impl Command for Left {
impl Command for MoveLeft {
type AnsiType = String;
fn ansi_code(&self) -> Self::AnsiType {
@ -290,18 +130,28 @@ impl Command for Left {
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
WinApiCursor::new().move_left(self.0)
sys::move_left(self.0)
}
}
/// A command to move the cursor given columns right.
/// 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 Right(pub u16);
pub struct MoveRight(pub u16);
impl Command for Right {
/// 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;
impl Command for MoveRight {
type AnsiType = String;
fn ansi_code(&self) -> Self::AnsiType {
@ -310,21 +160,11 @@ impl Command for Right {
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
WinApiCursor::new().move_right(self.0)
sys::move_right(self.0)
}
}
/// A command to save the cursor position.
///
/// # Notes
///
/// The cursor position is stored globally and is not related to the current/any
/// `TerminalCursor` instance.
///
/// Commands must be executed/queued for execution otherwise they do nothing.
pub struct SavePos;
impl Command for SavePos {
impl Command for SavePosition {
type AnsiType = &'static str;
fn ansi_code(&self) -> Self::AnsiType {
@ -333,18 +173,21 @@ impl Command for SavePos {
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
WinApiCursor::new().save_position()
sys::save_position()
}
}
/// A command to restore the saved cursor position.
/// A command that restores the saved terminal cursor position.
///
/// See the [SavePosition](./struct.SavePosition.html) command.
///
/// # Notes
///
/// Commands must be executed/queued for execution otherwise they do nothing.
pub struct ResetPos;
/// - The cursor position is stored globally.
/// - Commands must be executed/queued for execution otherwise they do nothing.
pub struct RestorePosition;
impl Command for ResetPos {
impl Command for RestorePosition {
type AnsiType = &'static str;
fn ansi_code(&self) -> Self::AnsiType {
@ -353,18 +196,15 @@ impl Command for ResetPos {
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
WinApiCursor::new().restore_position()
sys::restore_position()
}
}
/// A command to hide the cursor.
/// A command that hides the terminal cursor.
///
/// # Notes
///
/// The cursor position is stored globally and is not related to the current/any
/// `TerminalCursor` instance.
///
/// Commands must be executed/queued for execution otherwise they do nothing.
/// - Commands must be executed/queued for execution otherwise they do nothing.
pub struct Hide;
impl Command for Hide {
@ -376,18 +216,15 @@ impl Command for Hide {
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
WinApiCursor::new().hide()
sys::show_cursor(false)
}
}
/// A command to show the cursor.
/// A command that shows the terminal cursor.
///
/// # Notes
///
/// The cursor position is stored globally and is not related to the current/any
/// `TerminalCursor` instance.
///
/// Commands must be executed/queued for execution otherwise they do nothing.
/// - Commands must be executed/queued for execution otherwise they do nothing.
pub struct Show;
impl Command for Show {
@ -399,24 +236,23 @@ impl Command for Show {
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
WinApiCursor::new().show()
sys::show_cursor(true)
}
}
/// A command to enable the cursor blinking.
/// A command that enables blinking of the terminal cursor.
///
/// # Notes
///
/// Windows versions lower than Windows 10 do not support this functionality.
///
/// Commands must be executed/queued for execution otherwise they do nothing.
pub struct BlinkOn;
/// - 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;
impl Command for BlinkOn {
impl Command for EnableBlinking {
type AnsiType = &'static str;
fn ansi_code(&self) -> Self::AnsiType {
ansi::BLINKING_ON_CSI_SEQUENCE
ansi::ENABLE_BLINKING_CSI_SEQUENCE
}
#[cfg(windows)]
@ -425,20 +261,19 @@ impl Command for BlinkOn {
}
}
/// A command to disable the cursor blinking.
/// A command that disables blinking of the terminal cursor.
///
/// # Notes
///
/// Windows versions lower than Windows 10 do not support this functionality.
///
/// Commands must be executed/queued for execution otherwise they do nothing.
pub struct BlinkOff;
/// - 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;
impl Command for BlinkOff {
impl Command for DisableBlinking {
type AnsiType = &'static str;
fn ansi_code(&self) -> Self::AnsiType {
ansi::BLINKING_OFF_CSI_SEQUENCE
ansi::DISABLE_BLINKING_CSI_SEQUENCE
}
#[cfg(windows)]
@ -447,14 +282,90 @@ impl Command for BlinkOff {
}
}
impl_display!(for Goto);
impl_display!(for Up);
impl_display!(for Down);
impl_display!(for Left);
impl_display!(for Right);
impl_display!(for SavePos);
impl_display!(for ResetPos);
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);
impl_display!(for Hide);
impl_display!(for Show);
impl_display!(for BlinkOn);
impl_display!(for BlinkOff);
impl_display!(for EnableBlinking);
impl_display!(for DisableBlinking);
#[cfg(test)]
mod tests {
use super::{
position, MoveDown, MoveLeft, MoveRight, MoveTo, MoveUp, RestorePosition, SavePosition,
};
use crate::execute;
use std::io::{self, stdout, Write};
// 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);
}
}

30
src/cursor/ansi.rs Normal file
View File

@ -0,0 +1,30 @@
//! This module provides cursor related ANSI escape codes.
use crate::csi;
pub(crate) fn move_to_csi_sequence(x: u16, y: u16) -> String {
format!(csi!("{};{}H"), y + 1, x + 1)
}
pub(crate) fn move_up_csi_sequence(count: u16) -> String {
format!(csi!("{}A"), count)
}
pub(crate) fn move_right_csi_sequence(count: u16) -> String {
format!(csi!("{}C"), count)
}
pub(crate) fn move_down_csi_sequence(count: u16) -> String {
format!(csi!("{}B"), count)
}
pub(crate) fn move_left_csi_sequence(count: u16) -> String {
format!(csi!("{}D"), count)
}
pub(crate) static SAVE_POSITION_CSI_SEQUENCE: &'static str = "\x1B7";
pub(crate) static RESTORE_POSITION_CSI_SEQUENCE: &'static str = "\x1B8";
pub(crate) static HIDE_CSI_SEQUENCE: &'static str = csi!("?25l");
pub(crate) static SHOW_CSI_SEQUENCE: &'static str = csi!("?25h");
pub(crate) static ENABLE_BLINKING_CSI_SEQUENCE: &'static str = csi!("?12h");
pub(crate) static DISABLE_BLINKING_CSI_SEQUENCE: &'static str = csi!("?12l");

View File

@ -1,45 +0,0 @@
//! A module that contains all the actions related to cursor movement in the terminal.
//! Like: moving the cursor position; saving and resetting the cursor position; hiding showing and control
//! the blinking of the cursor.
//!
//! Note that positions of the cursor are 0 -based witch means that the coordinates (cells) starts counting from 0
use crate::utils::Result;
pub(crate) mod ansi;
#[cfg(windows)]
pub(crate) mod windows;
///! This trait defines the actions that can be performed with the terminal cursor.
///! This trait can be implemented so that a concrete implementation of the ITerminalCursor can fulfill
///! the wishes to work on a specific platform.
///!
///! ## For example:
///!
///! This trait is implemented for `WinApi` (Windows specific) and `ANSI` (Unix specific),
///! so that cursor related actions can be performed on both UNIX and Windows systems.
pub(crate) trait Cursor: Sync + Send {
/// Goto location (`x`, `y`) in the current terminal window.
fn goto(&self, x: u16, y: u16) -> Result<()>;
/// Get the cursor location `(x, y)` in the current terminal window.
fn pos(&self) -> Result<(u16, u16)>;
/// Move cursor `n` times up
fn move_up(&self, count: u16) -> Result<()>;
/// Move the cursor `n` times to the right.
fn move_right(&self, count: u16) -> Result<()>;
/// Move the cursor `n` times down.
fn move_down(&self, count: u16) -> Result<()>;
/// Move the cursor `n` times left.
fn move_left(&self, count: u16) -> Result<()>;
/// Save cursor position so that its saved position can be recalled later. Note that this position
/// is stored program based not per instance of the cursor struct.
fn save_position(&self) -> Result<()>;
/// Return to saved cursor position
fn restore_position(&self) -> Result<()>;
/// Hide the terminal cursor.
fn hide(&self) -> Result<()>;
/// Show the terminal cursor
fn show(&self) -> Result<()>;
/// Enable or disable the blinking of the cursor.
fn blink(&self, blink: bool) -> Result<()>;
}

View File

@ -1,166 +0,0 @@
//! This is an ANSI specific implementation for cursor related action.
//! This module is used for windows 10 terminals and UNIX terminals by default.
//! Note that the cursor position is 0 based. This means that we start counting at 0 when setting the cursor position etc.
use crate::utils::Result;
use crate::{csi, write_cout};
use super::{
super::sys::{get_cursor_position, show_cursor},
Cursor,
};
pub(crate) fn goto_csi_sequence(x: u16, y: u16) -> String {
format!(csi!("{};{}H"), y + 1, x + 1)
}
pub(crate) fn move_up_csi_sequence(count: u16) -> String {
format!(csi!("{}A"), count)
}
pub(crate) fn move_right_csi_sequence(count: u16) -> String {
format!(csi!("{}C"), count)
}
pub(crate) fn move_down_csi_sequence(count: u16) -> String {
format!(csi!("{}B"), count)
}
pub(crate) fn move_left_csi_sequence(count: u16) -> String {
format!(csi!("{}D"), count)
}
pub(crate) static SAVE_POSITION_CSI_SEQUENCE: &'static str = csi!("s");
pub(crate) static RESTORE_POSITION_CSI_SEQUENCE: &'static str = csi!("u");
pub(crate) static HIDE_CSI_SEQUENCE: &'static str = csi!("?25l");
pub(crate) static SHOW_CSI_SEQUENCE: &'static str = csi!("?25h");
pub(crate) static BLINKING_ON_CSI_SEQUENCE: &'static str = csi!("?12h");
pub(crate) static BLINKING_OFF_CSI_SEQUENCE: &'static str = csi!("?12l");
/// This struct is an ANSI implementation for cursor related actions.
pub(crate) struct AnsiCursor;
impl AnsiCursor {
pub(crate) fn new() -> AnsiCursor {
AnsiCursor
}
}
impl Cursor for AnsiCursor {
fn goto(&self, x: u16, y: u16) -> Result<()> {
write_cout!(goto_csi_sequence(x, y))?;
Ok(())
}
fn pos(&self) -> Result<(u16, u16)> {
get_cursor_position()
}
fn move_up(&self, count: u16) -> Result<()> {
write_cout!(move_up_csi_sequence(count))?;
Ok(())
}
fn move_right(&self, count: u16) -> Result<()> {
write_cout!(move_right_csi_sequence(count))?;
Ok(())
}
fn move_down(&self, count: u16) -> Result<()> {
write_cout!(move_down_csi_sequence(count))?;
Ok(())
}
fn move_left(&self, count: u16) -> Result<()> {
write_cout!(move_left_csi_sequence(count))?;
Ok(())
}
fn save_position(&self) -> Result<()> {
write_cout!(SAVE_POSITION_CSI_SEQUENCE)?;
Ok(())
}
fn restore_position(&self) -> Result<()> {
write_cout!(RESTORE_POSITION_CSI_SEQUENCE)?;
Ok(())
}
fn hide(&self) -> Result<()> {
show_cursor(false)?;
Ok(())
}
fn show(&self) -> Result<()> {
show_cursor(true)?;
Ok(())
}
fn blink(&self, blink: bool) -> Result<()> {
if blink {
write_cout!(BLINKING_ON_CSI_SEQUENCE)?;
} else {
write_cout!(BLINKING_OFF_CSI_SEQUENCE)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{AnsiCursor, Cursor};
// TODO - Test is ingored, because it's stalled on Travis CI
#[test]
#[ignore]
fn test_save_restore_position() {
if try_enable_ansi() {
let cursor = AnsiCursor::new();
let (saved_x, saved_y) = cursor.pos().unwrap();
cursor.save_position().unwrap();
cursor.goto(saved_x + 1, saved_y + 1).unwrap();
cursor.restore_position().unwrap();
let (x, y) = cursor.pos().unwrap();
assert_eq!(x, saved_x);
assert_eq!(y, saved_y);
}
}
// TODO - Test is ingored, because it's stalled on Travis CI
#[test]
#[ignore]
fn test_goto() {
if try_enable_ansi() {
let cursor = AnsiCursor::new();
let (saved_x, saved_y) = cursor.pos().unwrap();
cursor.goto(saved_x + 1, saved_y + 1).unwrap();
assert_eq!(cursor.pos().unwrap(), (saved_x + 1, saved_y + 1));
cursor.goto(saved_x, saved_y).unwrap();
assert_eq!(cursor.pos().unwrap(), (saved_x, saved_y));
}
}
fn try_enable_ansi() -> bool {
#[cfg(windows)]
{
if cfg!(target_os = "windows") {
use crate::utils::sys::winapi::ansi::set_virtual_terminal_processing;
// if it is not listed we should try with WinApi to check if we do support ANSI-codes.
match set_virtual_terminal_processing(true) {
Ok(_) => return true,
Err(_) => return false,
}
}
}
true
}
}

View File

@ -1,111 +0,0 @@
//! This is a WINAPI specific implementation for cursor related actions.
//! This module is used for Windows terminals that do not support ANSI escape codes.
//! Note that the cursor position is 0 based. This means that we start counting at 0 when setting the cursor position.
use crate::utils::Result;
use super::{super::sys::windows::ScreenBufferCursor, Cursor};
/// This struct is a windows implementation for cursor related actions.
pub(crate) struct WinApiCursor;
impl WinApiCursor {
pub(crate) fn new() -> WinApiCursor {
WinApiCursor
}
}
impl Cursor for WinApiCursor {
fn goto(&self, x: u16, y: u16) -> Result<()> {
let cursor = ScreenBufferCursor::new()?;
cursor.goto(x as i16, y as i16)?;
Ok(())
}
fn pos(&self) -> Result<(u16, u16)> {
let cursor = ScreenBufferCursor::new()?;
Ok(cursor.position()?.into())
}
fn move_up(&self, count: u16) -> Result<()> {
let (xpos, ypos) = self.pos()?;
self.goto(xpos, ypos - count)?;
Ok(())
}
fn move_right(&self, count: u16) -> Result<()> {
let (xpos, ypos) = self.pos()?;
self.goto(xpos + count, ypos)?;
Ok(())
}
fn move_down(&self, count: u16) -> Result<()> {
let (xpos, ypos) = self.pos()?;
self.goto(xpos, ypos + count)?;
Ok(())
}
fn move_left(&self, count: u16) -> Result<()> {
let (xpos, ypos) = self.pos()?;
self.goto(xpos - count, ypos)?;
Ok(())
}
fn save_position(&self) -> Result<()> {
ScreenBufferCursor::save_cursor_pos()?;
Ok(())
}
fn restore_position(&self) -> Result<()> {
ScreenBufferCursor::restore_cursor_pos()?;
Ok(())
}
fn hide(&self) -> Result<()> {
ScreenBufferCursor::new()?.set_visibility(false)?;
Ok(())
}
fn show(&self) -> Result<()> {
ScreenBufferCursor::new()?.set_visibility(true)?;
Ok(())
}
fn blink(&self, _blink: bool) -> Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{Cursor, WinApiCursor};
#[test]
fn test_goto() {
let cursor = WinApiCursor::new();
let (saved_x, saved_y) = cursor.pos().unwrap();
cursor.goto(saved_x + 1, saved_y + 1).unwrap();
assert_eq!(cursor.pos().unwrap(), (saved_x + 1, saved_y + 1));
cursor.goto(saved_x, saved_y).unwrap();
assert_eq!(cursor.pos().unwrap(), (saved_x, saved_y));
}
#[test]
fn test_save_restore_position() {
let cursor = WinApiCursor::new();
let (saved_x, saved_y) = cursor.pos().unwrap();
cursor.save_position().unwrap();
cursor.goto(saved_x + 1, saved_y + 1).unwrap();
cursor.restore_position().unwrap();
let (x, y) = cursor.pos().unwrap();
assert_eq!(x, saved_x);
assert_eq!(y, saved_y);
}
}

View File

@ -1,11 +1,14 @@
//! This module provides platform related functions.
#[cfg(unix)]
pub(crate) use self::unix::get_cursor_position;
#[cfg(unix)]
pub(crate) use self::unix::show_cursor;
pub use self::unix::position;
#[cfg(windows)]
pub(crate) use self::windows::get_cursor_position;
pub use self::windows::position;
#[cfg(windows)]
pub(crate) use self::windows::show_cursor;
pub(crate) use self::windows::{
move_down, move_left, move_right, move_to, move_up, restore_position, save_position,
show_cursor,
};
#[cfg(windows)]
pub(crate) mod windows;

View File

@ -1,3 +1,5 @@
//! UNIX related logic to cursor manipulation.
use std::io::{self, Write};
use crate::input::{InputEvent, TerminalInput};
@ -5,38 +7,29 @@ use crate::utils::{
sys::unix::{disable_raw_mode, enable_raw_mode, is_raw_mode_enabled},
Result,
};
use crate::{csi, write_cout};
pub(crate) fn get_cursor_position() -> Result<(u16, u16)> {
/// Returns the cursor position (column, row).
///
/// The top left cell is represented `0,0`.
pub fn position() -> Result<(u16, u16)> {
if is_raw_mode_enabled() {
pos_raw()
read_position_raw()
} else {
pos()
read_position()
}
}
pub(crate) fn show_cursor(show_cursor: bool) -> Result<()> {
if show_cursor {
write_cout!(csi!("?25h"))?;
} else {
write_cout!(csi!("?25l"))?;
}
Ok(())
}
fn pos() -> Result<(u16, u16)> {
fn read_position() -> Result<(u16, u16)> {
enable_raw_mode()?;
let pos = pos_raw();
let pos = read_position_raw();
disable_raw_mode()?;
pos
}
fn pos_raw() -> Result<(u16, u16)> {
// Where is the cursor?
// Use `ESC [ 6 n`.
fn read_position_raw() -> Result<(u16, u16)> {
// Use `ESC [ 6 n` to and retrieve the cursor position.
let mut stdout = io::stdout();
// Write command
stdout.write_all(b"\x1B[6n")?;
stdout.flush()?;

View File

@ -1,4 +1,4 @@
//! This module handles some logic for cursor interaction in the windows console.
//! WinApi related logic to cursor manipulation.
use std::io;
use std::sync::Mutex;
@ -14,7 +14,14 @@ use lazy_static::lazy_static;
use crate::utils::Result;
pub(crate) fn get_cursor_position() -> Result<(u16, u16)> {
lazy_static! {
static ref SAVED_CURSOR_POS: Mutex<Option<(i16, i16)>> = Mutex::new(None);
}
/// Returns the cursor position (column, row).
///
/// The top left cell is represented `0,0`.
pub fn position() -> Result<(u16, u16)> {
let cursor = ScreenBufferCursor::new()?;
Ok(cursor.position()?.into())
}
@ -23,28 +30,63 @@ pub(crate) fn show_cursor(show_cursor: bool) -> Result<()> {
ScreenBufferCursor::from(Handle::current_out_handle()?).set_visibility(show_cursor)
}
lazy_static! {
static ref SAVED_CURSOR_POS: Mutex<Option<(i16, i16)>> = Mutex::new(None);
pub(crate) fn move_to(column: u16, row: u16) -> Result<()> {
let cursor = ScreenBufferCursor::new()?;
cursor.move_to(column as i16, row as i16)?;
Ok(())
}
pub(crate) struct ScreenBufferCursor {
pub(crate) fn move_up(count: u16) -> Result<()> {
let (column, row) = position()?;
move_to(column, row - count)?;
Ok(())
}
pub(crate) fn move_right(count: u16) -> Result<()> {
let (column, row) = position()?;
move_to(column + count, row)?;
Ok(())
}
pub(crate) fn move_down(count: u16) -> Result<()> {
let (column, row) = position()?;
move_to(column, row + count)?;
Ok(())
}
pub(crate) fn move_left(count: u16) -> Result<()> {
let (column, row) = position()?;
move_to(column - count, row)?;
Ok(())
}
pub(crate) fn save_position() -> Result<()> {
ScreenBufferCursor::new()?.save_position()?;
Ok(())
}
pub(crate) fn restore_position() -> Result<()> {
ScreenBufferCursor::new()?.restore_position()?;
Ok(())
}
/// WinApi wrapper over terminal cursor behaviour.
struct ScreenBufferCursor {
screen_buffer: ScreenBuffer,
}
impl ScreenBufferCursor {
pub(crate) fn new() -> Result<ScreenBufferCursor> {
fn new() -> Result<ScreenBufferCursor> {
Ok(ScreenBufferCursor {
screen_buffer: ScreenBuffer::from(Handle::new(HandleType::CurrentOutputHandle)?),
})
}
/// get the current cursor position.
pub(crate) fn position(&self) -> Result<Coord> {
fn position(&self) -> Result<Coord> {
Ok(self.screen_buffer.info()?.cursor_pos())
}
/// Set the cursor position to the given x and y. Note that this is 0 based.
pub(crate) fn goto(&self, x: i16, y: i16) -> Result<()> {
fn move_to(&self, x: i16, y: i16) -> Result<()> {
if x < 0 || x >= <i16>::max_value() {
Err(io::Error::new(
io::ErrorKind::Other,
@ -78,8 +120,7 @@ impl ScreenBufferCursor {
Ok(())
}
/// change the cursor visibility.
pub(crate) fn set_visibility(&self, visible: bool) -> Result<()> {
fn set_visibility(&self, visible: bool) -> Result<()> {
let cursor_info = CONSOLE_CURSOR_INFO {
dwSize: 100,
bVisible: if visible { TRUE } else { FALSE },
@ -96,21 +137,16 @@ impl ScreenBufferCursor {
Ok(())
}
/// Reset to saved cursor position
pub(crate) fn restore_cursor_pos() -> Result<()> {
let cursor = ScreenBufferCursor::new()?;
fn restore_position(&self) -> Result<()> {
if let Some((x, y)) = *SAVED_CURSOR_POS.lock().unwrap() {
cursor.goto(x, y)?;
self.move_to(x, y)?;
}
Ok(())
}
/// Save current cursor position to recall later.
pub(crate) fn save_cursor_pos() -> Result<()> {
let cursor = ScreenBufferCursor::new()?;
let position = cursor.position()?;
fn save_position(&self) -> Result<()> {
let position = self.position()?;
let mut locked_pos = SAVED_CURSOR_POS.lock().unwrap();
*locked_pos = Some((position.x, position.y));
@ -134,3 +170,70 @@ impl From<HANDLE> for ScreenBufferCursor {
}
}
}
#[cfg(test)]
mod tests {
use super::{
move_down, move_left, move_right, move_to, move_up, position, restore_position,
save_position,
};
#[test]
fn test_move_to_winapi() {
let (saved_x, saved_y) = position().unwrap();
move_to(saved_x + 1, saved_y + 1).unwrap();
assert_eq!(position().unwrap(), (saved_x + 1, saved_y + 1));
move_to(saved_x, saved_y).unwrap();
assert_eq!(position().unwrap(), (saved_x, saved_y));
}
#[test]
fn test_move_right_winapi() {
let (saved_x, saved_y) = position().unwrap();
move_right(1).unwrap();
assert_eq!(position().unwrap(), (saved_x + 1, saved_y));
}
#[test]
fn test_move_left_winapi() {
move_to(2, 0).unwrap();
move_left(2).unwrap();
assert_eq!(position().unwrap(), (0, 0));
}
#[test]
fn test_move_up_winapi() {
move_to(0, 2).unwrap();
move_up(2).unwrap();
assert_eq!(position().unwrap(), (0, 0));
}
#[test]
fn test_move_down_winapi() {
move_to(0, 0).unwrap();
move_down(2).unwrap();
assert_eq!(position().unwrap(), (0, 2));
}
#[test]
fn test_save_restore_position_winapi() {
let (saved_x, saved_y) = position().unwrap();
save_position().unwrap();
move_to(saved_x + 1, saved_y + 1).unwrap();
restore_position().unwrap();
let (x, y) = position().unwrap();
assert_eq!(x, saved_x);
assert_eq!(y, saved_y);
}
}

View File

@ -52,10 +52,10 @@
//!
//! ```no_run
//! use std::io::Write;
//! use crossterm::{Goto, QueueableCommand};
//! use crossterm::{QueueableCommand, cursor};
//!
//! let mut stdout = std::io::stdout();
//! stdout.queue(Goto(5,5));
//! stdout.queue(cursor::MoveTo(5,5));
//!
//! // some other code ...
//!
@ -69,10 +69,10 @@
//!
//! ```no_run
//! use std::io::Write;
//! use crossterm::{queue, Goto, QueueableCommand};
//! use crossterm::{queue, QueueableCommand, cursor};
//!
//! let mut stdout = std::io::stdout();
//! queue!(stdout, Goto(5, 5));
//! queue!(stdout, cursor::MoveTo(5, 5));
//!
//! // some other code ...
//!
@ -93,20 +93,20 @@
//!
//! ```no_run
//! use std::io::Write;
//! use crossterm::{ExecutableCommand, Goto};
//! use crossterm::{ExecutableCommand, cursor};
//!
//! let mut stdout = std::io::stdout();
//! stdout.execute(Goto(5,5));
//! stdout.execute(cursor::MoveTo(5,5));
//! ```
//!
//! Macros:
//!
//! ```no_run
//! use std::io::Write;
//! use crossterm::{execute, ExecutableCommand, Goto};
//! use crossterm::{execute, ExecutableCommand, cursor};
//!
//! let mut stdout = std::io::stdout();
//! execute!(stdout, Goto(5, 5));
//! execute!(stdout, cursor::MoveTo(5, 5));
//! ```
//!
//! ## Examples
@ -117,7 +117,10 @@
//!
//! ```no_run
//! use std::io::{stdout, Write};
//! use crossterm::{ExecutableCommand, QueueableCommand, Color, PrintStyledFont, Colorize, Clear, ClearType, Goto, Result};
//! use crossterm::{
//! ExecutableCommand, QueueableCommand, Color, PrintStyledFont,
//! Colorize, Clear, ClearType, cursor::MoveTo, Result
//! };
//!
//! fn main() -> Result<()> {
//! let mut stdout = stdout();
@ -128,7 +131,7 @@
//! for x in 0..150 {
//! if (y == 0 || y == 40 - 1) || (x == 0 || x == 150 - 1) {
//! stdout
//! .queue(Goto(x,y))?
//! .queue(MoveTo(x,y))?
//! .queue(PrintStyledFont( "█".magenta()))?;
//! }
//! }
@ -142,7 +145,10 @@
//!
//! ```no_run
//! use std::io::{stdout, Write};
//! use crossterm::{execute, queue, Color, PrintStyledFont, Colorize, Goto, Clear, ClearType, Result};
//! use crossterm::{
//! execute, queue, Color, PrintStyledFont,
//! Colorize, cursor::MoveTo, Clear, ClearType, Result
//! };
//!
//! fn main() -> Result<()> {
//! let mut stdout = stdout();
@ -152,7 +158,7 @@
//! for y in 0..40 {
//! for x in 0..150 {
//! if (y == 0 || y == 40 - 1) || (x == 0 || x == 150 - 1) {
//! queue!(stdout, Goto(x,y), PrintStyledFont( "█".magenta()))?;
//! queue!(stdout, MoveTo(x,y), PrintStyledFont( "█".magenta()))?;
//! }
//! }
//! }
@ -161,11 +167,6 @@
//! }
//!```
#[cfg(feature = "cursor")]
pub use cursor::{
cursor, BlinkOff, BlinkOn, Down, Goto, Hide, Left, ResetPos, Right, SavePos, Show,
TerminalCursor, Up,
};
#[cfg(feature = "input")]
pub use input::{
input, AsyncReader, InputEvent, KeyEvent, MouseButton, MouseEvent, SyncReader, TerminalInput,
@ -186,6 +187,7 @@ pub use utils::{Command, ErrorKind, ExecutableCommand, Output, QueueableCommand,
pub use self::crossterm::Crossterm;
mod crossterm;
/// A functionality to work with the terminal cursor
#[cfg(feature = "cursor")]
pub mod cursor;

View File

@ -1,11 +1,11 @@
#[cfg(windows)]
use crate::utils::supports_ansi;
use crate::utils::Result;
pub(crate) use ansi::AnsiAlternateScreen;
#[cfg(windows)]
pub(crate) use windows::WinApiAlternateScreen;
#[cfg(windows)]
use crate::utils::supports_ansi;
use crate::utils::Result;
pub(crate) mod ansi;
#[cfg(windows)]
pub(crate) mod windows;

View File

@ -1,9 +1,8 @@
//! This is an `ANSI escape code` specific implementation for terminal related action.
//! This module is used for windows 10 terminals and unix terminals by default.
use crate::cursor::TerminalCursor;
use crate::utils::Result;
use crate::{csi, write_cout};
use crate::{csi, cursor, write_cout};
use super::{super::sys::get_terminal_size, ClearType, Terminal};
@ -45,7 +44,7 @@ impl Terminal for AnsiTerminal {
};
if clear_type == ClearType::All {
TerminalCursor::new().goto(0, 0)?;
write_cout!(cursor::MoveTo(0, 0))?;
}
Ok(())

View File

@ -6,7 +6,7 @@
use crossterm_winapi::{Console, Coord, Handle, ScreenBuffer, Size};
use crate::cursor::TerminalCursor;
use crate::cursor;
use crate::utils::{ErrorKind, Result};
use super::{super::sys::winapi::get_terminal_size, ClearType, Terminal};
@ -218,8 +218,7 @@ fn clear_entire_screen(buffer_size: Size, current_attribute: u16) -> Result<()>
clear(start_location, cells_to_write, current_attribute)?;
// put the cursor back at cell 0,0
let cursor = TerminalCursor::new();
cursor.goto(0, 0)?;
cursor::sys::move_to(0, 0)?;
Ok(())
}
@ -234,8 +233,7 @@ fn clear_current_line(location: Coord, buffer_size: Size, current_attribute: u16
clear(start_location, cells_to_write, current_attribute)?;
// put the cursor back at cell 1 on current row
let cursor = TerminalCursor::new();
cursor.goto(0, location.y as u16)?;
cursor::sys::move_to(0, location.y as u16)?;
Ok(())
}
@ -252,8 +250,7 @@ fn clear_until_line(location: Coord, buffer_size: Size, current_attribute: u16)
clear(start_location, cells_to_write, current_attribute)?;
// put the cursor back at original cursor position before we did the clearing
let cursor = TerminalCursor::new();
cursor.goto(x as u16, y as u16)?;
cursor::sys::move_to(x as u16, y as u16)?;
Ok(())
}