简体   繁体   中英

WM_INPUT is defective

I want to catch the key presses send to my window in order to store each key's state, and distinguish between the left and right instances of the key, like WM_LCONTROL and WM_RCONTROL .

As WM_KEYUP/DOWN doesn't offer this functionality so i moved to raw input and i need to process WM_INPUT .

The problem is that the flags from the RAWKEYBOARD structure, described here , don't work as they should.

When i press right control key, RI_KEY_E0 is set which is for the left version of the key, and when i press left control, neither RI_KEY_E0 nor RI_KEY_E1 are set. The alt key performs similarly, but i can workaround this problem, since left still provides different flags form the right key.

But for the left and right shift keys there aren't any flags set to distinguish the left version from the right version, so i can't do anything about them.

The virtual key code given when WM_INPUT arrives doesn't distinguish between left and right keys, and using MapVirtualKey on the scan code doesn't work either.

What's up with this unorthodox behavior of raw inputs?

Some code:

Registering:

RAWINPUTDEVICE rid;
rid.usUsagePage=0x01;
rid.usUsage=0x06;
rid.dwFlags=0; // I also tried RIDEV_APPKEYS,RIDEV_NOHOTKEYS,RIDEV_NOLEGACY, none fixed the problem
rid.hwndTarget=hwnd;
if(!RegisterRawInputDevices(&rid,1,sizeof(RAWINPUTDEVICE)))
    ExitError("Failed to register raw input device",true); //displays error and exits

Processing WM_INPUT:

case WM_INPUT:
{    
    RAWINPUT rw;
    UINT sz=sizeof(rw);
    u_char vk; //used to make code shorter
    USHORT flag; //used to make code shorter

    if(!GetRawInputData((HRAWINPUT)lparam,RID_INPUT,&rw,&sz,sizeof(RAWINPUTHEADER)))
        break;

    vk=rw.data.keyboard.VKey;
    flag=rw.data.keyboard.Flags;

    ...Process flags and save the actual key pressed in vk...

    keys[vk]=!(flag&RI_KEY_BREAK); //save key's state
    break;
}

WM_KEYDOWN/-UP does deliver the info you want ,you just have to read the decription in MSDN.

check bit 24 of the lParam parameter.

WM_KEYDOWN and WM_KEYUP send you VK_CONTROL in wParam when either control key is pressed. To find out whether or not the right-hand control key is pressed, read bit 24 of lParam , as described in the MSDN documentation . Bit 24 is described like this:

Indicates whether the key is an extended key, such as the right-hand ALT and CTRL keys that appear on an enhanced 101- or 102-key keyboard. The value is 1 if it is an extended key; otherwise, it is 0.

The documentation for RAWKEYBORAD is wrong. The constants RI_KEY_E0 and RI_KEY_E1 do not apply to left or right versions of the keys; instead they apply to scancode extensions.

The virtual-key values that are dumped into RAWKEYBOARD::VKey refer to the general purpose values like VK_SHIFT , VK_CONTROL , or VK_MENU .

To correct this it is possible to makes use of the RAWKEYBOARD::VKey and RAWKEYBOARD::MakeCode members. Different keys differentiate between different sides in different ways:

  • For the shift keys reassign using MapVirtualKey and passing RAWKEYBOARD::MakeCode and MAPVK_VSC_TO_VK_EX as parameters, and
  • For both the control and alt keys map based on the RI_KEY_E0 flag (like key.VKey = key.Flags & RI_KEY_E0 ? VK_RCONTROL : VK_LCONTROL for example).

The values you want from the numpad are depend on whether you treat NUMLK as being on or off.

I had the same confusion as you and I'm still learning about how keyboards transfer data but I'm finding this blog post on Molecular Musings particularly helpful. The reasoning behind this method can be found in this post.

You can distinguish left-right SHIFT/CONTROL/ALT VK codes like this:

 case WM_INPUT:
{
    HRAWINPUT dataHandle = reinterpret_cast<HRAWINPUT>(lParam);

    RAWINPUT input;
    UINT size = sizeof(input);

    ::GetRawInputData(dataHandle, RID_INPUT, &input, &size, sizeof(RAWINPUTHEADER));

    if (input.header.dwType != RIM_TYPEKEYBOARD)
        break;

    const RAWKEYBOARD& keyboard = input.data.keyboard;

    // Ignore key overrun state
    if (keyboard.MakeCode == KEYBOARD_OVERRUN_MAKE_CODE)
        return;

    // Ignore keys not mapped to any VK code
    // This effectively filters out scan code pre/postfix for some keys like PrintScreen.
    if (keyboard.VKey >= 0xff/*VK__none_*/)
        return;

    uint16_t scanCode = keyboard.MakeCode;

    // Scan codes could contain 0xe0 or 0xe1 one-byte prefix.
    // See https://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf
    scanCode |= (keyboard.Flags & RI_KEY_E0) ? 0xe000 : 0;
    scanCode |= (keyboard.Flags & RI_KEY_E1) ? 0xe100 : 0;

    uint16_t vkCode = keyboard.VKey;
    switch (vkCode)
    {
    case VK_SHIFT:   // -> VK_LSHIFT or VK_RSHIFT
    case VK_CONTROL: // -> VK_LCONTROL or VK_RCONTROL
    case VK_MENU:    // -> VK_LMENU or VK_RMENU
        vkCode = LOWORD(MapVirtualKeyW(scanCode, MAPVK_VSC_TO_VK_EX));
        break;
    }

    //...

    return 0;
}

This code should work at least from Vista.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM