namespace Terminal.Gui.Drivers; /// /// capable of converting the /// windows native class /// into Terminal.Gui shared representation /// (used by etc). /// internal class WindowsKeyConverter : IKeyConverter { /// public Key ToKey (WindowsConsole.InputRecord inputEvent) { if (inputEvent.KeyEvent.wVirtualKeyCode == (VK)ConsoleKey.Packet) { // Used to pass Unicode characters as if they were keystrokes. // The VK_PACKET key is the low word of a 32-bit // Virtual Key value used for non-keyboard input methods. inputEvent.KeyEvent = WindowsKeyHelper.FromVKPacketToKeyEventRecord (inputEvent.KeyEvent); } var keyInfo = WindowsConsole.ToConsoleKeyInfoEx (inputEvent.KeyEvent); //Debug.WriteLine ($"event: KBD: {GetKeyboardLayoutName()} {inputEvent.ToString ()} {keyInfo.ToString (keyInfo)}"); KeyCode map = WindowsKeyHelper.MapKey (keyInfo); if (map == KeyCode.Null) { return 0; } return new (map); } /// public WindowsConsole.InputRecord ToKeyInfo (Key key) { // Convert Key to ConsoleKeyInfo using the cross-platform mapping utility ConsoleKeyInfo consoleKeyInfo = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key.KeyCode); // Build the ControlKeyState from the ConsoleKeyInfo modifiers var controlKeyState = WindowsConsole.ControlKeyState.NoControlKeyPressed; if (consoleKeyInfo.Modifiers.HasFlag (ConsoleModifiers.Shift)) { controlKeyState |= WindowsConsole.ControlKeyState.ShiftPressed; } if (consoleKeyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt)) { controlKeyState |= WindowsConsole.ControlKeyState.LeftAltPressed; } if (consoleKeyInfo.Modifiers.HasFlag (ConsoleModifiers.Control)) { controlKeyState |= WindowsConsole.ControlKeyState.LeftControlPressed; } // Get the scan code using Windows API if available, otherwise use a simple heuristic ushort scanCode = GetScanCodeForKey (consoleKeyInfo.Key); // Create a KeyEventRecord with the converted values var keyEvent = new WindowsConsole.KeyEventRecord { bKeyDown = true, // Assume key down for conversion wRepeatCount = 1, wVirtualKeyCode = (VK)consoleKeyInfo.Key, wVirtualScanCode = scanCode, UnicodeChar = consoleKeyInfo.KeyChar, dwControlKeyState = controlKeyState }; // Create and return an InputRecord with the keyboard event return new() { EventType = WindowsConsole.EventType.Key, KeyEvent = keyEvent }; } /// /// Gets the hardware scan code for a given ConsoleKey. /// /// The ConsoleKey to get the scan code for. /// The scan code, or 0 if not available. /// /// On Windows, uses MapVirtualKey to get the actual scan code from the OS. /// This respects the current keyboard layout and is more accurate than a static lookup table. /// For test simulation purposes, returning 0 is acceptable as Windows doesn't strictly require it. /// private static ushort GetScanCodeForKey (ConsoleKey key) { //// For test simulation, scan codes aren't critical. However, we can use the Windows API //// to get the correct scan code if we're running on Windows. //if (Environment.OSVersion.Platform == PlatformID.Win32NT) //{ // try // { // // MapVirtualKey with MAPVK_VK_TO_VSC (0) converts VK to scan code // // This uses the current keyboard layout, so it's more accurate than a static table // uint scanCodeExtended = WindowsKeyboardLayout.MapVirtualKey ((VK)key, 0); // // The scan code is in the low byte // return (ushort)(scanCodeExtended & 0xFF); // } // catch // { // // If MapVirtualKey fails, fall back to simple heuristic // } //} // Fallback: Use a simple heuristic for common keys // For most test scenarios, these values work fine return key switch { ConsoleKey.Escape => 1, ConsoleKey.D1 => 2, ConsoleKey.D2 => 3, ConsoleKey.D3 => 4, ConsoleKey.D4 => 5, ConsoleKey.D5 => 6, ConsoleKey.D6 => 7, ConsoleKey.D7 => 8, ConsoleKey.D8 => 9, ConsoleKey.D9 => 10, ConsoleKey.D0 => 11, ConsoleKey.Tab => 15, ConsoleKey.Q => 16, ConsoleKey.W => 17, ConsoleKey.E => 18, ConsoleKey.R => 19, ConsoleKey.T => 20, ConsoleKey.Y => 21, ConsoleKey.U => 22, ConsoleKey.I => 23, ConsoleKey.O => 24, ConsoleKey.P => 25, ConsoleKey.Enter => 28, ConsoleKey.A => 30, ConsoleKey.S => 31, ConsoleKey.D => 32, ConsoleKey.F => 33, ConsoleKey.G => 34, ConsoleKey.H => 35, ConsoleKey.J => 36, ConsoleKey.K => 37, ConsoleKey.L => 38, ConsoleKey.Z => 44, ConsoleKey.X => 45, ConsoleKey.C => 46, ConsoleKey.V => 47, ConsoleKey.B => 48, ConsoleKey.N => 49, ConsoleKey.M => 50, ConsoleKey.Spacebar => 57, ConsoleKey.F1 => 59, ConsoleKey.F2 => 60, ConsoleKey.F3 => 61, ConsoleKey.F4 => 62, ConsoleKey.F5 => 63, ConsoleKey.F6 => 64, ConsoleKey.F7 => 65, ConsoleKey.F8 => 66, ConsoleKey.F9 => 67, ConsoleKey.F10 => 68, ConsoleKey.Home => 71, ConsoleKey.UpArrow => 72, ConsoleKey.PageUp => 73, ConsoleKey.LeftArrow => 75, ConsoleKey.RightArrow => 77, ConsoleKey.End => 79, ConsoleKey.DownArrow => 80, ConsoleKey.PageDown => 81, ConsoleKey.Insert => 82, ConsoleKey.Delete => 83, ConsoleKey.F11 => 87, ConsoleKey.F12 => 88, _ => 0 // Unknown or not needed for test simulation }; } }