Refactored Screen Module (#336)
This commit is contained in:
parent
dec0d74b32
commit
b4241e0107
@ -8,6 +8,11 @@
|
||||
- Replace `docs/UPGRADE.md` with the [Upgrade Paths](https://github.com/crossterm-rs/crossterm/wiki#upgrade-paths)
|
||||
documentation
|
||||
- Add `MoveToColumn`, `MoveToPreviousLine`, `MoveToNextLine` commands
|
||||
- Merge `screen` module into `terminal`
|
||||
- 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.
|
||||
|
||||
|
@ -1,14 +1,16 @@
|
||||
//
|
||||
// cargo run --example event-poll-read
|
||||
//
|
||||
use std::io::{stdout, Write};
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
io::{stdout, Write},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use crossterm::{
|
||||
cursor::position,
|
||||
event::{poll, read, DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
|
||||
execute,
|
||||
screen::RawScreen,
|
||||
terminal::{disable_raw_mode, enable_raw_mode},
|
||||
Result,
|
||||
};
|
||||
|
||||
@ -47,7 +49,7 @@ fn print_events() -> Result<()> {
|
||||
fn main() -> Result<()> {
|
||||
println!("{}", HELP);
|
||||
|
||||
let _r = RawScreen::into_raw_mode()?;
|
||||
enable_raw_mode();
|
||||
|
||||
let mut stdout = stdout();
|
||||
execute!(stdout, EnableMouseCapture)?;
|
||||
@ -57,5 +59,6 @@ fn main() -> Result<()> {
|
||||
}
|
||||
|
||||
execute!(stdout, DisableMouseCapture)?;
|
||||
Ok(())
|
||||
|
||||
disable_raw_mode()
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use crossterm::{
|
||||
cursor::position,
|
||||
event::{read, DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
|
||||
execute,
|
||||
screen::RawScreen,
|
||||
terminal::{disable_raw_mode, enable_raw_mode},
|
||||
Result,
|
||||
};
|
||||
|
||||
@ -39,7 +39,7 @@ fn print_events() -> Result<()> {
|
||||
fn main() -> Result<()> {
|
||||
println!("{}", HELP);
|
||||
|
||||
let _r = RawScreen::into_raw_mode()?;
|
||||
enable_raw_mode()?;
|
||||
|
||||
let mut stdout = stdout();
|
||||
execute!(stdout, EnableMouseCapture)?;
|
||||
@ -49,5 +49,6 @@ fn main() -> Result<()> {
|
||||
}
|
||||
|
||||
execute!(stdout, DisableMouseCapture)?;
|
||||
Ok(())
|
||||
|
||||
disable_raw_mode()
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
//
|
||||
// cargo run --features event-stream --example event-stream-async-std
|
||||
//
|
||||
use std::io::{stdout, Write};
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
io::{stdout, Write},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use futures::{future::FutureExt, select, StreamExt};
|
||||
use futures_timer::Delay;
|
||||
@ -11,7 +13,7 @@ use crossterm::{
|
||||
cursor::position,
|
||||
event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream, KeyCode},
|
||||
execute,
|
||||
screen::RawScreen,
|
||||
terminal::{disable_raw_mode, enable_raw_mode},
|
||||
Result,
|
||||
};
|
||||
|
||||
@ -55,7 +57,7 @@ async fn print_events() {
|
||||
fn main() -> Result<()> {
|
||||
println!("{}", HELP);
|
||||
|
||||
let _r = RawScreen::into_raw_mode()?;
|
||||
enable_raw_mode()?;
|
||||
|
||||
let mut stdout = stdout();
|
||||
execute!(stdout, EnableMouseCapture)?;
|
||||
@ -63,5 +65,6 @@ fn main() -> Result<()> {
|
||||
async_std::task::block_on(print_events());
|
||||
|
||||
execute!(stdout, DisableMouseCapture)?;
|
||||
Ok(())
|
||||
|
||||
disable_raw_mode()
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
//
|
||||
// cargo run --features event-stream --example event-stream-tokio
|
||||
//
|
||||
use std::io::{stdout, Write};
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
io::{stdout, Write},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use futures::{future::FutureExt, select, StreamExt};
|
||||
use futures_timer::Delay;
|
||||
@ -11,7 +13,7 @@ use crossterm::{
|
||||
cursor::position,
|
||||
event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream, KeyCode},
|
||||
execute,
|
||||
screen::RawScreen,
|
||||
terminal::{disable_raw_mode, enable_raw_mode},
|
||||
Result,
|
||||
};
|
||||
|
||||
@ -56,7 +58,7 @@ async fn print_events() {
|
||||
async fn main() -> Result<()> {
|
||||
println!("{}", HELP);
|
||||
|
||||
let _r = RawScreen::into_raw_mode()?;
|
||||
enable_raw_mode()?;
|
||||
|
||||
let mut stdout = stdout();
|
||||
execute!(stdout, EnableMouseCapture)?;
|
||||
@ -64,5 +66,6 @@ async fn main() -> Result<()> {
|
||||
print_events().await;
|
||||
|
||||
execute!(stdout, DisableMouseCapture)?;
|
||||
Ok(())
|
||||
|
||||
disable_raw_mode()
|
||||
}
|
||||
|
@ -5,10 +5,8 @@ use std::{
|
||||
|
||||
use crate::{
|
||||
event::{filter::CursorPositionFilter, poll_internal, read_internal, InternalEvent},
|
||||
utils::{
|
||||
sys::unix::{disable_raw_mode, enable_raw_mode, is_raw_mode_enabled},
|
||||
Result,
|
||||
},
|
||||
terminal::{disable_raw_mode, enable_raw_mode, sys::is_raw_mode_enabled},
|
||||
utils::Result,
|
||||
};
|
||||
|
||||
/// Returns the cursor position (column, row).
|
||||
|
@ -1,11 +1,10 @@
|
||||
use std::{collections::vec_deque::VecDeque, time::Duration};
|
||||
|
||||
use super::filter::Filter;
|
||||
#[cfg(unix)]
|
||||
use super::source::unix::UnixInternalEventSource;
|
||||
#[cfg(windows)]
|
||||
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.
|
||||
pub(crate) struct InternalEventReader {
|
||||
@ -121,8 +120,7 @@ impl InternalEventReader {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::VecDeque;
|
||||
use std::time::Duration;
|
||||
use std::{collections::VecDeque, time::Duration};
|
||||
|
||||
use crate::ErrorKind;
|
||||
|
||||
@ -130,7 +128,7 @@ mod tests {
|
||||
use super::super::filter::CursorPositionFilter;
|
||||
use super::{
|
||||
super::{filter::InternalEventFilter, Event},
|
||||
{EventSource, InternalEvent, InternalEventReader},
|
||||
EventSource, InternalEvent, InternalEventReader,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
@ -1,6 +1,5 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::io;
|
||||
use std::time::Duration;
|
||||
use std::{io, time::Duration};
|
||||
|
||||
use mio::{unix::EventedFd, Events, Poll, PollOpt, Ready, Token};
|
||||
use signal_hook::iterator::Signals;
|
||||
|
@ -2,8 +2,7 @@ use std::time::Duration;
|
||||
|
||||
use crossterm_winapi::{Console, Handle, InputEventType, KeyEventRecord, MouseEvent};
|
||||
|
||||
use crate::event::sys::windows::WinApiPoll;
|
||||
use crate::event::Event;
|
||||
use crate::event::{sys::windows::WinApiPoll, Event};
|
||||
|
||||
use super::super::{
|
||||
source::EventSource,
|
||||
|
@ -1,27 +1,25 @@
|
||||
//! This is a WINDOWS specific implementation for input related action.
|
||||
|
||||
use std::io;
|
||||
use std::io::ErrorKind;
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
use std::{io, io::ErrorKind, sync::Mutex, time::Duration};
|
||||
|
||||
use crossterm_winapi::{
|
||||
ConsoleMode, ControlKeyState, EventFlags, Handle, KeyEventRecord, MouseEvent, ScreenBuffer,
|
||||
Semaphore,
|
||||
};
|
||||
use winapi::shared::winerror::WAIT_TIMEOUT;
|
||||
use winapi::um::{
|
||||
use winapi::{
|
||||
shared::winerror::WAIT_TIMEOUT,
|
||||
um::{
|
||||
synchapi::WaitForMultipleObjects,
|
||||
winbase::{INFINITE, WAIT_ABANDONED_0, WAIT_FAILED, WAIT_OBJECT_0},
|
||||
};
|
||||
use winapi::um::{
|
||||
wincon::{
|
||||
LEFT_ALT_PRESSED, LEFT_CTRL_PRESSED, RIGHT_ALT_PRESSED, RIGHT_CTRL_PRESSED, SHIFT_PRESSED,
|
||||
LEFT_ALT_PRESSED, LEFT_CTRL_PRESSED, RIGHT_ALT_PRESSED, RIGHT_CTRL_PRESSED,
|
||||
SHIFT_PRESSED,
|
||||
},
|
||||
winuser::{
|
||||
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,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
@ -51,9 +51,6 @@
|
||||
//! - Module `event`
|
||||
//! - Mouse events - [`EnableMouseCapture`](event/struct.EnableMouseCapture.html),
|
||||
//! [`DisableMouseCapture`](event/struct.DisableMouseCapture.html)
|
||||
//! - Module `screen`
|
||||
//! - Alternate screen - [`EnterAlternateScreen`](screen/struct.EnterAlternateScreen.html),
|
||||
//! [`LeaveAlternateScreen`](screen/struct.LeaveAlternateScreen.html)
|
||||
//! - Module `style`
|
||||
//! - Colors - [`SetForegroundColor`](style/struct.SetForegroundColor.html),
|
||||
//! [`SetBackgroundColor`](style/struct.SetBackgroundColor.html),
|
||||
@ -65,6 +62,8 @@
|
||||
//! [`ScrollDown`](terminal/struct.ScrollDown.html)
|
||||
//! - Miscellaneous - [`Clear`](terminal/struct.Clear.html),
|
||||
//! [`SetSize`](terminal/struct.SetSize.html)
|
||||
//! - Alternate screen - [`EnterAlternateScreen`](screen/struct.EnterAlternateScreen.html),
|
||||
//! [`LeaveAlternateScreen`](screen/struct.LeaveAlternateScreen.html)
|
||||
//!
|
||||
//! ### Command Execution
|
||||
//!
|
||||
@ -234,8 +233,6 @@ pub use utils::{Command, ErrorKind, ExecutableCommand, QueueableCommand, Result}
|
||||
pub mod cursor;
|
||||
/// A module to read events.
|
||||
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.
|
||||
pub mod style;
|
||||
/// 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
|
||||
//! 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.
|
||||
//!
|
||||
//! ## 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
|
||||
//!
|
||||
//! ```no_run
|
||||
@ -18,7 +66,7 @@
|
||||
//!
|
||||
//! fn main() -> Result<()> {
|
||||
//! let (cols, rows) = size()?;
|
||||
//! // Do something with the terminal
|
||||
//! // Resize terminal and scroll up.
|
||||
//! execute!(
|
||||
//! stdout(),
|
||||
//! SetSize(10, 10),
|
||||
@ -33,21 +81,123 @@
|
||||
//!
|
||||
//! For manual execution control check out [crossterm::queue](../macro.queue.html).
|
||||
|
||||
#[cfg(windows)]
|
||||
use crossterm_winapi::{Handle, ScreenBuffer};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub use sys::{exit, size};
|
||||
|
||||
use crate::impl_display;
|
||||
#[doc(no_inline)]
|
||||
use crate::utils::Command;
|
||||
#[cfg(windows)]
|
||||
use crate::utils::Result;
|
||||
use crate::{impl_display, Result};
|
||||
|
||||
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))]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
||||
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_CURRENT_LINE_CSI_SEQUENCE: &str = csi!("2K");
|
||||
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 {
|
||||
format!(csi!("{}S"), count)
|
||||
|
@ -1,14 +1,14 @@
|
||||
//! This module provides platform related functions.
|
||||
|
||||
#[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)]
|
||||
pub(crate) use self::windows::{clear, scroll_down, scroll_up, set_size};
|
||||
#[cfg(windows)]
|
||||
pub use self::windows::{exit, size};
|
||||
pub(crate) use self::windows::{
|
||||
clear, disable_raw_mode, enable_raw_mode, exit, scroll_down, scroll_up, set_size, size,
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
pub(crate) mod windows;
|
||||
mod windows;
|
||||
|
||||
#[cfg(unix)]
|
||||
pub(crate) mod unix;
|
||||
mod unix;
|
||||
|
@ -1,15 +1,76 @@
|
||||
//! 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};
|
||||
|
||||
/// Exits the current application.
|
||||
pub fn exit() {
|
||||
lazy_static! {
|
||||
// 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);
|
||||
}
|
||||
|
||||
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
|
||||
/// the output as a u16.
|
||||
///
|
||||
@ -32,7 +93,7 @@ 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
|
||||
/// when in a subshell.
|
||||
@ -43,21 +104,19 @@ fn tput_size() -> Option<(u16, u16)> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the terminal size `(columns, rows)`.
|
||||
///
|
||||
/// The top left cell is represented `1,1`.
|
||||
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,
|
||||
};
|
||||
// Transform the given mode into an raw mode (non-canonical) mode.
|
||||
fn raw_terminal_attr(termios: &mut Termios) {
|
||||
unsafe { cfmakeraw(termios) }
|
||||
}
|
||||
|
||||
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())
|
||||
fn get_terminal_attr() -> Result<Termios> {
|
||||
unsafe {
|
||||
let mut termios = mem::zeroed();
|
||||
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.
|
||||
|
||||
use crossterm_winapi::{Console, Coord, Handle, ScreenBuffer, Size};
|
||||
use crossterm_winapi::{Console, ConsoleMode, 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};
|
||||
|
||||
/// Exits the current application.
|
||||
pub fn exit() {
|
||||
const RAW_MODE_MASK: DWORD = ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// Returns the terminal size `(columns, rows)`.
|
||||
///
|
||||
/// The top left cell is represented `1,1`.
|
||||
pub fn size() -> Result<(u16, u16)> {
|
||||
pub(crate) fn size() -> Result<(u16, u16)> {
|
||||
let terminal_size = ScreenBuffer::current()?.info()?.terminal_size();
|
||||
// windows starts counting at 0, unix at 1, add one to replicated unix behaviour.
|
||||
Ok((
|
||||
@ -73,7 +98,6 @@ pub(crate) fn scroll_down(row_count: u16) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the current terminal size
|
||||
pub(crate) fn set_size(width: u16, height: u16) -> Result<()> {
|
||||
if width <= 1 {
|
||||
return Err(ErrorKind::ResizingTerminalFailure(String::from(
|
||||
|
@ -1,76 +1,13 @@
|
||||
//! This module contains all `unix` specific terminal related logic.
|
||||
|
||||
use std::{io, mem, sync::Mutex};
|
||||
|
||||
pub use libc::termios as Termios;
|
||||
use libc::{cfmakeraw, tcgetattr, tcsetattr, STDIN_FILENO, TCSANOW};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use std::io;
|
||||
|
||||
use super::super::error::{ErrorKind, Result};
|
||||
|
||||
lazy_static! {
|
||||
// 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> {
|
||||
pub fn wrap_with_result(result: i32) -> Result<bool> {
|
||||
if result == -1 {
|
||||
Err(ErrorKind::IoError(io::Error::last_os_error()))
|
||||
} else {
|
||||
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