I would like to enumerate the list of all possible key combinations supported by the current keyboard layout (with virtual key codes, scan codes and their Unicode values). To map the remote user inputs to keys in order to simulate them.
I was expecting an API like UCKeyTranslate(ObjectiveC) for VC++ that can accept the virtual key codes and modifiers(ALT, SHIFT, CTRL) and provide me the scan codes, but couldn't find anything similar to that.
After a lot of research and spending 2 whole days, I had no other option to go with except for MapVirtualKeyEx.
I came with the following code but that has a lot of problems,
BOOL PopulateKeyMap()
{
TCHAR Buff[120 * sizeof(TCHAR)] = { 0 };
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, Buff, sizeof(Buff));
HKL hKeyboardLayout = ::LoadKeyboardLayout(Buff, KLF_ACTIVATE);
{
lock_guard<recursive_mutex> lockHolder(cs_populate_key);
if (hCurrentKeyboardLayout)
{
UnloadKeyboardLayout(hCurrentKeyboardLayout);
}
hCurrentKeyboardLayout = hKeyboardLayout;
// Prepopulate keyCodeDictionary with common key combinations.
for (int keyIndex = 0; keyIndex < KEY_CODES_DICT_SIZE; ++keyIndex)
{
{
unsigned int Vk;
tstring key_name = GetKeyName(keyIndex, Vk);
if (key_name.compare(_T("")) != 0)
{
SmartPtr<KeyCodeInfo> key = (new KeyCodeInfo);
if (key)
{
key->nIndex = keyIndex;
key->sVKCode = keyIndex;
key->nScanCode = Vk;
keyboard_map[key_name] = key;
}
}// End if
}
}// End for
bKeyMapInitialized = TRUE;
}
return TRUE;
}
tstring GetKeyName(unsigned int virtualKey, unsigned int &scanCode)
{
if (!hCurrentKeyboardLayout)
{
PopulateKeyMap();
}
scanCode = MapVirtualKeyEx(virtualKey, MAPVK_VK_TO_VSC, hCurrentKeyboardLayout);
// because MapVirtualKey strips the extended bit for some keys
switch (virtualKey)
{
case VK_LEFT: case VK_UP: case VK_RIGHT: case VK_DOWN: // arrow keys
case VK_PRIOR: case VK_NEXT: // page up and page down
case VK_END: case VK_HOME:
case VK_INSERT: case VK_DELETE:
case VK_DIVIDE: // numpad slash
case VK_NUMLOCK:
{
scanCode |= KF_EXTENDED; // set extended bit
break;
}
}
TCHAR keyName[256];
if (GetKeyNameText(scanCode << 16, keyName, sizeof(keyName)) != 0)
{
return keyName;
}
else
{
return _T("");
}
}
The MapVirtualKeyEx provides me only the list of basic scan codes and not the scan codes of the keys with the combinations of the modifiers (ALT, CTRL, SHIFT). Is there any way I can provide the combinations of the modifiers as input to the function so that I can generate the required key combinations?.
Any help would be appreciated. Thanks in advance.
Finally got a solution for this, reference https://dxr.mozilla.org/mozilla-central/source/widget/windows/KeyboardLayout.cpp
void
FillKbdState(PBYTE aKbdState,
const ShiftState aShiftState)
{
if (aShiftState & STATE_SHIFT) {
aKbdState[VK_SHIFT] |= 0x80;
}
else {
aKbdState[VK_SHIFT] &= ~0x80;
aKbdState[VK_LSHIFT] &= ~0x80;
aKbdState[VK_RSHIFT] &= ~0x80;
}
if (aShiftState & STATE_CONTROL) {
aKbdState[VK_CONTROL] |= 0x80;
}
else {
aKbdState[VK_CONTROL] &= ~0x80;
aKbdState[VK_LCONTROL] &= ~0x80;
aKbdState[VK_RCONTROL] &= ~0x80;
}
if (aShiftState & STATE_ALT) {
aKbdState[VK_MENU] |= 0x80;
}
else {
aKbdState[VK_MENU] &= ~0x80;
aKbdState[VK_LMENU] &= ~0x80;
aKbdState[VK_RMENU] &= ~0x80;
}
if (aShiftState & STATE_CAPSLOCK) {
aKbdState[VK_CAPITAL] |= 0x01;
}
else {
aKbdState[VK_CAPITAL] &= ~0x01;
}
}
inline int32_t GetKeyIndex(uint8_t aVirtualKey)
{
// Currently these 68 (NS_NUM_OF_KEYS) virtual keys are assumed
// to produce visible representation:
// 0x20 - VK_SPACE ' '
// 0x30..0x39 '0'..'9'
// 0x41..0x5A 'A'..'Z'
// 0x60..0x69 '0'..'9' on numpad
// 0x6A - VK_MULTIPLY '*' on numpad
// 0x6B - VK_ADD '+' on numpad
// 0x6D - VK_SUBTRACT '-' on numpad
// 0x6E - VK_DECIMAL '.' on numpad
// 0x6F - VK_DIVIDE '/' on numpad
// 0x6E - VK_DECIMAL '.'
// 0xBA - VK_OEM_1 ';:' for US
// 0xBB - VK_OEM_PLUS '+' any country
// 0xBC - VK_OEM_COMMA ',' any country
// 0xBD - VK_OEM_MINUS '-' any country
// 0xBE - VK_OEM_PERIOD '.' any country
// 0xBF - VK_OEM_2 '/?' for US
// 0xC0 - VK_OEM_3 '`~' for US
// 0xC1 - VK_ABNT_C1 '/?' for Brazilian
// 0xC2 - VK_ABNT_C2 separator key on numpad (Brazilian or JIS for Mac)
// 0xDB - VK_OEM_4 '[{' for US
// 0xDC - VK_OEM_5 '\|' for US
// 0xDD - VK_OEM_6 ']}' for US
// 0xDE - VK_OEM_7 ''"' for US
// 0xDF - VK_OEM_8
// 0xE1 - no name
// 0xE2 - VK_OEM_102 '\_' for JIS
// 0xE3 - no name
// 0xE4 - no name
static const int8_t xlat[256] =
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
//-----------------------------------------------------------------------
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10
0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, // 30
-1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 40
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, -1, -1, // 50
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, -1, 49, 50, 51, // 60
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 70
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 90
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 53, 54, 55, 56, 57, // B0
58, 59, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 61, 62, 63, 64, 65, // D0
-1, 66, 67, 68, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // F0
};
return xlat[aVirtualKey];
}
void PopulateKeyMap(HKL aLayout)
{
BYTE kbdState[256];
memset(kbdState, 0, sizeof(kbdState));
BYTE originalKbdState[256];
// Bitfield with all shift states that have at least one dead-key.
uint16_t shiftStatesWithDeadKeys = 0;
// Bitfield with all shift states that produce any possible dead-key base
// characters.
uint16_t shiftStatesWithBaseChars = 0;
::GetKeyboardState(originalKbdState);
int index = 0;
// For each shift state gather all printable characters that are produced
// for normal case when no any dead-key is active.
for (ShiftState shiftState = 0; shiftState < 16; shiftState++)
{
FillKbdState(kbdState, shiftState);
for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++)
{
int32_t vki = GetKeyIndex(virtualKey);
if (vki < 0)
{
continue;
}
wchar_t uniChars[5];
int32_t ret =
::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)uniChars,
ArrayLength(uniChars), 0, hCurrentKeyboardLayout);
// neither a dead-key nor there is no translation
if (ret > 0)
{
if (ret == 1)
{
// dead-key can pair only with exactly one base character.
shiftStatesWithBaseChars |= (1 << shiftState);
}
{
index++;
uniChars[ret] = '\0';
CString key_name(uniChars);
unsigned int scanCode = MapVirtualKeyEx(virtualKey, MAPVK_VK_TO_VSC, hCurrentKeyboardLayout);
// because MapVirtualKey strips the extended bit for some keys
switch (virtualKey)
{
case VK_LEFT: case VK_UP: case VK_RIGHT: case VK_DOWN: // arrow keys
case VK_PRIOR: case VK_NEXT: // page up and page down
case VK_END: case VK_HOME:
case VK_INSERT: case VK_DELETE:
case VK_DIVIDE: // numpad slash
case VK_NUMLOCK:
{
scanCode |= KF_EXTENDED; // set extended bit
break;
}
}
if (false == key_name.IsEmpty())
{
SmartPtr<KeyCodeInfo> key = (new KeyCodeInfo)->template DetachObject<KeyCodeInfo>();
if (key)
{
key->nIndex = index;
key->sVKCode = virtualKey;
key->nScanCode = scanCode;
keyboard_map[tstring(key_name.GetBuffer())] = key;
}
}// End if
}
}
}
}
::SetKeyboardState(originalKbdState);
}