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>
impl<W: Write> Screen<W>
Sourcepub fn new(writer: W, size: impl Into<Size>) -> Self
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.
Sourcepub fn writer_mut(&mut self) -> &mut W
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.
Sourcepub fn into_writer(self) -> W
pub fn into_writer(self) -> W
Consume the screen and return its writer, discarding anything still staged and unflushed.
Sourcepub fn set_cell(&mut self, pos: impl Into<Position>, cell: &Cell)
pub fn set_cell(&mut self, pos: impl Into<Position>, cell: &Cell)
Write cell at pos in the desired frame.
Sourcepub fn cell_mut(&mut self, pos: impl Into<Position>) -> Option<&mut Cell>
pub fn cell_mut(&mut self, pos: impl Into<Position>) -> Option<&mut Cell>
Borrow the cell at pos mutably, marking its columns touched.
Sourcepub fn render(&mut self) -> Result<()>
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.
Sourcepub fn invalidate(&mut self)
pub fn invalidate(&mut self)
Force a full redraw on the next render.
Sourcepub fn resize(&mut self, size: impl Into<Size>)
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.
Sourcepub fn insert_above(&mut self, content: &str) -> Result<()>
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.
Sourcepub fn move_cursor_to(&mut self, pos: impl Into<Position>) -> Result<()>
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.
Sourcepub fn move_cursor_by(&mut self, dx: i16, dy: i16) -> Result<()>
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.
Sourcepub fn set_cursor_position(&mut self, pos: impl Into<Position>)
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 itSourcepub fn clear_cursor_position(&mut self)
pub fn clear_cursor_position(&mut self)
Clear the staged cursor resting position, leaving the cursor wherever each frame’s cell diff ends.
Sourcepub fn tracked_cursor(&self) -> Option<Position>
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.
Sourcepub fn invalidate_tracked_cursor(&mut self)
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).
Sourcepub fn set_tracked_cursor(&mut self, pos: impl Into<Position>)
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.
Sourcepub fn set_fullscreen(&mut self, fullscreen: bool)
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.
Sourcepub fn fullscreen(&self) -> bool
pub fn fullscreen(&self) -> bool
Whether the managed area is the whole viewport rather than a band in
the normal buffer. See set_fullscreen.
Sourcepub fn set_cursor_visible(&mut self, visible: bool)
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.
Sourcepub fn cursor_visible(&self) -> bool
pub fn cursor_visible(&self) -> bool
Whether the terminal cursor is recorded as visible. See
set_cursor_visible.
Sourcepub fn set_grapheme_clusters(&mut self, enabled: bool)
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.
Sourcepub fn grapheme_clusters(&self) -> bool
pub fn grapheme_clusters(&self) -> bool
Whether text is measured per extended grapheme cluster. See
set_grapheme_clusters.
Sourcepub fn set_synchronized_output(&mut self, enabled: bool)
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.
Sourcepub fn synchronized_output(&self) -> bool
pub fn synchronized_output(&self) -> bool
Whether synchronized output frame wrapping is enabled.
Sourcepub fn set_scroll_optimize(&mut self, enabled: bool)
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.
Sourcepub fn set_color_profile(&mut self, profile: Profile)
pub fn set_color_profile(&mut self, profile: Profile)
Set the color profile used when emitting styled cells.
Sourcepub fn color_profile(&self) -> Profile
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.
Sourcepub fn set_optimizations(&mut self, optimizations: Optimizations)
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.
Sourcepub fn optimizations(&self) -> Optimizations
pub fn optimizations(&self) -> Optimizations
Return the renderer optimization flags currently in effect.
Trait Implementations§
Source§impl<W: Write> Bounded for Screen<W>
impl<W: Write> Bounded for Screen<W>
Source§impl<W: Write> SurfaceMut for Screen<W>
impl<W: Write> SurfaceMut for Screen<W>
Source§fn set_cell(&mut self, pos: Position, cell: &Cell)
fn set_cell(&mut self, pos: Position, cell: &Cell)
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 moreSource§fn insert_lines(&mut self, y: u16, n: u16, bounds_bottom: u16, fill: &Cell)
fn insert_lines(&mut self, y: u16, n: u16, bounds_bottom: u16, fill: &Cell)
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 moreSource§fn delete_lines(&mut self, y: u16, n: u16, bounds_bottom: u16, fill: &Cell)
fn delete_lines(&mut self, y: u16, n: u16, bounds_bottom: u16, fill: &Cell)
n rows at y, pulling existing rows up within
[y, bounds_bottom). The bottom n rows of the window are
filled with fill. Read moreSource§fn insert_cells(
&mut self,
pos: Position,
n: u16,
bounds_right: u16,
fill: &Cell,
)
fn insert_cells( &mut self, pos: Position, n: u16, bounds_right: u16, fill: &Cell, )
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 moreSource§fn delete_cells(
&mut self,
pos: Position,
n: u16,
bounds_right: u16,
fill: &Cell,
)
fn delete_cells( &mut self, pos: Position, n: u16, bounds_right: u16, fill: &Cell, )
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 moreSource§fn clear(&mut self)
fn clear(&mut self)
Cell::BLANK. Read moreSource§fn clear_rect(&mut self, rect: Rect)
fn clear_rect(&mut self, rect: Rect)
Cell::BLANK. Read moreSource§impl<W: Write> TextSurface for Screen<W>
impl<W: Write> TextSurface for Screen<W>
Source§fn width_mode(&self) -> WidthMode
fn width_mode(&self) -> WidthMode
Source§fn eaw_wide(&self) -> bool
fn eaw_wide(&self) -> bool
Source§fn set_str(
&mut self,
pos: impl Into<Position>,
s: &str,
style: impl Into<Style>,
) -> Position
fn set_str( &mut self, pos: impl Into<Position>, s: &str, style: impl Into<Style>, ) -> Position
Source§fn set_str_wrap(
&mut self,
pos: impl Into<Position>,
s: &str,
wrap: WrapMode,
style: impl Into<Style>,
) -> Position
fn set_str_wrap( &mut self, pos: impl Into<Position>, s: &str, wrap: WrapMode, style: impl Into<Style>, ) -> Position
Source§fn set_str_rect(
&mut self,
rect: impl Into<Rect>,
s: &str,
style: impl Into<Style>,
) -> Position
fn set_str_rect( &mut self, rect: impl Into<Rect>, s: &str, style: impl Into<Style>, ) -> Position
Source§fn set_str_rect_wrap(
&mut self,
rect: impl Into<Rect>,
s: &str,
wrap: WrapMode,
style: impl Into<Style>,
) -> Position
fn set_str_rect_wrap( &mut self, rect: impl Into<Rect>, s: &str, wrap: WrapMode, style: impl Into<Style>, ) -> Position
Source§fn set_str_truncate(
&mut self,
pos: impl Into<Position>,
s: &str,
tail: &str,
tail_style: impl Into<Style>,
) -> Position
fn set_str_truncate( &mut self, pos: impl Into<Position>, s: &str, tail: &str, tail_style: impl Into<Style>, ) -> Position
Source§fn set_str_rect_truncate(
&mut self,
rect: impl Into<Rect>,
s: &str,
tail: &str,
tail_style: impl Into<Style>,
) -> Position
fn set_str_rect_truncate( &mut self, rect: impl Into<Rect>, s: &str, tail: &str, tail_style: impl Into<Style>, ) -> Position
Source§fn str_width(&self, s: &str) -> u16
fn str_width(&self, s: &str) -> u16
s in terminal columns. Read moreSource§impl<W: Write> Write for Screen<W>
impl<W: Write> Write for Screen<W>
Source§fn write(&mut self, buf: &[u8]) -> Result<usize>
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 is_write_vectored(&self) -> bool
fn is_write_vectored(&self) -> bool
can_vector)1.0.0 · Source§fn write_all(&mut self, buf: &[u8]) -> Result<(), Error>
fn write_all(&mut self, buf: &[u8]) -> Result<(), Error>
Source§fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> Result<(), Error>
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> Result<(), Error>
write_all_vectored)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> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<S> Encode for S
impl<S> Encode for S
Source§fn encode<W: Write>(&self, w: &mut W) -> Result<()>
fn encode<W: Write>(&self, w: &mut W) -> Result<()>
w as escape sequences and text. Read more