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

Consider implicitly activating VT / ANSI escape-sequence support for native programs in conhost.exe console windows on Windows #19101

Closed
mklement0 opened this issue Feb 3, 2023 · 48 comments
Labels
Issue-Enhancement the issue is more of a feature request than a bug Resolution-External The issue is caused by external component(s). WG-Interactive-Console the console experience

Comments

@mklement0
Copy link
Contributor

mklement0 commented Feb 3, 2023

Summary of the new feature / enhancement

The following environments on Windows already automatically (and invariably) support VT / ANSI escape sequences in output from native (external) programs, notably to produce colored output.

  • Windows Terminal
  • Visual Studio Code's integrated terminal.

By contrast, in console windows (provided by conhost.exe) the support depends on a registry setting that is OFF by default (Set-ItemProperty HKCU:\Console VirtualTerminalLevel -Type DWORD 1 enables it).
(Note: Some CLIs explicitly enable VT support for themselves, using the WinAPI, pwsh.exe and wsl.exe being examples; these do not depend on the registry setting, but all other ones do.)

Making VT support implicit (independent of the registry setting) even for native programs seems worthwhile, to align with user expectations and a modern terminal user experience.

Given that the change only affects how data is displayed, there shouldn't be a backward-compatibility concern.

Implicitly enabling support would also:

Proposed technical implementation details (optional)

No response

@mklement0 mklement0 added Issue-Enhancement the issue is more of a feature request than a bug Needs-Triage The issue is new and needs to be triaged by a work group. labels Feb 3, 2023
@mklement0 mklement0 mentioned this issue Feb 3, 2023
5 tasks
@237dmitry
Copy link

By contrast, in legacy console windows (provided by conhost.exe) the support depends on a registry setting that is OFF by default (Set-ItemProperty HKCU:\Console VirtualTerminalLevel -Type DWORD 1 enables it).

As I remember the colored output was by default from win-10 or early. Perhaps you mean this setting? I didn't touch this checkbox. But I'm not sure if this is true with a clean install and not with an upgrade to a newer version.

Screenshot 2023-02-03 233202

Screenshot 2023-02-03 233243

The only thing I don't like about the old console is the lack of support for hyperlinks and *.otf fonts.

@mklement0
Copy link
Contributor Author

mklement0 commented Feb 3, 2023

No, the registry setting that enables VT support applies only to the "modern" implementation of conhost.exe console windows. Note that coloring can also be achieved via Win32 API (a much older mechanism), not necessarily via VT / ANSI escape sequences, and it is support for the latter that the registry setting controls.
If you have Python installed, you can experiment with this command (note that changing the registry setting requires opening a new window to take effect):

# Enclose in (...) as a workaround to enable VT support even without turning it on via the registry.
python -c 'print(''It ain\''t easy being \x1b[32mgreen\x1b[m.'')'

(I realize that my calling conhost.exe windows legacy may have caused confusion; I didn't mean to refer to an older implementation of conhost.exe windows, which is what the GUI option you're showing refers to, but I meant to contrast conhost.exe windows with what is arguably their modern successor, Windows Terminal.)

@237dmitry
Copy link

237dmitry commented Feb 3, 2023

If you have Python installed

No, I have not. There is full set (256) of `e[38;5;${num}m esc-sequences on screenshot.

@mklement0
Copy link
Contributor Author

I don't know what color-256 is, but that a given CLI manages to properly have its VT sequences rendered is not proof that the console window supports it by default, given that individual CLIs can explicitly enable such support for themselves (SetConsoleMode), which is also what PowerShell does, but only for its commands, not for native programs.

The python command doesn't do that, which is why I chose it; you can also compile a simple .NET (Core) CLI to see the problem. With the .NET SDK installed, you can also install dotnet-script (dotnet tool install -g dotnet-script) and then run dotnet-script eval 'Console.WriteLine("Next word is \u001b[32mgreen\u001b[m.");'

Try after Remove-ItemProperty -ErrorAction Ignore HKCU:\Console VirtualTerminalLevel first, then after Set-ItemProperty HKCU:\Console VirtualTerminalLevel -Type DWORD 1 (open a new window after every change).

Long story short: It is unreasonable to expect all CLIs to explicitly opt-into VT support, and, clearly, Windows Terminal and Visual Studio Code already have made the sensible choice to enable it for all programs.

@mklement0 mklement0 changed the title Consider implicitly activating VT / ANSI escape-sequence support for native programs in conhost.exe legacy console windows on Windows Consider implicitly activating VT / ANSI escape-sequence support for native programs in conhost.exe console windows on Windows Feb 3, 2023
@237dmitry
Copy link

237dmitry commented Feb 4, 2023

cmd.exe also supports esc-sequences. ^[ is ctrl+[

Screenshot 2023-02-04 074642

@mozhuanzuojing
Copy link

By contrast, in legacy console windows (provided by conhost.exe) the support depends on a registry setting that is OFF by default (Set-ItemProperty HKCU:\Console VirtualTerminalLevel -Type DWORD 1 enables it).

by it. resolved microsoft/vscode#173241

image

@mklement0
Copy link
Contributor Author

@237dmitry, good to know.
So cmd.exe behaves likes PowerShell:

  • It always has VT support for its internal commands (echo, type - others?)
  • For external programs that do not individually turn on VT support via SetConsoleMode() (the way pwsh.exe and wsl.exe do, for instance), VT support is enabled ONLY IF the DWORD value VirtualTerminalLevel of registry key HKEY_CURRENT_USER\Console is set to 1.

Try with dotnet-script eval "Console.WriteLine(\"Next word is \u001b[32mgreen\u001b[m.\");"

@mklement0
Copy link
Contributor Author

@mozhuanzuojing, I'm glad to hear it, though I'm still baffled as to why that is required on your system. On my Windows 11 22H2 system with Visual Studio Code 1.75, I do not have to use this setting - VT support is always on in the integrated terminal (irrespective of what shell is running there).

@rhubarb-geek-nz
Copy link

Long story short: It is unreasonable to expect all CLIs to explicitly opt-into VT support, and, clearly, Windows Terminal and Visual Studio Code already have made the sensible choice to enable it for all programs.

Is it an application specific setting ( as the settings for 'pwsh' above suggests) or a machine wide setting? Installing one program should not change the behaviour of others. A simple user option if they want the behaviour sounds like running PowerShell under Windows Terminal.

@237dmitry
Copy link

VirtualTerminalLevel

I have no this parameter in hkcu:\console

pwsh > gp hkcu:\console | select virt*      # nothing
cmd > reg query hkcu\console /v VirtualTerminalLevel

ERROR: The system was unable to find the specified registry key or value.

It always has VT support for its internal commands (echo, type - others?

echo works (on screenshot), prompt works too. I do not use cmd.exe since 2012.

python
dotnet-script

I'm not a developer and not from IT at all. I just see that everything is working.
Do not get me wrong.

@iSazonov iSazonov added the WG-Interactive-Console the console experience label Feb 4, 2023
@mklement0
Copy link
Contributor Author

mklement0 commented Feb 4, 2023

@rhubarb-geek-nz, the registry value is a persistent, user-specific setting that affects all (non-legacy) conhost.exe console windows - except if the program that creates the console window explicitly overrides the value, via a call to the WinAPI's SetConsoleMode function.

(I've removed my loose, contrast-with-Windows-Terminal uses of the term "legacy" from the initial post and previous comments, and now refer to it in the narrow senses of pre-VT-support older implementations of conhost.exe, as shown in @237dmitry's screenshot, to avoid confusion. We needn't worry about these legacy windows - this post is purely about the current, VT-capable-in-principle conhost.exe windows.)

@237dmitry:

You were the one to introduce the cmd.exe distraction; I just commented on it as a minor point of interest.
In fact, except for the legacy clarification, your comments have all been distractions:
I have laid out in detail what does and doesn't work, including why echo works, and why PowerShell-native commands work.
And clearly you do not see that everything is working, given that you haven't tried - or haven't reported on - the things I've explained don't work.
To avoid further distractions, please engage with specific points that I've made - for instance, don't tell me that echo works, when (a) you've clearly demonstrated that and (b) I've already acknowledged that and (c) I've already explained why that doesn't matter in the context of this issue.

@237dmitry
Copy link

To avoid further distractions

Ok, I gave a few working examples, talked about the settings. I see you are looking for something else.

@mklement0
Copy link
Contributor Author

@237dmitry, I conclude from your response that you are either unable or unwilling to engage with the specific arguments that have been made. The latter is what I am looking for. If there's something unclear about my arguments, ask for clarification. If you're unable or unwilling to personally verify repro steps, do not make claims such as "everything is working".

@mklement0
Copy link
Contributor Author

mklement0 commented Feb 4, 2023

@rhubarb-geek-nz, to be clear, what this proposal is saying is:

  • The registry setting shouldn't matter.
  • PowerShell should categorically and unconditionally enable VT support for its sessions (which means that there should be no difference between whether it is PowerShell-native commands or native (external) programs that are emitting VT sequences).
  • This means that conhost.exe console windows created without PowerShell acting as the shell would still be subject to the registry setting (e.g., via the Windows Run dialog (WinKey-R), or via cmd.exe (which itself does the same selective enabling/disabling that PowerShell currently does)).

@SteveL-MSFT
Copy link
Member

@mklement0 I'm pretty sure that conhost support of VT is incomplete compared to Windows Terminal. In Win11, you can have Windows Terminal as the default console host removing the need for conhost completely (except for edge compatibility cases). I don't think pwsh should override the setting of that registry value.

@mklement0
Copy link
Contributor Author

@SteveL-MSFT, PowerShell already does override it, for PowerShell-native commands - and seemingly relies on VT support for its own colored output (right?)

There's no reason not to enable it unconditionally, given that there are only downsides to having it disabled (garbled output) - am I missing something?

In other words:

  • If "It ain't easy being `e[32mgreen`e[m." works (PowerShell-native),
  • why shouldn't python -c 'print(''It ain\''t easy being \x1b[32mgreen\x1b[m.'')'?

given that the exact same string is emitted in both cases.

I see no benefit to printing It ain't easy being ←[32mgreen←[m. in the latter case, which is what currently happens by default.

@SteveL-MSFT
Copy link
Member

SteveL-MSFT commented Feb 6, 2023

@mklement0 perhaps you can open an issue in https://github.com/microsoft/terminal and see what arguments they would have to NOT have that registry setting enabled by default? It would seem like those would be reasons we should't override it? (I understand you're example with Python, but would defer to the conhost team to enumerate potential problem).

GitHub
The new Windows Terminal and the original Windows console host, all in the same place! - GitHub - microsoft/terminal: The new Windows Terminal and the original Windows console host, all in the same...

@rhubarb-geek-nz
Copy link

There's no reason not to enable it unconditionally, given that there are only downsides to having it disabled (garbled output) - am I missing something?

Is it a local powershell only specific setting or is it global affecting all applications whether pwsh is being used or not? In general installing an application should not change global settings without giving the user the option or even visibility. Installing one application should not change the behaviour of an existing app.

@mklement0
Copy link
Contributor Author

mklement0 commented Feb 6, 2023

@rhubarb-geek-nz, it would affect PowerShell (and applications run in it) only - no persistent setting involved, just an application-internal call to SetConsoleMode(). In other words: no system-wide side effects.

@rhubarb-geek-nz
Copy link

@rhubarb-geek-nz, it would affect PowerShell (and applications run in it) only - no persistent setting involved, just an application-internal call to SetConsoleMode(). In other words: no system-wide side effects.

Excellent, and of course it will then revert the console mode to the original value when the pwsh.exe process exits?

@mklement0
Copy link
Contributor Author

@rhubarb-geek-nz, PowerShell already does that (of necessity, given that it already always turns it on for itself).

@rhubarb-geek-nz
Copy link

@rhubarb-geek-nz, PowerShell already does that (of necessity, given that it already always turns it on for itself).

Cool. The reason I mention is is that "git" on Windows 10 does not play nicely with the console and often changes the behaviour of the backspace key to delete word when it exits, and the way to reset it is to run git again with no arguments and it sets it back correctly again.

@mklement0
Copy link
Contributor Author

mklement0 commented Feb 6, 2023

@SeeminglyScience
Copy link
Collaborator

Note that we currently explicitly turn off VT prior to invoking a native command, and then turn it back on after we have finished. (Technically we're setting all "Console Mode" settings, so it's possible that VT is incidental but from the comments it doesn't seem to be (though not conclusive))

Not arguing for or against, I don't actually know why we do it. Very interested in what Dustin comes back with

@mklement0
Copy link
Contributor Author

Thanks, @SeeminglyScience. It seems that this behavior - which was recently tinkered with (#16612) - is the cause of the bug in #18771.

Leaving VT support consistently enabled in-session would make such bugs go away, and I do hope that Dustin's response will indicate that it is safe to do so.

For now, I suggest reopening #18771, which was inappropriately closed.

@iSazonov
Copy link
Collaborator

iSazonov commented Feb 7, 2023

Perhaps we should think about Terminal mode and compatibility (conhost) mode for pwsh. This would essentially mean creating a modern ConsoleHost, which would follow Terminal entirely without any tricks.

@rhubarb-geek-nz
Copy link

Lets suggest a few goals.

  1. When pwsh.exe starts it should capture the current console mode and when it stops it should restore the console mode to whatever it was when it started.

  2. PowerShell should provide a consistent environment for scripts and any programs that they run.

  3. The behaviour of two or more child console apps in different threads sharing the same console is undefined.

  4. Any modern Win32 Console app that wants VT codes supported should be able to turn the VT mode on, or at least be expected to be responsible to.

So given that the current behaviour is apparently to turn VT mode off while running child programs this behaviour should be maintained in order to adhere to rule 2, a consistent execution environment.

@mklement0
Copy link
Contributor Author

mklement0 commented Feb 7, 2023

I have a better idea for consistency:

  1. (as you stated)
  2. PowerShell keeps VT support enabled for everything running in its session.

In this day and age, VT support should be the default, and especially cross-platform CLIs cannot reasonable assumed - nor should they be expected to - make WinAPI calls just to enable it.

Clearly, Windows Terminal has already made that choice and - at least based on my experience - so has Visual Studio Code.

The only reason not to do that would be to prevent bad things from happening due to conhost.exe's VT support possibly being less complete than Windows Terminal's and certain VT escape sequences perhaps having unwanted effects - I don't know the answer, but hopefully Dustin will tell us soon.


@iSazonov, can you say a bit more about creating a modern ConsoleHost? So if that were done, the above concern would be moot?

@iSazonov
Copy link
Collaborator

iSazonov commented Feb 8, 2023

@iSazonov, can you say a bit more about creating a modern ConsoleHost? So if that were done, the above concern would be moot?

As stated in the Terminal repository, the main goal of conhost is backward compatibility while Terminal is designed for innovation. If conhost and Terminal share the same code base, pwsh could benefit from this too. In other words, why would pwsh do what Terminal does better, it could inherit anything new from Terminal, maybe be a plugin or a layer.

@rhubarb-geek-nz
Copy link

pwsh.exe is a classic console application ( although written in dotnet ) it should work in any console/terminal environment, it does not need to provide its own. For example you can log into Windows using SSH from Linux or MacOS and run pwsh.exe, what effect does the VT flag now have?

@iSazonov
Copy link
Collaborator

iSazonov commented Feb 8, 2023

@rhubarb-geek-nz pwsh can have many ConsoleHost implementations. I guess you think the new host will work only as tab in Terminal. No, I say about using Terminal (shared) components. If they are not present on computer pwsh will use current host for compatibility. If they present (Terminal was installed) you automatically get all benefits in any scenario where it makes sense.

@o-sdn-o
Copy link

o-sdn-o commented Feb 8, 2023

Not arguing for or against, I don't actually know why we do it. Very interested in what Dustin comes back with

I don't know the answer, but hopefully Dustin will tell us soon

@SeeminglyScience @mklement0 Perhaps Dustin has already answered this question microsoft/terminal#6634 (comment)

@mklement0
Copy link
Contributor Author

mklement0 commented Feb 8, 2023

That's a good find, @o-sdn-o, thank you.
I'm not sure I get all the details, but the summary seems to be:

  • There are legacy CLIs that rely on the ability to "print raw control characters directly to the screen", pre-dating VT support in conhost.exe (I'm unclear on how this ability is a substitute for VT support and how such raw printing managed to produce coloring/formatting, assuming that is the intent (I cannot think of any other)).

  • Windows Terminal too seemingly still talks to a conhost.exe instance behind the scenes and "sets up the console host it talks to in such a way as to always enable ENABLE_VT_PROCESSING by default".

In other words: Windows Terminal has already broken backward compatibility.

@o-sdn-o
Copy link

o-sdn-o commented Feb 8, 2023

Perhaps in the vast majority of cases, the statement "print raw control characters directly to the screen" means printing Null-characters as whitespaces . Null\x00 and whitespace \x20 have the same glyph but have a different meaning depending on whether VT support is enabled or disabled.

@o-sdn-o
Copy link

o-sdn-o commented Feb 8, 2023

Consider the following example

#include <iostream>
int main()
{
    auto x = '\0';
    std::cout << "@" << x << "@\n";
}

VT disabled case output:

@ @

VT enabled case output:

@@

@mklement0
Copy link
Contributor Author

Thanks, @o-sdn-o - that strikes me as something we needn't worry about: such NUL chars. are presumably not emitted for formatting, given that spaces would do. And meaningfully detecting / visualizing control characters requires programmatic processing, which wouldn't be affected by this display change (which may be surprising, but as a purely display change that affects only the human observer, it wouldn't be considered a breaking change).

@o-sdn-o
Copy link

o-sdn-o commented Feb 9, 2023

Another example of hastily crafted cli app

#include <iostream>
#include <cstring>
int main()
{
    using namespace std;
    char buffer[80] = {};
    auto values = { "Attribute1", "Attribute2", "Attribute3" };
    auto offset = buffer + 5;
    for (auto v : values)
    {
        memcpy(offset, v, strlen(v));
        offset += 15;
    }
    cout << string{ buffer, sizeof(buffer) } << "\n";
}

VT disabled output:

    Attribute1     Attribute2     Attribute3                                   

VT enabled output:

Attribute1Attribute2Attribute3

@mklement0
Copy link
Contributor Author

Given that this is just a variation of your initial NUL scenario, the same arguments apply.

The question is: What raw control sequences designed specifically for for-display formatting (again, you wouldn't use NUL if it prints the same as a space) might legacy CLIs employ that would result in broken display formatting with VT support enabled, and how widespread is their use at this point? A bucket 3 judgment call can then be made.

Separately, if such CLIs must still be supported, a per call opt-out could be provided via the proposed Invoke-NativeCommand cmdlet - see #18991 - which would be helpful whether or not the change proposed here is made or not; something like Invoke-NativeCommand -NoVT { ... } could be a per-call override, irrespective of whether what is being overridden is PowerShell's new default behavior or the system-wide registry setting.

@o-sdn-o
Copy link

o-sdn-o commented Feb 9, 2023

Rough representation of possible differences.

The following glyphs are printed when VT support is disabled (nothing printed if VT support is enabled)

_ 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00
0x10 §

Formatting (in terms of cursor positioning)

_ Byte VT disabled VT enabled
NUL 0x00 Print whitespace Do nothing
ESC 0x1B Print VT-sequence initial byte (C1)
BS 0x08 Print Move cursor one cell/character back
DEL 0x7F Print Delete characters backwards
TAB 0x09 Move cursor forward Move cursor forward
EOL 0x0A Move cursor to the beginning of the next line Move cursor one line down (and optionally to the beginning of the line)
VT 0x0B Print Same as LF/EOL
FF 0x0C Print Same as LF/EOL
CR 0x0D Move cursor to the leftmost position Move cursor to the leftmost position

@rhubarb-geek-nz
Copy link

@mklement0
Copy link
Contributor Author

Great stuff, @o-sdn-o, thank you.

@rhubarb-geek-nz, processing in a pipeline is incidental to this issue (which indeed is a problem if the PowerShell pipeline is expected to relay raw bytes, which it currently cannot) - this is about direct-to-display output.

@theJasonHelmick
Copy link
Collaborator

theJasonHelmick commented Mar 22, 2023

@mklement0 - Thank you. The registry setting you're seeking is best set by the Windows team. the WG would recommend that you open an issue with them. Use the Windows Feedback Hub.
 https://support.microsoft.com/windows/send-feedback-to-microsoft-with-the-feedback-hub-app-f59187f8-8739-22d6-ba93-f66612949332

Learn how to use Feedback Hub to report problems or send suggestions to Microsoft.

@ghost ghost removed the Needs-Triage The issue is new and needs to be triaged by a work group. label Mar 22, 2023
@theJasonHelmick theJasonHelmick added the Resolution-External The issue is caused by external component(s). label Mar 22, 2023
@mklement0
Copy link
Contributor Author

mklement0 commented Mar 22, 2023

@theJasonHelmick:

  • Changing the registry setting would take effect system-wide (for the current user), and I wouldn't be surprised if the Windows team were hesitant to make that change (also - and this is just a hunch - Feedback Hub is where suggestions go to die).

  • PowerShell already does its own thing irrespective of the registry setting, albeit (confusingly) only selectively.
    The suggestion is to make it fully do its own thing - just like Visual Code and Windows Terminal already do - based on the rationale laid out above.
    That way, other environments wouldn't be affected, which strikes me as preferable.

Either way, the problem described in #19395 must be fixed.

@johan-boule
Copy link

Please write a summary that users can understand while closing a ticket.

@eabase
Copy link

eabase commented May 6, 2024

@o-sdn-o

Perhaps in the vast majority of cases, the statement "print raw control characters directly to the screen" means printing Null-characters as whitespaces . Null\x00 and whitespace \x20 have the same glyph but have a different meaning depending on whether VT support is enabled or disabled.

Indeed, this is the exact work around for using powershell (default) command window and python, when VT is not enabled.

You have to send a Null (?) using the os.system(), like this:

# python.exe -qu -X utf8=1 -c "import os; os.system(''); m='\x1b[30;41m YOUR_TEXT_HERE \x1b[0m'; print(m);"

# YOUR_TEXT_HERE                   <-- In Color!

# python.exe -qu -X utf8=1 -c "m='\x1b[30;41m YOUR_TEXT_HERE \x1b[0m'; print(m);"

#_[30;41m YOUR_TEXT_HERE_ [0m      <-- ANSI garabage

@o-sdn-o
Copy link

o-sdn-o commented May 7, 2024

You have to send a Null (?) using the os.system()

This is how magic happens:
python/cpython#74261 (comment)

...
cmd.exe enables virtual terminal mode, but only for itself. It disables VT mode before starting other programs, and also at shutdown. It appears you've found a bug in the case of "cmd.exe /c ...". You can get the same result via os.system(''). It's failing to disable VT mode after it exits.

Enabling VT mode by default is potentially a problem because a console buffer's mode is shared, inherited state. Adding a set_console_mode method on console files would be a useful convenience to manage this state. There could also be a couple IntFlag enums for the available input and output mode values.
...

@eabase
Copy link

eabase commented May 7, 2024

Thanks for linking that. I forgot it was using the native ctypes there.
I know about that old code since back in 2017 (when ANSI started to get fully supported), and have used all my windows with VT enabled, without ever running into any related issues (as stated). The only times I have issues, are when I have not enabled it.

So I don't see why we can't implement one/both of these following:

  • Why is VT is not enabled by default?
  • Why is VT not enabled automatically when there is a defined and "compatible" $env:TERM variable set?

@o-sdn-o
Copy link

o-sdn-o commented May 7, 2024

Perhaps this is due to the following:

PS: https://xkcd.com/1172/

xkcd

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue-Enhancement the issue is more of a feature request than a bug Resolution-External The issue is caused by external component(s). WG-Interactive-Console the console experience
Projects
None yet
Development

No branches or pull requests