mirror of
https://github.com/wez/wezterm.git
synced 2024-11-24 07:46:59 +03:00
Add BufferedTerminal
This commit is contained in:
parent
c91219d65b
commit
2559257869
36
examples/buffered_terminal.rs
Normal file
36
examples/buffered_terminal.rs
Normal file
@ -0,0 +1,36 @@
|
||||
//! This example shows how to use `BufferedTerminal` to queue
|
||||
//! up changes and then flush them. `BufferedTerminal` enables
|
||||
//! optimizing the output sequence to update the screen, which is
|
||||
//! important on links with poor connectivity.
|
||||
extern crate failure;
|
||||
extern crate termwiz;
|
||||
|
||||
use failure::Error;
|
||||
use termwiz::caps::Capabilities;
|
||||
use termwiz::cell::AttributeChange;
|
||||
use termwiz::color::AnsiColor;
|
||||
use termwiz::surface::Change;
|
||||
use termwiz::terminal::buffered::BufferedTerminal;
|
||||
use termwiz::terminal::{new_terminal, Terminal};
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
let caps = Capabilities::new_from_env()?;
|
||||
|
||||
let mut terminal = new_terminal(caps)?;
|
||||
terminal.set_raw_mode()?;
|
||||
|
||||
let mut buf = BufferedTerminal::new(terminal)?;
|
||||
|
||||
buf.add_change(Change::Attribute(AttributeChange::Foreground(
|
||||
AnsiColor::Maroon.into(),
|
||||
)));
|
||||
buf.add_change("Hello world\r\n");
|
||||
buf.add_change(Change::Attribute(AttributeChange::Foreground(
|
||||
AnsiColor::Red.into(),
|
||||
)));
|
||||
buf.add_change("and in red here\r\n");
|
||||
|
||||
buf.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
@ -5,7 +5,8 @@ use failure::Error;
|
||||
use termwiz::caps::Capabilities;
|
||||
use termwiz::cell::AttributeChange;
|
||||
use termwiz::color::AnsiColor;
|
||||
use termwiz::surface::{Change, Surface};
|
||||
use termwiz::surface::Change;
|
||||
use termwiz::terminal::buffered::BufferedTerminal;
|
||||
use termwiz::terminal::{new_terminal, Terminal};
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
@ -14,22 +15,18 @@ fn main() -> Result<(), Error> {
|
||||
let mut terminal = new_terminal(caps)?;
|
||||
terminal.set_raw_mode()?;
|
||||
|
||||
let size = terminal.get_screen_size()?;
|
||||
let mut screen = Surface::new(size.cols as usize, size.rows as usize);
|
||||
let mut buf = BufferedTerminal::new(terminal)?;
|
||||
|
||||
screen.add_change(Change::Attribute(AttributeChange::Foreground(
|
||||
buf.add_change(Change::Attribute(AttributeChange::Foreground(
|
||||
AnsiColor::Maroon.into(),
|
||||
)));
|
||||
screen.add_change("Hello world\r\n");
|
||||
screen.add_change(Change::Attribute(AttributeChange::Foreground(
|
||||
buf.add_change("Hello world\r\n");
|
||||
buf.add_change(Change::Attribute(AttributeChange::Foreground(
|
||||
AnsiColor::Red.into(),
|
||||
)));
|
||||
screen.add_change("and in red here\r\n");
|
||||
buf.add_change("and in red here\r\n");
|
||||
|
||||
let (_seq, changes) = screen.get_changes(0);
|
||||
terminal.render(&changes)?;
|
||||
//println!("changes: {:?}", changes);
|
||||
println!("size: {:?}", size);
|
||||
buf.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
//! This example shows how to render `Change`s directly to
|
||||
//! an instance of `Terminal`. When used in this way, the
|
||||
//! library performas no optimization on the change stream.
|
||||
//! Consider using the `Surface` struct to enable optimization.
|
||||
//! Consider using the `Surface` struct to enable optimization;
|
||||
//! the `buffered_terminal.rs` example demonstrates a simple
|
||||
//! way to enable optimizations.
|
||||
extern crate failure;
|
||||
extern crate termwiz;
|
||||
|
||||
|
@ -209,7 +209,7 @@ pub struct Surface {
|
||||
}
|
||||
|
||||
impl Surface {
|
||||
/// Create a new Surface surface with the specified width and height.
|
||||
/// Create a new Surface with the specified width and height.
|
||||
pub fn new(width: usize, height: usize) -> Self {
|
||||
let mut scr = Surface {
|
||||
width,
|
||||
@ -220,7 +220,12 @@ impl Surface {
|
||||
scr
|
||||
}
|
||||
|
||||
/// Resize the Surface surface to the specified width and height.
|
||||
/// Returns the (width, height) of the surface
|
||||
pub fn dimensions(&self) -> (usize, usize) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
|
||||
/// Resize the Surface to the specified width and height.
|
||||
/// If the width and/or height are smaller than previously, the rows and/or
|
||||
/// columns are truncated. If the width and/or height are larger than
|
||||
/// previously then an appropriate number of cells are added to the
|
||||
|
121
src/terminal/buffered.rs
Normal file
121
src/terminal/buffered.rs
Normal file
@ -0,0 +1,121 @@
|
||||
//! A Terminal buffered with a Surface
|
||||
|
||||
use failure::Error;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use surface::{SequenceNo, Surface};
|
||||
use terminal::Terminal;
|
||||
|
||||
/// `BufferedTerminal` is a convenience wrapper around both
|
||||
/// a `Terminal` and a `Surface`. It enables easier use of
|
||||
/// the output optimization features available to `Surface`
|
||||
/// and internally keeps track of the sequence number.
|
||||
/// `BufferedTerminal` derefs to `Surface` and makes available
|
||||
/// the surface API.
|
||||
/// The `flush` method is used to compute the optimized set
|
||||
/// of changes and actually render them to the underlying
|
||||
/// `Terminal`. No output will be visible until it is flushed!
|
||||
pub struct BufferedTerminal<T: Terminal> {
|
||||
terminal: T,
|
||||
surface: Surface,
|
||||
seqno: SequenceNo,
|
||||
}
|
||||
|
||||
impl<T: Terminal> BufferedTerminal<T> {
|
||||
/// Create a new `BufferedTerminal` with a `Surface` of
|
||||
/// a matching size.
|
||||
pub fn new(mut terminal: T) -> Result<Self, Error> {
|
||||
let size = terminal.get_screen_size()?;
|
||||
let surface = Surface::new(size.cols, size.rows);
|
||||
Ok(Self {
|
||||
terminal,
|
||||
surface,
|
||||
seqno: 0,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the underlying terminal instance
|
||||
pub fn terminal(&mut self) -> &mut T {
|
||||
&mut self.terminal
|
||||
}
|
||||
|
||||
/// Compute the set of changes needed to update the screen to
|
||||
/// match the current contents of the embedded `Surface` and
|
||||
/// send them to the `Terminal`.
|
||||
/// If some other process has output over the terminal screen,
|
||||
/// or other artifacts are present, this routine has no way to
|
||||
/// detect the lose of synchronization.
|
||||
/// Applications typically build in a refresh function (CTRL-L
|
||||
/// is common for unix applications) to request a repaint.
|
||||
/// You can use the `repaint` function for that situation.
|
||||
pub fn flush(&mut self) -> Result<(), Error> {
|
||||
{
|
||||
let (seq, changes) = self.surface.get_changes(self.seqno);
|
||||
// If we encounter an error during rendering, we want to
|
||||
// reset the sequence number so that a subsequent paint
|
||||
// renders all.
|
||||
self.seqno = 0;
|
||||
self.terminal.render(&changes)?;
|
||||
self.terminal.flush()?;
|
||||
self.seqno = seq;
|
||||
}
|
||||
self.surface.flush_changes_older_than(self.seqno);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clears the screen and re-draws the surface contents onto
|
||||
/// the Terminal.
|
||||
pub fn repaint(&mut self) -> Result<(), Error> {
|
||||
self.seqno = 0;
|
||||
self.flush()
|
||||
}
|
||||
|
||||
/// Check to see if the Terminal has been resized by its user.
|
||||
/// If it has, resize the surface to match the new dimensions
|
||||
/// and return true. If the terminal was resized, the application
|
||||
/// will typically want to apply changes to match the new size
|
||||
/// and follow it up with a `flush` call to update the screen.
|
||||
///
|
||||
/// Why isn't this automatic? On Unix systems the SIGWINCH signal
|
||||
/// is used to indicate that a terminal size has changed. This notification
|
||||
/// is completely out of band from the interactions with the underlying
|
||||
/// terminal device, and thus requires a function such as this one to
|
||||
/// be called after receipt of SIGWINCH, or just speculatively from time
|
||||
/// to time.
|
||||
///
|
||||
/// Attaching signal handlers unilaterally from a library is undesirable,
|
||||
/// as the embedding application may have strong opinions about how
|
||||
/// best to do such a thing, so we do not automatically configure a
|
||||
/// signal handler.
|
||||
///
|
||||
/// On Windows it is possible to receive notification about window
|
||||
/// resizing by processing input events. Enabling those requires
|
||||
/// manipulating the input mode and establishing a handler to
|
||||
/// consume the input records. Such a thing is possible, but is
|
||||
/// better suited for a higher level abstraction than this basic
|
||||
/// `BufferedTerminal` interface.
|
||||
pub fn check_for_resize(&mut self) -> Result<bool, Error> {
|
||||
let size = self.terminal.get_screen_size()?;
|
||||
let (width, height) = self.surface.dimensions();
|
||||
|
||||
if width != size.cols || height != size.rows {
|
||||
self.surface.resize(width, height);
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Terminal> Deref for BufferedTerminal<T> {
|
||||
type Target = Surface;
|
||||
|
||||
fn deref(&self) -> &Surface {
|
||||
&self.surface
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Terminal> DerefMut for BufferedTerminal<T> {
|
||||
fn deref_mut(&mut self) -> &mut Surface {
|
||||
&mut self.surface
|
||||
}
|
||||
}
|
@ -16,6 +16,8 @@ pub mod unix;
|
||||
#[cfg(windows)]
|
||||
pub mod windows;
|
||||
|
||||
pub mod buffered;
|
||||
|
||||
#[cfg(unix)]
|
||||
pub use self::unix::UnixTerminal;
|
||||
#[cfg(windows)]
|
||||
@ -68,6 +70,15 @@ pub trait Terminal: Read + Write {
|
||||
*/
|
||||
}
|
||||
|
||||
/// `SystemTerminal` is a concrete implementation of `Terminal`.
|
||||
/// Ideally you wouldn't reference `SystemTerminal` in consuming
|
||||
/// code. This type is exposed for convenience if you are doing
|
||||
/// something unusual and want easier access to the constructors.
|
||||
#[cfg(unix)]
|
||||
pub type SystemTerminal = UnixTerminal;
|
||||
#[cfg(windows)]
|
||||
pub type SystemTerminal = WindowsTerminal;
|
||||
|
||||
/// Construct a new instance of Terminal.
|
||||
/// The terminal will have a renderer that is influenced by the configuration
|
||||
/// in the provided `Capabilities` instance.
|
||||
@ -78,17 +89,7 @@ pub trait Terminal: Read + Write {
|
||||
/// constructors for `UnixTerminal` and `WindowsTerminal` and call whichever
|
||||
/// one is most suitable for your needs.
|
||||
pub fn new_terminal(caps: Capabilities) -> Result<impl Terminal, Error> {
|
||||
new_terminal_sys(caps)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn new_terminal_sys(caps: Capabilities) -> Result<impl Terminal, Error> {
|
||||
UnixTerminal::new(caps)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn new_terminal_sys(caps: Capabilities) -> Result<impl Terminal, Error> {
|
||||
WindowsTerminal::new(caps)
|
||||
SystemTerminal::new(caps)
|
||||
}
|
||||
|
||||
const BUF_SIZE: usize = 128;
|
||||
|
Loading…
Reference in New Issue
Block a user