terminal icon indicating copy to clipboard operation
terminal copied to clipboard

Paste with Ctrl removes leading special character in SSH

Open fbienz opened this issue 1 year ago • 10 comments

Windows Terminal version

1.20.11271.0

Windows build number

10.0.22631.0

Other Software

openssh-client/jammy-security,now 1:8.9p1-3ubuntu0.6

Steps to reproduce

Connect to remote Linux server via SSH using WSL or Powershell. Paste a string starting with one of these characters []{} using Ctrl+Shift+v, Ctrl+v or Ctrl+ Right Click. Example: [rlkdjf[]{}dt

Expected Behavior

Complete String gets pasted into the Shell. image

Actual Behavior

Leading character and the second character are removed from the String. image

Using Shift+Insert or Right Click does not remove the leading characters.

This has worked until the latest update (winget package). I'm assuming this is form the 1.20 update.

fbienz avatar May 14 '24 10:05 fbienz

Hi I'm an AI powered bot that finds similar issues based off the issue title.

Please view the issues below to see if they solve your problem, and if the issue describes your problem please consider closing this one and thumbs upping the other issue to help us prioritize it. Thank you!

Closed similar issues:

Note: You can give me feedback by thumbs upping or thumbs downing this comment.

github-actions[bot] avatar May 14 '24 10:05 github-actions[bot]

Thanks for the report! I'm sorry that we haven't been able to reproduce this yet. I just gave it a try with 1.22 (Canary) in zsh and bash over ssh, one of which supports bracketed paste mode and the other of which does not.

Would you be able to test this out in the Canary channel/?

DHowett avatar Aug 22 '24 16:08 DHowett

Hi,

I tested it again using following version: Terminal (Portable) Version: 1.22.240822001-llm

The Problem stays the same.

I have learned a few things though:

  • zsh does not have this issue in either version. (although i could only test it by launching zsh through bash)
  • The behavior is different for Debian 10 vs Debian 12 remote host

In case the remote host is Debian 12, pasting works without the brackets being removed, except when pasting a password in the password mask. In that case only right Shift+Insert and Right Click paste correctly.

Since there were some updates, this is the current version I'm using: 1.20.11781.0

fbienz avatar Aug 23 '24 08:08 fbienz

Does this repro if you use ssh within WSL /?

carlos-zamora avatar Jan 15 '25 22:01 carlos-zamora

Does this repro if you use ssh within WSL /?

Yes? I'm not sure I understand your question. Is additional Information required?

fbienz avatar Jan 20 '25 08:01 fbienz

Can you share the output of ssh -V on all client machines that are reproducing the issue /?

carlos-zamora avatar Jan 22 '25 22:01 carlos-zamora

The clients have the following SSH version: WSL: OpenSSH_8.9p1 Ubuntu-3ubuntu0.10, OpenSSL 3.0.2 15 Mar 2022 PowerShell: OpenSSH_for_Windows_9.5p1, LibreSSL 3.8.2

fbienz avatar Jan 27 '25 09:01 fbienz

I believe this bug is dependent on the keyboard layout. I can reproduce it when using a German or French layout, but not with a UK or US English layout.

And note that you need to start the session with the German or French layout already selected. If you open a tab with an English layout, and then switch to German, the bug won't be triggered. Also if you open a tab with the German layout, and then switch to US English, you should still be able to trigger the bug.

I'm guessing it might have something to do with the way we convert escape sequences into keyboard events before converting them back to escape sequences again (assuming we still do that). Possibly related to #17656 and #15083.

j4james avatar Apr 14 '25 21:04 j4james

@j4james Changing the keyboard layout to US did solve this issue in a new Terminal. For reference, I'm using the Swiss German layout as default.

fbienz avatar Apr 22 '25 07:04 fbienz

I can confirm too.

No problem if I start a new WSL shell session after selecting a US qwerty layout. Also, it keeps working if I change the layout after that.

But the problem happens if I start a session with a French azerty layout or a custom one, and still happens after I change the layout to US.

sylann avatar Apr 30 '25 09:04 sylann

In case anyone is curious what's actually happening here, this is the basic sequence of events:

  1. When the terminal pastes something, that content reaches conpty as a stream of characters. To keep things simple, let's assume in this case we're pasting a single [ character.
  2. In order to support legacy console apps that use the ReadConsoleInput API, those characters are then translated into press and release keyboard events.
  3. If you've got a German keyboard layout, the [ character is generated with AltGr+8, so that produces four events: AltGr press, 8 press, 8 release, AltGr release.
  4. Windows has this thing were it can emulate AltGr with Ctrl+Alt, so for consistency (I think) an AltGr event is always sent with the Ctrl flag set in the control key state, even though Ctrl isn't actually pressed.
  5. Because we're in WSL, these keyboard events now need to get translated back into a character stream, and in this process, control keys and release events are mostly ignored, so it's really just the 8 press event that we're translating.
  6. As mentioned in point 4, the 8 event has both Ctrl and AltGr flags set, so it looks like Ctrl+AltGr+8, but the code is actually already designed to handle that - it keeps track of when the control keys are pressed, and only considers the Ctrl key to be valid if there is a significant gap between it and the Alt key.
  7. But when you're pasting using a Ctrl+V shortcut, the input handler is going to receive a Ctrl press event before the paste is triggered. So by the time the auto-generated AltGr and 8 events arrive, there has been a significant gap since the Ctrl press, so it is considered valid.
  8. The end result is that the system sees this as a Ctrl+AltGr+8 key combination, which is converted into a ^[ character.

This would not be a problem if we could bypass that temporary conversion into keyboard events, and just pass the original input stream straight through to the WSL app that is expecting a character stream. That should also improve the paste performance. However, I'm not sure if that is actually feasible, because I don't know if we can tell in advance how an app is going to be reading the input.

Another potential solution would be to change the way we generate keyboard events for pasted content. If we can tell that the character stream is part of a paste (e.g. if we've previously been receiving win32-input sequences, and now we're receiving a simple character stream), then instead of generating press/release events for those characters, we could just convert them into VK_PACKET events, which should pass through the system cleanly (they're unaffected by the control key state).

j4james avatar Jun 21 '25 14:06 j4james

@j4james I believe the issue can be put differently:

We translate pastes to INPUT_RECORDs here: https://github.com/microsoft/terminal/blob/00ee88400aab0cd94409204ce0c6d20854ba7eff/src/terminal/adapter/InteractDispatch.cpp#L61-L75

...which then gets indirectly translated back to text here: https://github.com/microsoft/terminal/blob/00ee88400aab0cd94409204ce0c6d20854ba7eff/src/host/inputBuffer.cpp#L706-L715

...and then finally translated back to INPUT_RECORDs here: https://github.com/microsoft/terminal/blob/00ee88400aab0cd94409204ce0c6d20854ba7eff/src/host/inputBuffer.cpp#L794-L811

...and that doesn't round-trip. Calling InputBuffer::WriteString instead of InputBuffer::Write would solve this issue for Windows Terminal in particular. If you remember, we discussed this here: https://github.com/microsoft/terminal/pull/17833#discussion_r1739935654

Can we create a version of InteractDispatch::WriteString that doesn't go through TerminalInput? 🤔

lhecker avatar Jun 23 '25 20:06 lhecker

Calling InputBuffer::WriteString instead of InputBuffer::Write would solve this issue for Windows Terminal in particular.

That's assuming Windows Terminal is always in win32 input mode, which isn't guaranteed. But looking at the code again now, I think we can possibly just call InputBuffer::WriteString when VT input mode is enabled, regardless of what mode the terminal is using.

Can we create a version of InteractDispatch::WriteString that doesn't go through TerminalInput? 🤔

Isn't that just InteractDispatch::WriteStringRaw? In which case a simple solution could just be an update to InputStateMachineEngine::ActionPrintString to look like this:

if (_pDispatch->IsVtInputEnabled())
{
    _pDispatch->WriteStringRaw(string);
}
else
{
    _pDispatch->WriteString(string);
}

But I think there is a flaw in this approach. The WriteString path (which goes through InputBuffer::Write), does some CONSOLE_SUSPENDED handling which would not occur on the WriteStringRaw path (which goes through InputBuffer::WriteString). So it becomes a little more complicated if console suspension is expected to work (I'm not actually sure about that).

And if we do need to support console suspension, it may be easier to handle everything in the InteractDispatch::WriteString method itself, and in the raw case it can call a variant of InputBuffer::WriteString that includes the CONSOLE_SUSPENDED checks.

j4james avatar Jun 24 '25 14:06 j4james