Skip to main content

Screen

Struct Screen 

Source
pub struct Screen<W: Write> { /* private fields */ }
Expand description

A cell-diff renderer over a writer. See the module documentation.

Screen is Send and Sync whenever its writer is, so it can be moved onto another thread or held across an .await point.

Implementations§

Source§

impl<W: Write> Screen<W>

Source

pub fn new(writer: W, size: impl Into<Size>) -> Self

Build a screen rendering into writer, with a managed area of size.

Nothing is written and no terminal state is touched. The color profile defaults to Profile::Ansi and the optimizations to Optimizations::default; set them with set_color_profile and set_optimizations, or let Program detect both from the environment.

Source

pub fn writer(&self) -> &W

Borrow the writer frames are rendered into.

Source

pub fn writer_mut(&mut self) -> &mut W

Borrow the writer mutably.

Bytes written straight to the writer bypass the staging buffer, so they can land before escapes already staged by drawing or property methods. Prefer writing through the screen itself (it implements Write), which keeps everything in order.

Source

pub fn into_writer(self) -> W

Consume the screen and return its writer, discarding anything still staged and unflushed.

Source

pub fn set_cell(&mut self, pos: impl Into<Position>, cell: &Cell)

Write cell at pos in the desired frame.

Source

pub fn cell_mut(&mut self, pos: impl Into<Position>) -> Option<&mut Cell>

Borrow the cell at pos mutably, marking its columns touched.

Source

pub fn size(&self) -> Size

The managed area size in cells.

Source

pub fn render(&mut self) -> Result<()>

Diff the staged frame against the tracked terminal, stage the minimal escape bytes, and flush them to the writer.

When a declarative cursor rest position has been staged with set_cursor_position, the cursor is moved there at the end of the frame, inside the same hide/synchronized-output bracket as the cell diff, so it lands atomically and without flicker.

Source

pub fn invalidate(&mut self)

Force a full redraw on the next render.

Source

pub fn resize(&mut self, size: impl Into<Size>)

Resize the managed area. In fullscreen pass the terminal viewport size; inline, the terminal width and the application surface height.

Resizing discards the tracked terminal contents, so the next render is a full repaint. That holds even when the size is unchanged: a resize report can follow a font or window change that keeps the cell grid but moves where those cells land, and the tracked contents are then wrong in a way no diff can see. An explicit resize says the caller wants the area re-established, so it is.

Program::autoresize runs on every resize report, including ones that change nothing, so it skips the call when the size already matches rather than repainting on each.

Source

pub fn insert_above(&mut self, content: &str) -> Result<()>

Insert content into the scrollback above the managed area and flush it to the writer. Inline this pushes the lines into the terminal’s scrollback; in fullscreen they go into the alternate screen’s hidden scrollback. The managed area is preserved in place, so no redraw is needed and a following render sees no change. An empty string is a no-op.

§Errors

Returns any error from flushing the inserted lines to the writer.

Source

pub fn move_cursor_to(&mut self, pos: impl Into<Position>) -> Result<()>

Immediately move the terminal cursor to a buffer-relative position and flush.

The target is normalized against the managed area first: a column at or past the width wraps into the following row, and the row is clamped to the last row. The move is a no-op once the cursor already sits at that normalized position, so asking for a column one past the last is a request to wrap and is honored as one, even when the renderer is already tracking the cursor there with its wrap pending.

This is imperative: the move is emitted and flushed now, independent of render. It does not affect the declarative resting position staged with set_cursor_position; a subsequent render will snap the cursor back to that sticky position if one is set. To change where frames leave the cursor, use set_cursor_position instead.

Source

pub fn move_cursor_by(&mut self, dx: i16, dy: i16) -> Result<()>

Immediately move the terminal cursor relative to the tracked cursor and flush.

Convenience over move_cursor_to: the target is the tracked cursor offset by (dx, dy), saturating at the buffer origin and then normalized the same way, so a column past the width wraps into the following row and the row is clamped to the surface. An unknown tracked cursor is treated as the origin.

Source

pub fn set_cursor_position(&mut self, pos: impl Into<Position>)

Stage a declarative resting position for the cursor, applied at the end of every render.

This is the cursor analogue of set_cell: it stages intent rather than emitting now. render leaves the terminal cursor at the buffer-relative pos after each frame’s cell diff. Call clear_cursor_position to stop steering it and leave the cursor wherever the diff ended.

The position is sticky — it persists across frames and is re-applied on every render (cheaply, as a no-op when the cursor is already there) until you change or clear it. An app whose cursor follows content (e.g. a text field) should call this each time that content moves.

Cursor visibility is orthogonal: this never shows or hides the cursor. Use set_cursor_visible for that (or Program::show_cursor / hide_cursor, which also emit DECTCEM). A position outside the managed area is clamped to its edges.

The argument is anything that converts into a Position, so a bare (x, y) works:

let mut screen = uncurses::screen::Screen::new(Vec::new(), (20, 3));
screen.set_cursor_position((4, 0)); // stage
screen.clear_cursor_position();     // stop steering it
Source

pub fn clear_cursor_position(&mut self)

Clear the staged cursor resting position, leaving the cursor wherever each frame’s cell diff ends.

Source

pub fn tracked_cursor(&self) -> Option<Position>

The renderer’s tracked cursor: the buffer-relative cell where the renderer believes the terminal cursor currently sits, or None when that position is unknown (initially, after a screen reset, or after invalidate_tracked_cursor). This is bookkeeping, not a live cursor-position query.

Source

pub fn invalidate_tracked_cursor(&mut self)

Mark the tracked cursor position unknown, so the next staged move always emits rather than short-circuiting on a matching tracked position. Use after moving the terminal cursor by a means the renderer cannot see (e.g. a raw escape written directly).

Source

pub fn set_tracked_cursor(&mut self, pos: impl Into<Position>)

Set the tracked cursor to buffer-relative pos, with both axes known, without emitting any move. This only updates the renderer’s belief; the caller must have already placed the terminal cursor there (e.g. with a raw escape the renderer cannot see). For an actual cursor move use move_cursor_to.

Source

pub fn set_fullscreen(&mut self, fullscreen: bool)

Set whether the managed area is the whole viewport.

true means the managed area covers the whole terminal and is addressed with absolute moves — what you want on the alternate screen buffer. false (the default) makes it a band in the normal buffer, as tall as you draw and addressed with relative moves, leaving the scrollback above and the shell prompt below intact.

This only sets state — it emits nothing. Switching screen buffers is a terminal mode (DECSET/DECRST 1049) and belongs to whoever owns the terminal: Program::enter_alt_screen and Program::exit_alt_screen emit it and set this for you. Driving a bare Screen, emit the mode yourself and keep this in step, or the renderer will address the wrong buffer.

An actual change discards the tracked contents, so the next render is a full repaint.

Source

pub fn fullscreen(&self) -> bool

Whether the managed area is the whole viewport rather than a band in the normal buffer. See set_fullscreen.

Source

pub fn set_cursor_visible(&mut self, visible: bool)

Record whether the terminal cursor is visible.

This only sets state — it emits nothing. DECTCEM is a terminal mode and belongs to whoever owns the terminal: Program::show_cursor and Program::hide_cursor emit it and set this for you.

The renderer needs to know only so it can bracket a frame correctly: a visible cursor is hidden around the cell diff so it does not dance across cells as the renderer repositions it, and shown again after. If this said true while the cursor was actually hidden, that closing show would turn it back on.

Source

pub fn cursor_visible(&self) -> bool

Whether the terminal cursor is recorded as visible. See set_cursor_visible.

Source

pub fn set_grapheme_clusters(&mut self, enabled: bool)

Set whether text is measured per extended grapheme cluster (UTS-29 plus emoji presentation rules) rather than per code point (wcwidth-style). Affects set_str and insert_above.

This only sets state — it emits nothing. Unicode core (DECSET 2027) is a terminal mode and belongs to whoever owns the terminal: Program::enable_grapheme_clusters and Program::disable_grapheme_clusters emit it and set this for you. Measuring differently from the terminal misplaces every cell after the first cluster on a line, so the two must agree.

Changing the mode discards the tracked terminal contents, so the next render is a full repaint: what is already on screen was measured the other way. Setting the current value is a no-op.

The repaint clears the screen, but buffered cells keep the width they were measured with, so re-write any text whose measurement changes. Applications that redraw their content each frame get that for free.

Source

pub fn grapheme_clusters(&self) -> bool

Whether text is measured per extended grapheme cluster. See set_grapheme_clusters.

Source

pub fn set_synchronized_output(&mut self, enabled: bool)

Enable or disable synchronized-output frame wrapping.

When enabled, each non-empty render is wrapped in begin/end synchronized-output sequences (DEC mode 2026) so terminals that support it present the frame atomically, with no mid-frame repaint. Terminals that don’t support 2026 ignore the markers.

This is your switch to flip: uncurses does not second-guess it against detected capabilities. Program enables it automatically when the terminal reports 2026 support, which happens once the caller has asked and read the reply, and you can override that here at any time.

Enabling it also changes how the cursor is handled per frame. With sync off, a visible cursor is hidden around the cell diff so it doesn’t dance across cells as the renderer repositions it. With sync on, the frame is presented in one step, so that hide/show pair is dropped: it is redundant, and toggling the cursor every frame resets its blink phase, which reads as flicker.

This only sets state; the markers are emitted on the next render. Scroll detection is gated on this, so enabling it is what lets set_scroll_optimize take effect; that setting still has to be on, and the screen still has to be fullscreen. A terminal that advertises DEC 2026 but does not honour it therefore gets both the markers and the scroll plans that rely on them, and a scroll’s corrective repaint may be visible. Disable scroll optimization on such a terminal.

Source

pub fn synchronized_output(&self) -> bool

Whether synchronized output frame wrapping is enabled.

Source

pub fn set_scroll_optimize(&mut self, enabled: bool)

Enable or disable the renderer’s scroll-detection pass.

On by default, and best left on: when a run of rows has simply moved, telling the terminal to move them costs a handful of bytes instead of a repaint.

Detection additionally requires synchronized output; see below. Turning this off gives up scrolling entirely, including on frames where it would have been safe.

A fixed column is why that requirement exists. The scrolls uncurses emits are always full width: rows move with SU, IL/DL or a bare line feed, and the renderer does not set the left/right margins (DECLRMM and DECSLRM) that would confine them to a column range on a terminal supporting those. So a detected scroll moves that region too, and the renderer paints it back within the same frame. The end state is correct either way — which is why a test that compares the finished screen sees nothing wrong — but what the user sees is that region jumping and being put back, on every frame, for as long as they keep scrolling.

Because that intermediate state is only hidden when the frame is presented in one step, detection runs only under synchronized output, which wraps the frame in DEC 2026. Without it no scroll is emitted at all, whatever this setting says, and rows are redrawn directly instead.

Synchronized output is off by default, and a Program turns it on only once the terminal has reported 2026 support, which takes an explicit query_capabilities. An application that never asks never gets scroll optimization, whatever its terminal supports.

Detection is skipped outside fullscreen regardless of this setting.

This only sets state; it takes effect on the next render.

Source

pub fn set_color_profile(&mut self, profile: Profile)

Set the color profile used when emitting styled cells.

Source

pub fn color_profile(&self) -> Profile

Return the color profile used when emitting styled cells.

This is the profile the renderer downsamples colors to, set by set_color_profile or detected from the environment by Program. Pass it to Encode::encode_with to serialize a surface the same way this screen renders it.

Source

pub fn set_optimizations(&mut self, optimizations: Optimizations)

Set the renderer optimization flags.

TABS and BS take effect immediately but do not persist across a raw-mode entry: Program::init and Program::resume enable both, since raw mode is what makes them safe. Every other flag — including ONLCR, which is opt-in and never granted — is left exactly as set here.

Source

pub fn optimizations(&self) -> Optimizations

Return the renderer optimization flags currently in effect.

Trait Implementations§

Source§

impl<W: Write> Bounded for Screen<W>

Source§

fn bounds(&self) -> Rect

Return the valid region in this value’s own coordinate space. Read more
Source§

fn width(&self) -> u16

Return the width of Self::bounds in terminal cell columns. Read more
Source§

fn height(&self) -> u16

Return the height of Self::bounds in terminal cell rows. Read more
Source§

fn contains(&self, pos: Position) -> bool

Test whether a position lies inside Self::bounds. Read more
Source§

impl<W: Write> Surface for Screen<W>

Source§

fn cell(&self, pos: Position) -> Option<&Cell>

Read the cell at a position. Read more
Source§

fn draw<T: SurfaceMut + ?Sized>(&self, target: &mut T, at: Position)

Copy self’s cells into target, mapping the top-left of self.bounds() to at in target coordinates. Read more
Source§

impl<W: Write> SurfaceMut for Screen<W>

Source§

fn set_cell(&mut self, pos: Position, cell: &Cell)

Place cell at pos. Implementations are responsible for wide-cell semantics (continuation markers, blanking covered cells) and any dirty tracking they care to do. Taking &Cell lets implementations skip the clone when the destination already matches. Read more
Source§

fn cell_mut(&mut self, pos: Position) -> Option<&mut Cell>

Mutable handle to the cell at pos. Returns None for out-of-bounds positions. Read more
Source§

fn insert_lines(&mut self, y: u16, n: u16, bounds_bottom: u16, fill: &Cell)

Insert n blank rows at y, pushing existing rows down within [y, bounds_bottom). Rows pushed past bounds_bottom are lost. Freed top rows are filled with fill. Read more
Source§

fn delete_lines(&mut self, y: u16, n: u16, bounds_bottom: u16, fill: &Cell)

Delete n rows at y, pulling existing rows up within [y, bounds_bottom). The bottom n rows of the window are filled with fill. Read more
Source§

fn insert_cells( &mut self, pos: Position, n: u16, bounds_right: u16, fill: &Cell, )

Insert n blank cells at pos, pushing cells in [pos.x, bounds_right) right within row pos.y. Cells pushed past bounds_right are lost. The freed slots [pos.x, pos.x + n) are filled with fill. Read more
Source§

fn delete_cells( &mut self, pos: Position, n: u16, bounds_right: u16, fill: &Cell, )

Delete n cells at pos, pulling cells in [pos.x + n, bounds_right) left within row pos.y. The freed slots [bounds_right - n, bounds_right) are filled with fill. Read more
Source§

fn fill(&mut self, cell: &Cell)

Fill the entire surface bounds with cell. Read more
Source§

fn fill_rect(&mut self, rect: Rect, cell: &Cell)

Fill the intersection of rect and Bounded::bounds with cell. Read more
Source§

fn clear(&mut self)

Clear the entire surface bounds to Cell::BLANK. Read more
Source§

fn clear_rect(&mut self, rect: Rect)

Clear a rectangle to Cell::BLANK. Read more
Source§

impl<W: Write> TextSurface for Screen<W>

Source§

fn width_mode(&self) -> WidthMode

Return the width-measurement mode used when shaping strings. Read more
Source§

fn eaw_wide(&self) -> bool

Return the East-Asian Ambiguous width policy for this surface. Read more
Source§

fn set_str( &mut self, pos: impl Into<Position>, s: &str, style: impl Into<Style>, ) -> Position

Paint s at pos, clipped to the surface bounds. Read more
Source§

fn set_str_wrap( &mut self, pos: impl Into<Position>, s: &str, wrap: WrapMode, style: impl Into<Style>, ) -> Position

Paint s at pos with an explicit right-edge behavior. Read more
Source§

fn set_str_rect( &mut self, rect: impl Into<Rect>, s: &str, style: impl Into<Style>, ) -> Position

Paint s inside rect, clipped to the surface bounds. Read more
Source§

fn set_str_rect_wrap( &mut self, rect: impl Into<Rect>, s: &str, wrap: WrapMode, style: impl Into<Style>, ) -> Position

Paint s inside rect with an explicit right-edge behavior. Read more
Source§

fn set_str_truncate( &mut self, pos: impl Into<Position>, s: &str, tail: &str, tail_style: impl Into<Style>, ) -> Position

Paint s at pos, truncating with a tail indicator on overflow. Read more
Source§

fn set_str_rect_truncate( &mut self, rect: impl Into<Rect>, s: &str, tail: &str, tail_style: impl Into<Style>, ) -> Position

Paint s inside rect, truncating with a tail indicator on overflow. Read more
Source§

fn str_width(&self, s: &str) -> u16

Measure the display width of s in terminal columns. Read more
Source§

fn grapheme_width(&self, g: &str) -> u8

Measure one extended grapheme cluster in cells under this surface’s width mode and East-Asian Ambiguous policy. Read more
Source§

fn grapheme_cells<'a>(&self, s: &'a str) -> impl Iterator<Item = (&'a str, u8)>

Iterate s as (cluster, width) pairs under this surface’s width mode and East-Asian Ambiguous policy. Read more
Source§

impl<W: Write> Write for Screen<W>

Source§

fn write(&mut self, buf: &[u8]) -> Result<usize>

Append raw bytes to the staging buffer, ordered with any staged mode or frame bytes. They reach the writer on the next flush.

Source§

fn flush(&mut self) -> Result<()>

Drain the staging buffer to the writer and flush it.

1.36.0 · Source§

fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize, Error>

Like write, except that it writes from a slice of buffers. Read more
Source§

fn is_write_vectored(&self) -> bool

🔬This is a nightly-only experimental API. (can_vector)
Determines if this Writer has an efficient write_vectored implementation. Read more
1.0.0 · Source§

fn write_all(&mut self, buf: &[u8]) -> Result<(), Error>

Attempts to write an entire buffer into this writer. Read more
Source§

fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> Result<(), Error>

🔬This is a nightly-only experimental API. (write_all_vectored)
Attempts to write multiple buffers into this writer. Read more
1.0.0 · Source§

fn write_fmt(&mut self, args: Arguments<'_>) -> Result<(), Error>

Writes a formatted string into this writer, returning any error encountered. Read more
1.0.0 · Source§

fn by_ref(&mut self) -> &mut Self
where Self: Sized,

Creates a “by reference” adapter for this instance of Write. Read more

Auto Trait Implementations§

§

impl<W> Freeze for Screen<W>
where W: Freeze,

§

impl<W> RefUnwindSafe for Screen<W>
where W: RefUnwindSafe,

§

impl<W> Send for Screen<W>
where W: Send,

§

impl<W> Sync for Screen<W>
where W: Sync,

§

impl<W> Unpin for Screen<W>
where W: Unpin,

§

impl<W> UnsafeUnpin for Screen<W>
where W: UnsafeUnpin,

§

impl<W> UnwindSafe for Screen<W>
where W: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<S> Encode for S
where S: Surface + ?Sized,

Source§

fn encode<W: Write>(&self, w: &mut W) -> Result<()>

Write the surface to w as escape sequences and text. Read more
Source§

fn encode_with<W: Write>(&self, w: &mut W, profile: Profile) -> Result<()>

Write the surface to w, downsampling colors to profile. Read more
Source§

fn display(&self) -> SurfaceDisplay<'_, Self>

Borrow the surface as a Display adapter. Read more
Source§

fn display_with(&self, profile: Profile) -> SurfaceDisplay<'_, Self>

Borrow the surface as a Display adapter that downsamples colors to profile. Read more
§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.