WindowsKeyboardLayout.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. using System.Runtime.InteropServices;
  2. namespace Terminal.Gui.Drivers;
  3. /// <summary>
  4. /// Windows-specific keyboard layout information using P/Invoke.
  5. /// This class encapsulates all Windows API calls for keyboard layout operations.
  6. /// </summary>
  7. internal static class WindowsKeyboardLayout
  8. {
  9. #if !WT_ISSUE_8871_FIXED // https://github.com/microsoft/terminal/issues/8871
  10. /// <summary>
  11. /// Translates (maps) a virtual-key code into a scan code or character value, or translates a scan code into a
  12. /// virtual-key code.
  13. /// </summary>
  14. /// <param name="vk"></param>
  15. /// <param name="uMapType">
  16. /// If MAPVK_VK_TO_CHAR (2) - The uCode parameter is a virtual-key code and is translated into an
  17. /// un-shifted character value in the low order word of the return value.
  18. /// </param>
  19. /// <param name="dwhkl"></param>
  20. /// <returns>
  21. /// An un-shifted character value in the low order word of the return value. Dead keys (diacritics) are indicated
  22. /// by setting the top bit of the return value. If there is no translation, the function returns 0. See Remarks.
  23. /// </returns>
  24. [DllImport ("user32.dll", EntryPoint = "MapVirtualKeyExW", CharSet = CharSet.Unicode)]
  25. private static extern uint MapVirtualKeyEx (VK vk, uint uMapType, nint dwhkl);
  26. /// <summary>Retrieves the active input locale identifier (formerly called the keyboard layout).</summary>
  27. /// <param name="idThread">0 for current thread</param>
  28. /// <returns>
  29. /// The return value is the input locale identifier for the thread. The low word contains a Language Identifier
  30. /// for the input language and the high word contains a device handle to the physical layout of the keyboard.
  31. /// </returns>
  32. [DllImport ("user32.dll", EntryPoint = "GetKeyboardLayout", CharSet = CharSet.Unicode)]
  33. private static extern nint GetKeyboardLayout (nint idThread);
  34. [DllImport ("user32.dll")]
  35. private static extern nint GetForegroundWindow ();
  36. [DllImport ("user32.dll")]
  37. private static extern nint GetWindowThreadProcessId (nint hWnd, nint ProcessId);
  38. /// <summary>
  39. /// Translates the specified virtual-key code and keyboard state to the corresponding Unicode character or
  40. /// characters using the Win32 API MapVirtualKey.
  41. /// </summary>
  42. /// <param name="vk"></param>
  43. /// <returns>
  44. /// An un-shifted character value in the low order word of the return value. Dead keys (diacritics) are indicated
  45. /// by setting the top bit of the return value. If there is no translation, the function returns 0.
  46. /// </returns>
  47. public static uint MapVKtoChar (VK vk)
  48. {
  49. if (Environment.OSVersion.Platform != PlatformID.Win32NT)
  50. {
  51. return 0;
  52. }
  53. nint tid = GetWindowThreadProcessId (GetForegroundWindow (), 0);
  54. nint hkl = GetKeyboardLayout (tid);
  55. return MapVirtualKeyEx (vk, 2, hkl);
  56. }
  57. #else
  58. /// <summary>
  59. /// Translates (maps) a virtual-key code into a scan code or character value, or translates a scan code into a virtual-key code.
  60. /// </summary>
  61. /// <param name="vk"></param>
  62. /// <param name="uMapType">
  63. /// If MAPVK_VK_TO_CHAR (2) - The uCode parameter is a virtual-key code and is translated into an unshifted
  64. /// character value in the low order word of the return value.
  65. /// </param>
  66. /// <returns>An unshifted character value in the low order word of the return value. Dead keys (diacritics)
  67. /// are indicated by setting the top bit of the return value. If there is no translation,
  68. /// the function returns 0. See Remarks.</returns>
  69. [DllImport ("user32.dll", EntryPoint = "MapVirtualKeyW", CharSet = CharSet.Unicode)]
  70. private static extern uint MapVirtualKey (VK vk, uint uMapType = 2);
  71. public static uint MapVKtoChar (VK vk) => MapVirtualKey (vk, 2);
  72. #endif
  73. /// <summary>
  74. /// Retrieves the name of the active input locale identifier (formerly called the keyboard layout) for the calling
  75. /// thread.
  76. /// </summary>
  77. /// <param name="pwszKLID"></param>
  78. /// <returns></returns>
  79. [DllImport ("user32.dll")]
  80. private static extern bool GetKeyboardLayoutName ([Out] StringBuilder pwszKLID);
  81. /// <summary>
  82. /// Retrieves the name of the active input locale identifier (formerly called the keyboard layout) for the calling
  83. /// thread.
  84. /// </summary>
  85. /// <returns></returns>
  86. public static string GetKeyboardLayoutName ()
  87. {
  88. if (Environment.OSVersion.Platform != PlatformID.Win32NT)
  89. {
  90. return "none";
  91. }
  92. var klidSB = new StringBuilder ();
  93. GetKeyboardLayoutName (klidSB);
  94. return klidSB.ToString ();
  95. }
  96. }