WindowsKeyboardLayout.cs 4.7 KB

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