aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2021-03-13 16:52:33 -0500
committerJesse Luehrs <doy@tozt.net>2021-03-13 17:08:19 -0500
commit616fddad3e8557d3a4248fd25d00c256eab2c827 (patch)
treedcd5bd048085c1cc0c4b7f2d2b393c711f72a4e6
parentf0c07dfee8b65a36033943f888c1da5f8ec96098 (diff)
downloadtextmode-616fddad3e8557d3a4248fd25d00c256eab2c827.tar.gz
textmode-616fddad3e8557d3a4248fd25d00c256eab2c827.zip
docs
-rw-r--r--src/blocking/input.rs58
-rw-r--r--src/blocking/output.rs24
-rw-r--r--src/color.rs2
-rw-r--r--src/error.rs6
-rw-r--r--src/input.rs60
-rw-r--r--src/key.rs6
-rw-r--r--src/lib.rs89
-rw-r--r--src/output.rs26
8 files changed, 259 insertions, 12 deletions
diff --git a/src/blocking/input.rs b/src/blocking/input.rs
index 7a35f25..c668857 100644
--- a/src/blocking/input.rs
+++ b/src/blocking/input.rs
@@ -5,12 +5,16 @@ use std::os::unix::io::AsRawFd as _;
use crate::private::Input as _;
+/// Switches the terminal on `stdin` to raw mode, and restores it when this
+/// object goes out of scope.
pub struct RawGuard {
termios: Option<nix::sys::termios::Termios>,
}
impl RawGuard {
- #[allow(clippy::new_without_default)]
+ /// Switches the terminal on `stdin` to raw mode and returns a guard
+ /// object. This is typically called as part of
+ /// [`Input::new`](Input::new).
pub fn new() -> Result<Self> {
let stdin = std::io::stdin().as_raw_fd();
let termios =
@@ -28,6 +32,7 @@ impl RawGuard {
})
}
+ /// Switch back from raw mode early.
pub fn cleanup(&mut self) -> Result<()> {
if let Some(termios) = self.termios.take() {
let stdin = std::io::stdin().as_raw_fd();
@@ -44,11 +49,18 @@ impl RawGuard {
}
impl Drop for RawGuard {
+ /// Calls `cleanup`.
fn drop(&mut self) {
let _ = self.cleanup();
}
}
+/// Manages handling terminal input from `stdin`.
+///
+/// The primary interface provided is [`read_key`](Input::read_key). You can
+/// additionally configure the types of keypresses you are interested in
+/// through the `parse_*` methods. This configuration can be changed between
+/// any two calls to [`read_key`](Input::read_key).
pub struct Input {
raw: Option<RawGuard>,
@@ -112,14 +124,17 @@ impl crate::private::Input for Input {
}
}
-#[allow(clippy::new_without_default)]
impl Input {
+ /// Creates a new `Input` instance containing a [`RawGuard`](RawGuard)
+ /// instance.
pub fn new() -> Result<Self> {
let mut self_ = Self::new_without_raw();
self_.raw = Some(RawGuard::new()?);
Ok(self_)
}
+ /// Creates a new `Input` instance without creating a
+ /// [`RawGuard`](RawGuard) instance.
pub fn new_without_raw() -> Self {
Self {
raw: None,
@@ -133,30 +148,61 @@ impl Input {
}
}
+ /// Removes the [`RawGuard`](RawGuard) instance stored in this `Input`
+ /// instance and returns it. This can be useful if you need to manage the
+ /// lifetime of the [`RawGuard`](RawGuard) instance separately.
+ pub fn take_raw_guard(&mut self) -> Option<RawGuard> {
+ self.raw.take()
+ }
+
+ /// Sets whether `read_key` should try to produce
+ /// [`String`](crate::Key::String) or [`Char`](crate::Key::Char) keys when
+ /// possible, rather than [`Bytes`](crate::Key::Bytes) or
+ /// [`Byte`](crate::Key::Byte) keys. Note that
+ /// [`Bytes`](crate::Key::Bytes) or [`Byte`](crate::Key::Byte) keys may
+ /// still be produced if the input fails to be parsed as UTF-8. Defaults
+ /// to true.
pub fn parse_utf8(&mut self, parse: bool) {
self.parse_utf8 = parse;
}
+ /// Sets whether `read_key` should produce [`Ctrl`](crate::Key::Ctrl) keys
+ /// when possible, rather than [`Bytes`](crate::Key::Bytes) or
+ /// [`Byte`](crate::Key::Byte) keys. Defaults to true.
pub fn parse_ctrl(&mut self, parse: bool) {
self.parse_ctrl = parse;
}
+ /// Sets whether `read_key` should produce [`Meta`](crate::Key::Meta) keys
+ /// when possible, rather than producing the
+ /// [`Escape`](crate::Key::Escape) key separately. Defaults to true.
pub fn parse_meta(&mut self, parse: bool) {
self.parse_meta = parse;
}
+ /// Sets whether `read_key` should produce keys other than
+ /// [`String`](crate::Key::String), [`Char`](crate::Key::Char),
+ /// [`Bytes`](crate::Key::Bytes), [`Byte`](crate::Key::Byte),
+ /// [`Ctrl`](crate::Key::Ctrl), or [`Meta`](crate::Key::Meta). Defaults to
+ /// true.
pub fn parse_special_keys(&mut self, parse: bool) {
self.parse_special_keys = parse;
}
+ /// Sets whether `read_key` should produce individual
+ /// [`Char`](crate::Key::Char) or [`Byte`](crate::Key::Byte) keys, rather
+ /// than combining them into [`String`](crate::Key::String) or
+ /// [`Bytes`](crate::Key::Bytes) keys when possible. When this is true,
+ /// [`String`](crate::Key::String) and [`Bytes`](crate::Key::Bytes) will
+ /// never be returned, and when this is false, [`Char`](crate::Key::Char)
+ /// and [`Byte`](crate::Key::Byte) will never be returned. Defaults to
+ /// true.
pub fn parse_single(&mut self, parse: bool) {
self.parse_single = parse;
}
- pub fn take_raw_guard(&mut self) -> Option<RawGuard> {
- self.raw.take()
- }
-
+ /// Reads a keypress from the terminal on `stdin`. Returns `Ok(None)` on
+ /// EOF.
pub fn read_key(&mut self) -> Result<Option<crate::Key>> {
self.fill_buf()?;
diff --git a/src/blocking/output.rs b/src/blocking/output.rs
index 83466f8..77e13d2 100644
--- a/src/blocking/output.rs
+++ b/src/blocking/output.rs
@@ -4,16 +4,22 @@ use std::io::Write as _;
use crate::private::Output as _;
+/// Switches the terminal on `stdout` to alternate screen mode, and restores
+/// it when this object goes out of scope.
pub struct ScreenGuard {
cleaned_up: bool,
}
impl ScreenGuard {
+ /// Switches the terminal on `stdout` to alternate screen mode and returns
+ /// a guard object. This is typically called as part of
+ /// [`Output::new`](Output::new).
pub fn new() -> Result<Self> {
write_stdout(crate::INIT)?;
Ok(Self { cleaned_up: false })
}
+ /// Switch back from alternate screen mode early.
pub fn cleanup(&mut self) -> Result<()> {
if self.cleaned_up {
return Ok(());
@@ -24,11 +30,18 @@ impl ScreenGuard {
}
impl Drop for ScreenGuard {
+ /// Calls `cleanup`.
fn drop(&mut self) {
let _ = self.cleanup();
}
}
+/// Manages drawing to the terminal on `stdout`.
+///
+/// Most functionality is provided by the [`Textmode`](crate::Textmode) trait.
+/// You should call those trait methods to draw to the in-memory screen, and
+/// then call [`refresh`](Output::refresh) when you want to update the
+/// terminal on `stdout`.
pub struct Output {
screen: Option<ScreenGuard>,
@@ -57,12 +70,16 @@ impl crate::private::Output for Output {
impl crate::Textmode for Output {}
impl Output {
+ /// Creates a new `Output` instance containing a
+ /// [`ScreenGuard`](ScreenGuard) instance.
pub fn new() -> Result<Self> {
let mut self_ = Self::new_without_screen();
self_.screen = Some(ScreenGuard::new()?);
Ok(self_)
}
+ /// Creates a new `Output` instance without creating a
+ /// [`ScreenGuard`](ScreenGuard) instance.
pub fn new_without_screen() -> Self {
let (rows, cols) = match terminal_size::terminal_size() {
Some((terminal_size::Width(w), terminal_size::Height(h))) => {
@@ -80,10 +97,17 @@ impl Output {
}
}
+ /// Removes the [`ScreenGuard`](ScreenGuard) instance stored in this
+ /// `Output` instance and returns it. This can be useful if you need to
+ /// manage the lifetime of the [`ScreenGuard`](ScreenGuard) instance
+ /// separately.
pub fn take_screen_guard(&mut self) -> Option<ScreenGuard> {
self.screen.take()
}
+ /// Draws the in-memory screen to the terminal on `stdout`. This is done
+ /// using a diff mechanism to only update the parts of the terminal which
+ /// are different from the in-memory screen.
pub fn refresh(&mut self) -> Result<()> {
let diff = self.next().screen().state_diff(self.cur().screen());
write_stdout(&diff)?;
diff --git a/src/color.rs b/src/color.rs
index c2ba2b3..289bed2 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -1,3 +1,5 @@
+//! Constants for the basic 16 terminal colors.
+
pub const BLACK: vt100::Color = vt100::Color::Idx(0);
pub const RED: vt100::Color = vt100::Color::Idx(1);
pub const GREEN: vt100::Color = vt100::Color::Idx(2);
diff --git a/src/error.rs b/src/error.rs
index f2e774e..27f5bd5 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,16 +1,22 @@
+/// Type for errors returned by this crate.
#[derive(thiserror::Error, Debug)]
pub enum Error {
+ /// error reading from stdin
#[error("error reading from stdin")]
ReadStdin(#[source] std::io::Error),
+ /// error enabling terminal raw mode
#[error("error enabling terminal raw mode")]
SetRaw(#[source] nix::Error),
+ /// error restoring terminal from raw mode
#[error("error restoring terminal from raw mode")]
UnsetRaw(#[source] nix::Error),
+ /// error writing to stdout
#[error("error writing to stdout")]
WriteStdout(#[source] std::io::Error),
}
+/// Convenience wrapper for a `Result` using `textmode::Error`.
pub type Result<T> = std::result::Result<T, Error>;
diff --git a/src/input.rs b/src/input.rs
index abcdd7f..bd10b86 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -5,12 +5,16 @@ use std::os::unix::io::AsRawFd as _;
use crate::private::Input as _;
+/// Switches the terminal on `stdin` to raw mode, and restores it when this
+/// object goes out of scope.
pub struct RawGuard {
termios: Option<nix::sys::termios::Termios>,
}
impl RawGuard {
- #[allow(clippy::new_without_default)]
+ /// Switches the terminal on `stdin` to raw mode and returns a guard
+ /// object. This is typically called as part of
+ /// [`Input::new`](Input::new).
pub async fn new() -> Result<Self> {
let stdin = std::io::stdin().as_raw_fd();
let termios = blocking::unblock(move || {
@@ -33,6 +37,7 @@ impl RawGuard {
})
}
+ /// Switch back from raw mode early.
pub async fn cleanup(&mut self) -> Result<()> {
if let Some(termios) = self.termios.take() {
let stdin = std::io::stdin().as_raw_fd();
@@ -52,6 +57,9 @@ impl RawGuard {
}
impl Drop for RawGuard {
+ /// Calls `cleanup`. Note that this may block, due to Rust's current lack
+ /// of an async drop mechanism. If this could be a problem, you should
+ /// call `cleanup` manually instead.
fn drop(&mut self) {
futures_lite::future::block_on(async {
let _ = self.cleanup().await;
@@ -59,6 +67,12 @@ impl Drop for RawGuard {
}
}
+/// Manages handling terminal input from `stdin`.
+///
+/// The primary interface provided is [`read_key`](Input::read_key). You can
+/// additionally configure the types of keypresses you are interested in
+/// through the `parse_*` methods. This configuration can be changed between
+/// any two calls to [`read_key`](Input::read_key).
pub struct Input {
stdin: blocking::Unblock<std::io::Stdin>,
raw: Option<RawGuard>,
@@ -123,14 +137,17 @@ impl crate::private::Input for Input {
}
}
-#[allow(clippy::new_without_default)]
impl Input {
+ /// Creates a new `Input` instance containing a [`RawGuard`](RawGuard)
+ /// instance.
pub async fn new() -> Result<Self> {
let mut self_ = Self::new_without_raw();
self_.raw = Some(RawGuard::new().await?);
Ok(self_)
}
+ /// Creates a new `Input` instance without creating a
+ /// [`RawGuard`](RawGuard) instance.
pub fn new_without_raw() -> Self {
Self {
stdin: blocking::Unblock::new(std::io::stdin()),
@@ -145,30 +162,61 @@ impl Input {
}
}
+ /// Removes the [`RawGuard`](RawGuard) instance stored in this `Input`
+ /// instance and returns it. This can be useful if you need to manage the
+ /// lifetime of the [`RawGuard`](RawGuard) instance separately.
+ pub fn take_raw_guard(&mut self) -> Option<RawGuard> {
+ self.raw.take()
+ }
+
+ /// Sets whether `read_key` should try to produce
+ /// [`String`](crate::Key::String) or [`Char`](crate::Key::Char) keys when
+ /// possible, rather than [`Bytes`](crate::Key::Bytes) or
+ /// [`Byte`](crate::Key::Byte) keys. Note that
+ /// [`Bytes`](crate::Key::Bytes) or [`Byte`](crate::Key::Byte) keys may
+ /// still be produced if the input fails to be parsed as UTF-8. Defaults
+ /// to true.
pub fn parse_utf8(&mut self, parse: bool) {
self.parse_utf8 = parse;
}
+ /// Sets whether `read_key` should produce [`Ctrl`](crate::Key::Ctrl) keys
+ /// when possible, rather than [`Bytes`](crate::Key::Bytes) or
+ /// [`Byte`](crate::Key::Byte) keys. Defaults to true.
pub fn parse_ctrl(&mut self, parse: bool) {
self.parse_ctrl = parse;
}
+ /// Sets whether `read_key` should produce [`Meta`](crate::Key::Meta) keys
+ /// when possible, rather than producing the
+ /// [`Escape`](crate::Key::Escape) key separately. Defaults to true.
pub fn parse_meta(&mut self, parse: bool) {
self.parse_meta = parse;
}
+ /// Sets whether `read_key` should produce keys other than
+ /// [`String`](crate::Key::String), [`Char`](crate::Key::Char),
+ /// [`Bytes`](crate::Key::Bytes), [`Byte`](crate::Key::Byte),
+ /// [`Ctrl`](crate::Key::Ctrl), or [`Meta`](crate::Key::Meta). Defaults to
+ /// true.
pub fn parse_special_keys(&mut self, parse: bool) {
self.parse_special_keys = parse;
}
+ /// Sets whether `read_key` should produce individual
+ /// [`Char`](crate::Key::Char) or [`Byte`](crate::Key::Byte) keys, rather
+ /// than combining them into [`String`](crate::Key::String) or
+ /// [`Bytes`](crate::Key::Bytes) keys when possible. When this is true,
+ /// [`String`](crate::Key::String) and [`Bytes`](crate::Key::Bytes) will
+ /// never be returned, and when this is false, [`Char`](crate::Key::Char)
+ /// and [`Byte`](crate::Key::Byte) will never be returned. Defaults to
+ /// true.
pub fn parse_single(&mut self, parse: bool) {
self.parse_single = parse;
}
- pub fn take_raw_guard(&mut self) -> Option<RawGuard> {
- self.raw.take()
- }
-
+ /// Reads a keypress from the terminal on `stdin`. Returns `Ok(None)` on
+ /// EOF.
pub async fn read_key(&mut self) -> Result<Option<crate::Key>> {
self.fill_buf().await?;
diff --git a/src/key.rs b/src/key.rs
index 8c97ad8..505817f 100644
--- a/src/key.rs
+++ b/src/key.rs
@@ -1,9 +1,12 @@
+/// Type representing a keypress.
#[derive(Eq, PartialEq, Debug, Clone)]
pub enum Key {
String(String),
Char(char),
Bytes(Vec<u8>),
Byte(u8),
+ /// The associated value will be a byte corresponding to the lowercase
+ /// letter for the control code. For instance, `^A` will be `Ctrl(b'a')`.
Ctrl(u8),
Meta(u8),
Backspace,
@@ -22,10 +25,13 @@ pub enum Key {
Delete,
PageUp,
PageDown,
+ /// The associated value will be the number corresponding to the `F` key.
+ /// For instance, `F1` will be `F(1)`.
F(u8),
}
impl Key {
+ /// Returns bytes generated by the given key press.
pub fn into_bytes(self) -> Vec<u8> {
use Key::*;
match self {
diff --git a/src/lib.rs b/src/lib.rs
index ca58146..27723fb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,71 @@
#![allow(clippy::collapsible_if)]
+//! `textmode` is a library for terminal interaction built on top of a real
+//! terminal parsing library. It allows you to do arbitrary drawing operations
+//! on an in-memory screen, and then update the visible terminal output to
+//! reflect the in-memory screen via an optimized diff algorithm when you are
+//! finished. Being built on a real terminal parsing library means that while
+//! normal curses-like operations are available:
+//!
+//! ```no_run
+//! use textmode::Textmode;
+//! # #[cfg(feature = "async")]
+//! # fn main() -> textmode::Result<()> {
+//! # futures_lite::future::block_on(async { run().await })
+//! # }
+//! # #[cfg(feature = "async")]
+//! # async fn run() -> textmode::Result<()> {
+//! let mut tm = textmode::Output::new().await?;
+//! tm.clear();
+//! tm.move_to(5, 5);
+//! tm.set_fgcolor(textmode::color::RED);
+//! tm.write_str("foo");
+//! tm.refresh().await?;
+//! # Ok(())
+//! # }
+//! # #[cfg(not(feature = "async"))]
+//! # fn main() -> textmode::Result<()> {
+//! # let mut tm = textmode::blocking::Output::new()?;
+//! # tm.clear();
+//! # tm.move_to(5, 5);
+//! # tm.set_fgcolor(textmode::color::RED);
+//! # tm.write_str("foo");
+//! # tm.refresh()?;
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! you can also write data containing arbitrary terminal escape codes to the
+//! output and they will also do the right thing:
+//!
+//! ```no_run
+//! # use textmode::Textmode;
+//! # #[cfg(feature = "async")]
+//! # fn main() -> textmode::Result<()> {
+//! # futures_lite::future::block_on(async { run().await })
+//! # }
+//! # #[cfg(feature = "async")]
+//! # async fn run() -> textmode::Result<()> {
+//! # let mut tm = textmode::Output::new().await?;
+//! tm.write(b"\x1b[34m\x1b[3;9Hbar\x1b[m");
+//! tm.refresh().await?;
+//! # Ok(())
+//! # }
+//! # #[cfg(not(feature = "async"))]
+//! # fn main() -> textmode::Result<()> {
+//! # let mut tm = textmode::blocking::Output::new()?;
+//! # tm.write(b"\x1b[34m\x1b[3;9Hbar\x1b[m");
+//! # tm.refresh()?;
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! This module is split into two main parts: [`Output`](Output) and
+//! [`Input`](Input). See the documentation for those types for more details.
+//! Additionally, the [`blocking`] module provides an equivalent interface
+//! with blocking calls instead of async.
+
+/// Blocking interface.
pub mod blocking;
pub mod color;
@@ -21,24 +87,32 @@ pub use input::{Input, RawGuard};
const INIT: &[u8] = b"\x1b7\x1b[?47h\x1b[2J\x1b[H\x1b[?25h";
const DEINIT: &[u8] = b"\x1b[?47l\x1b8\x1b[?25h";
+/// Provides the methods used to manipulate the in-memory screen.
pub trait Textmode: private::Output {
+ /// Returns the in-memory screen itself. This is the screen that will be
+ /// drawn on the next call to `refresh`.
fn screen(&self) -> &vt100::Screen {
self.next().screen()
}
+ /// Writes a sequence of bytes, potentially containing terminal escape
+ /// sequences, to the in-memory screen.
fn write(&mut self, buf: &[u8]) {
self.next_mut().process(buf);
}
+ /// Sets the terminal size for the in-memory screen.
fn set_size(&mut self, rows: u16, cols: u16) {
self.cur_mut().set_size(rows, cols);
self.next_mut().set_size(rows, cols);
}
+ /// Writes a string of printable characters to the in-memory screen.
fn write_str(&mut self, text: &str) {
self.write(text.as_bytes());
}
+ /// Moves the in-memory screen's cursor.
fn move_to(&mut self, row: u16, col: u16) {
self.write(b"\x1b[");
self.write_u16(row + 1);
@@ -47,18 +121,23 @@ pub trait Textmode: private::Output {
self.write(b"H");
}
+ /// Clears the in-memory screen.
fn clear(&mut self) {
self.write(b"\x1b[2J");
}
+ /// Clears the line containing the cursor on the in-memory screen.
fn clear_line(&mut self) {
self.write(b"\x1b[K");
}
+ /// Clears the in-memory screen's currently active drawing attributes.
fn reset_attributes(&mut self) {
self.write(b"\x1b[m");
}
+ /// Sets the foreground color for subsequent drawing operations to the
+ /// in-memory screen.
fn set_fgcolor(&mut self, color: vt100::Color) {
match color {
vt100::Color::Default => {
@@ -91,6 +170,8 @@ pub trait Textmode: private::Output {
}
}
+ /// Sets the background color for subsequent drawing operations to the
+ /// in-memory screen.
fn set_bgcolor(&mut self, color: vt100::Color) {
match color {
vt100::Color::Default => {
@@ -123,6 +204,8 @@ pub trait Textmode: private::Output {
}
}
+ /// Sets whether subsequent text drawn to the in-memory screen should be
+ /// bold.
fn set_bold(&mut self, bold: bool) {
if bold {
self.write(b"\x1b[1m");
@@ -131,6 +214,8 @@ pub trait Textmode: private::Output {
}
}
+ /// Sets whether subsequent text drawn to the in-memory screen should be
+ /// italic.
fn set_italic(&mut self, italic: bool) {
if italic {
self.write(b"\x1b[3m");
@@ -139,6 +224,8 @@ pub trait Textmode: private::Output {
}
}
+ /// Sets whether subsequent text drawn to the in-memory screen should be
+ /// underlined.
fn set_underline(&mut self, underline: bool) {
if underline {
self.write(b"\x1b[4m");
@@ -147,6 +234,8 @@ pub trait Textmode: private::Output {
}
}
+ /// Sets whether subsequent text drawn to the in-memory screen should have
+ /// its colors inverted.
fn set_inverse(&mut self, inverse: bool) {
if inverse {
self.write(b"\x1b[7m");
diff --git a/src/output.rs b/src/output.rs
index 1e0ed43..4b2fb06 100644
--- a/src/output.rs
+++ b/src/output.rs
@@ -4,11 +4,16 @@ use futures_lite::io::AsyncWriteExt as _;
use crate::private::Output as _;
+/// Switches the terminal on `stdout` to alternate screen mode, and restores
+/// it when this object goes out of scope.
pub struct ScreenGuard {
cleaned_up: bool,
}
impl ScreenGuard {
+ /// Switches the terminal on `stdout` to alternate screen mode and returns
+ /// a guard object. This is typically called as part of
+ /// [`Output::new`](Output::new).
pub async fn new() -> Result<Self> {
write_stdout(
&mut blocking::Unblock::new(std::io::stdout()),
@@ -18,6 +23,7 @@ impl ScreenGuard {
Ok(Self { cleaned_up: false })
}
+ /// Switch back from alternate screen mode early.
pub async fn cleanup(&mut self) -> Result<()> {
if self.cleaned_up {
return Ok(());
@@ -32,6 +38,9 @@ impl ScreenGuard {
}
impl Drop for ScreenGuard {
+ /// Calls `cleanup`. Note that this may block, due to Rust's current lack
+ /// of an async drop mechanism. If this could be a problem, you should
+ /// call `cleanup` manually instead.
fn drop(&mut self) {
futures_lite::future::block_on(async {
let _ = self.cleanup().await;
@@ -39,6 +48,12 @@ impl Drop for ScreenGuard {
}
}
+/// Manages drawing to the terminal on `stdout`.
+///
+/// Most functionality is provided by the [`Textmode`](crate::Textmode) trait.
+/// You should call those trait methods to draw to the in-memory screen, and
+/// then call [`refresh`](Output::refresh) when you want to update the
+/// terminal on `stdout`.
pub struct Output {
stdout: blocking::Unblock<std::io::Stdout>,
screen: Option<ScreenGuard>,
@@ -68,12 +83,16 @@ impl crate::private::Output for Output {
impl crate::Textmode for Output {}
impl Output {
+ /// Creates a new `Output` instance containing a
+ /// [`ScreenGuard`](ScreenGuard) instance.
pub async fn new() -> Result<Self> {
let mut self_ = Self::new_without_screen();
self_.screen = Some(ScreenGuard::new().await?);
Ok(self_)
}
+ /// Creates a new `Output` instance without creating a
+ /// [`ScreenGuard`](ScreenGuard) instance.
pub fn new_without_screen() -> Self {
let (rows, cols) = match terminal_size::terminal_size() {
Some((terminal_size::Width(w), terminal_size::Height(h))) => {
@@ -91,10 +110,17 @@ impl Output {
}
}
+ /// Removes the [`ScreenGuard`](ScreenGuard) instance stored in this
+ /// `Output` instance and returns it. This can be useful if you need to
+ /// manage the lifetime of the [`ScreenGuard`](ScreenGuard) instance
+ /// separately.
pub fn take_screen_guard(&mut self) -> Option<ScreenGuard> {
self.screen.take()
}
+ /// Draws the in-memory screen to the terminal on `stdout`. This is done
+ /// using a diff mechanism to only update the parts of the terminal which
+ /// are different from the in-memory screen.
pub async fn refresh(&mut self) -> Result<()> {
let diff = self.next().screen().state_diff(self.cur().screen());
write_stdout(&mut self.stdout, &diff).await?;