You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ConPTY modifies escape sequences passed to process input #12166
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?
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.
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.
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
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
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
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.
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.
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.
James Holderness (@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?
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.
James Holderness (@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.
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.
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. 😄
Activity
237dmitry commentedon Jan 15, 2022
I think it depends not on the terminal, but on what is running in it. For example in PSReadline
Ctrl+Left:And
`e[5Dworking:In cmd
^[isCtrl+[:segrey commentedon Jan 15, 2022
Nice example, it also prints for me
Not Repaint Not Repaint Not Repaint Not Repaint Not Repaint Not Repaint 12345.However, here
`e[5Dis 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 runoutput would be:
This is because
`e[5Dis handled as "move cursor 5 characters left", not "jump cursor backward".When
Ctrl+Leftis 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 commentedon Jan 15, 2022
Is this input?
segrey commentedon Jan 15, 2022
237dmitry I guess no, because in this case
`e[5D_2345string is written to shell's output stream, so it's equivalent toYou can try MiniTerm and replace
cmd.exewithnode.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 commentedon 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[5Dis 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;5Das an output sequence is just going to move the cursor left by 1, since the second parameter will be ignored.segrey commentedon Jan 16, 2022
James Holderness (@j4james) Thanks for the reply. Putting aside what the proper escape sequence for
Ctrl+Leftis, why does writing\x01b[5Dstring to a shell input result in\x01b[Dstring read by the shell?j4james commentedon 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_KEYDOWNandWM_KEYUPmessages with theVK_LEFTkeycode rather the a VT escape sequence.So what happens when conpty tries to process your
\x01b[5Dsequence? That looks like a LeftArrow key, but the5doesn't represent a valid modifier parameter, so that is presumedly just ignored. So your\x01b[5Dis 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_KEYDOWNandWM_KEYUPmessages, 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 commentedon Jan 17, 2022
James Holderness (@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 commentedon 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_INPUTmode.segrey commentedon Jan 18, 2022
Dustin L. Howett (@DHowett) Awesome, thanks! ENABLE_VIRTUAL_TERMINAL_INPUT is on by default in my case. I guess this can be closed now.
DHowett commentedon 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. 😄