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

ConPTY modifies escape sequences passed to process input #12166

Closed
segrey opened this issue Jan 14, 2022 · 11 comments
Closed

ConPTY modifies escape sequences passed to process input #12166

segrey opened this issue Jan 14, 2022 · 11 comments
Labels
Needs-Tag-Fix Doesn't match tag requirements Needs-Triage It's a new issue that the core contributor team needs to triage at the next triage meeting

Comments

@segrey
Copy link

segrey commented Jan 14, 2022

Windows Terminal version

1.11.3471.0 (not sure Windows Terminal version matters)

Windows build number

10.0.22000.434

Other Software

No response

Steps to reproduce

Thanks for ConPTY, it's great! I'm developing a terminal emulator using ConPTY API to start a shell process and I've come across the following problem: when Ctrl+Left is pressed, ESC [ 5 D bytes are written to shell's input stream. However, shell sees it as ESC [ D. It's reproduced when cmd.exe or powershell.exe is used as a shell, but works correctly with wsl.exe. Is it a known issue?

More detailed steps to reproduce:

  1. Run forked MiniTerm app (https://github.com/segrey/terminal/tree/main/samples/ConPTY/MiniTerm). Hardcoded cmd.exe will be started.
  2. Type something in the command prompt (e.g. "aaaa bbbb") and press Ctrl+Left/Right. The cursor is moved one position instead of jumping over word.
  3. To view how shell sees written characters, comment 26th line of Program.cs and uncomment the next one. You'll need Node.js installed. Restart the app. Now pressing Ctrl+Left prints "\u001b[D" whereas "\u001b[5D" is written to the process input.
  4. If you replace writer.Write("\x001b[5D") with writer.Write("\x001b[1;5D"), Ctrl+Left works correctly, but I'd like to keep the same logic for Unix and Windows, because \x001b[5D works on Unix correctly.

Expected Behavior

The original escape sequence should be passed to shell: ESC [ 5 D

Actual Behavior

Cut escape sequence is passed to shell: ESC [ D

@ghost ghost added Needs-Triage It's a new issue that the core contributor team needs to triage at the next triage meeting Needs-Tag-Fix Doesn't match tag requirements labels Jan 14, 2022
@237dmitry
Copy link

237dmitry commented Jan 15, 2022

I think it depends not on the terminal, but on what is running in it. For example in PSReadline Ctrl+Left:

PS > Get-PSReadLineKeyHandler | ? Key -eq 'Ctrl+LeftArrow' | fl *

Key         : Ctrl+LeftArrow
Function    : BackwardWord
Description : Move the cursor to the beginning of the current or previous word
Group       : CursorMovement

And `e[5D working:

PS > 0..5 | % { Write-Host "Not Repaint 12345`e[5D" -n }
Not Repaint Not Repaint Not Repaint Not Repaint Not Repaint Not Repaint 12345

In cmd ^[ is Ctrl+[:

C:\> echo aaaaa bbbbb^[[5D^[[91mbbbbb 

@segrey
Copy link
Author

segrey commented Jan 15, 2022

0..5 | % { Write-Host "Not Repaint 12345`e[5D" -n }

Nice example, it also prints for me Not Repaint Not Repaint Not Repaint Not Repaint Not Repaint Not Repaint 12345.
However, here `e[5D is written by shell to its output stream and my problem is that ConPTY cuts escape sequences passed to shell's input. For example, if you run

0..5 | % { Write-Host "Not Repaint 123456`e[5D" -n }

output would be:

Not Repaint 1Not Repaint 1Not Repaint 1Not Repaint 1Not Repaint 1Not Repaint 123456

This is because `e[5D is handled as "move cursor 5 characters left", not "jump cursor backward".

When Ctrl+Left is pressed, an escape sequence is written to shell's input, then shell analyzes it and writes to its output another escape sequence specifying how many characters cursor should be moved to the left. If a cursor is placed at the end of five character word, then both escape sequences can be the same. Seems it's the source of confusion.

@237dmitry
Copy link

Is this input?

PS > "`e[5D_2345" | pwsh -c { [console]::CursorLeft = 5; $input }
_2345

PS > "`e[5D_2345" | pwsh -c { [console]::CursorLeft = 10; $input }
     _2345

@segrey
Copy link
Author

segrey commented Jan 15, 2022

@237dmitry I guess no, because in this case `e[5D_2345 string is written to shell's output stream, so it's equivalent to

pwsh -c { [console]::CursorLeft = 10; "`e[5D_2345" }

You can try MiniTerm and replace cmd.exe with node.exe dump-stdin.js (see step 3 from the description). In this case, shell is dumb - it doesn't output any escape sequences, just dumps its own stdin. It will help to understand my issue.

@j4james
Copy link
Collaborator

j4james commented Jan 16, 2022

Perhaps I've misunderstood your problem, but it sounds to me like you're confusing input and output sequences. On a Unix system, trying running showkey -a, and then pressing Ctrl+LeftArrow. On nearly all of the terminals I've tested, the sequence generated is ^[[1;5D (where ^[ is ESC). This includes XTerm, Gnome Terminal, Konsole, MLTerm, Alacritty, Kitty, st, Contour, and WezTerm.

The sequence \x01b[5D is an output sequence for moving the cursor left by 5 characters, but it doesn't mean anything as an input sequence as far as I know. The input sequence for a LeftArrow without any modifiers is \x01b[D, so it's possible some apps might interpret it as that. Also \x01b[1;5D as an output sequence is just going to move the cursor left by 1, since the second parameter will be ignored.

@segrey
Copy link
Author

segrey commented Jan 16, 2022

@j4james Thanks for the reply. Putting aside what the proper escape sequence for Ctrl+Left is, why does writing \x01b[5D string to a shell input result in \x01b[D string read by the shell?

@j4james
Copy link
Collaborator

j4james commented Jan 16, 2022

When you're connecting through conpty, it needs to process your input before forwarding it on to the hosted application or shell. Bear in mind that the process on the other end might be a legacy Windows app, expecting to receive WM_KEYDOWN and WM_KEYUP messages with the VK_LEFT keycode rather the a VT escape sequence.

So what happens when conpty tries to process your \x01b[5D sequence? That looks like a LeftArrow key, but the 5 doesn't represent a valid modifier parameter, so that is presumedly just ignored. So your \x01b[5D is going to end up being passed through the system as an unmodified LeftArrow.

Eventually it'll get to a point where it needs to generate an appropriate pair of WM_KEYDOWN and WM_KEYUP messages, or a VT sequence, depending on what the host application has requested. The VT sequence for a LeftArrow is just \x01b[D, so that's what your shell ultimately receives.

I'm not overly familiar with this area of the code, so I might have some of the details wrong, but I think that's the gist of it.

@segrey
Copy link
Author

segrey commented Jan 17, 2022

@j4james Thanks for the details. I've replaced \x01b[5D (that happens to work for some shells) with more correct \x01b[1;5D, so this issue doesn't bother me anymore. However, if possible, it'd be great if ConPTY would be unopinionated and wouldn't change unrecognized escape sequences. Feel free to close this issue if needed.

@DHowett
Copy link
Member

DHowett commented Jan 18, 2022

FWIW the version of ConPTY shipped in the OS lags behind the version in Terminal by quite a few months.

The version that ships with Terminal (which we're hoping to pull out into a nuget package or something) does pass through all VT input unchanged, if the application on the other end is in ENABLE_VIRTUAL_TERMINAL_INPUT mode.

@segrey
Copy link
Author

segrey commented Jan 18, 2022

@DHowett Awesome, thanks! ENABLE_VIRTUAL_TERMINAL_INPUT is on by default in my case. I guess this can be closed now.

@segrey segrey closed this as completed Jan 18, 2022
@DHowett
Copy link
Member

DHowett commented Jan 18, 2022

@Sergey just to close the loop, this landed in #4856, and was fixed in a couple subsequent PRs. :)

We initially added it so that a compliant terminal emulator could generate mouse input that we would pass through to an application such that that we didn't have to add a bidirectional mouse input translator. 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs-Tag-Fix Doesn't match tag requirements Needs-Triage It's a new issue that the core contributor team needs to triage at the next triage meeting
Projects
None yet
Development

No branches or pull requests

4 participants