WindowsKeyConverter.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. #nullable enable
  2. namespace Terminal.Gui.Drivers;
  3. /// <summary>
  4. /// <see cref="IKeyConverter{T}"/> capable of converting the
  5. /// windows native <see cref="WindowsConsole.InputRecord"/> class
  6. /// into Terminal.Gui shared <see cref="Key"/> representation
  7. /// (used by <see cref="View"/> etc).
  8. /// </summary>
  9. internal class WindowsKeyConverter : IKeyConverter<WindowsConsole.InputRecord>
  10. {
  11. /// <inheritdoc/>
  12. public Key ToKey (WindowsConsole.InputRecord inputEvent)
  13. {
  14. if (inputEvent.KeyEvent.wVirtualKeyCode == (VK)ConsoleKey.Packet)
  15. {
  16. // Used to pass Unicode characters as if they were keystrokes.
  17. // The VK_PACKET key is the low word of a 32-bit
  18. // Virtual Key value used for non-keyboard input methods.
  19. inputEvent.KeyEvent = WindowsKeyHelper.FromVKPacketToKeyEventRecord (inputEvent.KeyEvent);
  20. }
  21. var keyInfo = WindowsConsole.ToConsoleKeyInfoEx (inputEvent.KeyEvent);
  22. //Debug.WriteLine ($"event: KBD: {GetKeyboardLayoutName()} {inputEvent.ToString ()} {keyInfo.ToString (keyInfo)}");
  23. KeyCode map = WindowsKeyHelper.MapKey (keyInfo);
  24. if (map == KeyCode.Null)
  25. {
  26. return 0;
  27. }
  28. return new (map);
  29. }
  30. /// <inheritdoc/>
  31. public WindowsConsole.InputRecord ToKeyInfo (Key key)
  32. {
  33. // Convert Key to ConsoleKeyInfo using the cross-platform mapping utility
  34. ConsoleKeyInfo consoleKeyInfo = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key.KeyCode);
  35. // Build the ControlKeyState from the ConsoleKeyInfo modifiers
  36. var controlKeyState = WindowsConsole.ControlKeyState.NoControlKeyPressed;
  37. if (consoleKeyInfo.Modifiers.HasFlag (ConsoleModifiers.Shift))
  38. {
  39. controlKeyState |= WindowsConsole.ControlKeyState.ShiftPressed;
  40. }
  41. if (consoleKeyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt))
  42. {
  43. controlKeyState |= WindowsConsole.ControlKeyState.LeftAltPressed;
  44. }
  45. if (consoleKeyInfo.Modifiers.HasFlag (ConsoleModifiers.Control))
  46. {
  47. controlKeyState |= WindowsConsole.ControlKeyState.LeftControlPressed;
  48. }
  49. // Get the scan code using Windows API if available, otherwise use a simple heuristic
  50. ushort scanCode = GetScanCodeForKey (consoleKeyInfo.Key);
  51. // Create a KeyEventRecord with the converted values
  52. var keyEvent = new WindowsConsole.KeyEventRecord
  53. {
  54. bKeyDown = true, // Assume key down for conversion
  55. wRepeatCount = 1,
  56. wVirtualKeyCode = (VK)consoleKeyInfo.Key,
  57. wVirtualScanCode = scanCode,
  58. UnicodeChar = consoleKeyInfo.KeyChar,
  59. dwControlKeyState = controlKeyState
  60. };
  61. // Create and return an InputRecord with the keyboard event
  62. return new()
  63. {
  64. EventType = WindowsConsole.EventType.Key,
  65. KeyEvent = keyEvent
  66. };
  67. }
  68. /// <summary>
  69. /// Gets the hardware scan code for a given ConsoleKey.
  70. /// </summary>
  71. /// <param name="key">The ConsoleKey to get the scan code for.</param>
  72. /// <returns>The scan code, or 0 if not available.</returns>
  73. /// <remarks>
  74. /// On Windows, uses MapVirtualKey to get the actual scan code from the OS.
  75. /// This respects the current keyboard layout and is more accurate than a static lookup table.
  76. /// For test simulation purposes, returning 0 is acceptable as Windows doesn't strictly require it.
  77. /// </remarks>
  78. private static ushort GetScanCodeForKey (ConsoleKey key)
  79. {
  80. //// For test simulation, scan codes aren't critical. However, we can use the Windows API
  81. //// to get the correct scan code if we're running on Windows.
  82. //if (Environment.OSVersion.Platform == PlatformID.Win32NT)
  83. //{
  84. // try
  85. // {
  86. // // MapVirtualKey with MAPVK_VK_TO_VSC (0) converts VK to scan code
  87. // // This uses the current keyboard layout, so it's more accurate than a static table
  88. // uint scanCodeExtended = WindowsKeyboardLayout.MapVirtualKey ((VK)key, 0);
  89. // // The scan code is in the low byte
  90. // return (ushort)(scanCodeExtended & 0xFF);
  91. // }
  92. // catch
  93. // {
  94. // // If MapVirtualKey fails, fall back to simple heuristic
  95. // }
  96. //}
  97. // Fallback: Use a simple heuristic for common keys
  98. // For most test scenarios, these values work fine
  99. return key switch
  100. {
  101. ConsoleKey.Escape => 1,
  102. ConsoleKey.D1 => 2,
  103. ConsoleKey.D2 => 3,
  104. ConsoleKey.D3 => 4,
  105. ConsoleKey.D4 => 5,
  106. ConsoleKey.D5 => 6,
  107. ConsoleKey.D6 => 7,
  108. ConsoleKey.D7 => 8,
  109. ConsoleKey.D8 => 9,
  110. ConsoleKey.D9 => 10,
  111. ConsoleKey.D0 => 11,
  112. ConsoleKey.Tab => 15,
  113. ConsoleKey.Q => 16,
  114. ConsoleKey.W => 17,
  115. ConsoleKey.E => 18,
  116. ConsoleKey.R => 19,
  117. ConsoleKey.T => 20,
  118. ConsoleKey.Y => 21,
  119. ConsoleKey.U => 22,
  120. ConsoleKey.I => 23,
  121. ConsoleKey.O => 24,
  122. ConsoleKey.P => 25,
  123. ConsoleKey.Enter => 28,
  124. ConsoleKey.A => 30,
  125. ConsoleKey.S => 31,
  126. ConsoleKey.D => 32,
  127. ConsoleKey.F => 33,
  128. ConsoleKey.G => 34,
  129. ConsoleKey.H => 35,
  130. ConsoleKey.J => 36,
  131. ConsoleKey.K => 37,
  132. ConsoleKey.L => 38,
  133. ConsoleKey.Z => 44,
  134. ConsoleKey.X => 45,
  135. ConsoleKey.C => 46,
  136. ConsoleKey.V => 47,
  137. ConsoleKey.B => 48,
  138. ConsoleKey.N => 49,
  139. ConsoleKey.M => 50,
  140. ConsoleKey.Spacebar => 57,
  141. ConsoleKey.F1 => 59,
  142. ConsoleKey.F2 => 60,
  143. ConsoleKey.F3 => 61,
  144. ConsoleKey.F4 => 62,
  145. ConsoleKey.F5 => 63,
  146. ConsoleKey.F6 => 64,
  147. ConsoleKey.F7 => 65,
  148. ConsoleKey.F8 => 66,
  149. ConsoleKey.F9 => 67,
  150. ConsoleKey.F10 => 68,
  151. ConsoleKey.Home => 71,
  152. ConsoleKey.UpArrow => 72,
  153. ConsoleKey.PageUp => 73,
  154. ConsoleKey.LeftArrow => 75,
  155. ConsoleKey.RightArrow => 77,
  156. ConsoleKey.End => 79,
  157. ConsoleKey.DownArrow => 80,
  158. ConsoleKey.PageDown => 81,
  159. ConsoleKey.Insert => 82,
  160. ConsoleKey.Delete => 83,
  161. ConsoleKey.F11 => 87,
  162. ConsoleKey.F12 => 88,
  163. _ => 0 // Unknown or not needed for test simulation
  164. };
  165. }
  166. }