Commit eb43657
fix(ext/node): rewrite Windows TTY reading to match libuv (console mode, encoding, raw + line mode) (#32999)
## Summary
Rewrites the Windows TTY read/write paths to match libuv's behavior,
fixing regressions introduced in the `node:tty` rewrite (#32777, landed
in v2.7.6).
### Console mode restoration
`uv_tty_set_mode(NORMAL)` was replacing the console mode with a
hardcoded subset of flags (`ECHO|LINE|PROCESSED` = 0x0007), losing
`ENABLE_QUICK_EDIT_MODE`, `ENABLE_INSERT_MODE`, and
`ENABLE_EXTENDED_FLAGS` that Windows sets by default. This broke
interactive input after the first raw-mode cycle (e.g. vite create's
multi-step prompts). Now restores the original mode saved at init time,
matching libuv's behavior.
### TTY output encoding
`tty_try_write` used `WriteFile` which writes raw bytes interpreted
according to the console's active code page. On non-UTF-8 code pages
(common on Windows), this garbled Unicode output -- box-drawing
characters, accented text, CJK, etc. Now converts UTF-8 to UTF-16 and
uses `WriteConsoleW`, matching libuv's approach.
### Raw mode reading rewrite
Replaced `ReadFile`-based console reading with `ReadConsoleInputW` for
raw mode, matching libuv's `uv_process_tty_read_raw_req` approach.
`ReadFile` on a console handle only consumes KEY_DOWN events that
produce characters, blocking on KEY_UP/FOCUS/MOUSE events and freezing
the event loop. The new implementation:
- Reads individual `INPUT_RECORD` structs via `ReadConsoleInputW`
- Filters non-key events, KEY_UP events (except Alt composition), and
numpad keys during Alt-code entry
- Encodes Unicode characters to UTF-8 with surrogate pair support
- Maps function keys (arrows, F1-F12, Home/End/etc.) to VT100/xterm
escape sequences (ported from libuv's `get_vt100_fn_key`)
- Handles key repeat counts and Alt-prefix for escape sequences
### Line mode reading rewrite
Replaced blocking `ReadFile` with `ReadConsoleW` on a worker thread,
matching libuv's `uv_tty_line_read_thread` + `QueueUserWorkItem`
approach. `ReadFile`/`ReadConsoleW` block until Enter is pressed, so
they must run off the event loop thread. The worker thread reads UTF-16
via `ReadConsoleW`, converts to UTF-8, and wakes the event loop when the
line is complete.
### Async console input notification
Uses `RegisterWaitForSingleObject` (matching libuv's
`uv__tty_queue_read_raw`) to register a one-shot thread pool wait on the
console input handle. When input becomes available, the callback wakes
the tokio event loop. Without this, tokio would park after the first
keystroke and never re-poll the TTY.
### Input gating
Only allocates buffers and calls `read_cb` when there is actually data
to process, preventing spurious `read_cb(nread=0)` calls on every poll
cycle.
Fixes #32996
Fixes #32997
Fixes #32639
Fixes #33002
Fixes #32992
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>1 parent 7250271 commit eb43657
8 files changed
Lines changed: 1159 additions & 83 deletions
File tree
- libs/core
- uv_compat
- tests
- integration
- testdata/run
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
199 | 199 | | |
200 | 200 | | |
201 | 201 | | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
202 | 212 | | |
203 | 213 | | |
204 | 214 | | |
| |||
568 | 578 | | |
569 | 579 | | |
570 | 580 | | |
571 | | - | |
| 581 | + | |
572 | 582 | | |
573 | 583 | | |
| 584 | + | |
574 | 585 | | |
575 | 586 | | |
576 | | - | |
| 587 | + | |
577 | 588 | | |
578 | 589 | | |
579 | 590 | | |
| |||
0 commit comments