Getting modified keycode(char) when pressing modifier keys

Viewed 183

I'm trying to implement keyboard input in C and I don't understand how to capture modified keycodes and get a character from it. Sure, I can create a huge switch statement like this:

// if (shift is pressed) {
switch (vkCode)
{
    case 0x30:
        log(")");
        break;
    case 0x31:
        log("!");
        break;
     case 0x32:
     //...

This would be ok for one language, but implementing this for different keyboard layouts would be a nightmare.

I found a simple way to convert a virtual key to a character that corresponds to the active keyboard layout:

HKL lang = GetLang();
UINT ck = MapVirtualKeyEx(vKey, MAPVK_VK_TO_CHAR, lang);
printf("%c", ck);

Is there a way to get the modified char in a similar way?
Or how would one go about this?

2 Answers

If you call TranslateMessage in your message loop, this function will cause an additional WM_CHAR message to be created when a WM_KEYDOWN message is received. This WM_CHAR message will contain the typed character, taking into account whether the SHIFT key is pressed. For example, if the user types a b will holding the SHIFT key, then the WM_CHAR message will contain the character code for a B.

As stated above, TranslateMessage will do all the work for you. If, for some reason, you do not want to use this function, you could try using the functions ToAscii or ToUnicode instead. But, as stated in the documentation, these functions have the disadvantage that they are unable to handle dead keys properly. Therefore, I recommend using TranslateMessage.

Thanks to some pointers from Andreas Wenzel in the comments I figured out how to achieve what I wanted. Basically, we want to use ToUnicodeEx. It has some caveats and I would recommend reading the documentation.

To identify the language ToUnicodeEx takes an input locale identifier(HKL) which can be retrieved with GetKeyboardLayout function.

In case someone wants to do something similar I will leave an example.

if(GetRawInputData((HRAWINPUT)lParam, RID_INPUT, buffer, &dwSize, sizeof(RAWINPUTHEADER)) == dwSize)
{
    RAWINPUT* raw = (RAWINPUT*)buffer;
    if (raw->header.dwType == RIM_TYPEKEYBOARD)
    {
        const RAWKEYBOARD rk = raw->data.keyboard;
        USHORT MakeCode = rk.Message; // scanCode
        USHORT Flags    = rk.Flags;
        USHORT VKey     = rk.VKey;    // virtual Key
        UINT   Message  = rk.Message;
        ULONG  ExtraInf = rk.ExtraInformation;

        if ((Flags & RI_KEY_BREAK) != 0)
            break;

        HKL lang = GetLang();

        CHAR c[H_KEY_SIZE];
        ZeroMemory(c, H_KEY_SIZE);
        
        BYTE lpKeyState[256];
        GetKeyboardState(lpKeyState);
    
        WCHAR pwszBuff[12];
        int res = ToUnicodeEx(VKey, MakeCode, lpKeyState, pwszBuff, 12, 0, lang);
        if (res == 0)
        {
            // The specified virtual key has no translation for the current state of the keyboard.
            // Nothing was written to the buffer specified by pwszBuff.
            // Handle this if you want
        }
    
        strcat_s(c, H_KEY_SIZE, (CONST PCHAR)pwszBuff);
        printf("output: %s ", c);
    }
}

Related