Ctrl+Alt+Shift+2 doesn't send NUL in VT input mode #7247

Closed
opened 2026-01-31 00:58:56 +00:00 by claunia · 11 comments
Owner

Originally created by @zadjii-msft on GitHub (Apr 1, 2020).

Originally assigned to: @lhecker on GitHub.

@zadjii-msft:

Okay so Ctrl+Alt+Space was fixed for conhost, but it looks like it's still broken in the Terminal 😢 Now it's just generating a ^@, a single NUL.

I think the Terminal is synthesizing the right sequence, but maybe conpty is generating the wrong input for it now, or maybe the input that's generated by conpty doesn't get re-translated back to ^[^@ correctly. I can try to keep investigating to figure out where the miscommunication is, if you need help.

@lhecker:

@zadjii-msft Yeah I was just about to say that as well.

As I wrote above the most puzzling thing for me though is if you change this if condition (line 510) to a simple ch != 0 - which should be correct as well.
If you attach a debugger you'll see that TerminalInput definitely invokes _pfnWriteEvents with a \x1b\x00 sequence.
But then showkey shows a ^[^R sequence instead. How can this happen? > I don't understand how such a benign change to the if condition can cause this. 😞

Okay I maybe saw what happened here.

  1. On a en-us keyboard, '@' is shift+2.
  2. When conpty sees a \x1b\x0, it converts it to [Shift_down, Ctrl_down, Alt_down, 2_down, 2_up, Alt_up, Ctrl_up, Shift_up].
    • I did not pay a ton of attention to the keys that got synthesized here. I need to re-investigate the [2_down, 2_up]
  3. When we get back to TerminalInput::HandleKey, we end up skipping that branch, because the ch was 0n50, which is 0x32, which is '2'.

I'm not sure what's exactly the right behavior here. I'd think we probably should send a different key, but I'll need to investigate more. It's possible that the ch != 0 change would work just fine, but I want to make sure that the upstream input from conpty is actually correct.
I've got a braindead fix that would fix showkey -a, but that doesn't really resolve the issue here for Win32 applications unfortunately, so I'm gonna file a separate issue to track this investigation.

Originally posted by @zadjii-msft in https://github.com/microsoft/terminal/pull/4192#issuecomment-607326733

Originally created by @zadjii-msft on GitHub (Apr 1, 2020). Originally assigned to: @lhecker on GitHub. @zadjii-msft: > Okay so <kbd>Ctrl+Alt+Space</kbd> was fixed for conhost, but it looks like it's still broken in the Terminal 😢 Now it's just generating a `^@`, a single NUL. > > I think the Terminal is synthesizing the right sequence, but maybe conpty is generating the wrong input for it now, or maybe the input that's generated by conpty doesn't get re-translated back to `^[^@` correctly. I can try to keep investigating to figure out where the miscommunication is, if you need help. @lhecker: > @zadjii-msft Yeah I was just about to say that as well. > > As I wrote above the most puzzling thing for me though is if you change [this if condition (line 510)](https://github.com/microsoft/terminal/pull/4192/files#diff-3895074b0184ca0d36ef271db540a282R510) to a simple `ch != 0` - which _should_ be correct as well. > If you attach a debugger you'll see that `TerminalInput` _definitely_ invokes `_pfnWriteEvents` with a `\x1b\x00` sequence. > But then showkey shows a `^[^R` sequence instead. How can this happen? > I don't understand how such a benign change to the if condition can cause this. :disappointed: Okay I maybe saw what happened here. 1. On a en-us keyboard, '@' is <kbd>shift+2</kbd>. 2. When conpty sees a `\x1b\x0`, it converts it to [Shift_down, Ctrl_down, Alt_down, 2_down, 2_up, Alt_up, Ctrl_up, Shift_up]. - I did not pay a ton of attention to the keys that got synthesized here. I need to re-investigate the [2_down, 2_up] 3. When we get back to `TerminalInput::HandleKey`, we end up skipping that branch, because the `ch` was `0n50`, which is `0x32`, which is `'2'`. I'm not sure what's exactly the right behavior here. I'd think we probably should send a different key, but I'll need to investigate more. It's possible that the `ch != 0` change would work just fine, but I want to make sure that the upstream input from conpty is _actually_ correct. I've got a [braindead fix](https://github.com/microsoft/terminal/commit/77eee7066e5606f32359ec5ea8b291e42aa86ca9) that would fix `showkey -a`, but that doesn't really resolve the issue here for Win32 applications unfortunately, so I'm gonna file a separate issue to track this investigation. _Originally posted by @zadjii-msft in https://github.com/microsoft/terminal/pull/4192#issuecomment-607326733_
Author
Owner

@zadjii-msft commented on GitHub (Apr 1, 2020):

master, Conhost+conpty, Ctrl+alt+space, (i/o modes):(0x 1f7, 0x f) (Win32 input)

Down: 1 Repeat: 1 KeyCode: 0x10 ScanCode: 0x2a Char: \0 (0x0) KeyState: 0x10
Down: 1 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x12
Down: 1 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x1a
Down: 1 Repeat: 1 KeyCode: 0x32 ScanCode: 0x3 Char: \0 (0x0) KeyState: 0x1a
Down: 0 Repeat: 1 KeyCode: 0x32 ScanCode: 0x3 Char: \0 (0x0) KeyState: 0x1a
Down: 0 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x12
Down: 0 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x10
Down: 0 Repeat: 1 KeyCode: 0x10 ScanCode: 0x2a Char: \0 (0x0) KeyState: 0x0

The interesting input here is Down: 1 Repeat: 1 KeyCode: 0x32 ScanCode: 0x3 Char: \0 (0x0) KeyState: 0x1a

master, Conhost+conpty, Ctrl+alt+space, (i/o modes):(0x 3f7, 0x f) (VT input)

Down: 1 Repeat: 1 KeyCode: 0x10 ScanCode: 0x2a Char: \0 (0x0) KeyState: 0x10
Down: 1 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x12
Down: 1 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x1a
Down: 1 Repeat: 1 KeyCode: 0x32 ScanCode: 0x0 Char: \0 (0x0) KeyState: 0x1a
Down: 0 Repeat: 1 KeyCode: 0x32 ScanCode: 0x3 Char: \0 (0x0) KeyState: 0x1a
Down: 0 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x12
Down: 0 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x10
Down: 0 Repeat: 1 KeyCode: 0x10 ScanCode: 0x2a Char: \0 (0x0) KeyState: 0x0

master, Conhost, Ctrl+alt+space, (i/o modes):(0x 1f7, 0x f) (Win32 input)

Down: 1 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x28
Down: 1 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x2a
Down: 1 Repeat: 1 KeyCode: 0x20 ScanCode: 0x39 Char: \0 (0x0) KeyState: 0x2a
Down: 0 Repeat: 1 KeyCode: 0x20 ScanCode: 0x39 Char: \0 (0x0) KeyState: 0x2a
Down: 0 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x22
Down: 0 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x20

master, Conhost, Ctrl+alt+space, (i/o modes):(0x 3f7, 0x f) (VT input)

Down: 1 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x28
Down: 1 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x2a
Down: 1 Repeat: 1 KeyCode: 0x0 ScanCode: 0x0 Char: ^[ (0x1b) KeyState: 0x0
Down: 1 Repeat: 1 KeyCode: 0x0 ScanCode: 0x0 Char: \0 (0x0) KeyState: 0x0
Down: 0 Repeat: 1 KeyCode: 0x20 ScanCode: 0x39 Char: \0 (0x0) KeyState: 0x2a
Down: 0 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x22
Down: 0 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x20

master, Conhost, Ctrl+alt+shift+2, (i/o modes):(0x 1f7, 0x f) (Win32 input)

Down: 1 Repeat: 1 KeyCode: 0x10 ScanCode: 0x2a Char: \0 (0x0) KeyState: 0x30
Down: 1 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x38
Down: 1 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x3a
Down: 1 Repeat: 1 KeyCode: 0x32 ScanCode: 0x3 Char: \0 (0x0) KeyState: 0x3a
Down: 0 Repeat: 1 KeyCode: 0x32 ScanCode: 0x3 Char: \0 (0x0) KeyState: 0x3a
Down: 0 Repeat: 1 KeyCode: 0x10 ScanCode: 0x2a Char: \0 (0x0) KeyState: 0x20

NOTE: this keystroke is what conpty is attempting to replicate

@zadjii-msft commented on GitHub (Apr 1, 2020): #### `master`, Conhost+conpty, <kbd>Ctrl+alt+space</kbd>, (i/o modes):`(0x 1f7, 0x f)` (Win32 input) ``` Down: 1 Repeat: 1 KeyCode: 0x10 ScanCode: 0x2a Char: \0 (0x0) KeyState: 0x10 Down: 1 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x12 Down: 1 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x1a Down: 1 Repeat: 1 KeyCode: 0x32 ScanCode: 0x3 Char: \0 (0x0) KeyState: 0x1a Down: 0 Repeat: 1 KeyCode: 0x32 ScanCode: 0x3 Char: \0 (0x0) KeyState: 0x1a Down: 0 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x12 Down: 0 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x10 Down: 0 Repeat: 1 KeyCode: 0x10 ScanCode: 0x2a Char: \0 (0x0) KeyState: 0x0 ``` The interesting input here is `Down: 1 Repeat: 1 KeyCode: 0x32 ScanCode: 0x3 Char: \0 (0x0) KeyState: 0x1a` #### `master`, Conhost+conpty, <kbd>Ctrl+alt+space</kbd>, (i/o modes):`(0x 3f7, 0x f)` (VT input) ``` Down: 1 Repeat: 1 KeyCode: 0x10 ScanCode: 0x2a Char: \0 (0x0) KeyState: 0x10 Down: 1 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x12 Down: 1 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x1a Down: 1 Repeat: 1 KeyCode: 0x32 ScanCode: 0x0 Char: \0 (0x0) KeyState: 0x1a Down: 0 Repeat: 1 KeyCode: 0x32 ScanCode: 0x3 Char: \0 (0x0) KeyState: 0x1a Down: 0 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x12 Down: 0 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x10 Down: 0 Repeat: 1 KeyCode: 0x10 ScanCode: 0x2a Char: \0 (0x0) KeyState: 0x0 ``` #### `master`, Conhost, <kbd>Ctrl+alt+space</kbd>, (i/o modes):`(0x 1f7, 0x f)` (Win32 input) ``` Down: 1 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x28 Down: 1 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x2a Down: 1 Repeat: 1 KeyCode: 0x20 ScanCode: 0x39 Char: \0 (0x0) KeyState: 0x2a Down: 0 Repeat: 1 KeyCode: 0x20 ScanCode: 0x39 Char: \0 (0x0) KeyState: 0x2a Down: 0 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x22 Down: 0 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x20 ``` #### `master`, Conhost, <kbd>Ctrl+alt+space</kbd>, (i/o modes):`(0x 3f7, 0x f)` (VT input) ``` Down: 1 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x28 Down: 1 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x2a Down: 1 Repeat: 1 KeyCode: 0x0 ScanCode: 0x0 Char: ^[ (0x1b) KeyState: 0x0 Down: 1 Repeat: 1 KeyCode: 0x0 ScanCode: 0x0 Char: \0 (0x0) KeyState: 0x0 Down: 0 Repeat: 1 KeyCode: 0x20 ScanCode: 0x39 Char: \0 (0x0) KeyState: 0x2a Down: 0 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x22 Down: 0 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x20 ``` #### `master`, Conhost, <kbd>Ctrl+alt+shift+2</kbd>, (i/o modes):`(0x 1f7, 0x f)` (Win32 input) ``` Down: 1 Repeat: 1 KeyCode: 0x10 ScanCode: 0x2a Char: \0 (0x0) KeyState: 0x30 Down: 1 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x38 Down: 1 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x3a Down: 1 Repeat: 1 KeyCode: 0x32 ScanCode: 0x3 Char: \0 (0x0) KeyState: 0x3a Down: 0 Repeat: 1 KeyCode: 0x32 ScanCode: 0x3 Char: \0 (0x0) KeyState: 0x3a Down: 0 Repeat: 1 KeyCode: 0x10 ScanCode: 0x2a Char: \0 (0x0) KeyState: 0x20 ``` NOTE: this keystroke is what conpty is attempting to replicate
Author
Owner

@zadjii-msft commented on GitHub (Apr 1, 2020):

Okay I see what the problem is - I think it is the think @lhecker called out in #4192. Even in straight-up conhost, Ctrl+alt+shift+2 does not generate \x1b\x0.

IMO, @lhecker if you want to do the fix proposed in #4192, the ch != 0 fix, then I say go for it, and add this to the growing list of bugs that PR fixes 😆

I'll submit the other thing I found during the course of this investigation, but that will effectively hide the showkey -a repro case in the Terminal.

@zadjii-msft commented on GitHub (Apr 1, 2020): Okay I see what the problem is - I think it is the think @lhecker called out in #4192. Even in straight-up conhost, <kbd>Ctrl+alt+shift+2</kbd> does not generate `\x1b\x0`. IMO, @lhecker if you want to do the fix proposed in #4192, the `ch != 0` fix, then I say go for it, and add this to the growing list of bugs that PR fixes 😆 I'll submit the other thing I found during the course of this investigation, but that will effectively hide the `showkey -a` repro case in the Terminal.
Author
Owner

@lhecker commented on GitHub (Apr 1, 2020):

@zadjii-msft I believe we're misunderstanding each other.

If I press Ctrl+Alt+Space or Ctrl+Alt+Shift+2, showkey -a will show the following output:

Ctrl+Alt condition showkey -a output
ch >= 0x40 && ch < 0x7F ^@
ch != 0 ^[^R

But as far as I can see it should be ^[^@, right?
Or am I misunderstanding you and ^[^R is actually correct? (...because, as you mentioned above, there's another bug involved here, which distorts the showkey output?)

That said, would you'd like me to modify #4192 to use a ch != 0 condition?
I can't decide by myself if I should. 😄

@lhecker commented on GitHub (Apr 1, 2020): @zadjii-msft I believe we're misunderstanding each other. If I press <kbd>Ctrl+Alt+Space</kbd> or <kbd>Ctrl+Alt+Shift+2</kbd>, `showkey -a` will show the following output: [`Ctrl+Alt` condition](https://github.com/microsoft/terminal/pull/4192/files#diff-3895074b0184ca0d36ef271db540a282R510) | `showkey -a` output -|- `ch >= 0x40 && ch < 0x7F` | `^@` `ch != 0` | `^[^R` But as far as I can see it should be `^[^@`, right? Or am I misunderstanding you and `^[^R` is actually correct? (...because, as you mentioned above, there's another bug involved here, which distorts the showkey output?) That said, would you'd like me to modify #4192 to use a `ch != 0` condition? I can't decide by myself if I should. :smile:
Author
Owner

@zadjii-msft commented on GitHub (Apr 1, 2020):

Oh jeez, you should be seeing ^[^@. Now I'm not sure what the right fix is.

@zadjii-msft commented on GitHub (Apr 1, 2020): Oh jeez, you should be seeing `^[^@`. Now I'm not sure what the right fix is.
Author
Owner

@lhecker commented on GitHub (Apr 1, 2020):

I figured it out. I didn't realize I had to manually attach myself to OpenConsole.exe for debugging. 🙈
You basically already said all the right things (especially steps 2 and 3 above), but I didn't understand you, because I've been lacking the knowledge how all the code works together.

Anyways... I fixed the problem. (lol)
Turns out if you use MapVirtualKeyW you're probably doing something wrong. I guess? 😄

I replaced this:
713550d56c/src/terminal/input/terminalInput.cpp (L488)

with a call to ToUnicodeEx, similar to Terminal::_CharacterFromKeyEvent.
This call allows us to reliably turn the input event Alt+Ctrl+Shift+2 into an Alt+Ctrl+@ event, which then gets translated to Alt+0x00 and sent away. 🙂

Would you like me to add it to #4192 or open a separate PR afterwards?
I believe it makes sense to fix it separately.

@lhecker commented on GitHub (Apr 1, 2020): I figured it out. I didn't realize I had to manually attach myself to OpenConsole.exe for debugging. 🙈 You basically already said all the right things (especially steps 2 and 3 above), but I didn't understand you, because I've been lacking the knowledge how all the code works together. Anyways... I fixed the problem. (lol) Turns out if you use `MapVirtualKeyW` you're probably doing something wrong. I guess? 😄 I replaced this: https://github.com/microsoft/terminal/blob/713550d56cbfbbdac3d2c27d2e88d89773f46f71/src/terminal/input/terminalInput.cpp#L488 with a call to `ToUnicodeEx`, similar to `Terminal::_CharacterFromKeyEvent`. This call allows us to reliably turn the input event <kbd>Alt+Ctrl+Shift+2</kbd> into an <kbd>Alt+Ctrl+@</kbd> event, which then gets translated to `Alt+0x00` and sent away. 🙂 Would you like me to add it to #4192 or open a separate PR afterwards? I believe it makes sense to fix it separately.
Author
Owner

@zadjii-msft commented on GitHub (Apr 1, 2020):

I didn't realize I had to manually attach myself to OpenConsole.exe for debugging.

Literally my least favorite thing about using VS. JUST ATTACH TO CHILD PROCESSES. Or at least give me an option to auto-attach, sheesh.

Turns out if you use MapVirtualKeyW you're probably doing something wrong

You're sure right about that 😄 Glad you got that figured out!

Would you like me to add it to #4192 or open a separate PR afterwards?

Let's open another PR, since this has gotten bigger than a oneline fix ☺️

@zadjii-msft commented on GitHub (Apr 1, 2020): > I didn't realize I had to manually attach myself to OpenConsole.exe for debugging. Literally my least favorite thing about using VS. JUST ATTACH TO CHILD PROCESSES. Or at least give me an option to auto-attach, sheesh. > Turns out if you use `MapVirtualKeyW` you're probably doing something wrong You're sure right about that 😄 Glad you got that figured out! > Would you like me to add it to #4192 or open a separate PR afterwards? Let's open another PR, since this has gotten bigger than a oneline fix ☺️
Author
Owner

@lhecker commented on GitHub (Apr 1, 2020):

@zadjii-msft 🤔🤔🤔 https://marketplace.visualstudio.com/items?itemName=vsdbgplat.MicrosoftChildProcessDebuggingPowerTool

@lhecker commented on GitHub (Apr 1, 2020): @zadjii-msft 🤔🤔🤔 https://marketplace.visualstudio.com/items?itemName=vsdbgplat.MicrosoftChildProcessDebuggingPowerTool
Author
Owner

@zadjii-msft commented on GitHub (Apr 1, 2020):

MY HERO

@zadjii-msft commented on GitHub (Apr 1, 2020): MY HERO
Author
Owner

@DHowett-MSFT commented on GitHub (Apr 1, 2020):

Time to e-mail the entire team this finding

@DHowett-MSFT commented on GitHub (Apr 1, 2020): Time to e-mail the entire team this finding
Author
Owner

@DHowett-MSFT commented on GitHub (Apr 3, 2020):

Milestoning this for 1.x and taking off Triage

@DHowett-MSFT commented on GitHub (Apr 3, 2020): Milestoning this for 1.x and taking off Triage
Author
Owner

@ghost commented on GitHub (Mar 1, 2021):

:tada:This issue was addressed in #5272, which has now been successfully released as Windows Terminal Preview v1.7.572.0.🎉

Handy links:

@ghost commented on GitHub (Mar 1, 2021): :tada:This issue was addressed in #5272, which has now been successfully released as `Windows Terminal Preview v1.7.572.0`.:tada: Handy links: * [Release Notes](https://github.com/microsoft/terminal/releases/tag/v1.7.572.0) * [Store Download](https://www.microsoft.com/store/apps/9n8g5rfz9xk3?cid=storebadge&ocid=badge)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#7247