v0.8.0
Added
-
DEC Special Graphics is translated, so an ncurses border reads as
┌───┐rather thanlqqqk.ESC ( 0selects the line-drawing set —
it is whatsmacs/rmacsare on an xterm terminfo, so it is how every
ncurses application and plenty that are not draw their frames — and the
bytes used to reach the grid untranslated. That put the crate's own
promise in the wrong: a user sees a box, and a test asserting a border was
right failed against an application that was correct, while a snapshot
that had blessedlqqqkwent on passing after the border broke. The
vt100backend drops the designation entirely, so termlens tracks the
G0/G1 designations (ESC ( Ps/ESC ) Ps) and theSO/SIlocking
shifts itself and hands both parsers the glyph a byte draws. Scope is
stated rather than implied: only the DEC Special Graphics set is
translated, other designations read as ASCII, G2/G3 and the single shifts
are not modelled, and a hard reset (RIS) returns both sets to ASCII. -
Terminal::scroll_with, withScroll::ctrl()/alt()/shift()
and aScrollChordtype.Ctrl-wheel is zoom andShift-wheel is
horizontal scrolling in a large share of terminal applications, and
neither could be sent:scrolltook a bare direction, so the binding most
likely to be wired to the wrong handler was the one with no coverage. The
modifier bits ride on the wheel's button code exactly as they do on a
click, andscrollis now the unmodified case ofscroll_with, as
clickis ofclick_with. The wheel gets a chord type of its own rather
than a widerMouseChord, so a wheel direction cannot be handed to
click_with: a notch has no release, and the type keeps that from being a
runtime surprise. -
ScreenimplementsPartialEqandEq. It was the only public
value type without them, so "did anything change?" was written by
comparingto_string()renderings — which is text only, and so passes
two screens that differ in a highlight, a colour or a concealed field.
Equality means the same observation: cells, cursor, size, and every
piece of out-of-band state including the cumulativerepaintsand
bellscounters, so two visually identical snapshots either side of a
bell compare unequal because they are different moments. The doc says
which comparison to reach for when "looks the same" is what is meant. -
A content wait that fails while rows have scrolled off says so. The
most-copied line in these docs,wait_until(|s| s.contains(..)), reads
the visible grid, so text the application printed and then scrolled away
in the same burst can never satisfy it — and the screen embedded in the
timeout does not show the text either, so the failure read as "the app
never printed it".Error::TimeoutandError::Eoffromwait_until
andwait_framenow say how many rows have scrolled off the top and
point atScreen::full_text. The note is conditional, because the wait
cannot know what an arbitrary closure was looking for; it is silent when
nothing has scrolled.containsandfinddocument where they stop.
Changed
resizeis refused once the child has released the terminal, with
the sameError::Writethatsendreturns, naming the child and its exit
status. It used to succeed, and the snapshot then reported a geometry no
application ever rendered at with the dead child's last frame clipped
underneath it — the one operation on a departed child that silently
mutated observable state. The final screen stays readable at the size the
child exited with. A size that cannot work is stillError::Size, checked
first.
Fixed
-
Bitmap::coloursis linear in the pixel count, and its order is
defined. It counted distinct colours with a scan of the distinct set per
pixel — O(pixels × colours), 9 ms for 96x96 and extrapolating to seconds
for a screenshot, in a test suite, where a wait that takes seven seconds
looks like a hang. It now counts in one pass. Ties, which the old
implementation happened to order by first appearance through a stable
sort, are now ordered that way on purpose and the doc says so: a test
asserting oncolours()[0]has one answer. -
wait_frameafterresizecould miss the repaint that answered it.
resizesent theSIGWINCHfirst and took the frame cursor afterwards, so
a fast application's acknowledging repaint could complete in that gap, be
counted as a frame from before the resize, and never be offered — the
wait then timed out reporting that the application had not repainted,
while the live screen showed that it had. Found by the stress workflow at
16 threads, once in 25 runs. The grid is now resized and the cursor taken
before the signal goes out, under one lock, so a frame drawn in answer to
the resize is always newer than the cursor and always lands in a grid of
the new size. -
resizedocuments the resize-then-type trap. A keystroke that reaches
a crossterm application in the same instant as theSIGWINCHcan be lost:
its event reader returns theResizeas soon as the poll reports the
signal and abandons the input readiness delivered alongside, and the poll
is edge-triggered, so the byte is not offered again until more input
arrives. The stress workflow caught termlens's own suite doing exactly that
— a resize followed at once byEschung the fixture about one run in
forty. The test now waits for the application to acknowledge the resize,
which is the advice the doc gives. -
resizedocuments what happens to history. Rows already in scrollback
keep the width they were captured at and rows captured afterwards have the
new width, sofull_text()after a narrowing resize can hold both
geometries. That was true before and said nowhere a caller would meet it;
it is now a recorded decision onresizeandscrollback_text, with the
alternative — discarding history on resize — rejected in writing rather
than by omission. -
Doc comments on
TerminalBuilder::size,spawnandresizename
Error::Sizefor a rejected size, notError::Input. The links
resolved, to the wrong variant, so following them gave amatcharm that
never fires — andErroris#[non_exhaustive], so a wildcard elsewhere
would have swallowed the mistake silently. -
Colon-form SGR colours now reach cell styles.
38:2::r:g:b,
38:2:r:g:b, and indexed foreground and background colours are normalized
before the backend parses them, matching their semicolon-form equivalents. -
Mouse
click/click_with/drag/scrollrefuse coordinates
outside the current grid. A real terminal cannot produce an off-window
mouse event; sending one was the same class of mistake as clicking with
no tracking enabled. The error names the position and the grid size at
the time of the call (so a post-resizerejection is obvious).drag
checks both endpoints only — the interpolated path cannot leave the
rectangle they span. Separately, the SGR encoder no longer wraps or
panics atu16::MAX: the 1-based+ 1is done inu32.
Security
SECURITY.mdno longer claims the crate has nounsafe. It has two
blocks, both FFI and both present since the features they serve landed:
dup(2), which opens the responder thread's writer on the PTY master, and
kill(2)behindTerminal::signal. Neither touches memory the child can
influence and the parsing path has none, which is the claim that matters;
the policy now says exactly that rather than something stronger and
false.