Refactored Screen Module (#336)
This commit is contained in:
parent
dec0d74b32
commit
b4241e0107
11
CHANGELOG.md
11
CHANGELOG.md
@ -7,9 +7,14 @@
|
|||||||
documentation
|
documentation
|
||||||
- Replace `docs/UPGRADE.md` with the [Upgrade Paths](https://github.com/crossterm-rs/crossterm/wiki#upgrade-paths)
|
- Replace `docs/UPGRADE.md` with the [Upgrade Paths](https://github.com/crossterm-rs/crossterm/wiki#upgrade-paths)
|
||||||
documentation
|
documentation
|
||||||
- Add `MoveToColumn`, `MoveToPreviousLine`, `MoveToNextLine` commands
|
- Add `MoveToColumn`, `MoveToPreviousLine`, `MoveToNextLine` commands
|
||||||
- Replace `utils::Output` command with `style::Print` command
|
- Merge `screen` module into `terminal`
|
||||||
- Fix enable/disable mouse capture commands on Windows.
|
- Remove `screen::AlternateScreen`
|
||||||
|
- Remove `screen::Rawscreen`
|
||||||
|
* Move and rename `Rawscreen::into_raw_mode` and `Rawscreen::disable_raw_mode` to `terminal::enable_raw_mode` and `terminal::disable_raw_mode`
|
||||||
|
- Move `screen::EnterAlternateScreen` and `screen::LeaveAlternateScreen` to `terminal::EnterAlternateScreen` and `terminal::LeaveAlternateScreen`
|
||||||
|
- Replace `utils::Output` command with `style::Print` command
|
||||||
|
- Fix enable/disable mouse capture commands on Windows.
|
||||||
|
|
||||||
# Version 0.13.3
|
# Version 0.13.3
|
||||||
|
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
//
|
//
|
||||||
// cargo run --example event-poll-read
|
// cargo run --example event-poll-read
|
||||||
//
|
//
|
||||||
use std::io::{stdout, Write};
|
use std::{
|
||||||
use std::time::Duration;
|
io::{stdout, Write},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
cursor::position,
|
cursor::position,
|
||||||
event::{poll, read, DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
|
event::{poll, read, DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
|
||||||
execute,
|
execute,
|
||||||
screen::RawScreen,
|
terminal::{disable_raw_mode, enable_raw_mode},
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,7 +49,7 @@ fn print_events() -> Result<()> {
|
|||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
println!("{}", HELP);
|
println!("{}", HELP);
|
||||||
|
|
||||||
let _r = RawScreen::into_raw_mode()?;
|
enable_raw_mode();
|
||||||
|
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
execute!(stdout, EnableMouseCapture)?;
|
execute!(stdout, EnableMouseCapture)?;
|
||||||
@ -57,5 +59,6 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
execute!(stdout, DisableMouseCapture)?;
|
execute!(stdout, DisableMouseCapture)?;
|
||||||
Ok(())
|
|
||||||
|
disable_raw_mode()
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use crossterm::{
|
|||||||
cursor::position,
|
cursor::position,
|
||||||
event::{read, DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
|
event::{read, DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
|
||||||
execute,
|
execute,
|
||||||
screen::RawScreen,
|
terminal::{disable_raw_mode, enable_raw_mode},
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ fn print_events() -> Result<()> {
|
|||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
println!("{}", HELP);
|
println!("{}", HELP);
|
||||||
|
|
||||||
let _r = RawScreen::into_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
|
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
execute!(stdout, EnableMouseCapture)?;
|
execute!(stdout, EnableMouseCapture)?;
|
||||||
@ -49,5 +49,6 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
execute!(stdout, DisableMouseCapture)?;
|
execute!(stdout, DisableMouseCapture)?;
|
||||||
Ok(())
|
|
||||||
|
disable_raw_mode()
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
//
|
//
|
||||||
// cargo run --features event-stream --example event-stream-async-std
|
// cargo run --features event-stream --example event-stream-async-std
|
||||||
//
|
//
|
||||||
use std::io::{stdout, Write};
|
use std::{
|
||||||
use std::time::Duration;
|
io::{stdout, Write},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use futures::{future::FutureExt, select, StreamExt};
|
use futures::{future::FutureExt, select, StreamExt};
|
||||||
use futures_timer::Delay;
|
use futures_timer::Delay;
|
||||||
@ -11,7 +13,7 @@ use crossterm::{
|
|||||||
cursor::position,
|
cursor::position,
|
||||||
event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream, KeyCode},
|
event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream, KeyCode},
|
||||||
execute,
|
execute,
|
||||||
screen::RawScreen,
|
terminal::{disable_raw_mode, enable_raw_mode},
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -55,7 +57,7 @@ async fn print_events() {
|
|||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
println!("{}", HELP);
|
println!("{}", HELP);
|
||||||
|
|
||||||
let _r = RawScreen::into_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
|
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
execute!(stdout, EnableMouseCapture)?;
|
execute!(stdout, EnableMouseCapture)?;
|
||||||
@ -63,5 +65,6 @@ fn main() -> Result<()> {
|
|||||||
async_std::task::block_on(print_events());
|
async_std::task::block_on(print_events());
|
||||||
|
|
||||||
execute!(stdout, DisableMouseCapture)?;
|
execute!(stdout, DisableMouseCapture)?;
|
||||||
Ok(())
|
|
||||||
|
disable_raw_mode()
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
//
|
//
|
||||||
// cargo run --features event-stream --example event-stream-tokio
|
// cargo run --features event-stream --example event-stream-tokio
|
||||||
//
|
//
|
||||||
use std::io::{stdout, Write};
|
use std::{
|
||||||
use std::time::Duration;
|
io::{stdout, Write},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use futures::{future::FutureExt, select, StreamExt};
|
use futures::{future::FutureExt, select, StreamExt};
|
||||||
use futures_timer::Delay;
|
use futures_timer::Delay;
|
||||||
@ -11,7 +13,7 @@ use crossterm::{
|
|||||||
cursor::position,
|
cursor::position,
|
||||||
event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream, KeyCode},
|
event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream, KeyCode},
|
||||||
execute,
|
execute,
|
||||||
screen::RawScreen,
|
terminal::{disable_raw_mode, enable_raw_mode},
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -56,7 +58,7 @@ async fn print_events() {
|
|||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
println!("{}", HELP);
|
println!("{}", HELP);
|
||||||
|
|
||||||
let _r = RawScreen::into_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
|
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
execute!(stdout, EnableMouseCapture)?;
|
execute!(stdout, EnableMouseCapture)?;
|
||||||
@ -64,5 +66,6 @@ async fn main() -> Result<()> {
|
|||||||
print_events().await;
|
print_events().await;
|
||||||
|
|
||||||
execute!(stdout, DisableMouseCapture)?;
|
execute!(stdout, DisableMouseCapture)?;
|
||||||
Ok(())
|
|
||||||
|
disable_raw_mode()
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,8 @@ use std::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
event::{filter::CursorPositionFilter, poll_internal, read_internal, InternalEvent},
|
event::{filter::CursorPositionFilter, poll_internal, read_internal, InternalEvent},
|
||||||
utils::{
|
terminal::{disable_raw_mode, enable_raw_mode, sys::is_raw_mode_enabled},
|
||||||
sys::unix::{disable_raw_mode, enable_raw_mode, is_raw_mode_enabled},
|
utils::Result,
|
||||||
Result,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Returns the cursor position (column, row).
|
/// Returns the cursor position (column, row).
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
use std::{collections::vec_deque::VecDeque, time::Duration};
|
use std::{collections::vec_deque::VecDeque, time::Duration};
|
||||||
|
|
||||||
use super::filter::Filter;
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use super::source::unix::UnixInternalEventSource;
|
use super::source::unix::UnixInternalEventSource;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use super::source::windows::WindowsEventSource;
|
use super::source::windows::WindowsEventSource;
|
||||||
use super::{source::EventSource, timeout::PollTimeout, InternalEvent, Result};
|
use super::{filter::Filter, source::EventSource, timeout::PollTimeout, InternalEvent, Result};
|
||||||
|
|
||||||
/// Can be used to read `InternalEvent`s.
|
/// Can be used to read `InternalEvent`s.
|
||||||
pub(crate) struct InternalEventReader {
|
pub(crate) struct InternalEventReader {
|
||||||
@ -121,8 +120,7 @@ impl InternalEventReader {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::collections::VecDeque;
|
use std::{collections::VecDeque, time::Duration};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use crate::ErrorKind;
|
use crate::ErrorKind;
|
||||||
|
|
||||||
@ -130,7 +128,7 @@ mod tests {
|
|||||||
use super::super::filter::CursorPositionFilter;
|
use super::super::filter::CursorPositionFilter;
|
||||||
use super::{
|
use super::{
|
||||||
super::{filter::InternalEventFilter, Event},
|
super::{filter::InternalEventFilter, Event},
|
||||||
{EventSource, InternalEvent, InternalEventReader},
|
EventSource, InternalEvent, InternalEventReader,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::io;
|
use std::{io, time::Duration};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use mio::{unix::EventedFd, Events, Poll, PollOpt, Ready, Token};
|
use mio::{unix::EventedFd, Events, Poll, PollOpt, Ready, Token};
|
||||||
use signal_hook::iterator::Signals;
|
use signal_hook::iterator::Signals;
|
||||||
|
@ -2,8 +2,7 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use crossterm_winapi::{Console, Handle, InputEventType, KeyEventRecord, MouseEvent};
|
use crossterm_winapi::{Console, Handle, InputEventType, KeyEventRecord, MouseEvent};
|
||||||
|
|
||||||
use crate::event::sys::windows::WinApiPoll;
|
use crate::event::{sys::windows::WinApiPoll, Event};
|
||||||
use crate::event::Event;
|
|
||||||
|
|
||||||
use super::super::{
|
use super::super::{
|
||||||
source::EventSource,
|
source::EventSource,
|
||||||
|
@ -1,26 +1,24 @@
|
|||||||
//! This is a WINDOWS specific implementation for input related action.
|
//! This is a WINDOWS specific implementation for input related action.
|
||||||
|
|
||||||
use std::io;
|
use std::{io, io::ErrorKind, sync::Mutex, time::Duration};
|
||||||
use std::io::ErrorKind;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use crossterm_winapi::{
|
use crossterm_winapi::{
|
||||||
ConsoleMode, ControlKeyState, EventFlags, Handle, KeyEventRecord, MouseEvent, ScreenBuffer,
|
ConsoleMode, ControlKeyState, EventFlags, Handle, KeyEventRecord, MouseEvent, ScreenBuffer,
|
||||||
Semaphore,
|
Semaphore,
|
||||||
};
|
};
|
||||||
use winapi::shared::winerror::WAIT_TIMEOUT;
|
use winapi::{
|
||||||
use winapi::um::{
|
shared::winerror::WAIT_TIMEOUT,
|
||||||
synchapi::WaitForMultipleObjects,
|
um::{
|
||||||
winbase::{INFINITE, WAIT_ABANDONED_0, WAIT_FAILED, WAIT_OBJECT_0},
|
synchapi::WaitForMultipleObjects,
|
||||||
};
|
winbase::{INFINITE, WAIT_ABANDONED_0, WAIT_FAILED, WAIT_OBJECT_0},
|
||||||
use winapi::um::{
|
wincon::{
|
||||||
wincon::{
|
LEFT_ALT_PRESSED, LEFT_CTRL_PRESSED, RIGHT_ALT_PRESSED, RIGHT_CTRL_PRESSED,
|
||||||
LEFT_ALT_PRESSED, LEFT_CTRL_PRESSED, RIGHT_ALT_PRESSED, RIGHT_CTRL_PRESSED, SHIFT_PRESSED,
|
SHIFT_PRESSED,
|
||||||
},
|
},
|
||||||
winuser::{
|
winuser::{
|
||||||
VK_BACK, VK_CONTROL, VK_DELETE, VK_DOWN, VK_END, VK_ESCAPE, VK_F1, VK_F24, VK_HOME,
|
VK_BACK, VK_CONTROL, VK_DELETE, VK_DOWN, VK_END, VK_ESCAPE, VK_F1, VK_F24, VK_HOME,
|
||||||
VK_INSERT, VK_LEFT, VK_MENU, VK_NEXT, VK_PRIOR, VK_RETURN, VK_RIGHT, VK_SHIFT, VK_UP,
|
VK_INSERT, VK_LEFT, VK_MENU, VK_NEXT, VK_PRIOR, VK_RETURN, VK_RIGHT, VK_SHIFT, VK_UP,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -51,9 +51,6 @@
|
|||||||
//! - Module `event`
|
//! - Module `event`
|
||||||
//! - Mouse events - [`EnableMouseCapture`](event/struct.EnableMouseCapture.html),
|
//! - Mouse events - [`EnableMouseCapture`](event/struct.EnableMouseCapture.html),
|
||||||
//! [`DisableMouseCapture`](event/struct.DisableMouseCapture.html)
|
//! [`DisableMouseCapture`](event/struct.DisableMouseCapture.html)
|
||||||
//! - Module `screen`
|
|
||||||
//! - Alternate screen - [`EnterAlternateScreen`](screen/struct.EnterAlternateScreen.html),
|
|
||||||
//! [`LeaveAlternateScreen`](screen/struct.LeaveAlternateScreen.html)
|
|
||||||
//! - Module `style`
|
//! - Module `style`
|
||||||
//! - Colors - [`SetForegroundColor`](style/struct.SetForegroundColor.html),
|
//! - Colors - [`SetForegroundColor`](style/struct.SetForegroundColor.html),
|
||||||
//! [`SetBackgroundColor`](style/struct.SetBackgroundColor.html),
|
//! [`SetBackgroundColor`](style/struct.SetBackgroundColor.html),
|
||||||
@ -65,6 +62,8 @@
|
|||||||
//! [`ScrollDown`](terminal/struct.ScrollDown.html)
|
//! [`ScrollDown`](terminal/struct.ScrollDown.html)
|
||||||
//! - Miscellaneous - [`Clear`](terminal/struct.Clear.html),
|
//! - Miscellaneous - [`Clear`](terminal/struct.Clear.html),
|
||||||
//! [`SetSize`](terminal/struct.SetSize.html)
|
//! [`SetSize`](terminal/struct.SetSize.html)
|
||||||
|
//! - Alternate screen - [`EnterAlternateScreen`](screen/struct.EnterAlternateScreen.html),
|
||||||
|
//! [`LeaveAlternateScreen`](screen/struct.LeaveAlternateScreen.html)
|
||||||
//!
|
//!
|
||||||
//! ### Command Execution
|
//! ### Command Execution
|
||||||
//!
|
//!
|
||||||
@ -234,8 +233,6 @@ pub use utils::{Command, ErrorKind, ExecutableCommand, QueueableCommand, Result}
|
|||||||
pub mod cursor;
|
pub mod cursor;
|
||||||
/// A module to read events.
|
/// A module to read events.
|
||||||
pub mod event;
|
pub mod event;
|
||||||
/// A module to work with the terminal screen and modes.
|
|
||||||
pub mod screen;
|
|
||||||
/// A module to apply attributes and colors on your text.
|
/// A module to apply attributes and colors on your text.
|
||||||
pub mod style;
|
pub mod style;
|
||||||
/// A module to work with the terminal.
|
/// A module to work with the terminal.
|
||||||
|
209
src/screen.rs
209
src/screen.rs
@ -1,209 +0,0 @@
|
|||||||
//! # Screen
|
|
||||||
//!
|
|
||||||
//! The `screen` module provides the functionality to work with the terminal screen.
|
|
||||||
//!
|
|
||||||
//! 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.
|
|
||||||
//!
|
|
||||||
//! ## Screen Buffer
|
|
||||||
//!
|
|
||||||
//! A screen buffer is a two-dimensional array of characters and color data to be output in a console window.
|
|
||||||
//! A terminal can have multiple of those screen buffers, and the active screen buffer is the one that is
|
|
||||||
//! displayed on the screen.
|
|
||||||
//!
|
|
||||||
//! Crossterm allows you to switch between those buffers; the screen you are working in is called the
|
|
||||||
//! 'main screen'. We call the other screen the 'alternate screen'. One note to take is that crossterm
|
|
||||||
//! does not support the creation and switching between several buffers.
|
|
||||||
//!
|
|
||||||
//! ### Alternate Screen
|
|
||||||
//!
|
|
||||||
//! Normally you are working on the main screen but an alternate screen is somewhat different from a
|
|
||||||
//! normal screen. For example, it has the exact dimensions of the terminal window, without any
|
|
||||||
//! scroll back region. An example of this is vim when it is launched from bash.
|
|
||||||
//!
|
|
||||||
//! Vim uses the entirety of the screen to edit the file, then exits to bash leaving the original buffer unchanged.
|
|
||||||
//!
|
|
||||||
//! Crossterm provides the ability to switch to the alternate screen, make some changes, and then go back
|
|
||||||
//! to the main screen. The main screen will still have its original data since we made all the edits on
|
|
||||||
//! the alternate screen.
|
|
||||||
//!
|
|
||||||
//! ### Raw Mode
|
|
||||||
//!
|
|
||||||
//! By default, the terminal behaves in a certain way.
|
|
||||||
//! You can think of going to a new line if the input is at the end of the current line, or interpreting backspace
|
|
||||||
//! to remove letters. Sometimes it can be useful to disable these modes because this is undesirable.
|
|
||||||
//! This may be undesirable if your application wants to read the input without it being shown on the screen.
|
|
||||||
//! Raw modes are the modes to create this possibility.
|
|
||||||
//!
|
|
||||||
//! 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
|
|
||||||
|
|
||||||
// This brings the trait into scope, so we're able to call enter()/leave(),
|
|
||||||
// but it it's false positive for unused_imports check
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use alternate::AlternateScreen as _;
|
|
||||||
|
|
||||||
use crate::utils::{Command, Result};
|
|
||||||
|
|
||||||
pub use self::raw::{IntoRawMode, RawScreen};
|
|
||||||
|
|
||||||
mod alternate;
|
|
||||||
mod raw;
|
|
||||||
mod sys;
|
|
||||||
|
|
||||||
/// An alternate screen.
|
|
||||||
///
|
|
||||||
/// With this type, you will be able to switch to the alternate screen and then back to
|
|
||||||
/// the main screen.
|
|
||||||
///
|
|
||||||
/// Be aware that you'll be switched back to the main screen when you drop the
|
|
||||||
/// `AlternateScreen` value.
|
|
||||||
///
|
|
||||||
/// It's recommended to use the command API. See the
|
|
||||||
/// [`EnterAlternateScreen`](struct.EnterAlternateScreen.html)
|
|
||||||
/// and [`LeaveAlternateScreen`](struct.LeaveAlternateScreen.html)
|
|
||||||
/// commands documentation for more info.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// Alternate screen with raw mode enabled:
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// use crossterm::{screen::{AlternateScreen}, Result};
|
|
||||||
///
|
|
||||||
/// fn main() -> Result<()> {
|
|
||||||
/// let _alternate = AlternateScreen::to_alternate(true)?;
|
|
||||||
///
|
|
||||||
/// // Do something on the alternate screen in the raw mode
|
|
||||||
///
|
|
||||||
/// Ok(())
|
|
||||||
/// } // `_alternate` dropped here <- raw mode disabled & back to main screen
|
|
||||||
/// ```
|
|
||||||
pub struct AlternateScreen {
|
|
||||||
#[cfg(windows)]
|
|
||||||
alternate: Box<(dyn alternate::AlternateScreen + Sync + Send)>,
|
|
||||||
#[cfg(unix)]
|
|
||||||
alternate: alternate::AnsiAlternateScreen,
|
|
||||||
raw_screen: Option<RawScreen>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AlternateScreen {
|
|
||||||
/// Switches to the alternate screen.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `raw_mode` - `true` enables the raw mode as well
|
|
||||||
///
|
|
||||||
/// # Notes
|
|
||||||
///
|
|
||||||
/// You'll be automatically switched to the main screen if this function
|
|
||||||
/// fails.
|
|
||||||
#[allow(clippy::wrong_self_convention)]
|
|
||||||
pub fn to_alternate(raw_mode: bool) -> Result<AlternateScreen> {
|
|
||||||
let alternate = alternate::alternate_screen();
|
|
||||||
alternate.enter()?;
|
|
||||||
|
|
||||||
let mut alternate = AlternateScreen {
|
|
||||||
alternate,
|
|
||||||
raw_screen: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
if raw_mode {
|
|
||||||
// If into_raw_mode fails, `alternate` will be dropped and
|
|
||||||
// we'll switch back to the main screen.
|
|
||||||
alternate.raw_screen = Some(RawScreen::into_raw_mode()?);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(alternate)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Switches to the main screen.
|
|
||||||
pub fn to_main(&self) -> Result<()> {
|
|
||||||
self.alternate.leave()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for AlternateScreen {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let _ = self.to_main();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A command to switch to the alternate screen.
|
|
||||||
///
|
|
||||||
/// # Notes
|
|
||||||
///
|
|
||||||
/// Commands must be executed/queued for execution otherwise they do nothing.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// use std::io::{stdout, Write};
|
|
||||||
/// use crossterm::{execute, Result,screen::{EnterAlternateScreen, LeaveAlternateScreen}};
|
|
||||||
///
|
|
||||||
/// fn main() -> Result<()> {
|
|
||||||
/// execute!(stdout(), EnterAlternateScreen)?;
|
|
||||||
///
|
|
||||||
/// // Do anything on the alternate screen
|
|
||||||
///
|
|
||||||
/// execute!(stdout(), LeaveAlternateScreen)
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub struct EnterAlternateScreen;
|
|
||||||
|
|
||||||
impl Command for EnterAlternateScreen {
|
|
||||||
type AnsiType = &'static str;
|
|
||||||
|
|
||||||
fn ansi_code(&self) -> Self::AnsiType {
|
|
||||||
alternate::ansi::ENTER_ALTERNATE_SCREEN_CSI_SEQUENCE
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
fn execute_winapi(&self) -> Result<()> {
|
|
||||||
let alternate = alternate::alternate_screen();
|
|
||||||
alternate.enter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A command to switch back to the main screen.
|
|
||||||
///
|
|
||||||
/// # Notes
|
|
||||||
///
|
|
||||||
/// Commands must be executed/queued for execution otherwise they do nothing.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// use std::io::{stdout, Write};
|
|
||||||
/// use crossterm::{execute, Result, screen::{EnterAlternateScreen, LeaveAlternateScreen}};
|
|
||||||
///
|
|
||||||
/// fn main() -> Result<()> {
|
|
||||||
/// execute!(stdout(), EnterAlternateScreen)?;
|
|
||||||
///
|
|
||||||
/// // Do anything on the alternate screen
|
|
||||||
///
|
|
||||||
/// execute!(stdout(), LeaveAlternateScreen)
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub struct LeaveAlternateScreen;
|
|
||||||
|
|
||||||
impl Command for LeaveAlternateScreen {
|
|
||||||
type AnsiType = &'static str;
|
|
||||||
|
|
||||||
fn ansi_code(&self) -> Self::AnsiType {
|
|
||||||
alternate::ansi::LEAVE_ALTERNATE_SCREEN_CSI_SEQUENCE
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
fn execute_winapi(&self) -> Result<()> {
|
|
||||||
let alternate = alternate::alternate_screen();
|
|
||||||
alternate.leave()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
pub(crate) use ansi::AnsiAlternateScreen;
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub(crate) use windows::WinApiAlternateScreen;
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
use crate::utils::functions::supports_ansi;
|
|
||||||
use crate::utils::Result;
|
|
||||||
|
|
||||||
pub(crate) mod ansi;
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub(crate) mod windows;
|
|
||||||
|
|
||||||
pub(crate) trait AlternateScreen: Sync + Send {
|
|
||||||
fn enter(&self) -> Result<()>;
|
|
||||||
fn leave(&self) -> Result<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub(crate) fn alternate_screen() -> Box<dyn AlternateScreen + Send + Sync> {
|
|
||||||
if supports_ansi() {
|
|
||||||
Box::new(AnsiAlternateScreen)
|
|
||||||
} else {
|
|
||||||
Box::new(WinApiAlternateScreen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub(crate) fn alternate_screen() -> AnsiAlternateScreen {
|
|
||||||
AnsiAlternateScreen
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
use std::io::{stdout, Write};
|
|
||||||
|
|
||||||
use crate::{csi, utils::Result};
|
|
||||||
|
|
||||||
use super::AlternateScreen;
|
|
||||||
|
|
||||||
pub(crate) const ENTER_ALTERNATE_SCREEN_CSI_SEQUENCE: &str = csi!("?1049h");
|
|
||||||
pub(crate) const LEAVE_ALTERNATE_SCREEN_CSI_SEQUENCE: &str = csi!("?1049l");
|
|
||||||
|
|
||||||
pub(crate) struct AnsiAlternateScreen;
|
|
||||||
|
|
||||||
impl AlternateScreen for AnsiAlternateScreen {
|
|
||||||
fn enter(&self) -> Result<()> {
|
|
||||||
let mut stdout = stdout();
|
|
||||||
write!(stdout, "{}", ENTER_ALTERNATE_SCREEN_CSI_SEQUENCE)?;
|
|
||||||
stdout.flush()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn leave(&self) -> Result<()> {
|
|
||||||
let mut stdout = stdout();
|
|
||||||
write!(stdout, "{}", LEAVE_ALTERNATE_SCREEN_CSI_SEQUENCE)?;
|
|
||||||
stdout.flush()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
use crossterm_winapi::{Handle, ScreenBuffer};
|
|
||||||
|
|
||||||
use crate::utils::Result;
|
|
||||||
|
|
||||||
use super::AlternateScreen;
|
|
||||||
|
|
||||||
pub(crate) struct WinApiAlternateScreen;
|
|
||||||
|
|
||||||
impl AlternateScreen for WinApiAlternateScreen {
|
|
||||||
fn enter(&self) -> Result<()> {
|
|
||||||
let alternate_screen = ScreenBuffer::create();
|
|
||||||
alternate_screen.show()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn leave(&self) -> Result<()> {
|
|
||||||
let screen_buffer = ScreenBuffer::from(Handle::output_handle()?);
|
|
||||||
screen_buffer.show()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
use std::io::{Stdout, Write};
|
|
||||||
|
|
||||||
use crate::utils::Result;
|
|
||||||
|
|
||||||
use super::sys;
|
|
||||||
|
|
||||||
/// A raw screen.
|
|
||||||
///
|
|
||||||
/// Be aware that the raw mode is disabled when you drop the `RawScreen` value.
|
|
||||||
/// Call the [`keep_raw_mode_on_drop`](struct.RawScreen.html#method.keep_raw_mode_on_drop)
|
|
||||||
/// method to disable this behavior (keep the raw mode enabled).
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// Basic usage:
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// use crossterm::{screen::RawScreen, Result};
|
|
||||||
///
|
|
||||||
/// fn main() -> Result<()> {
|
|
||||||
/// let _raw = RawScreen::into_raw_mode()?;
|
|
||||||
/// // Do something in the raw mode
|
|
||||||
/// Ok(())
|
|
||||||
/// } // `_raw` is dropped here <- raw mode is disabled
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Do not disable the raw mode implicitly:
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// use crossterm::{screen::RawScreen, Result};
|
|
||||||
///
|
|
||||||
/// fn main() -> Result<()> {
|
|
||||||
/// let mut raw = RawScreen::into_raw_mode()?;
|
|
||||||
/// raw.keep_raw_mode_on_drop();
|
|
||||||
/// // Feel free to leave `raw` on it's own/drop it, the raw
|
|
||||||
/// // mode won't be disabled
|
|
||||||
///
|
|
||||||
/// // Do something in the raw mode
|
|
||||||
///
|
|
||||||
/// // Disable raw mode explicitly
|
|
||||||
/// RawScreen::disable_raw_mode()
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub struct RawScreen {
|
|
||||||
disable_raw_mode_on_drop: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RawScreen {
|
|
||||||
// TODO enable_raw_mode() to keep it synced with enable/disable?
|
|
||||||
/// Enables raw mode.
|
|
||||||
pub fn into_raw_mode() -> Result<RawScreen> {
|
|
||||||
#[cfg(unix)]
|
|
||||||
let mut command = sys::unix::RawModeCommand::new();
|
|
||||||
#[cfg(windows)]
|
|
||||||
let mut command = sys::winapi::RawModeCommand::new();
|
|
||||||
|
|
||||||
command.enable()?;
|
|
||||||
|
|
||||||
Ok(RawScreen {
|
|
||||||
disable_raw_mode_on_drop: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disables raw mode.
|
|
||||||
pub fn disable_raw_mode() -> Result<()> {
|
|
||||||
#[cfg(unix)]
|
|
||||||
let mut command = sys::unix::RawModeCommand::new();
|
|
||||||
#[cfg(windows)]
|
|
||||||
let command = sys::winapi::RawModeCommand::new();
|
|
||||||
|
|
||||||
command.disable()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Keeps the raw mode enabled when `self` is dropped.
|
|
||||||
///
|
|
||||||
/// See the [`RawScreen`](struct.RawScreen.html) documentation for more
|
|
||||||
/// information.
|
|
||||||
pub fn keep_raw_mode_on_drop(&mut self) {
|
|
||||||
self.disable_raw_mode_on_drop = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allows to enable raw mode.
|
|
||||||
///
|
|
||||||
/// Why this type must be implemented on writers?
|
|
||||||
///
|
|
||||||
/// TTYs has their state controlled by the writer, not the reader. You use the writer to
|
|
||||||
/// clear the screen, move the cursor and so on, so naturally you use the writer to change
|
|
||||||
/// the mode as well.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// use std::io::stdout;
|
|
||||||
/// use crossterm::{screen::IntoRawMode, Result};
|
|
||||||
///
|
|
||||||
/// fn main() -> Result<()> {
|
|
||||||
/// let stdout = stdout();
|
|
||||||
/// let _raw = stdout.into_raw_mode()?;
|
|
||||||
///
|
|
||||||
/// // Do something in the raw mode
|
|
||||||
///
|
|
||||||
/// Ok(())
|
|
||||||
/// } // `_raw` dropped here <- raw mode disabled
|
|
||||||
/// ```
|
|
||||||
pub trait IntoRawMode: Write + Sized {
|
|
||||||
/// Enables raw mode.
|
|
||||||
fn into_raw_mode(self) -> Result<RawScreen>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoRawMode for Stdout {
|
|
||||||
fn into_raw_mode(self) -> Result<RawScreen> {
|
|
||||||
RawScreen::into_raw_mode()?;
|
|
||||||
// this make's sure that raw screen will be disabled when it goes out of scope.
|
|
||||||
Ok(RawScreen {
|
|
||||||
disable_raw_mode_on_drop: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for RawScreen {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if self.disable_raw_mode_on_drop {
|
|
||||||
let _ = RawScreen::disable_raw_mode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
#[cfg(unix)]
|
|
||||||
pub(crate) mod unix;
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub(crate) mod winapi;
|
|
@ -1,22 +0,0 @@
|
|||||||
use crate::utils::Result;
|
|
||||||
|
|
||||||
/// This command is used for enabling and disabling raw mode for the terminal.
|
|
||||||
pub struct RawModeCommand;
|
|
||||||
|
|
||||||
impl RawModeCommand {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
RawModeCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enables raw mode.
|
|
||||||
pub fn enable(&mut self) -> Result<()> {
|
|
||||||
crate::utils::sys::unix::enable_raw_mode()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disables raw mode.
|
|
||||||
pub fn disable(&mut self) -> Result<()> {
|
|
||||||
crate::utils::sys::unix::disable_raw_mode()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
use crossterm_winapi::{ConsoleMode, Handle};
|
|
||||||
use winapi::{shared::minwindef::DWORD, um::wincon};
|
|
||||||
|
|
||||||
use crate::utils::Result;
|
|
||||||
|
|
||||||
use self::wincon::{ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT};
|
|
||||||
|
|
||||||
/// This command is used for enabling and disabling raw mode for Windows systems.
|
|
||||||
/// For more info check: https://docs.microsoft.com/en-us/windows/console/high-level-console-modes.
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct RawModeCommand {
|
|
||||||
mask: DWORD,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RawModeCommand {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
RawModeCommand {
|
|
||||||
mask: ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RawModeCommand {
|
|
||||||
/// Enables raw mode.
|
|
||||||
pub fn enable(&mut self) -> Result<()> {
|
|
||||||
let console_mode = ConsoleMode::from(Handle::input_handle()?);
|
|
||||||
|
|
||||||
let dw_mode = console_mode.mode()?;
|
|
||||||
|
|
||||||
let new_mode = dw_mode & !self.mask;
|
|
||||||
|
|
||||||
console_mode.set_mode(new_mode)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disables raw mode.
|
|
||||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
|
||||||
pub fn disable(&self) -> Result<()> {
|
|
||||||
let console_mode = ConsoleMode::from(Handle::input_handle()?);
|
|
||||||
|
|
||||||
let dw_mode = console_mode.mode()?;
|
|
||||||
|
|
||||||
let new_mode = dw_mode | self.mask;
|
|
||||||
|
|
||||||
console_mode.set_mode(new_mode)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
168
src/terminal.rs
168
src/terminal.rs
@ -7,9 +7,57 @@
|
|||||||
//! [examples](https://github.com/crossterm-rs/examples) repository
|
//! [examples](https://github.com/crossterm-rs/examples) repository
|
||||||
//! to demonstrate the capabilities.
|
//! to demonstrate the capabilities.
|
||||||
//!
|
//!
|
||||||
//! Terminal actions can be performed with commands.
|
//! Most terminal actions can be performed with commands.
|
||||||
//! Please have a look at [command documention](../index.html#command-api) for a more detailed documentation.
|
//! Please have a look at [command documention](../index.html#command-api) for a more detailed documentation.
|
||||||
//!
|
//!
|
||||||
|
//! ## 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
|
||||||
|
//!
|
||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
@ -18,7 +66,7 @@
|
|||||||
//!
|
//!
|
||||||
//! fn main() -> Result<()> {
|
//! fn main() -> Result<()> {
|
||||||
//! let (cols, rows) = size()?;
|
//! let (cols, rows) = size()?;
|
||||||
//! // Do something with the terminal
|
//! // Resize terminal and scroll up.
|
||||||
//! execute!(
|
//! execute!(
|
||||||
//! stdout(),
|
//! stdout(),
|
||||||
//! SetSize(10, 10),
|
//! SetSize(10, 10),
|
||||||
@ -33,21 +81,123 @@
|
|||||||
//!
|
//!
|
||||||
//! For manual execution control check out [crossterm::queue](../macro.queue.html).
|
//! For manual execution control check out [crossterm::queue](../macro.queue.html).
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
use crossterm_winapi::{Handle, ScreenBuffer};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub use sys::{exit, size};
|
|
||||||
|
|
||||||
use crate::impl_display;
|
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
use crate::utils::Command;
|
use crate::utils::Command;
|
||||||
#[cfg(windows)]
|
use crate::{impl_display, Result};
|
||||||
use crate::utils::Result;
|
|
||||||
|
|
||||||
mod ansi;
|
mod ansi;
|
||||||
mod sys;
|
pub(crate) mod sys;
|
||||||
|
|
||||||
/// Represents different options how to clear the terminal.
|
/// 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exits the current application.
|
||||||
|
pub fn exit() {
|
||||||
|
sys::exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the terminal size `(columns, rows)`.
|
||||||
|
///
|
||||||
|
/// The top left cell is represented `(1, 1)`.
|
||||||
|
pub fn size() -> Result<(u16, u16)> {
|
||||||
|
sys::size()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
pub struct EnterAlternateScreen;
|
||||||
|
|
||||||
|
impl Command for EnterAlternateScreen {
|
||||||
|
type AnsiType = &'static str;
|
||||||
|
|
||||||
|
fn ansi_code(&self) -> Self::AnsiType {
|
||||||
|
ansi::ENTER_ALTERNATE_SCREEN_CSI_SEQUENCE
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn execute_winapi(&self) -> Result<()> {
|
||||||
|
let alternate_screen = ScreenBuffer::create();
|
||||||
|
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)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
pub struct LeaveAlternateScreen;
|
||||||
|
|
||||||
|
impl Command for LeaveAlternateScreen {
|
||||||
|
type AnsiType = &'static str;
|
||||||
|
|
||||||
|
fn ansi_code(&self) -> Self::AnsiType {
|
||||||
|
ansi::LEAVE_ALTERNATE_SCREEN_CSI_SEQUENCE
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn execute_winapi(&self) -> Result<()> {
|
||||||
|
let screen_buffer = ScreenBuffer::from(Handle::current_out_handle()?);
|
||||||
|
screen_buffer.show()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Different ways to clear the terminal buffer.
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
||||||
pub enum ClearType {
|
pub enum ClearType {
|
||||||
|
@ -7,6 +7,8 @@ pub(crate) const CLEAR_FROM_CURSOR_DOWN_CSI_SEQUENCE: &str = csi!("J");
|
|||||||
pub(crate) const CLEAR_FROM_CURSOR_UP_CSI_SEQUENCE: &str = csi!("1J");
|
pub(crate) const CLEAR_FROM_CURSOR_UP_CSI_SEQUENCE: &str = csi!("1J");
|
||||||
pub(crate) const CLEAR_FROM_CURRENT_LINE_CSI_SEQUENCE: &str = csi!("2K");
|
pub(crate) const CLEAR_FROM_CURRENT_LINE_CSI_SEQUENCE: &str = csi!("2K");
|
||||||
pub(crate) const CLEAR_UNTIL_NEW_LINE_CSI_SEQUENCE: &str = csi!("K");
|
pub(crate) const CLEAR_UNTIL_NEW_LINE_CSI_SEQUENCE: &str = csi!("K");
|
||||||
|
pub(crate) const ENTER_ALTERNATE_SCREEN_CSI_SEQUENCE: &str = csi!("?1049h");
|
||||||
|
pub(crate) const LEAVE_ALTERNATE_SCREEN_CSI_SEQUENCE: &str = csi!("?1049l");
|
||||||
|
|
||||||
pub(crate) fn scroll_up_csi_sequence(count: u16) -> String {
|
pub(crate) fn scroll_up_csi_sequence(count: u16) -> String {
|
||||||
format!(csi!("{}S"), count)
|
format!(csi!("{}S"), count)
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
//! This module provides platform related functions.
|
//! This module provides platform related functions.
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub use self::unix::{exit, size};
|
pub(crate) use self::unix::{disable_raw_mode, enable_raw_mode, exit, is_raw_mode_enabled, size};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub(crate) use self::windows::{clear, scroll_down, scroll_up, set_size};
|
pub(crate) use self::windows::{
|
||||||
#[cfg(windows)]
|
clear, disable_raw_mode, enable_raw_mode, exit, scroll_down, scroll_up, set_size, size,
|
||||||
pub use self::windows::{exit, size};
|
};
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub(crate) mod windows;
|
mod windows;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub(crate) mod unix;
|
mod unix;
|
||||||
|
@ -1,15 +1,76 @@
|
|||||||
//! UNIX related logic for terminal manipulation.
|
//! UNIX related logic for terminal manipulation.
|
||||||
use std::process;
|
use std::{mem, process, sync::Mutex};
|
||||||
|
|
||||||
use libc::{ioctl, winsize, STDOUT_FILENO, TIOCGWINSZ};
|
use libc::{
|
||||||
|
cfmakeraw, ioctl, tcgetattr, tcsetattr, termios as Termios, winsize, STDIN_FILENO,
|
||||||
|
STDOUT_FILENO, TCSANOW, TIOCGWINSZ,
|
||||||
|
};
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use crate::utils::{sys::unix::wrap_with_result, Result};
|
use crate::utils::{sys::unix::wrap_with_result, Result};
|
||||||
|
|
||||||
/// Exits the current application.
|
lazy_static! {
|
||||||
pub fn exit() {
|
// Some(Termios) -> we're in the raw mode and this is the previous mode
|
||||||
|
// None -> we're not in the raw mode
|
||||||
|
static ref TERMINAL_MODE_PRIOR_RAW_MODE: Mutex<Option<Termios>> = Mutex::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_raw_mode_enabled() -> bool {
|
||||||
|
TERMINAL_MODE_PRIOR_RAW_MODE.lock().unwrap().is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn exit() {
|
||||||
::std::process::exit(0);
|
::std::process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn size() -> Result<(u16, u16)> {
|
||||||
|
// http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc
|
||||||
|
let mut size = winsize {
|
||||||
|
ws_row: 0,
|
||||||
|
ws_col: 0,
|
||||||
|
ws_xpixel: 0,
|
||||||
|
ws_ypixel: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(true) = wrap_with_result(unsafe { ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) }) {
|
||||||
|
Ok((size.ws_col, size.ws_row))
|
||||||
|
} else {
|
||||||
|
tput_size().ok_or_else(|| std::io::Error::last_os_error().into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn enable_raw_mode() -> Result<()> {
|
||||||
|
let mut original_mode = TERMINAL_MODE_PRIOR_RAW_MODE.lock().unwrap();
|
||||||
|
|
||||||
|
if original_mode.is_some() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ios = get_terminal_attr()?;
|
||||||
|
let original_mode_ios = ios;
|
||||||
|
|
||||||
|
raw_terminal_attr(&mut ios);
|
||||||
|
set_terminal_attr(&ios)?;
|
||||||
|
|
||||||
|
// Keep it last - set the original mode only if we were able to switch to the raw mode
|
||||||
|
*original_mode = Some(original_mode_ios);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn disable_raw_mode() -> Result<()> {
|
||||||
|
let mut original_mode = TERMINAL_MODE_PRIOR_RAW_MODE.lock().unwrap();
|
||||||
|
|
||||||
|
if let Some(original_mode_ios) = original_mode.as_ref() {
|
||||||
|
set_terminal_attr(original_mode_ios)?;
|
||||||
|
// Keep it last - remove the original mode only if we were able to switch back
|
||||||
|
*original_mode = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// execute tput with the given argument and parse
|
/// execute tput with the given argument and parse
|
||||||
/// the output as a u16.
|
/// the output as a u16.
|
||||||
///
|
///
|
||||||
@ -32,10 +93,10 @@ fn tput_value(arg: &str) -> Option<u16> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// return the size of the screen as determined by tput
|
/// Returns the size of the screen as determined by tput.
|
||||||
///
|
///
|
||||||
/// This alternate way of computing the size is useful
|
/// This alternate way of computing the size is useful
|
||||||
/// when in a subshell.
|
/// when in a subshell.
|
||||||
fn tput_size() -> Option<(u16, u16)> {
|
fn tput_size() -> Option<(u16, u16)> {
|
||||||
match (tput_value("cols"), tput_value("lines")) {
|
match (tput_value("cols"), tput_value("lines")) {
|
||||||
(Some(w), Some(h)) => Some((w, h)),
|
(Some(w), Some(h)) => Some((w, h)),
|
||||||
@ -43,21 +104,19 @@ fn tput_size() -> Option<(u16, u16)> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the terminal size `(columns, rows)`.
|
// Transform the given mode into an raw mode (non-canonical) mode.
|
||||||
///
|
fn raw_terminal_attr(termios: &mut Termios) {
|
||||||
/// The top left cell is represented `1,1`.
|
unsafe { cfmakeraw(termios) }
|
||||||
pub fn size() -> Result<(u16, u16)> {
|
}
|
||||||
// http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc
|
|
||||||
let mut size = winsize {
|
|
||||||
ws_row: 0,
|
|
||||||
ws_col: 0,
|
|
||||||
ws_xpixel: 0,
|
|
||||||
ws_ypixel: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Ok(true) = wrap_with_result(unsafe { ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) }) {
|
fn get_terminal_attr() -> Result<Termios> {
|
||||||
Ok((size.ws_col, size.ws_row))
|
unsafe {
|
||||||
} else {
|
let mut termios = mem::zeroed();
|
||||||
tput_size().ok_or_else(|| std::io::Error::last_os_error().into())
|
wrap_with_result(tcgetattr(STDIN_FILENO, &mut termios))?;
|
||||||
|
Ok(termios)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_terminal_attr(termios: &Termios) -> Result<bool> {
|
||||||
|
wrap_with_result(unsafe { tcsetattr(STDIN_FILENO, TCSANOW, termios) })
|
||||||
|
}
|
||||||
|
@ -1,18 +1,43 @@
|
|||||||
//! WinApi related logic for terminal manipulation.
|
//! WinApi related logic for terminal manipulation.
|
||||||
|
use crossterm_winapi::{Console, ConsoleMode, Coord, Handle, ScreenBuffer, Size};
|
||||||
use crossterm_winapi::{Console, Coord, Handle, ScreenBuffer, Size};
|
use winapi::{
|
||||||
|
shared::minwindef::DWORD,
|
||||||
|
um::wincon::{ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{cursor, terminal::ClearType, utils::Result, ErrorKind};
|
use crate::{cursor, terminal::ClearType, utils::Result, ErrorKind};
|
||||||
|
|
||||||
/// Exits the current application.
|
const RAW_MODE_MASK: DWORD = ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT;
|
||||||
pub fn exit() {
|
|
||||||
|
pub(crate) fn enable_raw_mode() -> Result<()> {
|
||||||
|
let console_mode = ConsoleMode::from(Handle::input_handle()?);
|
||||||
|
|
||||||
|
let dw_mode = console_mode.mode()?;
|
||||||
|
|
||||||
|
let new_mode = dw_mode & !RAW_MODE_MASK;
|
||||||
|
|
||||||
|
console_mode.set_mode(new_mode)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn disable_raw_mode() -> Result<()> {
|
||||||
|
let console_mode = ConsoleMode::from(Handle::input_handle()?);
|
||||||
|
|
||||||
|
let dw_mode = console_mode.mode()?;
|
||||||
|
|
||||||
|
let new_mode = dw_mode | RAW_MODE_MASK;
|
||||||
|
|
||||||
|
console_mode.set_mode(new_mode)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn exit() {
|
||||||
::std::process::exit(256);
|
::std::process::exit(256);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the terminal size `(columns, rows)`.
|
pub(crate) fn size() -> Result<(u16, u16)> {
|
||||||
///
|
|
||||||
/// The top left cell is represented `1,1`.
|
|
||||||
pub fn size() -> Result<(u16, u16)> {
|
|
||||||
let terminal_size = ScreenBuffer::current()?.info()?.terminal_size();
|
let terminal_size = ScreenBuffer::current()?.info()?.terminal_size();
|
||||||
// windows starts counting at 0, unix at 1, add one to replicated unix behaviour.
|
// windows starts counting at 0, unix at 1, add one to replicated unix behaviour.
|
||||||
Ok((
|
Ok((
|
||||||
@ -73,7 +98,6 @@ pub(crate) fn scroll_down(row_count: u16) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the current terminal size
|
|
||||||
pub(crate) fn set_size(width: u16, height: u16) -> Result<()> {
|
pub(crate) fn set_size(width: u16, height: u16) -> Result<()> {
|
||||||
if width <= 1 {
|
if width <= 1 {
|
||||||
return Err(ErrorKind::ResizingTerminalFailure(String::from(
|
return Err(ErrorKind::ResizingTerminalFailure(String::from(
|
||||||
|
@ -1,76 +1,13 @@
|
|||||||
//! This module contains all `unix` specific terminal related logic.
|
//! This module contains all `unix` specific terminal related logic.
|
||||||
|
|
||||||
use std::{io, mem, sync::Mutex};
|
use std::io;
|
||||||
|
|
||||||
pub use libc::termios as Termios;
|
|
||||||
use libc::{cfmakeraw, tcgetattr, tcsetattr, STDIN_FILENO, TCSANOW};
|
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
use super::super::error::{ErrorKind, Result};
|
use super::super::error::{ErrorKind, Result};
|
||||||
|
|
||||||
lazy_static! {
|
pub fn wrap_with_result(result: i32) -> Result<bool> {
|
||||||
// Some(Termios) -> we're in the raw mode and this is the previous mode
|
|
||||||
// None -> we're not in the raw mode
|
|
||||||
static ref TERMINAL_MODE_PRIOR_RAW_MODE: Mutex<Option<Termios>> = Mutex::new(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn is_raw_mode_enabled() -> bool {
|
|
||||||
TERMINAL_MODE_PRIOR_RAW_MODE.lock().unwrap().is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn wrap_with_result(result: i32) -> Result<bool> {
|
|
||||||
if result == -1 {
|
if result == -1 {
|
||||||
Err(ErrorKind::IoError(io::Error::last_os_error()))
|
Err(ErrorKind::IoError(io::Error::last_os_error()))
|
||||||
} else {
|
} else {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transform the given mode into an raw mode (non-canonical) mode.
|
|
||||||
pub(crate) fn raw_terminal_attr(termios: &mut Termios) {
|
|
||||||
unsafe { cfmakeraw(termios) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_terminal_attr() -> Result<Termios> {
|
|
||||||
unsafe {
|
|
||||||
let mut termios = mem::zeroed();
|
|
||||||
wrap_with_result(tcgetattr(STDIN_FILENO, &mut termios))?;
|
|
||||||
Ok(termios)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_terminal_attr(termios: &Termios) -> Result<bool> {
|
|
||||||
wrap_with_result(unsafe { tcsetattr(STDIN_FILENO, TCSANOW, termios) })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn enable_raw_mode() -> Result<()> {
|
|
||||||
let mut original_mode = TERMINAL_MODE_PRIOR_RAW_MODE.lock().unwrap();
|
|
||||||
|
|
||||||
if original_mode.is_some() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut ios = get_terminal_attr()?;
|
|
||||||
let original_mode_ios = ios;
|
|
||||||
|
|
||||||
raw_terminal_attr(&mut ios);
|
|
||||||
set_terminal_attr(&ios)?;
|
|
||||||
|
|
||||||
// Keep it last - set the original mode only if we were able to switch to the raw mode
|
|
||||||
*original_mode = Some(original_mode_ios);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn disable_raw_mode() -> Result<()> {
|
|
||||||
let mut original_mode = TERMINAL_MODE_PRIOR_RAW_MODE.lock().unwrap();
|
|
||||||
|
|
||||||
if let Some(original_mode_ios) = original_mode.as_ref() {
|
|
||||||
set_terminal_attr(original_mode_ios)?;
|
|
||||||
// Keep it last - remove the original mode only if we were able to switch back
|
|
||||||
*original_mode = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user