Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

win,tty: allow setting ENABLE_VIRTUAL_TERMINAL_INPUT for raw mode #4688

Merged
merged 4 commits into from
Feb 21, 2025

Conversation

addaleax
Copy link
Contributor

@addaleax addaleax commented Jan 29, 2025

Windows provides the ENABLE_VIRTUAL_TERMINAL_INPUT flag for TTY input streams as a companion flag to ENABLE_VIRTUAL_TERMINAL_PROCESSING, which libuv is already setting for TTY output streams.

Setting this flag lets the terminal emulator perform some of the processing that libuv already currently does for input events, but most notably enables receiving control sequences that are otherwise entirely unavailable, e.g. for bracketed paste (which the Node.js readline implementation added basic support for in nodejs/node@87af913b66eab78088acfd).

libuv currently already provides translations for key events to control sequences, i.e. what this mode is intended to provide, but libuv does not and cannot translate all such events. Since the control sequences differ from the ones that Windows has chosen to standardize on, and applications may not be expecting this change, this is opt-in for now (but ideally will be the default behavior starting in libuv v2.x, should that ever happen).

Another downside of this change is that not all shells reset this mode when an application exits. For example, when running a Node.js program with this flag enabled inside of PowerShell in Windows terminal, if the application exits while in raw TTY input mode, neither the shell nor the terminal emulator reset this flag, rendering the input stream unusable.

While there's general awareness of the problem that console state is global state rather than per-process (same as on UNIX platforms), it seems that applications like PowerShell aren't expecting to need to unset this flag on the input stream, only its output counterpart (e.g. https://github.com/PowerShell/PowerShell/blob/4e7942135f998ab40fd3ae298b020e161a76d4ef/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs#L1156).

Hence, uv_tty_reset_mode() is extended to reset the terminal to its original state if the new mode is being used.

Refs: nodejs/node@87af913
Refs: microsoft/terminal#4954

@addaleax
Copy link
Contributor Author

I see there's related CI failures here, but I think I'd first like to get some feedback on the general approach before starting to fix those up.

@bnoordhuis
Copy link
Member

If libuv should add support for automatically resetting TTY state when the application exits, that would make sense to me; unfortunately, I don't know if there's an easy way to support this.

There's uv_tty_reset_mode() that embedders are supposed to call (and Node.js does, including on Windows.)

I don't think it's possible to fully automate a TTY reset in a bulletproof manner. Example edge case: what if libuv is loaded and unloaded as a DLL? What if it's loaded and unloaded multiple times?

@bnoordhuis
Copy link
Member

bnoordhuis commented Jan 29, 2025

Looking at the test failures suggests this change changes the observable behavior. That suggests it should be opt-in.

@addaleax addaleax changed the title win,tty: try to set ENABLE_VIRTUAL_TERMINAL_INPUT for raw mode win,tty: allow setting ENABLE_VIRTUAL_TERMINAL_INPUT for raw mode Jan 31, 2025
@addaleax addaleax force-pushed the win-tty-ENABLE_VIRTUAL_TERMINAL_INPUT branch 2 times, most recently from 20506b2 to c379f91 Compare January 31, 2025 15:17
@addaleax
Copy link
Contributor Author

addaleax commented Jan 31, 2025

@bnoordhuis Thanks for taking a look! Updated the PR and the PR description/commit message.

Yes, this does result in changes in observable behavior. Whether these would be breaking changes is probably a matter of perspective, though -- the origin of these changes is the fact that different UNIX terminal emulators chose different escape sequences to represent the same keys, and libuv already provided its own translation for some Windows key events to escape sequences, while Windows chose different ones when it added ENABLE_VIRTUAL_TERMINAL_INPUT (both compatible with different pre-existing terminal emulators, though). In other words, if an application is set up to run inside of different terminal emulators, it should already be able to handle these divergences.

Ultimately, I do think that setting ENABLE_VIRTUAL_TERMINAL_INPUT should be the default on Windows eventually. I don't know if libuv 2.x is ever planned (last conversation I remember about this was years ago) Just saw that libuv 2.x might be coming sometime soon – that seems like a good point for turning it into the default raw mode for everyone. For now, this is an opt-in feature -- I do plan to enable it in Node.js, since Node.js's readline implementation already handles the divergence between libuv's own logic here and what Windows decided on.

And uv_tty_reset_mode() sounds like a fine place to do this. I've also enabled the reset only if this feature is being used, so that there are no behavioral changes for existing applications at all, but lmk if you have strong feelings about changing that (I don't).

Windows provides the `ENABLE_VIRTUAL_TERMINAL_INPUT` flag for TTY input
streams as a companion flag to `ENABLE_VIRTUAL_TERMINAL_PROCESSING`,
which libuv is already setting for TTY output streams.

Setting this flag lets the terminal emulator perform some of the
processing that libuv already currently does for input events,
but most notably enables receiving control sequences that are
otherwise entirely unavailable, e.g. for bracketed paste
(which the Node.js readline implementation added basic support for
in nodejs/node@87af913b66eab78088acfd).

libuv currently already provides translations for key events to
control sequences, i.e. what this mode is intended to provide,
but libuv does not and cannot translate all such events.
Since the control sequences differ from the ones that Windows
has chosen to standardize on, and applications may not be expecting
this change, this is opt-in for now (but ideally will be the default
behavior starting in libuv v2.x, should that ever happen).

Another downside of this change is that not all shells reset
this mode when an application exits. For example, when running a
Node.js program with this flag enabled inside of PowerShell in
Windows terminal, if the application exits while in raw TTY input mode,
neither the shell nor the terminal emulator reset this flag, rendering
the input stream unusable.

While there's general awareness of the problem that console state is
global state rather than per-process (same as on UNIX platforms),
it seems that applications like PowerShell aren't expecting to need to
unset this flag on the input stream, only its output counterpart
(e.g. https://github.com/PowerShell/PowerShell/blob/4e7942135f998ab40fd3ae298b020e161a76d4ef/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs#L1156).

Hence, `uv_tty_reset_mode()` is extended to reset the terminal
to its original state if the new mode is being used.

Refs: nodejs/node@87af913
Refs: microsoft/terminal#4954
@addaleax addaleax force-pushed the win-tty-ENABLE_VIRTUAL_TERMINAL_INPUT branch from 0dce57b to 11d4e61 Compare January 31, 2025 16:59
@addaleax addaleax force-pushed the win-tty-ENABLE_VIRTUAL_TERMINAL_INPUT branch from 03d28bc to c785048 Compare February 6, 2025 15:22
@addaleax addaleax requested a review from bnoordhuis February 10, 2025 13:21
@addaleax
Copy link
Contributor Author

@bnoordhuis If you get the chance to take another look, that would be great 🙂

Copy link
Member

@bnoordhuis bnoordhuis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM but maybe another maintainer can take a quick look just in case?

@vtjnash
Copy link
Member

vtjnash commented Feb 21, 2025

I don't see any obvious issues with it. There are many existing state management bugs in the tty code on both platforms, and this seems to be handle everything consistent with the existing bugs, which seems an adequate state of affairs, and a strict improvement from before the PR.

@bnoordhuis bnoordhuis merged commit 843b64f into libuv:v1.x Feb 21, 2025
40 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants