X Tutup
The Wayback Machine - https://web.archive.org/web/20241115074019/https://github.com/PowerShell/PowerShell/issues/12268
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

Linux pwsh enables DECCKM, but Windows pwsh cannot handle it; arrow keys stop working after exiting linux powershell #12268

Closed
DHowett-MSFT opened this issue Apr 6, 2020 · 33 comments · Fixed by #14943
Labels
Issue-Bug Issue has been identified as a bug in the product OS-Linux Resolution-Fixed The issue is fixed. Waiting - DotNetCore waiting on a fix/change in .NET WG-Interactive-Console the console experience
Milestone

Comments

@DHowett-MSFT
Copy link

Steps to reproduce

  1. Use Windows PowerShell 7 and PowerShell 7 in WSL.
  2. Launch Windows Powershell in Windows Terminal
  3. Launch wsl, pwsh
  4. Exit Linux powershell
  5. Observe the arrow keys

Linux powershell emits the escape sequence DECSET DECCKM[1] on startup before it emits the first prompt.

This reconfigures the cursor keys, which usually emit CSI A...D, to emit SS3 A...D instead.

Linux powershell can handle SS3 arrow key sequences. This is fine.

When Linux powershell exits, it does not restore DECCKM to its original state. All applications that manipulate global terminal state are expected to return the terminal to "normal" once they are done.

Because the state is not restored when you exit Linux powershell, Windows powershell (which doesn't know that the terminal state has been changed) will start to receive SS3 arrow key sequences.

Windows powershell cannot handle SS3 arrow key sequences, and it ignores them, so the arrow keys do not work.

This also reproduces when you SSH to a linux machine, but WSL is easier for a self-contained repro.

Expected behavior

DECCKM is disabled on exit.

Actual behavior

DECCKM remains enabled on exit, downstream applications get confused.

Environment data

(dhowett-sl) ~ % $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.0.0
PSEdition                      Core
GitCommitId                    7.0.0
OS                             Microsoft Windows 10.0.19041
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

(dhowett-sl) ~ % wsl pwsh -c '\$PSVersionTable'

Name                           Value
----                           -----
PSVersion                      7.0.0
PSEdition                      Core
GitCommitId                    7.0.0
OS                             Linux 4.19.84-microsoft-standard #1 SMP Wed Nov 13 11:44:37 UTC 2019
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

[1] Notes about DECCKM

CSI ? Pm h
          DEC Private Mode Set (DECSET).
            Ps = 1  ⇒  Application Cursor Keys (DECCKM), VT100.

DECCKM reconfigures the arrow keys as follows

Key            Normal     Application
-------------+----------+-------------
Cursor Up    | CSI A    | SS3 A
Cursor Down  | CSI B    | SS3 B
Cursor Right | CSI C    | SS3 C
Cursor Left  | CSI D    | SS3 D
-------------+----------+-------------

[2] I've investigated PowerShell, PSReadline and dotnet/runtime, and I cannot determine where DECCKM is being enabled.

@DHowett-MSFT DHowett-MSFT added the Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a label Apr 6, 2020
@DHowett-MSFT
Copy link
Author

Originally filed: microsoft/terminal#5257

@LookoutHill
Copy link

@DHowett-MSFT As a workaround it seems like one could create a script that would emit the appropriate reset sequence. Would you post this sequence in PowerShell string format?

@DHowett-MSFT
Copy link
Author

"`e[?1l"

@LookoutHill
Copy link

Thanks for that. It gave me enough info to interpret this arcane knowledge. That sequence doesn't restore functionality, but the "Reset to Initial State" sequence ("`ec") does.

@DHowett-MSFT
Copy link
Author

It legitimately looks like it's being stripped out of the output.

When I type this:

(dhowett-sl) ~ % "a`e[?1lb"

I get this out of the pty:

ab

but when I run this:

(dhowett-sl) ~ % wsl printf 'a\\e[?1lb'

I get

a␛[?1lb

I wonder why it's being filtered from powershell's output stream. Similar sequences, like "`e[?2004l", are not.

@DHowett-MSFT
Copy link
Author

As a workaround, you can do this:

"`e[?;1l"

Masking that you're requesting DECRST 1 by placing a ; before it confuses powershell, but Terminal can understand it.

@iSazonov
Copy link
Collaborator

iSazonov commented Apr 7, 2020

I've investigated PowerShell, PSReadline and dotnet/runtime, and I cannot determine where DECCKM is being enabled.

Wow!
/cc @daxian-dbw who have huge experience in the area.

@iSazonov iSazonov added WG-Interactive-Console the console experience OS-Linux labels Apr 7, 2020
@DHowett-MSFT
Copy link
Author

Of note, this reproduces without PSReadline available 😄

(I tracked down why I can't forcibly print "`e[?1l" -- PowerShell isn't using VT input on Windows, so that value is technically meaningless to it. Conhost is ignoring it.)

@lukaszroz
Copy link

I have come across this bug when I SSH from Ubuntu to Windows machine. It works when I SSH from Bash without starting PowerShell. To reproduce in PowerShell on Ubuntu in Gnome Terminal:

$ cat #try up/down/right/left arrow, Enter, Ctrl-D
^[[A^[[B^[[C^[[D

$ pwsh
PowerShell 7.0.3
> ssh user@host
PowerShell 7.0.3
> exit #arrow keys don't work
Connection to host  closed.
> exit
$ cat #try up/down/right/left arrow, Enter, Ctrl-D
^[OA^[OB^[OC^[OD
ABCD

After some digging I have found dotnet/corefx#6488. The Pull Request fixed a number of bugs related to terminal escape codes handling. It seems to me the DECCKM might be set in System.Console, see lines 937 - 947

@lukaszroz
Copy link

Found the issue: dotnet/runtime#27626

@DHowett
Copy link

DHowett commented Sep 1, 2020

Excellent investigation. Thanks!

@tig
Copy link

tig commented Oct 21, 2020

Tagging @TylerLeonhardt so he's aware of this relative to https://github.com/PowerShell/GraphicalTools

Specifically, this is why out-consolegridview has issues on Linux where the cursor keys don't work after using it. Related to PowerShell/ConsoleGuiTools#99.

GitHub
A module that mixes PowerShell and GUIs! - built on Avalonia and gui.cs - PowerShell/GraphicalTools

@mklement0
Copy link
Contributor

This also reproduces when you SSH to a linux machine

There is another case, namely an "intra-Unix" one:

The cursor keys in ksh only work if application-cursor mode is off (keypad_local / rmkx)

Since PowerShell unconditionally turns that mode on (keypad_transmit / smkx), even a simple call to the PowerShell CLI such as
pwsh -noprofile -c 'get-date'
breaks cursor-key handling in ksh.

(bash and zsh are not affected, because they work in either mode.)

@iSazonov iSazonov added Issue-Bug Issue has been identified as a bug in the product and removed Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a labels Jan 16, 2021
@iSazonov
Copy link
Collaborator

How can we detect whether SS3 mode is on?

@DHowett
Copy link

DHowett commented Jan 16, 2021

You don’t need to detect it. An application should set the states it needs on startup and set them back to reasonable defaults for a terminal on exit. Nothing to remember, nothing to restore.

@mklement0
Copy link
Contributor

@DHowett, this isn't so much about terminals, but about the shells that run in them, and as my previous comment implies, different shells:

  • require different settings

  • seem to rely on commands they invoke not to change these settings.

For instance:

  • ksh malfunctions if a command turns application-cursor mode on and leaves it on.

  • pwsh malfunctions if a command turns application-cursor mode off and leaves it off.

Therefore, while it may be a challenge to implement, restoring the previous state seems like the right thing to do.

@DHowett
Copy link

DHowett commented Jan 17, 2021

@mklement0 I'm also not talking about terminals. As the reporter of the issue and engineering owner for a terminal, I'm supposed to be aware of how applications expect to work... so here goes:

A shell that cannot go from a terminal left in its default state to its required state is broken.
A shell that requires a state that is different from the default must restore the default when it exits naturally or launches an application that will bootstrap from the default state¹. If it does not do so, it is broken.

Introducing another back-and-forth call/response to determine the application keypad mode state—ESPECIALLY if you're going to do it every time control returns to the user—is madness. It limits the ability of users to pre-fill the keyboard input queue and the ability for terminals and applications across high-latency links to properly coordinate. There's a default assumption here: applications are started with the terminal in its default state, and applications that start other applications return the terminal to its default state.

¹ Per the above axiom, this is all applications.

@DHowett
Copy link

DHowett commented Jan 17, 2021

Your example above indicates that ksh is operating as expected (terminal not in default state => it isn't expected to get to the state it needs when that state is the default state) and that pwsh is broken (it expects a non-default state, but does not request it.)

@mklement0
Copy link
Contributor

@DHowett:

Given your premise, PowerShell is broken in two respects:

  • (a) Intra-session, it doesn't actively restore application keypad mode after running a command, even though it should expect the mode to potentially have been reset to the default state, i.e., off.

  • (b) On exiting a session, it doesn't reset the terminal to the default state (it doesn't turn application keypad mode off again).

Fixing (a) would require the very back-and-forth you think should be avoided - even though probably only a minority of commands (other shells, CUIs) change the terminal state.

So I gather that:

  • the conclusion is that PowerShell shouldn't require application keypad mode to be ON for its own sake, correct?

  • if you have any insight on what such a change would entail technically / if it is feasible at all, please share.

@DHowett
Copy link

DHowett commented Jan 17, 2021

So, I do think powershell (or according to an earlier comment, the .NET runtime 😦) is broken here.

Before reading user input where it cares about the format of the arrow/line editing keys, it should set the mode it wants. After it's done, it should reset that mode. That's very cheap, and it doesn't require any back-and-forth.

I've observed zsh doing this:

  1. Emit \e[?1h (enable DECCKM)
  2. Emit my prompt
  3. Accept input
  4. On CR/LF, emit \e[?1l (disable DECCKM)
  5. Spawn an application
  6. Application exits.
  7. GOTO 1.

There's no querying, just assertion. "DECCKM should be on now." "DECCKM should be off now." It never needs to know the state, because of two things:

  1. If DECCKM is off, \e[?1h turns it on.
  2. If DECCKM is on, \e[?1h does nothing.

@iSazonov
Copy link
Collaborator

@DHowett Many thanks! I think we should ask @SteveL-MSFT to add this in next milestone plan.

@tig
Copy link

tig commented Jan 22, 2021

@SteveL-MSFT & @TylerLeonhardt - is there a PowerShell issue tracking this?

This impacts Out-ConsoleGridView pretty significantly: PowerShell/ConsoleGuiTools#134

@DHowett
Copy link

DHowett commented Jan 22, 2021

image

This appears to be the PowerShell issue tracking this. o_O

@wez
Copy link

wez commented Mar 3, 2021

FWIW, this issue also applies to users of powershell on macos, running in any terminal emulator--it's not linux-specific.

@SteveL-MSFT
Copy link
Member

SteveL-MSFT commented Mar 4, 2021

The problem is dotnet/runtime#27626. Seems like dotnet is not in a hurry to fix as it's in future milestone. We can probably explicitly emit the DECRST 1 seq on exit in the interim.

@SteveL-MSFT SteveL-MSFT added Waiting - DotNetCore waiting on a fix/change in .NET Resolution-External The issue is caused by external component(s). and removed Resolution-External The issue is caused by external component(s). labels Mar 4, 2021
@SteveL-MSFT SteveL-MSFT added this to the 7.2-Consider milestone Mar 4, 2021
@wez
Copy link

wez commented Mar 4, 2021

Please also disable that mode when running child processes!

@iSazonov
Copy link
Collaborator

iSazonov commented Mar 4, 2021

@wez Welcome to review #14943. You can download artifacts there and check its locally.

@wez
Copy link

wez commented Mar 4, 2021

I don't particularly want to dive into the code in powershell; I'm here because cli/cli#3071 was initially reported as a bug in my terminal emulator :-p

The test scenario is:

  • on macos
  • launch powershell
  • inside powershell, run gh auth login
  • The unexpected cursor key encoding causes that program to crash

@iSazonov
Copy link
Collaborator

iSazonov commented Mar 4, 2021

@wez You can just download the compiled artifacts, tests them in your environment and give a feedback before the PR will be merged.

@mklement0
Copy link
Contributor

mklement0 commented Mar 4, 2021

@wez, you can emulate what the PR will implement as follows:

$Host.UI.Write("`e[?1l"); gh auth login; $Host.UI.Write("`e[?1h")

For entire-session support - as a stopgap that should also work for older PS versions - you can place the following at the end of your $PROFILE file (assumes use of PSReadLine, which is true by default):

# Turns application-key mode ON before displaying the PS prompt and back OFF right after.
$function:PSConsoleHostReadLine = " `$Host.UI.Write(`"``e[?1h`"); $function:PSConsoleHostReadLine; `$Host.UI.Write(`"``e[?1l`") "
# For non-interactive invocations of the PowerShell CLI: turn application-key mode off too, 
# So that it is left OFF on exiting (obviously won't work for -NoProfile invocations).
$Host.UI.Write("`e[?1l")

@wez
Copy link

wez commented Mar 4, 2021

Thanks, but I don't use powershell; as I mentioned above, I'm just here because this issue was raised by one of the users of my terminal. @gaborbernat: perhaps you could try those suggestions?

@gaborbernat
Copy link

I can confirm that @mklement0 solution works on:

Name                           Value
----                           -----
PSVersion                      7.1.2
PSEdition                      Core
GitCommitId                    7.1.2
OS                             Darwin 19.6.0 Darwin Kernel Version 19.6.0: Tue Nov 10 00:10:30 PST 2020; root:xnu-6153.141.10~1/RELEASE_X86_64
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

@iSazonov iSazonov added the Resolution-Fixed The issue is fixed. label Apr 2, 2021
@ghost
Copy link

ghost commented Apr 14, 2021

🎉This issue was addressed in #14943, which has now been successfully released as v7.2.0-preview.5.:tada:

Handy links:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue-Bug Issue has been identified as a bug in the product OS-Linux Resolution-Fixed The issue is fixed. Waiting - DotNetCore waiting on a fix/change in .NET WG-Interactive-Console the console experience
Projects
None yet
10 participants
X Tutup