Removed old Cursor Api (#289)
This commit is contained in:
parent
1587d10dc8
commit
0479d68f50
17
CHANGELOG.md
17
CHANGELOG.md
@ -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
|
||||
|
||||
|
@ -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 {
|
||||
|
439
src/cursor.rs
439
src/cursor.rs
@ -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.
|
||||
/// A command that moves the terminal cursor to the given position (column, row).
|
||||
///
|
||||
/// # 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()
|
||||
}
|
||||
/// * 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);
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// Commands must be executed/queued for execution otherwise they do nothing.
|
||||
pub struct Goto(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
30
src/cursor/ansi.rs
Normal 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");
|
@ -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<()>;
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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()?;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
36
src/lib.rs
36
src/lib.rs
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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(())
|
||||
|
@ -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(())
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user