diff --git a/examples/alternate_screen.rs b/examples/alternate_screen.rs new file mode 100644 index 0000000..91d28b1 --- /dev/null +++ b/examples/alternate_screen.rs @@ -0,0 +1,17 @@ +extern crate termion; + +use termion::screen::*; +use std::io::{Write, stdout}; +use std::{time, thread}; + +fn main() { + { + let mut screen = AlternateScreen::from(stdout()); + write!(screen, "Welcome to the alternate screen.\n\nPlease wait patiently until we arrive back at the main screen in a about three seconds.").unwrap(); + screen.flush().unwrap(); + + thread::sleep(time::Duration::from_secs(3)); + } + + println!("Phew! We are back."); +} diff --git a/examples/alternate_screen_raw.rs b/examples/alternate_screen_raw.rs new file mode 100644 index 0000000..beea9e9 --- /dev/null +++ b/examples/alternate_screen_raw.rs @@ -0,0 +1,40 @@ +extern crate termion; + +use termion::event::Key; +use termion::input::TermRead; +use termion::raw::IntoRawMode; +use termion::screen::*; +use std::io::{Write, stdout, stdin}; + +fn write_alt_screen_msg(screen: &mut W) { + write!(screen, "{}{}Welcome to the alternate screen.{}Press '1' to switch to the main screen or '2' to switch to the alternate screen.{}Press 'q' to exit (and switch back to the main screen).", + termion::clear::All, + termion::cursor::Goto(1, 1), + termion::cursor::Goto(1, 3), + termion::cursor::Goto(1, 4)).unwrap(); +} + +fn main() { + let stdin = stdin(); + let mut screen = AlternateScreen::from(stdout().into_raw_mode().unwrap()); + write!(screen, "{}", termion::cursor::Hide).unwrap(); + write_alt_screen_msg(&mut screen); + + screen.flush().unwrap(); + + for c in stdin.keys() { + match c.unwrap() { + Key::Char('q') => break, + Key::Char('1') => { + write!(screen, "{}", ToMainScreen).unwrap(); + }, + Key::Char('2') => { + write!(screen, "{}", ToAlternateScreen).unwrap(); + write_alt_screen_msg(&mut screen); + }, + _ => {}, + } + screen.flush().unwrap(); + } + write!(screen, "{}", termion::cursor::Show).unwrap(); +} diff --git a/src/lib.rs b/src/lib.rs index 668209f..7e286bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,5 +34,6 @@ pub mod cursor; pub mod event; pub mod input; pub mod raw; +pub mod screen; pub mod scroll; pub mod style; diff --git a/src/screen.rs b/src/screen.rs new file mode 100644 index 0000000..ef70a74 --- /dev/null +++ b/src/screen.rs @@ -0,0 +1,93 @@ +//! Managing switching between main and alternate screen buffers. +//! +//! Note that this implementation uses xterm's new escape sequences for screen switching and thus +//! only works for xterm compatible terminals (which should be most terminals nowadays). +//! +//! # Example +//! +//! ```rust +//! use termion::screen::AlternateScreen; +//! use std::io::{Write, stdout}; +//! +//! fn main() { +//! { +//! let mut screen = AlternateScreen::from(stdout()); +//! write!(screen, "Writing to alternate screen!").unwrap(); +//! screen.flush().unwrap(); +//! } +//! println!("Writing to main screen."); +//! } +//! ``` + +use std::io::{self, Write}; +use std::ops; +use std::fmt; + +/// Switch to the main screen buffer of the terminal. +pub struct ToMainScreen; + +impl fmt::Display for ToMainScreen { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, csi!("?1049l")) + } +} + +/// Switch to the alternate screen buffer of the terminal. +pub struct ToAlternateScreen; + +impl fmt::Display for ToAlternateScreen { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, csi!("?1049h")) + } +} + +/// A terminal restorer, which wraps a type implementing Write, and causes all writes to be written +/// to an alternate screen. +/// +/// This is achieved by switching the terminal to the alternate screen on creation and +/// automatically switching it back to the original screen on drop. +pub struct AlternateScreen { + /// The output target. + output: W, +} + +impl AlternateScreen { + /// Create an alternate screen wrapper struct for the provided output and switch the terminal + /// to the alternate screen. + pub fn from(mut output: W) -> Self { + write!(output, "{}", ToAlternateScreen).expect("switch to alternate screen"); + AlternateScreen { + output: output, + } + } +} + +impl Drop for AlternateScreen { + fn drop(&mut self) { + write!(self, "{}", ToMainScreen).expect("switch to main screen"); + } +} + +impl ops::Deref for AlternateScreen { + type Target = W; + + fn deref(&self) -> &W { + &self.output + } +} + +impl ops::DerefMut for AlternateScreen { + fn deref_mut(&mut self) -> &mut W { + &mut self.output + } +} + +impl Write for AlternateScreen { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.output.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.output.flush() + } +}