minicrossterm/src/terminal.rs

211 lines
5.5 KiB
Rust
Raw Normal View History

//! # Terminal
//!
2019-10-29 19:14:47 +11:00
//! The `terminal` module provides functionality to work with the terminal.
//!
//! This documentation does not contain a lot of examples. The reason is that it's fairly
//! obvious how to use this crate. Although, we do provide
//! [examples](https://github.com/crossterm-rs/examples) repository
//! to demonstrate the capabilities.
//!
2019-10-29 19:14:47 +11:00
//! Terminal actions can be performed with commands.
//! Please have a look at [command documention](../index.html#command-api) for a more detailed documentation.
//!
2019-10-29 19:14:47 +11:00
//! ## Examples
//!
//! ```no_run
//! use std::io::{stdout, Write};
2019-10-29 19:14:47 +11:00
//! use crossterm::{execute, Result, terminal::{ScrollUp, SetSize, size}};
//!
//! fn main() -> Result<()> {
2019-10-29 19:14:47 +11:00
//! let (cols, rows) = size()?;
//! // Do something with the terminal
//! 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-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).
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
2019-11-19 07:50:57 +11:00
pub use sys::{exit, size};
2019-10-29 19:14:47 +11:00
use crate::impl_display;
#[doc(no_inline)]
2019-10-29 19:14:47 +11:00
use crate::utils::Command;
#[cfg(windows)]
2019-10-29 19:14:47 +11:00
use crate::utils::Result;
2019-10-29 19:14:47 +11:00
mod ansi;
mod sys;
/// Represents different options how to clear the terminal.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum ClearType {
/// All cells.
All,
/// 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.
///
/// # Notes
///
/// Commands must be executed/queued for execution otherwise they do nothing.
pub struct ScrollUp(pub u16);
impl Command for ScrollUp {
type AnsiType = String;
fn ansi_code(&self) -> Self::AnsiType {
2019-10-29 19:14:47 +11:00
ansi::scroll_up_csi_sequence(self.0)
}
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
2019-10-29 19:14:47 +11:00
sys::scroll_up(self.0)
}
}
2019-10-29 19:14:47 +11:00
/// A command that scrolls the terminal screen a given number of rows down.
///
/// # Notes
///
/// Commands must be executed/queued for execution otherwise they do nothing.
pub struct ScrollDown(pub u16);
impl Command for ScrollDown {
type AnsiType = String;
fn ansi_code(&self) -> Self::AnsiType {
2019-10-29 19:14:47 +11:00
ansi::scroll_down_csi_sequence(self.0)
}
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
2019-10-29 19:14:47 +11:00
sys::scroll_down(self.0)
}
}
2019-10-29 19:14:47 +11:00
/// A command that clears the terminal screen buffer.
///
/// See the [`ClearType`](enum.ClearType.html) enum.
///
/// # Notes
///
/// Commands must be executed/queued for execution otherwise they do nothing.
pub struct Clear(pub ClearType);
impl Command for Clear {
type AnsiType = &'static str;
fn ansi_code(&self) -> Self::AnsiType {
match self.0 {
2019-10-29 19:14:47 +11:00
ClearType::All => ansi::CLEAR_ALL_CSI_SEQUENCE,
ClearType::FromCursorDown => ansi::CLEAR_FROM_CURSOR_DOWN_CSI_SEQUENCE,
ClearType::FromCursorUp => ansi::CLEAR_FROM_CURSOR_UP_CSI_SEQUENCE,
ClearType::CurrentLine => ansi::CLEAR_FROM_CURRENT_LINE_CSI_SEQUENCE,
ClearType::UntilNewLine => ansi::CLEAR_UNTIL_NEW_LINE_CSI_SEQUENCE,
}
}
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
sys::clear(self.0)
}
}
2019-10-29 19:14:47 +11:00
/// A command that sets the terminal size `(columns, rows)`.
///
/// # Notes
///
/// Commands must be executed/queued for execution otherwise they do nothing.
pub struct SetSize(pub u16, pub u16);
impl Command for SetSize {
type AnsiType = String;
fn ansi_code(&self) -> Self::AnsiType {
2019-10-29 19:14:47 +11:00
ansi::set_size_csi_sequence(self.0, self.1)
}
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
2019-10-29 19:14:47 +11:00
sys::set_size(self.0, self.1)
}
}
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 {
use std::{
io::{stdout, Write},
thread, time,
};
use crate::execute;
use super::{size, SetSize};
// Test is disabled, because it's failing on Travis CI
#[test]
#[ignore]
fn test_resize_ansi() {
try_enable_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());
}
fn try_enable_ansi() -> bool {
#[cfg(windows)]
{
if cfg!(target_os = "windows") {
use crate::utils::sys::windows::set_virtual_terminal_processing;
2019-10-29 19:14:47 +11:00
// 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
}
}