keyboard_layout.cpp
1 #include "pch.h" 2 #include <array> 3 #include <algorithm> 4 5 #include "keyboard_layout_impl.h" 6 #include "shared_constants.h" 7 8 constexpr DWORD numpadOriginBit = 1ull << 31; 9 10 LayoutMap::LayoutMap() : 11 impl(new LayoutMap::LayoutMapImpl()) 12 { 13 } 14 15 LayoutMap::~LayoutMap() 16 { 17 delete impl; 18 } 19 20 void LayoutMap::UpdateLayout() 21 { 22 impl->UpdateLayout(); 23 } 24 25 std::wstring LayoutMap::GetKeyName(DWORD key) 26 { 27 return impl->GetKeyName(key); 28 } 29 30 DWORD LayoutMap::GetKeyFromName(const std::wstring& name) 31 { 32 auto list = impl->GetKeyNameList(false); 33 for (const auto& [value, key] : list) 34 { 35 if (key == name) 36 return value; 37 } 38 return {}; 39 } 40 41 std::vector<DWORD> LayoutMap::GetKeyCodeList(const bool isShortcut) 42 { 43 return impl->GetKeyCodeList(isShortcut); 44 } 45 46 std::vector<std::pair<DWORD, std::wstring>> LayoutMap::GetKeyNameList(const bool isShortcut) 47 { 48 return impl->GetKeyNameList(isShortcut); 49 } 50 51 // Function to return the unicode string name of the key 52 std::wstring LayoutMap::LayoutMapImpl::GetKeyName(DWORD key) 53 { 54 std::wstring result = L"Undefined"; 55 std::lock_guard<std::mutex> lock(keyboardLayoutMap_mutex); 56 UpdateLayout(); 57 58 auto it = keyboardLayoutMap.find(key); 59 if (it != keyboardLayoutMap.end()) 60 { 61 result = it->second; 62 } 63 return result; 64 } 65 66 bool mapKeycodeToUnicode(const int vCode, HKL layout, const BYTE* keyState, std::array<wchar_t, 3>& outBuffer) 67 { 68 // Get the scan code from the virtual key code 69 const UINT scanCode = MapVirtualKeyExW(vCode, MAPVK_VK_TO_VSC, layout); 70 // Get the unicode representation from the virtual key code and scan code pair 71 const UINT wFlags = 1 << 2; // If bit 2 is set, keyboard state is not changed (Windows 10, version 1607 and newer) 72 const int result = ToUnicodeEx(vCode, scanCode, keyState, outBuffer.data(), static_cast<int>(outBuffer.size()), wFlags, layout); 73 return result != 0; 74 } 75 76 // Update Keyboard layout according to input locale identifier 77 void LayoutMap::LayoutMapImpl::UpdateLayout() 78 { 79 // Get keyboard layout for current thread 80 const HKL layout = GetKeyboardLayout(0); 81 if (layout == previousLayout) 82 { 83 return; 84 } 85 previousLayout = layout; 86 if (!isKeyCodeListGenerated) 87 { 88 unicodeKeys.clear(); 89 unknownKeys.clear(); 90 } 91 92 std::array<BYTE, 256> btKeys = { 0 }; 93 // Only set the Caps Lock key to on for the key names in uppercase 94 btKeys[VK_CAPITAL] = 1; 95 96 // Iterate over all the virtual key codes. virtual key 0 is not used 97 for (int i = 1; i < 256; i++) 98 { 99 std::array<wchar_t, 3> szBuffer = { 0 }; 100 if (mapKeycodeToUnicode(i, layout, btKeys.data(), szBuffer)) 101 { 102 keyboardLayoutMap[i] = szBuffer.data(); 103 if (!isKeyCodeListGenerated) 104 { 105 unicodeKeys[i] = szBuffer.data(); 106 } 107 continue; 108 } 109 110 // Store the virtual key code as string 111 std::wstring vk = L"VK "; 112 vk += std::to_wstring(i); 113 keyboardLayoutMap[i] = vk; 114 if (!isKeyCodeListGenerated) 115 { 116 unknownKeys[i] = vk; 117 } 118 } 119 120 // Override special key names like Shift, Ctrl, etc. because they don't have unicode mappings and key names like Enter, Space as they appear as "\r", " " 121 // To do: localization 122 keyboardLayoutMap[VK_CANCEL] = L"Break"; 123 keyboardLayoutMap[VK_BACK] = L"Backspace"; 124 keyboardLayoutMap[VK_TAB] = L"Tab"; 125 keyboardLayoutMap[VK_CLEAR] = L"Clear"; 126 keyboardLayoutMap[VK_SHIFT] = L"Shift"; 127 keyboardLayoutMap[VK_CONTROL] = L"Ctrl"; 128 keyboardLayoutMap[VK_MENU] = L"Alt"; 129 keyboardLayoutMap[VK_PAUSE] = L"Pause"; 130 keyboardLayoutMap[VK_CAPITAL] = L"Caps Lock"; 131 keyboardLayoutMap[VK_ESCAPE] = L"Esc"; 132 keyboardLayoutMap[VK_SPACE] = L"Space"; 133 134 keyboardLayoutMap[VK_LEFT] = L"Left"; 135 keyboardLayoutMap[VK_RIGHT] = L"Right"; 136 keyboardLayoutMap[VK_UP] = L"Up"; 137 keyboardLayoutMap[VK_DOWN] = L"Down"; 138 keyboardLayoutMap[VK_INSERT] = L"Insert"; 139 keyboardLayoutMap[VK_DELETE] = L"Delete"; 140 keyboardLayoutMap[VK_PRIOR] = L"PgUp"; 141 keyboardLayoutMap[VK_NEXT] = L"PgDn"; 142 keyboardLayoutMap[VK_HOME] = L"Home"; 143 keyboardLayoutMap[VK_END] = L"End"; 144 keyboardLayoutMap[VK_RETURN] = L"Enter"; 145 146 keyboardLayoutMap[VK_LEFT | numpadOriginBit] = L"Left (Numpad)"; 147 keyboardLayoutMap[VK_RIGHT | numpadOriginBit] = L"Right (Numpad)"; 148 keyboardLayoutMap[VK_UP | numpadOriginBit] = L"Up (Numpad)"; 149 keyboardLayoutMap[VK_DOWN | numpadOriginBit] = L"Down (Numpad)"; 150 keyboardLayoutMap[VK_INSERT | numpadOriginBit] = L"Insert (Numpad)"; 151 keyboardLayoutMap[VK_DELETE | numpadOriginBit] = L"Delete (Numpad)"; 152 keyboardLayoutMap[VK_PRIOR | numpadOriginBit] = L"PgUp (Numpad)"; 153 keyboardLayoutMap[VK_NEXT | numpadOriginBit] = L"PgDn (Numpad)"; 154 keyboardLayoutMap[VK_HOME | numpadOriginBit] = L"Home (Numpad)"; 155 keyboardLayoutMap[VK_END | numpadOriginBit] = L"End (Numpad)"; 156 keyboardLayoutMap[VK_RETURN | numpadOriginBit] = L"Enter (Numpad)"; 157 keyboardLayoutMap[VK_DIVIDE | numpadOriginBit] = L"/ (Numpad)"; 158 159 keyboardLayoutMap[VK_SUBTRACT] = L"- (Subtract)"; 160 keyboardLayoutMap[VK_SELECT] = L"Select"; 161 keyboardLayoutMap[VK_PRINT] = L"Print"; 162 keyboardLayoutMap[VK_EXECUTE] = L"Execute"; 163 keyboardLayoutMap[VK_SNAPSHOT] = L"Print Screen"; 164 keyboardLayoutMap[VK_HELP] = L"Help"; 165 keyboardLayoutMap[VK_LWIN] = L"Win (Left)"; 166 keyboardLayoutMap[VK_RWIN] = L"Win (Right)"; 167 keyboardLayoutMap[VK_APPS] = L"Apps/Menu"; 168 keyboardLayoutMap[VK_SLEEP] = L"Sleep"; 169 keyboardLayoutMap[VK_NUMPAD0] = L"NumPad 0"; 170 keyboardLayoutMap[VK_NUMPAD1] = L"NumPad 1"; 171 keyboardLayoutMap[VK_NUMPAD2] = L"NumPad 2"; 172 keyboardLayoutMap[VK_NUMPAD3] = L"NumPad 3"; 173 keyboardLayoutMap[VK_NUMPAD4] = L"NumPad 4"; 174 keyboardLayoutMap[VK_NUMPAD5] = L"NumPad 5"; 175 keyboardLayoutMap[VK_NUMPAD6] = L"NumPad 6"; 176 keyboardLayoutMap[VK_NUMPAD7] = L"NumPad 7"; 177 keyboardLayoutMap[VK_NUMPAD8] = L"NumPad 8"; 178 keyboardLayoutMap[VK_NUMPAD9] = L"NumPad 9"; 179 keyboardLayoutMap[VK_SEPARATOR] = L"Separator"; 180 keyboardLayoutMap[VK_F1] = L"F1"; 181 keyboardLayoutMap[VK_F2] = L"F2"; 182 keyboardLayoutMap[VK_F3] = L"F3"; 183 keyboardLayoutMap[VK_F4] = L"F4"; 184 keyboardLayoutMap[VK_F5] = L"F5"; 185 keyboardLayoutMap[VK_F6] = L"F6"; 186 keyboardLayoutMap[VK_F7] = L"F7"; 187 keyboardLayoutMap[VK_F8] = L"F8"; 188 keyboardLayoutMap[VK_F9] = L"F9"; 189 keyboardLayoutMap[VK_F10] = L"F10"; 190 keyboardLayoutMap[VK_F11] = L"F11"; 191 keyboardLayoutMap[VK_F12] = L"F12"; 192 keyboardLayoutMap[VK_F13] = L"F13"; 193 keyboardLayoutMap[VK_F14] = L"F14"; 194 keyboardLayoutMap[VK_F15] = L"F15"; 195 keyboardLayoutMap[VK_F16] = L"F16"; 196 keyboardLayoutMap[VK_F17] = L"F17"; 197 keyboardLayoutMap[VK_F18] = L"F18"; 198 keyboardLayoutMap[VK_F19] = L"F19"; 199 keyboardLayoutMap[VK_F20] = L"F20"; 200 keyboardLayoutMap[VK_F21] = L"F21"; 201 keyboardLayoutMap[VK_F22] = L"F22"; 202 keyboardLayoutMap[VK_F23] = L"F23"; 203 keyboardLayoutMap[VK_F24] = L"F24"; 204 keyboardLayoutMap[VK_NUMLOCK] = L"Num Lock"; 205 keyboardLayoutMap[VK_SCROLL] = L"Scroll Lock"; 206 keyboardLayoutMap[VK_LSHIFT] = L"Shift (Left)"; 207 keyboardLayoutMap[VK_RSHIFT] = L"Shift (Right)"; 208 keyboardLayoutMap[VK_LCONTROL] = L"Ctrl (Left)"; 209 keyboardLayoutMap[VK_RCONTROL] = L"Ctrl (Right)"; 210 keyboardLayoutMap[VK_LMENU] = L"Alt (Left)"; 211 keyboardLayoutMap[VK_RMENU] = L"Alt (Right)"; 212 keyboardLayoutMap[VK_BROWSER_BACK] = L"Browser Back"; 213 keyboardLayoutMap[VK_BROWSER_FORWARD] = L"Browser Forward"; 214 keyboardLayoutMap[VK_BROWSER_REFRESH] = L"Browser Refresh"; 215 keyboardLayoutMap[VK_BROWSER_STOP] = L"Browser Stop"; 216 keyboardLayoutMap[VK_BROWSER_SEARCH] = L"Browser Search"; 217 keyboardLayoutMap[VK_BROWSER_FAVORITES] = L"Browser Favorites"; 218 keyboardLayoutMap[VK_BROWSER_HOME] = L"Browser Home"; 219 keyboardLayoutMap[VK_VOLUME_MUTE] = L"Volume Mute"; 220 keyboardLayoutMap[VK_VOLUME_DOWN] = L"Volume Down"; 221 keyboardLayoutMap[VK_VOLUME_UP] = L"Volume Up"; 222 keyboardLayoutMap[VK_MEDIA_NEXT_TRACK] = L"Next Track"; 223 keyboardLayoutMap[VK_MEDIA_PREV_TRACK] = L"Previous Track"; 224 keyboardLayoutMap[VK_MEDIA_STOP] = L"Stop Media"; 225 keyboardLayoutMap[VK_MEDIA_PLAY_PAUSE] = L"Play/Pause Media"; 226 keyboardLayoutMap[VK_LAUNCH_MAIL] = L"Start Mail"; 227 keyboardLayoutMap[VK_LAUNCH_MEDIA_SELECT] = L"Select Media"; 228 keyboardLayoutMap[VK_LAUNCH_APP1] = L"Start App 1"; 229 keyboardLayoutMap[VK_LAUNCH_APP2] = L"Start App 2"; 230 keyboardLayoutMap[VK_PACKET] = L"Packet"; 231 keyboardLayoutMap[VK_ATTN] = L"Attn"; 232 keyboardLayoutMap[VK_CRSEL] = L"CrSel"; 233 keyboardLayoutMap[VK_EXSEL] = L"ExSel"; 234 keyboardLayoutMap[VK_EREOF] = L"Erase EOF"; 235 keyboardLayoutMap[VK_PLAY] = L"Play"; 236 keyboardLayoutMap[VK_ZOOM] = L"Zoom"; 237 keyboardLayoutMap[VK_PA1] = L"PA1"; 238 keyboardLayoutMap[VK_OEM_CLEAR] = L"Clear"; 239 keyboardLayoutMap[0xFF] = L"Undefined"; 240 keyboardLayoutMap[CommonSharedConstants::VK_WIN_BOTH] = L"Win"; 241 keyboardLayoutMap[VK_KANA] = L"IME Kana"; 242 keyboardLayoutMap[VK_HANGEUL] = L"IME Hangeul"; 243 keyboardLayoutMap[VK_HANGUL] = L"IME Hangul"; 244 keyboardLayoutMap[VK_IME_ON] = L"IME On"; 245 keyboardLayoutMap[VK_JUNJA] = L"IME Junja"; 246 keyboardLayoutMap[VK_FINAL] = L"IME Final"; 247 keyboardLayoutMap[VK_HANJA] = L"IME Hanja"; 248 keyboardLayoutMap[VK_KANJI] = L"IME Kanji"; 249 keyboardLayoutMap[VK_IME_OFF] = L"IME Off"; 250 keyboardLayoutMap[VK_CONVERT] = L"IME Convert"; 251 keyboardLayoutMap[VK_NONCONVERT] = L"IME Non-Convert"; 252 keyboardLayoutMap[VK_ACCEPT] = L"IME Kana"; 253 keyboardLayoutMap[VK_MODECHANGE] = L"IME Mode Change"; 254 keyboardLayoutMap[VK_DECIMAL] = L". (Numpad)"; 255 keyboardLayoutMap[CommonSharedConstants::VK_DISABLED] = L"Disable"; 256 } 257 258 // Function to return the list of key codes in the order for the drop down. It creates it if it doesn't exist 259 std::vector<DWORD> LayoutMap::LayoutMapImpl::GetKeyCodeList(const bool isShortcut) 260 { 261 std::lock_guard<std::mutex> lock(keyboardLayoutMap_mutex); 262 UpdateLayout(); 263 std::vector<DWORD> keyCodes; 264 if (!isKeyCodeListGenerated) 265 { 266 // Add character keys 267 for (auto& it : unicodeKeys) 268 { 269 // If it was not renamed with a special name 270 if (it.second == keyboardLayoutMap[it.first]) 271 { 272 keyCodes.push_back(it.first); 273 } 274 } 275 276 // Add modifier keys in alphabetical order 277 keyCodes.push_back(VK_MENU); 278 keyCodes.push_back(VK_LMENU); 279 keyCodes.push_back(VK_RMENU); 280 keyCodes.push_back(VK_CONTROL); 281 keyCodes.push_back(VK_LCONTROL); 282 keyCodes.push_back(VK_RCONTROL); 283 keyCodes.push_back(VK_SHIFT); 284 keyCodes.push_back(VK_LSHIFT); 285 keyCodes.push_back(VK_RSHIFT); 286 keyCodes.push_back(CommonSharedConstants::VK_WIN_BOTH); 287 keyCodes.push_back(VK_LWIN); 288 keyCodes.push_back(VK_RWIN); 289 290 // Add all other special keys 291 std::vector<DWORD> specialKeys; 292 for (int i = 1; i < 256; i++) 293 { 294 // If it is not already been added (i.e. it was either a modifier or had a unicode representation) 295 if (std::find(keyCodes.begin(), keyCodes.end(), i) == keyCodes.end()) 296 { 297 // If it is any other key but it is not named as VK # 298 auto it = unknownKeys.find(i); 299 if (it == unknownKeys.end()) 300 { 301 specialKeys.push_back(i); 302 } 303 else if (unknownKeys[i] != keyboardLayoutMap[i]) 304 { 305 specialKeys.push_back(i); 306 } 307 } 308 } 309 310 // Add numpad keys 311 for (auto it = keyboardLayoutMap.rbegin(); it->first & numpadOriginBit; ++it) 312 { 313 keyCodes.push_back(it->first); 314 } 315 316 // Sort the special keys in alphabetical order 317 std::sort(specialKeys.begin(), specialKeys.end(), [&](const DWORD& lhs, const DWORD& rhs) { 318 return keyboardLayoutMap[lhs] < keyboardLayoutMap[rhs]; 319 }); 320 for (int i = 0; i < specialKeys.size(); i++) 321 { 322 keyCodes.push_back(specialKeys[i]); 323 } 324 325 // Add unknown keys 326 for (auto& it : unknownKeys) 327 { 328 // If it was not renamed with a special name 329 if (it.second == keyboardLayoutMap[it.first]) 330 { 331 keyCodes.push_back(it.first); 332 } 333 } 334 keyCodeList = keyCodes; 335 isKeyCodeListGenerated = true; 336 } 337 else 338 { 339 keyCodes = keyCodeList; 340 } 341 342 // If it is a key list for the shortcut control then we add a "None" key at the start 343 if (isShortcut) 344 { 345 keyCodes.insert(keyCodes.begin(), 0); 346 } 347 348 return keyCodes; 349 } 350 351 std::vector<std::pair<DWORD, std::wstring>> LayoutMap::LayoutMapImpl::GetKeyNameList(const bool isShortcut) 352 { 353 std::vector<std::pair<DWORD, std::wstring>> keyNames; 354 std::vector<DWORD> keyCodes = GetKeyCodeList(isShortcut); 355 std::lock_guard<std::mutex> lock(keyboardLayoutMap_mutex); 356 // If it is a key list for the shortcut control then we add a "None" key at the start 357 if (isShortcut) 358 { 359 keyNames.push_back({ 0, L"None" }); 360 for (int i = 1; i < keyCodes.size(); i++) 361 { 362 keyNames.push_back({ keyCodes[i], keyboardLayoutMap[keyCodes[i]] }); 363 } 364 } 365 else 366 { 367 for (int i = 0; i < keyCodes.size(); i++) 368 { 369 keyNames.push_back({ keyCodes[i], keyboardLayoutMap[keyCodes[i]] }); 370 } 371 } 372 373 return keyNames; 374 }