/ src / common / interop / keyboard_layout.cpp
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  }