fix: keep echo on in the cooked and well-done modes - #50
Merged
Conversation
Member
|
Co-Authored-By on the commit please :) |
passcod
approved these changes
Aug 26, 2026
`VtCooked` and `VtWellDone` wipe every termios flag field and re-insert a hardcoded set that never contained `ECHO`, so applying either left a terminal that accepts input without showing it — the state people run `reset` to escape. `VtWellDone` is what `watchexec --clear=reset` applies, which is how this reaches users. Set the echo flags (`ECHO`, `ECHOE`, `ECHOK`, plus `ECHOCTL`/`ECHOKE` where they exist) along with `IEXTEN`, and stop setting `ISTRIP`: it clears the eighth bit of every input byte, which no multi-byte UTF-8 sequence survives, and it contradicted the `IUTF8` set beside it. Also carry the character size and the line speed across the reset rather than wiping them. Those describe the line itself rather than how the terminal behaves on it, and clearing the screen has no way to know what to put back: the wipe left `CSIZE` at `CS5` and, on Linux where the speed lives in the control flags, the baud rate at `B0` — which on a real serial line means "hang up". The mode bodies move into named functions so the new pty tests exercise the real ones rather than a copy of their contents. Fixes #49 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Marukome0743
force-pushed
the
fix-echo-in-cooked-modes
branch
from
August 26, 2026 05:46
d99bbbd to
311d6d3
Compare
Contributor
Author
Thanks for the quick review! Added a |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #49.
What was wrong
vt_cooked()andvt_well_done()go throughwrite_termios, which reads thecurrent attributes, wipes every flag field, and re-inserts a hardcoded set. That
set never contained
ECHO, so after either mode runs the terminal accepts inputwithout showing it.
VtWellDoneis whatwatchexec --clear=resetapplies, whichis how this reaches users — the symptom is reported downstream at
mise#8269.
Neither of the two commands people reach for fully undoes it:
stty sanerestoresthe echo flags but leaves
ISTRIPset, andresetclearsISTRIPbut leavesIEXTENoff. Issue #49 has the measurements.What this changes
ECHO,ECHOE,ECHOK, andECHOCTL/ECHOKEon theplatforms where nix exposes them, plus
IEXTEN. Both modes share oneusable_local_flags()so they cannot drift apart.ISTRIP. It clears the eighth bit of every input byte, so nomulti-byte UTF-8 sequence can survive to be interpreted as UTF-8 — which
contradicted the
IUTF8thatvt_well_donesets beside it.the line rather than how the terminal behaves on it, and a screen clear has no
way to know what to put back. The wipe left
CSIZEatCS5and, on Linux wherethe speed lives in the control flags, the baud rate at
B0. A pty absorbs mostof that, but on a real serial console
B0means hang up the line.The rest of each mode is unchanged, and the doc comments are updated to match —
they enumerate the flags, so they were describing the old behaviour.
I went with keeping the authoritative from-scratch write rather than switching to
read-and-modify, since that design is deliberate and documented. If you would
rather have the other approach, I am happy to redo it.
Structure
The two mode bodies move from closures into named
cooked_mode/well_done_modefunctions, and the duplicated body of
write_termiosmoves into awrite_termios_to(fd, f)that takes the descriptor. That removes the copy-pastedtty/
/dev/ttybranches and lets the tests drive the real mode functions against adescriptor of their own.
Tests
Four unit tests, each run against both modes, on a pty from
nix::pty::openptysothey never touch the test runner's own terminal:
echo_survives— the echo flags,IEXTEN,ICANONandISIGare all set afterwardsthe_eighth_bit_is_not_stripped—ISTRIPis not set afterwardsthe_line_configuration_is_left_alone—CSIZEand the speed come out as they went ina_terminal_already_without_echo_is_recovered— starting from a terminal with everylocal flag cleared, which is what these modes exist to rescue, still yields echo
nix::ptyis behind thetermfeature, which is already enabled, so there is nodependency change. The test module is
cfg'd off the targets whereopenptyornix::ptydo not exist (redox, fuchsia, aix), matching the crate's existingtarget handling.
What I could not verify locally
I have no Rust toolchain on this machine, so I have not compiled this — I checked
every API against the nix 0.31.3 sources (
tcgetattr/tcsetattrare generic overAsFd,openptyisterm-gated and absent on aix,ECHOCTL/ECHOKEare absenton redox,
libc_bitflags!derivesDebug), and formatted with rustfmt against therepo's
.rustfmt.toml. CI is the real check here.