Win32 input make/break events are generated at the same time #11632

Closed
opened 2026-01-31 02:53:03 +00:00 by claunia · 6 comments
Owner

Originally created by @o-sdn-o on GitHub (Nov 29, 2020).

Environment

Windows build number: 10.0.19041.572
Windows Terminal version (if applicable): master

In the classic windows console, you can track alphanumeric key combinations (for example, tracking the keys w, a, s, d simultaneously). This tracking cannot be done in Windows Terminal.

Steps to reproduce

Run the following code
Press a couple of alphanumeric keys at the same time, e.g. q+w+e

#include <iostream>
#include <unordered_set>
#include <Windows.h>

int main()
{
    DWORD                    Count;
    INPUT_RECORD             Reply;
    std::unordered_set<WORD> State;

    auto Input = GetStdHandle(STD_INPUT_HANDLE);

    while (WAIT_OBJECT_0 == WaitForSingleObject(Input, INFINITE))
    {
        ReadConsoleInput(Input, &Reply, 1, &Count);
        switch (Reply.EventType)
        {
            case KEY_EVENT:
            {
                //std::cout
                //    << " Down: "     << Reply.Event.KeyEvent.bKeyDown
                //    << " Repeat: "   << Reply.Event.KeyEvent.wRepeatCount
                //    << " KeyCode: "  << Reply.Event.KeyEvent.wVirtualKeyCode
                //    << " ScanCode: " << Reply.Event.KeyEvent.wVirtualScanCode
                //    << " Char: "     << Reply.Event.KeyEvent.uChar.UnicodeChar
                //    << " KeyState: " << Reply.Event.KeyEvent.dwControlKeyState
                //    << std::endl << std::flush;

                auto ScanCode = Reply.Event.KeyEvent.wVirtualScanCode;
                auto KeyDown = Reply.Event.KeyEvent.bKeyDown;
                auto Changed = KeyDown ? State.emplace(ScanCode).second
                                       : State.erase(ScanCode);
                if (Changed)
                {
                    std::cout << "chord:";
                    for (auto Key : State) std::cout << " " << Key;
                    std::cout << std::endl << std::flush;
                }
                break;
            }
            default:
                break;
        }
    }
}

Expected behavior

Key combinations are tracked (classic console behavior)

D:\sources\temp\ConsoleApplication1\Debug>ConsoleApplication1.exe
chord: 16
chord: 16 17
chord: 16 17 18
chord: 16 17
chord: 16
chord:
chord: 16
chord: 16 17
chord: 16 17 18
chord: 17 18
chord: 18
chord:

Actual behavior

Single keystrokes are tracked only (Windows Terminal behavior)

D:\sources\temp\ConsoleApplication1\Debug>ConsoleApplication1.exe
chord: 16
chord:
chord: 17
chord:
chord: 18
chord:
chord: 18
chord:
chord: 18
chord:
chord: 18
chord:
chord: 18
chord:
...
Originally created by @o-sdn-o on GitHub (Nov 29, 2020). <!-- 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨 I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING: 1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement. 2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement. 3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number). 4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement. 5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement. All good? Then proceed! --> <!-- This bug tracker is monitored by Windows Terminal development team and other technical folks. **Important: When reporting BSODs or security issues, DO NOT attach memory dumps, logs, or traces to Github issues**. Instead, send dumps/traces to secure@microsoft.com, referencing this GitHub issue. If this is an application crash, please also provide a Feedback Hub submission link so we can find your diagnostic data on the backend. Use the category "Apps > Windows Terminal (Preview)" and choose "Share My Feedback" after submission to get the link. Please use this form and describe your issue, concisely but precisely, with as much detail as possible. --> # Environment ```none Windows build number: 10.0.19041.572 Windows Terminal version (if applicable): master ``` In the classic windows console, you can track alphanumeric key combinations (for example, tracking the keys <kbd>w</kbd>, <kbd>a</kbd>, <kbd>s</kbd>, <kbd>d</kbd> simultaneously). This tracking cannot be done in Windows Terminal. # Steps to reproduce Run the following code Press a couple of alphanumeric keys at the same time, e.g. <kbd>q</kbd>+<kbd>w</kbd>+<kbd>e</kbd> ```c++ #include <iostream> #include <unordered_set> #include <Windows.h> int main() { DWORD Count; INPUT_RECORD Reply; std::unordered_set<WORD> State; auto Input = GetStdHandle(STD_INPUT_HANDLE); while (WAIT_OBJECT_0 == WaitForSingleObject(Input, INFINITE)) { ReadConsoleInput(Input, &Reply, 1, &Count); switch (Reply.EventType) { case KEY_EVENT: { //std::cout // << " Down: " << Reply.Event.KeyEvent.bKeyDown // << " Repeat: " << Reply.Event.KeyEvent.wRepeatCount // << " KeyCode: " << Reply.Event.KeyEvent.wVirtualKeyCode // << " ScanCode: " << Reply.Event.KeyEvent.wVirtualScanCode // << " Char: " << Reply.Event.KeyEvent.uChar.UnicodeChar // << " KeyState: " << Reply.Event.KeyEvent.dwControlKeyState // << std::endl << std::flush; auto ScanCode = Reply.Event.KeyEvent.wVirtualScanCode; auto KeyDown = Reply.Event.KeyEvent.bKeyDown; auto Changed = KeyDown ? State.emplace(ScanCode).second : State.erase(ScanCode); if (Changed) { std::cout << "chord:"; for (auto Key : State) std::cout << " " << Key; std::cout << std::endl << std::flush; } break; } default: break; } } } ``` # Expected behavior Key combinations are tracked (classic console behavior) ``` D:\sources\temp\ConsoleApplication1\Debug>ConsoleApplication1.exe chord: 16 chord: 16 17 chord: 16 17 18 chord: 16 17 chord: 16 chord: chord: 16 chord: 16 17 chord: 16 17 18 chord: 17 18 chord: 18 chord: ``` # Actual behavior Single keystrokes are tracked only (Windows Terminal behavior) ``` D:\sources\temp\ConsoleApplication1\Debug>ConsoleApplication1.exe chord: 16 chord: chord: 17 chord: chord: 18 chord: chord: 18 chord: chord: 18 chord: chord: 18 chord: chord: 18 chord: ... ```
claunia added the Issue-BugIssue-TaskIn-PRArea-InputNeeds-Tag-FixProduct-Terminal labels 2026-01-31 02:53:03 +00:00
Author
Owner

@zadjii-msft commented on GitHub (Nov 30, 2020):

Yep, this is unfortunately true. Since the Terminal is using the UWP XAML Input stack, we only get three types of input events: key downs, key ups, and character received events. Since we don't get full unicode character input from just the key down/ups, we have to rely on the character events for that. But there's no character up/down events, only received. So we've got to send a fake key down/up pair immediately. I'm sure if there was a way to avoid doing this, @lhecker would have thought of it by now 😜

For the sake of linking: #4999 dealt with a lot of similar issues. At least conpty is now capable of theoretically handling this, if only the Terminal was.

@zadjii-msft commented on GitHub (Nov 30, 2020): Yep, this is unfortunately true. Since the Terminal is using the UWP XAML Input stack, we only get three types of input events: key _downs_, key _ups_, and character _received_ events. Since we don't get full unicode character input from just the key down/ups, we have to rely on the character events for that. But there's no character up/down events, only _received_. So we've got to send a fake key down/up pair immediately. I'm sure if there was a way to avoid doing this, @lhecker would have thought of it by now 😜 For the sake of linking: #4999 dealt with a lot of similar issues. At least conpty is now capable of _theoretically_ handling this, if only the Terminal was.
Author
Owner

@lhecker commented on GitHub (Nov 30, 2020):

We could integrate the UWP based WT more deeply with our Win32 entrypoint.
That would allow us to use something like our old approach documented here.

@lhecker commented on GitHub (Nov 30, 2020): We could integrate the UWP based WT more deeply with our Win32 entrypoint. That would allow us to use something like our old approach documented [here](https://github.com/microsoft/terminal/blob/ae550e0969595f062b15c2ff5cc33d4afe8ebc3f/src/interactivity/win32/consoleKeyInfo.hpp#L9-L18).
Author
Owner

@DHowett commented on GitHub (Dec 3, 2020):

I've renamed this and iceboxed it. There's some ways in which we'll want to be compatible with the console, and a great many in which we will not.

This one requires enough work (and subversion of the "modern" input stack) that I would bin it into the "not" category, but... that's me.

@DHowett commented on GitHub (Dec 3, 2020): I've renamed this and iceboxed it. There's some ways in which we'll want to be compatible with the console, and a great many in which we will not. This one requires enough work (and subversion of the "modern" input stack) that I would bin it into the "not" category, but... that's me.
Author
Owner

@j4james commented on GitHub (Sep 1, 2022):

I've just run into this issue while trying to get certain VT input modes to work in Windows Terminal, and I'm trying to work out what the actual problem is here. I get that the key down event doesn't include character data, but we've already got code in place that can generate that character data automatically (see the _CharacterFromKeyEvent method). Are there known cases where that doesn't work correctly?

But lets assume the _CharacterFromKeyEvent method isn't good enough. Couldn't we at least use the CharacterReceived handler to generate the key down events (as we're doing now), but then still use the KeyUp handler for the corresponding key up events? There may then be edge cases where the character data could be incorrect for the key up, but would that actually affect anyone? Because a typical WM_KEYUP message wouldn't include that info anyway.

And if that really is still a concern, another approach we could take would be to save the most recent character in the CharacterReceived handler (just store it in an array mapped to the scan code), and then the KeyUp handler could easily look up the corresponding character data for a particular scan code when the key is released. Even with multiple keys pressed simultaneously I would think the scan code should suffice as a unique id.

Are there more factors at play here that I'm overlooking? Or does anyone have suggestions for sample keystrokes I should be testing that would be expected to fail? Because everything I've tried so far seemed to work OK (or at least no worse than the current implementation).

@j4james commented on GitHub (Sep 1, 2022): I've just run into this issue while trying to get certain VT input modes to work in Windows Terminal, and I'm trying to work out what the actual problem is here. I get that the key down event doesn't include character data, but we've already got code in place that can generate that character data automatically (see the `_CharacterFromKeyEvent` method). Are there known cases where that doesn't work correctly? But lets assume the `_CharacterFromKeyEvent` method isn't good enough. Couldn't we at least use the `CharacterReceived` handler to generate the key down events (as we're doing now), but then still use the `KeyUp` handler for the corresponding key up events? There may then be edge cases where the character data could be incorrect for the key up, but would that actually affect anyone? Because a typical `WM_KEYUP` message wouldn't include that info anyway. And if that really is still a concern, another approach we could take would be to save the most recent character in the `CharacterReceived` handler (just store it in an array mapped to the scan code), and then the `KeyUp` handler could easily look up the corresponding character data for a particular scan code when the key is released. Even with multiple keys pressed simultaneously I would think the scan code should suffice as a unique id. Are there more factors at play here that I'm overlooking? Or does anyone have suggestions for sample keystrokes I should be testing that would be expected to fail? Because everything I've tried so far seemed to work OK (or at least no worse than the current implementation).
Author
Owner

@DHowett commented on GitHub (Apr 6, 2023):

Are there more factors at play here that I'm overlooking?

Probably there aren't -- I think you've by this point investigated it deeper than the rest of us in recent history. We just got another dupe for it, and I am wondering if there's something we can do about it...

While we're breaking input, and all. 😁

@DHowett commented on GitHub (Apr 6, 2023): > Are there more factors at play here that I'm overlooking? Probably there aren't -- I think you've by this point investigated it deeper than the rest of us in recent history. We just got another dupe for it, and I _am_ wondering if there's something we can do about it... While we're breaking input, and all. 😁
Author
Owner

@j4james commented on GitHub (Apr 6, 2023):

I've got an old branch with my initial simple proposal (relying on _CharacterFromKeyEvent to generate the character data), which I can turn into a PR tomorrow if you'd like. It's only a couple of lines, so it's not a big deal if we need to drop it in favor of something more complicated.

@j4james commented on GitHub (Apr 6, 2023): I've got an old branch with my initial simple proposal (relying on `_CharacterFromKeyEvent` to generate the character data), which I can turn into a PR tomorrow if you'd like. It's only a couple of lines, so it's not a big deal if we need to drop it in favor of something more complicated.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#11632