2019-10-23 01:33:38 +11:00
//! # Terminal
//!
2019-10-29 19:14:47 +11:00
//! The `terminal` module provides functionality to work with the terminal.
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
2019-12-13 17:12:35 +11:00
//! [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) repository
2019-10-23 01:33:38 +11:00
//! to demonstrate the capabilities.
//!
2019-12-05 03:40:11 +11:00
//! Most terminal actions can be performed with commands.
2020-09-02 06:07:12 +10:00
//! Please have a look at [command documentation](../index.html#command-api) for a more detailed documentation.
2019-10-23 01:33:38 +11:00
//!
2019-12-05 03:40:11 +11:00
//! ## Screen Buffer
//!
//! A screen buffer is a two-dimensional array of character
//! and color data which is displayed in a terminal screen.
//!
//! The terminal has several of those buffers and is able to switch between them.
//! The default screen in which you work is called the 'main screen'.
//! The other screens are called the 'alternative screen'.
//!
//! It is important to understand that crossterm does not yet support creating screens,
//! or switch between more than two buffers, and only offers the ability to change
//! between the 'alternate' and 'main screen'.
//!
//! ### Alternate Screen
//!
//! By default, you will be working on the main screen.
//! There is also another screen called the 'alternative' screen.
//! This screen is slightly different from the main screen.
//! For example, it has the exact dimensions of the terminal window,
//! without any scroll-back area.
//!
//! Crossterm offers the possibility to switch to the 'alternative' screen,
//! make some modifications, and move back to the 'main' screen again.
//! The main screen will stay intact and will have the original data as we performed all
//! operations on the alternative screen.
//!
//! An good example of this is Vim.
//! When it is launched from bash, a whole new buffer is used to modify a file.
//! Then, when the modification is finished, it closes again and continues on the main screen.
//!
//! ### Raw Mode
//!
//! By default, the terminal functions in a certain way.
//! For example, it will move the cursor to the beginning of the next line when the input hits the end of a line.
//! Or that the backspace is interpreted for character removal.
//!
//! Sometimes these default modes are irrelevant,
//! and in this case, we can turn them off.
//! This is what happens when you enable raw modes.
//!
//! Those modes will be set when enabling raw modes:
//!
//! - Input will not be forwarded to screen
//! - Input will not be processed on enter press
//! - Input will not be line buffered (input sent byte-by-byte to input buffer)
//! - Special keys like backspace and CTL+C will not be processed by terminal driver
//! - New line character will not be processed therefore `println!` can't be used, use `write!` instead
//!
2021-07-27 02:21:21 +10:00
//! Raw mode can be enabled/disabled with the [enable_raw_mode](terminal::enable_raw_mode) and [disable_raw_mode](terminal::disable_raw_mode) functions.
//!
2019-10-29 19:14:47 +11:00
//! ## Examples
2019-10-23 01:33:38 +11:00
//!
//! ```no_run
//! use std::io::{stdout, Write};
2019-10-29 19:14:47 +11:00
//! use crossterm::{execute, Result, terminal::{ScrollUp, SetSize, size}};
2019-10-23 01:33:38 +11:00
//!
//! fn main() -> Result<()> {
2019-10-29 19:14:47 +11:00
//! let (cols, rows) = size()?;
2019-12-05 03:40:11 +11:00
//! // Resize terminal and scroll up.
2019-10-23 01:33:38 +11:00
//! execute!(
//! stdout(),
//! SetSize(10, 10),
//! ScrollUp(5)
//! )?;
//!
//! // Be a good citizen, cleanup
2019-10-29 19:14:47 +11:00
//! execute!(stdout(), SetSize(cols, rows))?;
//! Ok(())
2019-10-23 01:33:38 +11:00
//! }
//! ```
2019-11-19 07:50:57 +11:00
//!
2019-10-29 19:14:47 +11:00
//! For manual execution control check out [crossterm::queue](../macro.queue.html).
2020-12-28 17:56:32 +11:00
use std ::fmt ;
2019-12-05 03:40:11 +11:00
#[ cfg(windows) ]
2020-09-11 18:19:41 +10:00
use crossterm_winapi ::{ ConsoleMode , Handle , ScreenBuffer } ;
2019-10-23 01:33:38 +11:00
#[ cfg(feature = " serde " ) ]
use serde ::{ Deserialize , Serialize } ;
2020-09-11 18:19:41 +10:00
#[ cfg(windows) ]
use winapi ::um ::wincon ::ENABLE_WRAP_AT_EOL_OUTPUT ;
2019-10-23 01:33:38 +11:00
#[ doc(no_inline) ]
2019-12-12 03:10:34 +11:00
use crate ::Command ;
2021-01-04 00:29:29 +11:00
use crate ::{ csi , impl_display , Result } ;
2019-10-23 01:33:38 +11:00
2019-12-05 03:40:11 +11:00
pub ( crate ) mod sys ;
2021-08-13 21:13:37 +10:00
/// Tells whether the raw mode is enabled.
///
/// Please have a look at the [raw mode](./#raw-mode) section.
pub fn is_raw_mode_enabled ( ) -> Result < bool > {
#[ cfg(unix) ]
{
Ok ( sys ::is_raw_mode_enabled ( ) )
}
#[ cfg(windows) ]
{
sys ::is_raw_mode_enabled ( )
}
}
2019-12-05 03:40:11 +11:00
/// Enables raw mode.
///
/// Please have a look at the [raw mode](./#raw-mode) section.
pub fn enable_raw_mode ( ) -> Result < ( ) > {
sys ::enable_raw_mode ( )
}
/// Disables raw mode.
///
/// Please have a look at the [raw mode](./#raw-mode) section.
pub fn disable_raw_mode ( ) -> Result < ( ) > {
sys ::disable_raw_mode ( )
}
/// Returns the terminal size `(columns, rows)`.
///
/// The top left cell is represented `(1, 1)`.
pub fn size ( ) -> Result < ( u16 , u16 ) > {
sys ::size ( )
}
2020-09-11 18:19:41 +10:00
/// Disables line wrapping.
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
pub struct DisableLineWrap ;
impl Command for DisableLineWrap {
2020-12-28 17:56:32 +11:00
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
2021-01-04 00:29:29 +11:00
f . write_str ( csi! ( " ?7l " ) )
2020-09-11 18:19:41 +10:00
}
#[ cfg(windows) ]
2021-06-10 23:20:00 +10:00
fn execute_winapi ( & self ) -> Result < ( ) > {
2020-09-11 18:19:41 +10:00
let screen_buffer = ScreenBuffer ::current ( ) ? ;
let console_mode = ConsoleMode ::from ( screen_buffer . handle ( ) . clone ( ) ) ;
let new_mode = console_mode . mode ( ) ? & ! ENABLE_WRAP_AT_EOL_OUTPUT ;
console_mode . set_mode ( new_mode ) ? ;
Ok ( ( ) )
}
}
/// Enable line wrapping.
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
pub struct EnableLineWrap ;
impl Command for EnableLineWrap {
2020-12-28 17:56:32 +11:00
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
2021-01-04 00:29:29 +11:00
f . write_str ( csi! ( " ?7h " ) )
2020-09-11 18:19:41 +10:00
}
#[ cfg(windows) ]
2021-06-10 23:20:00 +10:00
fn execute_winapi ( & self ) -> Result < ( ) > {
2020-09-11 18:19:41 +10:00
let screen_buffer = ScreenBuffer ::current ( ) ? ;
let console_mode = ConsoleMode ::from ( screen_buffer . handle ( ) . clone ( ) ) ;
let new_mode = console_mode . mode ( ) ? | ENABLE_WRAP_AT_EOL_OUTPUT ;
console_mode . set_mode ( new_mode ) ? ;
Ok ( ( ) )
}
}
2019-12-05 03:40:11 +11:00
/// A command that switches to alternate screen.
///
/// # Notes
///
/// * Commands must be executed/queued for execution otherwise they do nothing.
/// * Use [LeaveAlternateScreen](./struct.LeaveAlternateScreen.html) command to leave the entered alternate screen.
///
/// # Examples
///
/// ```no_run
/// use std::io::{stdout, Write};
/// use crossterm::{execute, Result, terminal::{EnterAlternateScreen, LeaveAlternateScreen}};
///
/// fn main() -> Result<()> {
/// execute!(stdout(), EnterAlternateScreen)?;
///
/// // Do anything on the alternate screen
///
/// execute!(stdout(), LeaveAlternateScreen)
/// }
/// ```
///
2020-01-29 06:20:26 +11:00
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
2019-12-05 03:40:11 +11:00
pub struct EnterAlternateScreen ;
impl Command for EnterAlternateScreen {
2020-12-28 17:56:32 +11:00
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
2021-01-04 00:29:29 +11:00
f . write_str ( csi! ( " ?1049h " ) )
2019-12-05 03:40:11 +11:00
}
#[ cfg(windows) ]
2021-06-10 23:20:00 +10:00
fn execute_winapi ( & self ) -> Result < ( ) > {
2021-06-10 23:55:34 +10:00
let alternate_screen = ScreenBuffer ::create ( ) ? ;
2019-12-05 03:40:11 +11:00
alternate_screen . show ( ) ? ;
Ok ( ( ) )
}
}
/// A command that switches back to the main screen.
///
/// # Notes
///
/// * Commands must be executed/queued for execution otherwise they do nothing.
/// * Use [EnterAlternateScreen](./struct.EnterAlternateScreen.html) to enter the alternate screen.
///
/// # Examples
///
/// ```no_run
/// use std::io::{stdout, Write};
/// use crossterm::{execute, Result, terminal::{EnterAlternateScreen, LeaveAlternateScreen}};
///
/// fn main() -> Result<()> {
/// execute!(stdout(), EnterAlternateScreen)?;
///
/// // Do anything on the alternate screen
///
/// execute!(stdout(), LeaveAlternateScreen)
/// }
/// ```
///
2020-01-29 06:20:26 +11:00
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
2019-12-05 03:40:11 +11:00
pub struct LeaveAlternateScreen ;
impl Command for LeaveAlternateScreen {
2020-12-28 17:56:32 +11:00
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
2021-01-04 00:29:29 +11:00
f . write_str ( csi! ( " ?1049l " ) )
2019-12-05 03:40:11 +11:00
}
#[ cfg(windows) ]
2021-06-10 23:20:00 +10:00
fn execute_winapi ( & self ) -> Result < ( ) > {
2019-12-05 03:40:11 +11:00
let screen_buffer = ScreenBuffer ::from ( Handle ::current_out_handle ( ) ? ) ;
screen_buffer . show ( ) ? ;
Ok ( ( ) )
}
}
2019-10-23 01:33:38 +11:00
2019-12-05 03:40:11 +11:00
/// Different ways to clear the terminal buffer.
2019-10-23 01:33:38 +11:00
#[ cfg_attr(feature = " serde " , derive(Serialize, Deserialize)) ]
#[ derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash) ]
pub enum ClearType {
/// All cells.
All ,
2021-08-13 21:10:32 +10:00
/// All plus history
Purge ,
2019-10-23 01:33:38 +11:00
/// All cells from the cursor position downwards.
FromCursorDown ,
/// All cells from the cursor position upwards.
FromCursorUp ,
/// All cells at the cursor row.
CurrentLine ,
/// All cells from the cursor position until the new line.
UntilNewLine ,
}
2019-10-29 19:14:47 +11:00
/// A command that scrolls the terminal screen 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.
2020-01-29 06:20:26 +11:00
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
2019-10-23 01:33:38 +11:00
pub struct ScrollUp ( pub u16 ) ;
impl Command for ScrollUp {
2020-12-28 17:56:32 +11:00
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
2021-01-04 00:29:29 +11:00
if self . 0 ! = 0 {
write! ( f , csi! ( " {}S " ) , self . 0 ) ? ;
}
Ok ( ( ) )
2019-10-23 01:33:38 +11:00
}
#[ cfg(windows) ]
2021-06-10 23:20:00 +10:00
fn execute_winapi ( & self ) -> Result < ( ) > {
2019-10-29 19:14:47 +11:00
sys ::scroll_up ( self . 0 )
2019-10-23 01:33:38 +11:00
}
}
2019-10-29 19:14:47 +11:00
/// A command that scrolls the terminal screen 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.
2020-01-29 06:20:26 +11:00
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
2019-10-23 01:33:38 +11:00
pub struct ScrollDown ( pub u16 ) ;
impl Command for ScrollDown {
2020-12-28 17:56:32 +11:00
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
2021-01-04 00:29:29 +11:00
if self . 0 ! = 0 {
write! ( f , csi! ( " {}T " ) , self . 0 ) ? ;
}
Ok ( ( ) )
2019-10-23 01:33:38 +11:00
}
#[ cfg(windows) ]
2021-06-10 23:20:00 +10:00
fn execute_winapi ( & self ) -> Result < ( ) > {
2019-10-29 19:14:47 +11:00
sys ::scroll_down ( self . 0 )
2019-10-23 01:33:38 +11:00
}
}
2019-10-29 19:14:47 +11:00
/// A command that clears the terminal screen buffer.
2019-10-23 01:33:38 +11:00
///
/// See the [`ClearType`](enum.ClearType.html) enum.
///
/// # Notes
///
/// Commands must be executed/queued for execution otherwise they do nothing.
2020-01-29 06:20:26 +11:00
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
2019-10-23 01:33:38 +11:00
pub struct Clear ( pub ClearType ) ;
impl Command for Clear {
2020-12-28 17:56:32 +11:00
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
f . write_str ( match self . 0 {
2021-01-04 00:29:29 +11:00
ClearType ::All = > csi! ( " 2J " ) ,
2021-08-13 21:10:32 +10:00
ClearType ::Purge = > csi! ( " 3J " ) ,
2021-01-04 00:29:29 +11:00
ClearType ::FromCursorDown = > csi! ( " J " ) ,
ClearType ::FromCursorUp = > csi! ( " 1J " ) ,
ClearType ::CurrentLine = > csi! ( " 2K " ) ,
ClearType ::UntilNewLine = > csi! ( " K " ) ,
2020-12-28 17:56:32 +11:00
} )
2019-10-23 01:33:38 +11:00
}
#[ cfg(windows) ]
2021-06-10 23:20:00 +10:00
fn execute_winapi ( & self ) -> Result < ( ) > {
2019-11-19 22:18:24 +11:00
sys ::clear ( self . 0 )
2019-10-23 01:33:38 +11:00
}
}
2019-10-29 19:14:47 +11:00
/// A command that sets the terminal size `(columns, rows)`.
2019-10-23 01:33:38 +11:00
///
/// # Notes
///
/// Commands must be executed/queued for execution otherwise they do nothing.
2020-01-29 06:20:26 +11:00
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
2019-10-23 01:33:38 +11:00
pub struct SetSize ( pub u16 , pub u16 ) ;
impl Command for SetSize {
2020-12-28 17:56:32 +11:00
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
2021-01-04 00:29:29 +11:00
write! ( f , csi! ( " 8;{};{}t " ) , self . 1 , self . 0 )
2019-10-23 01:33:38 +11:00
}
#[ cfg(windows) ]
2021-06-10 23:20:00 +10:00
fn execute_winapi ( & self ) -> Result < ( ) > {
2019-10-29 19:14:47 +11:00
sys ::set_size ( self . 0 , self . 1 )
2019-10-23 01:33:38 +11:00
}
}
2020-05-16 15:41:43 +10:00
/// A command that sets the terminal title
///
/// # Notes
///
/// Commands must be executed/queued for execution otherwise they do nothing.
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
2020-12-31 04:50:44 +11:00
pub struct SetTitle < T > ( pub T ) ;
2020-05-16 15:41:43 +10:00
2020-12-31 04:50:44 +11:00
impl < T : fmt ::Display > Command for SetTitle < T > {
2020-12-28 17:56:32 +11:00
fn write_ansi ( & self , f : & mut impl fmt ::Write ) -> fmt ::Result {
2021-01-04 00:29:29 +11:00
write! ( f , " \x1B ]0;{} \x07 " , & self . 0 )
2020-05-16 15:41:43 +10:00
}
2020-05-19 03:01:39 +10:00
#[ cfg(windows) ]
2021-06-10 23:20:00 +10:00
fn execute_winapi ( & self ) -> Result < ( ) > {
2020-12-31 04:50:44 +11:00
sys ::set_window_title ( & self . 0 )
2020-05-16 15:41:43 +10:00
}
}
2019-10-23 01:33:38 +11:00
impl_display! ( for ScrollUp ) ;
impl_display! ( for ScrollDown ) ;
impl_display! ( for SetSize ) ;
impl_display! ( for Clear ) ;
2019-10-29 19:14:47 +11:00
#[ cfg(test) ]
mod tests {
2020-12-28 17:56:32 +11:00
use std ::{ io ::stdout , thread , time } ;
2019-10-29 19:14:47 +11:00
use crate ::execute ;
2021-08-13 21:13:37 +10:00
use super ::* ;
2019-10-29 19:14:47 +11:00
// Test is disabled, because it's failing on Travis CI
#[ test ]
#[ ignore ]
fn test_resize_ansi ( ) {
let ( width , height ) = size ( ) . unwrap ( ) ;
execute! ( stdout ( ) , SetSize ( 35 , 35 ) ) . unwrap ( ) ;
// see issue: https://github.com/eminence/terminal-size/issues/11
thread ::sleep ( time ::Duration ::from_millis ( 30 ) ) ;
assert_eq! ( ( 35 , 35 ) , size ( ) . unwrap ( ) ) ;
// reset to previous size
execute! ( stdout ( ) , SetSize ( width , height ) ) . unwrap ( ) ;
// see issue: https://github.com/eminence/terminal-size/issues/11
thread ::sleep ( time ::Duration ::from_millis ( 30 ) ) ;
assert_eq! ( ( width , height ) , size ( ) . unwrap ( ) ) ;
}
2021-08-13 21:13:37 +10:00
#[ test ]
fn test_raw_mode ( ) {
// check we start from normal mode (may fail on some test harnesses)
assert_eq! ( is_raw_mode_enabled ( ) . unwrap ( ) , false ) ;
// enable the raw mode
2021-08-25 21:19:53 +10:00
if enable_raw_mode ( ) . is_err ( ) {
// Enabling raw mode doesn't work on the ci
// So we just ignore it
return ;
}
2021-08-13 21:13:37 +10:00
// check it worked (on unix it doesn't really check the underlying
// tty but rather check that the code is consistent)
assert_eq! ( is_raw_mode_enabled ( ) . unwrap ( ) , true ) ;
// enable it again, this should not change anything
enable_raw_mode ( ) . unwrap ( ) ;
// check we're still in raw mode
assert_eq! ( is_raw_mode_enabled ( ) . unwrap ( ) , true ) ;
// now let's disable it
disable_raw_mode ( ) . unwrap ( ) ;
// check we're back to normal mode
assert_eq! ( is_raw_mode_enabled ( ) . unwrap ( ) , false ) ;
}
2019-10-29 19:14:47 +11:00
}