WindowsKeyConverter.cs 7.1 KB

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