Fix native-Windows input parity and terminal restore - #56
Merged
Conversation
Two independent native-Windows parity fixes in the etr client. 1. Special characters no longer "eaten" (issue #54): the stdin reader used std::io::stdin().read(), which on Windows goes through Rust std's ReadConsoleW shim (UTF-16->UTF-8 + line cooking). Even in raw mode it batches input and drops non-UTF-8 bytes, which made special characters vanish (zellij keybindings needing ^g) and caused the first-line-not- echoed bug. It now reads the console input handle directly with ReadFile (read_stdin); with ENABLE_VIRTUAL_TERMINAL_INPUT on this returns the same unbatched, per-keystroke VT byte stream a Unix terminal emits. enable_vt_console also sets the console input codepage to UTF-8 (65001), saved/restored on exit, so typed multi-byte input reaches the remote as UTF-8. Adds windows-sys feature Win32_Storage_FileSystem for ReadFile. 2. Local terminal restored on exit: a remote full-screen app leaves the local terminal in alternate-screen/mouse/paste/hidden-cursor modes that disable_raw_mode does not undo, so after a hard drop or ~. the mouse wheel spewed escapes and the terminal was unusable. restore_terminal() now emits VT resets on every final-exit path: a cursor-safe part (TERM_RESET_MODES) on every exit and a screen-restoring part (TERM_RESET_SCREEN, which homes the cursor) only on unclean exits. Avoids a full RIS so scrollback is kept. Version 0.6.4 -> 0.6.5. Test count 110 -> 112 (reset-sequence regressions). Assisted-By: Claude Opus 4.8
Verified end-to-end against a real Unix etrs (WSL Fedora 44): remote prompt renders, PTY command round-trips, and the client emits the cursor-safe terminal-restore sequence on clean exit (fix #2 confirmed in the live byte stream). The console input-VT path (fix #1) needs interactive keystrokes and is flagged for manual confirmation. Also notes an adjacent pre-existing gap: redirected stdin ends a remote-command session on EOF before output arrives. Assisted-By: Claude Opus 4.8
Drove the live etr client with real console key events (WriteConsoleInputW) against a WSL Fedora 44 etrs. Confirms fix #1 end-to-end: Ctrl+G->0x07, arrow keys, rapid bursts and Unicode all survive the raw+VT-input ReadFile path un-eaten, and injected keystrokes reach the remote per-keystroke (remote zsh-syntax-highlighting recolours char-by-char). Confirms fix #2: cursor-safe terminal-restore sequence emitted on clean exit. Updates NOTES accordingly. Assisted-By: Claude Opus 4.8
Promote the redirected-stdin truncation note from the v0.6.5 verification footnote into the Known gaps / next steps list so it is discoverable as tracked open work, with a sketch of the ssh-parity fix (half-close stdin, keep draining PTY output). Assisted-By: Claude Opus 4.8
`just install` (and other bash-shebang recipes) fail from PowerShell/nushell with "could not find cygpath": just tries to translate the shebang interpreter path via cygpath, absent without Git Bash on PATH. Recorded in Known gaps with the cargo-install workaround and a sketch of a cross-shell fix. Assisted-By: Claude Opus 4.8
The single stdin reader thread is spawned before the QUIC connect, but raw +
VT-input mode is only enabled after connect. On Windows a ReadFile issued while
the console is still in cooked/line mode stays line-buffered for that whole
read, so the first line was held client-side until Enter ("no echo until first
Enter"). The v0.6.5 ReadFile change did not fix this — it is a timing problem,
not a read-mechanism one.
Gate the Windows reader on a one-shot signal fired right after the first
enable_raw_mode + enable_vt_console, so its first read happens in raw + VT mode
and is per-keystroke. Unix is unaffected (ungated, never had the bug).
Verified with an A/B console-keystroke harness that snapshots the client's
stdout before Enter: pre-fix the typed first line is absent (line-buffered);
with the gate it appears, echoed back per-keystroke. NOTES corrected (the
earlier claim that ReadFile alone fixed #54 was wrong).
Assisted-By: Claude Opus 4.8
enable_vt_console sets ENABLE_VIRTUAL_TERMINAL_INPUT, but crossterm's disable_raw_mode only ORs the line/echo/processed-input bits back — it never clears the VT-input flag. So after etr exited, the console was left with VT-input enabled and the local shell echoed typed characters but would not accept Enter (the VT-translated Enter wasn't seen as line submission). Capture the console's exact original input/output modes + input codepage once (capture_console_originals, before raw mode is first enabled) and restore them verbatim on every exit path (restore_console_state), which clears the leftover VT-input flag. Verified with a harness: input mode restored byte-identical (0x01f7 -> 0x01f7), VT_INPUT not left set. Pre-existing since v0.6.4; no-op on Unix. NOTES also records a related server-side gap (clean shell `exit` sometimes reconnects instead of quitting because the Disconnect races the connection close). Assisted-By: Claude Opus 4.8
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.
Summary
Closes the native-Windows client's remaining feature-parity gaps with SSH for basic interactive use. Four independent fixes, all in
src/bin/etr.rs(client only — no server/protocol changes; existingetrsis fully compatible):^G). The stdin reader usedstd::io::stdin().read(), which on Windows goes through Rust std'sReadConsoleWshim and drops bytes that aren't clean UTF-8. It now reads the console input handle directly withReadFile(withENABLE_VIRTUAL_TERMINAL_INPUT), delivering every keystroke — control chars, arrows, Unicode — intact. Input codepage is set to UTF-8 so typed multi-byte input reaches the remote correctly.ReadFilebefore raw mode was enabled (raw mode is only turned on after the QUIC connect), so a cooked/line-mode read held the first line until Enter. The Windows reader now waits until raw + VT-input mode is active before its first read.~.those weren't undone (mouse wheel spewed escapes, cursor hidden). The client now emits VT resets on exit — a cursor-safe set on every exit and a screen-restoring set only on unclean exits.disable_raw_modedoesn't clear theENABLE_VIRTUAL_TERMINAL_INPUTflagenable_vt_consolesets, so the console was left in VT-input mode and the local shell echoed characters but wouldn't accept Enter. The client now captures the console's exact original modes/codepage once and restores them verbatim on exit.All fixes are
#[cfg(windows)]-scoped or no-ops on Unix.Test plan
just prgate passes: fmt, clippy-D warnings, 112 tests, man pages build.etrswith synthesized real console key events (WriteConsoleInputW):a,Ctrl+G→0x07, Up-arrow→ESC [ A, rapid burst,é→UTF-8 all delivered intact via the raw+VTReadFilepath.zsh-syntax-highlightingrecoloured char-by-char); first line echoed before Enter (A/B pre-Enter snapshot); on exit the client emitted the cursor-safe reset.0x01f7→0x01f7), VT-input flag cleared.^G, first-line echo fixed, terminal usable after shutdown.Notes
exitcan race the server Disconnect and makeetrreconnect instead of quit (server-side);justrecipes needcygpathon native Windows shells;etr host 'cmd' </dev/nulltruncates on stdin-EOF.