Получение измененного кода ключа (char) при нажатии клавиш-модификаторов

#c #windows #input

#c #Windows #ввод

Вопрос:

Я пытаюсь реализовать ввод с клавиатуры на C, и я не понимаю, как захватить измененные коды клавиш и получить из него символ. Конечно, я могу создать огромный оператор switch, подобный этому:

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

Это было бы нормально для одного языка, но реализация этого для разных раскладок клавиатуры была бы кошмаром.

Я нашел простой способ преобразовать виртуальную клавишу в символ, соответствующий активной раскладке клавиатуры:

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

Есть ли способ получить измененный символ аналогичным образом?
Или как бы это сделать?

Комментарии:

1. Что именно вы подразумеваете под "modified keycode" and "modified char" ? Вы хотите сопоставить определенные виртуальные ключевые коды с определенными символами? Что не так с вашим решением использования MapVirtualKeyEx ? Разве это не делает именно то, что вы хотите?

2. @AndreasWenzel В основном MapVirtualKeyEx принимает виртуальный ключ и возвращает символ в зависимости от того, какая у вас раскладка клавиатуры. Но это может не учитываться, если вы удерживаете клавишу-модификатор нажатой, например, клавишу alt, для получения, скажем, «* «.

3. Вы говорите, что вам нужно нажать "alt key" , чтобы произвести * ? Вы имеете в виду SHIFT ключ?

4. @AndreasWenzel Да, извините 🙂

Ответ №1:

Если вы вызываете TranslateMessage цикл сообщений, эта функция приведет к созданию дополнительного WM_CHAR сообщения при получении WM_KEYDOWN сообщения. Это WM_CHAR сообщение будет содержать введенный символ с учетом того, нажата ли SHIFTклавиша. Например, если пользователь вводит a b , удерживая SHIFTклавишу, то WM_CHAR сообщение будет содержать код символа для a B .

Как указано выше, TranslateMessage сделает всю работу за вас. Если по какой-либо причине вы не хотите использовать эту функцию, вы можете попробовать использовать функции ToAscii или ToUnicode вместо этого. Но, как указано в документации, у этих функций есть недостаток, заключающийся в том, что они не могут правильно обрабатывать мертвые клавиши. Поэтому я рекомендую использовать TranslateMessage .

Комментарии:

1. Хорошо, прямо сейчас я использую WndProc и WM_INPUT для получения необработанного ввода с клавиатуры, потому что я использую ввод клавиш и для простой игры. Есть ли способ вместо прослушивания WM_CHAR напрямую запрашивать WM_CHAR, когда я получаю сообщение WM_INPUT?

2. Я думаю, вы указали мне правильное направление. Я изучу функцию «ToUnicode».

3. @objectnabb: Я считаю, что для простой игры нет необходимости использовать необработанный ввод с клавиатуры. Должно быть проще использовать WM_KEYDOWN и WM_KEYUP , хотя вам также может потребоваться обработать WM_SYSKEYDOWN и WM_SYSKEYUP . Функция TranslateMessage создаст дополнительное WM_CHAR сообщение. Если вам действительно нужен необработанный ввод с клавиатуры, я считаю, что его можно обрабатывать WM_INPUT в дополнение ко всем другим упомянутым сообщениям, но это может привести к беспорядку, и я сомневаюсь, что это будет лучшим решением.

Ответ №2:

Благодаря некоторым указателям Андреаса Венцеля в комментариях я понял, как добиться того, чего я хотел. В принципе, мы хотим использовать ToUnicodeEx . В нем есть некоторые оговорки, и я бы рекомендовал ознакомиться с документацией.

Для идентификации языка ToUnicodeEx требуется входной идентификатор локали (HKL), который можно получить с помощью функции GetKeyboardLayout .

На случай, если кто-то захочет сделать что-то подобное, я оставлю пример.

 if(GetRawInputData((HRAWINPUT)lParam, RID_INPUT, buffer, amp;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 amp; 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);
    }
}