using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Runtime.InteropServices; using System.Text; namespace Terminal.Gui.ConsoleDrivers; /// /// Helper class to handle the scan code and virtual key from a . /// public static class ConsoleKeyMapping { #if !WT_ISSUE_8871_FIXED // https://github.com/microsoft/terminal/issues/8871 /// /// Translates (maps) a virtual-key code into a scan code or character value, or translates a scan code into a virtual-key code. /// /// /// /// If MAPVK_VK_TO_CHAR (2) - The uCode parameter is a virtual-key code and is translated into an un-shifted /// character value in the low order word of the return value. /// /// /// An un-shifted character value in the low order word of the return value. Dead keys (diacritics) /// are indicated by setting the top bit of the return value. If there is no translation, /// the function returns 0. See Remarks. [DllImport ("user32.dll", EntryPoint = "MapVirtualKeyExW", CharSet = CharSet.Unicode)] extern static uint MapVirtualKeyEx (VK vk, uint uMapType, IntPtr dwhkl); /// /// Retrieves the active input locale identifier (formerly called the keyboard layout). /// /// 0 for current thread /// The return value is the input locale identifier for the thread. /// The low word contains a Language Identifier for the input language /// and the high word contains a device handle to the physical layout of the keyboard. /// [DllImport ("user32.dll", EntryPoint = "GetKeyboardLayout", CharSet = CharSet.Unicode)] extern static IntPtr GetKeyboardLayout (IntPtr idThread); //[DllImport ("user32.dll", EntryPoint = "GetKeyboardLayoutNameW", CharSet = CharSet.Unicode)] //extern static uint GetKeyboardLayoutName (uint idThread); [DllImport ("user32.dll")] extern static IntPtr GetForegroundWindow (); [DllImport ("user32.dll")] extern static IntPtr GetWindowThreadProcessId (IntPtr hWnd, IntPtr ProcessId); /// /// Translates the specified virtual-key code and keyboard state to the corresponding Unicode character or characters using /// the Win32 API MapVirtualKey. /// /// /// An un-shifted character value in the low order word of the return value. Dead keys (diacritics) /// are indicated by setting the top bit of the return value. If there is no translation, /// the function returns 0. public static uint MapVKtoChar (VK vk) { if (Environment.OSVersion.Platform != PlatformID.Win32NT) { return 0; } var tid = GetWindowThreadProcessId (GetForegroundWindow (), 0); var hkl = GetKeyboardLayout (tid); return MapVirtualKeyEx (vk, 2, hkl); } #else /// /// Translates (maps) a virtual-key code into a scan code or character value, or translates a scan code into a virtual-key code. /// /// /// /// If MAPVK_VK_TO_CHAR (2) - The uCode parameter is a virtual-key code and is translated into an unshifted /// character value in the low order word of the return value. /// /// An unshifted character value in the low order word of the return value. Dead keys (diacritics) /// are indicated by setting the top bit of the return value. If there is no translation, /// the function returns 0. See Remarks. [DllImport ("user32.dll", EntryPoint = "MapVirtualKeyW", CharSet = CharSet.Unicode)] extern static uint MapVirtualKey (VK vk, uint uMapType = 2); uint MapVKtoChar (VK vk) => MapVirtualKeyToCharEx (vk); #endif /// /// Retrieves the name of the active input locale identifier (formerly called the keyboard layout) for the calling thread. /// /// /// [DllImport ("user32.dll")] extern static bool GetKeyboardLayoutName ([Out] StringBuilder pwszKLID); /// /// Retrieves the name of the active input locale identifier (formerly called the keyboard layout) for the calling thread. /// /// public static string GetKeyboardLayoutName () { if (Environment.OSVersion.Platform != PlatformID.Win32NT) { return "none"; } StringBuilder klidSB = new StringBuilder (); GetKeyboardLayoutName (klidSB); return klidSB.ToString (); } class ScanCodeMapping : IEquatable { public uint ScanCode; public VK VirtualKey; public ConsoleModifiers Modifiers; public uint UnicodeChar; public ScanCodeMapping (uint scanCode, VK virtualKey, ConsoleModifiers modifiers, uint unicodeChar) { ScanCode = scanCode; VirtualKey = virtualKey; Modifiers = modifiers; UnicodeChar = unicodeChar; } public bool Equals (ScanCodeMapping other) { return ScanCode.Equals (other.ScanCode) && VirtualKey.Equals (other.VirtualKey) && Modifiers.Equals (other.Modifiers) && UnicodeChar.Equals (other.UnicodeChar); } } static ConsoleModifiers GetModifiers (ConsoleModifiers modifiers) { if (modifiers.HasFlag (ConsoleModifiers.Shift) && !modifiers.HasFlag (ConsoleModifiers.Alt) && !modifiers.HasFlag (ConsoleModifiers.Control)) { return ConsoleModifiers.Shift; } else if (modifiers == (ConsoleModifiers.Alt | ConsoleModifiers.Control)) { return modifiers; } return 0; } static ScanCodeMapping GetScanCode (string propName, uint keyValue, ConsoleModifiers modifiers) { switch (propName) { case "UnicodeChar": var sCode = _scanCodes.FirstOrDefault ((e) => e.UnicodeChar == keyValue && e.Modifiers == modifiers); if (sCode == null && modifiers == (ConsoleModifiers.Alt | ConsoleModifiers.Control)) { return _scanCodes.FirstOrDefault ((e) => e.UnicodeChar == keyValue && e.Modifiers == 0); } return sCode; case "VirtualKey": sCode = _scanCodes.FirstOrDefault ((e) => e.VirtualKey == (VK)keyValue && e.Modifiers == modifiers); if (sCode == null && modifiers == (ConsoleModifiers.Alt | ConsoleModifiers.Control)) { return _scanCodes.FirstOrDefault ((e) => e.VirtualKey == (VK)keyValue && e.Modifiers == 0); } return sCode; } return null; } // BUGBUG: This API is not correct. It is only used by WindowsDriver in VKPacket scenarios /// /// Get the scan code from a . /// /// The console key info. /// The value if apply. public static uint GetScanCodeFromConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) { var mod = GetModifiers (consoleKeyInfo.Modifiers); ScanCodeMapping scode = GetScanCode ("VirtualKey", (uint)consoleKeyInfo.Key, mod); if (scode != null) { return scode.ScanCode; } return 0; } // BUGBUG: This API is not correct. It is only used by FakeDriver and VkeyPacketSimulator /// /// Gets the from the provided . /// /// The key code. /// The console key info. public static ConsoleKeyInfo GetConsoleKeyInfoFromKeyCode (KeyCode key) { var modifiers = MapToConsoleModifiers (key); var keyValue = MapKeyCodeToConsoleKey (key, out bool isConsoleKey); if (isConsoleKey) { var mod = GetModifiers (modifiers); var scode = GetScanCode ("VirtualKey", (uint)keyValue, mod); if (scode != null) { return new ConsoleKeyInfo ((char)scode.UnicodeChar, (ConsoleKey)scode.VirtualKey, modifiers.HasFlag (ConsoleModifiers.Shift), modifiers.HasFlag (ConsoleModifiers.Alt), modifiers.HasFlag (ConsoleModifiers.Control)); } } else { var keyChar = GetKeyCharFromUnicodeChar ((uint)keyValue, modifiers, out uint consoleKey, out _, isConsoleKey); if (consoleKey != 0) { return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)consoleKey, modifiers.HasFlag (ConsoleModifiers.Shift), modifiers.HasFlag (ConsoleModifiers.Alt), modifiers.HasFlag (ConsoleModifiers.Control)); } } return new ConsoleKeyInfo ((char)keyValue, ConsoleKey.None, modifiers.HasFlag (ConsoleModifiers.Shift), modifiers.HasFlag (ConsoleModifiers.Alt), modifiers.HasFlag (ConsoleModifiers.Control)); } /// /// Map existing modifiers to . /// /// The key code. /// The console modifiers. public static ConsoleModifiers MapToConsoleModifiers (KeyCode key) { var modifiers = new ConsoleModifiers (); if (key.HasFlag (KeyCode.ShiftMask)) { modifiers |= ConsoleModifiers.Shift; } if (key.HasFlag (KeyCode.AltMask)) { modifiers |= ConsoleModifiers.Alt; } if (key.HasFlag (KeyCode.CtrlMask)) { modifiers |= ConsoleModifiers.Control; } return modifiers; } /// /// Gets from modifiers. /// /// The shift key. /// The alt key. /// The control key. /// The console modifiers. public static ConsoleModifiers GetModifiers (bool shift, bool alt, bool control) { var modifiers = new ConsoleModifiers (); if (shift) { modifiers |= ConsoleModifiers.Shift; } if (alt) { modifiers |= ConsoleModifiers.Alt; } if (control) { modifiers |= ConsoleModifiers.Control; } return modifiers; } /// /// Get the from a unicode character and modifiers (e.g. (Key)'a' and (Key)Key.CtrlMask). /// /// The key as a unicode codepoint. /// The modifier keys. /// The resulting scan code. /// The . static ConsoleKeyInfo GetConsoleKeyInfoFromKeyChar (uint keyValue, ConsoleModifiers modifiers, out uint scanCode) { scanCode = 0; if (keyValue == 0) { return new ConsoleKeyInfo ((char)keyValue, ConsoleKey.None, modifiers.HasFlag (ConsoleModifiers.Shift), modifiers.HasFlag (ConsoleModifiers.Alt), modifiers.HasFlag (ConsoleModifiers.Control)); } uint outputChar = keyValue; uint consoleKey; if (keyValue > byte.MaxValue) { var sCode = _scanCodes.FirstOrDefault ((e) => e.UnicodeChar == keyValue); if (sCode == null) { consoleKey = (byte)(keyValue & byte.MaxValue); sCode = _scanCodes.FirstOrDefault ((e) => e.VirtualKey == (VK)consoleKey); if (sCode == null) { consoleKey = 0; outputChar = keyValue; } else { outputChar = (char)(keyValue >> 8); } } else { consoleKey = (byte)sCode.VirtualKey; outputChar = keyValue; } } else { consoleKey = (byte)keyValue; outputChar = '\0'; } return new ConsoleKeyInfo ((char)outputChar, (ConsoleKey)consoleKey, modifiers.HasFlag (ConsoleModifiers.Shift), modifiers.HasFlag (ConsoleModifiers.Alt), modifiers.HasFlag (ConsoleModifiers.Control)); } // Used only by unit tests internal static uint GetKeyChar (uint keyValue, ConsoleModifiers modifiers) { if (modifiers == ConsoleModifiers.Shift && keyValue - 32 is >= 'A' and <= 'Z') { return keyValue - 32; } else if (modifiers == ConsoleModifiers.None && keyValue is >= 'A' and <= 'Z') { return keyValue + 32; } if (modifiers == ConsoleModifiers.Shift && keyValue - 32 is >= 'À' and <= 'Ý') { return keyValue - 32; } else if (modifiers == ConsoleModifiers.None && keyValue is >= 'À' and <= 'Ý') { return keyValue + 32; } if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '0') { return keyValue + 13; } else if (modifiers == ConsoleModifiers.None && keyValue - 13 is '0') { return keyValue - 13; } if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is >= '1' and <= '9' and not '7') { return keyValue - 16; } else if (modifiers == ConsoleModifiers.None && keyValue + 16 is >= '1' and <= '9' and not '7') { return keyValue + 16; } if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '7') { return keyValue - 8; } else if (modifiers == ConsoleModifiers.None && keyValue + 8 is '7') { return keyValue + 8; } if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '\'') { return keyValue + 24; } else if (modifiers == ConsoleModifiers.None && keyValue - 24 is '\'') { return keyValue - 24; } if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '«') { return keyValue + 16; } else if (modifiers == ConsoleModifiers.None && keyValue - 16 is '«') { return keyValue - 16; } if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '\\') { return keyValue + 32; } else if (modifiers == ConsoleModifiers.None && keyValue - 32 is '\\') { return keyValue - 32; } if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '+') { return keyValue - 1; } else if (modifiers == ConsoleModifiers.None && keyValue + 1 is '+') { return keyValue + 1; } if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '´') { return keyValue - 84; } else if (modifiers == ConsoleModifiers.None && keyValue + 84 is '´') { return keyValue + 84; } if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is 'º') { return keyValue - 16; } else if (modifiers == ConsoleModifiers.None && keyValue + 16 is 'º') { return keyValue + 16; } if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '~') { return keyValue - 32; } else if (modifiers == ConsoleModifiers.None && keyValue + 32 is '~') { return keyValue + 32; } if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '<') { return keyValue + 2; } else if (modifiers == ConsoleModifiers.None && keyValue - 2 is '<') { return keyValue - 2; } if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is ',') { return keyValue + 15; } else if (modifiers == ConsoleModifiers.None && keyValue - 15 is ',') { return keyValue - 15; } if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '.') { return keyValue + 12; } else if (modifiers == ConsoleModifiers.None && keyValue - 12 is '.') { return keyValue - 12; } if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '-') { return keyValue + 50; } else if (modifiers == ConsoleModifiers.None && keyValue - 50 is '-') { return keyValue - 50; } return keyValue; } /// /// Get the output character from the , with the correct /// and the scan code used on . /// /// The unicode character. /// The modifiers keys. /// The resulting console key. /// The resulting scan code. /// Indicates if the is a . /// The output character or the . /// This is only used by the and by unit tests. internal static uint GetKeyCharFromUnicodeChar (uint unicodeChar, ConsoleModifiers modifiers, out uint consoleKey, out uint scanCode, bool isConsoleKey = false) { uint decodedChar = unicodeChar >> 8 == 0xff ? unicodeChar & 0xff : unicodeChar; uint keyChar = decodedChar; consoleKey = 0; var mod = GetModifiers (modifiers); scanCode = 0; ScanCodeMapping scode = null; if (unicodeChar != 0 && unicodeChar >> 8 != 0xff && isConsoleKey) { scode = GetScanCode ("VirtualKey", decodedChar, mod); } if (isConsoleKey && scode != null) { consoleKey = (uint)scode.VirtualKey; keyChar = scode.UnicodeChar; scanCode = scode.ScanCode; } if (scode == null) { scode = unicodeChar != 0 ? GetScanCode ("UnicodeChar", decodedChar, mod) : null; if (scode != null) { consoleKey = (uint)scode.VirtualKey; keyChar = scode.UnicodeChar; scanCode = scode.ScanCode; } } if (decodedChar != 0 && scanCode == 0 && char.IsLetter ((char)decodedChar)) { string stFormD = ((char)decodedChar).ToString ().Normalize (System.Text.NormalizationForm.FormD); for (int i = 0; i < stFormD.Length; i++) { var uc = CharUnicodeInfo.GetUnicodeCategory (stFormD [i]); if (uc != UnicodeCategory.NonSpacingMark && uc != UnicodeCategory.OtherLetter) { consoleKey = char.ToUpper (stFormD [i]); scode = GetScanCode ("VirtualKey", char.ToUpper (stFormD [i]), 0); if (scode != null) { scanCode = scode.ScanCode; } } } } if (keyChar < 255 && consoleKey == 0 && scanCode == 0) { scode = GetScanCode ("VirtualKey", keyChar, mod); if (scode != null) { consoleKey = (uint)scode.VirtualKey; keyChar = scode.UnicodeChar; scanCode = scode.ScanCode; } } return keyChar; } /// /// Maps a unicode character (e.g. (Key)'a') to a uint representing a . /// /// The key value. /// Indicates if the is a . /// means the return value is in the ConsoleKey enum. /// means the return value can be mapped to a valid unicode character. /// /// The or the . /// This is only used by the and by unit tests. internal static uint MapKeyCodeToConsoleKey (KeyCode keyValue, out bool isConsoleKey) { isConsoleKey = true; keyValue = keyValue & ~KeyCode.CtrlMask & ~KeyCode.ShiftMask & ~KeyCode.AltMask; switch (keyValue) { case KeyCode.Enter: return (uint)ConsoleKey.Enter; case KeyCode.CursorUp: return (uint)ConsoleKey.UpArrow; case KeyCode.CursorDown: return (uint)ConsoleKey.DownArrow; case KeyCode.CursorLeft: return (uint)ConsoleKey.LeftArrow; case KeyCode.CursorRight: return (uint)ConsoleKey.RightArrow; case KeyCode.PageUp: return (uint)ConsoleKey.PageUp; case KeyCode.PageDown: return (uint)ConsoleKey.PageDown; case KeyCode.Home: return (uint)ConsoleKey.Home; case KeyCode.End: return (uint)ConsoleKey.End; case KeyCode.Insert: return (uint)ConsoleKey.Insert; case KeyCode.Delete: return (uint)ConsoleKey.Delete; case KeyCode.F1: return (uint)ConsoleKey.F1; case KeyCode.F2: return (uint)ConsoleKey.F2; case KeyCode.F3: return (uint)ConsoleKey.F3; case KeyCode.F4: return (uint)ConsoleKey.F4; case KeyCode.F5: return (uint)ConsoleKey.F5; case KeyCode.F6: return (uint)ConsoleKey.F6; case KeyCode.F7: return (uint)ConsoleKey.F7; case KeyCode.F8: return (uint)ConsoleKey.F8; case KeyCode.F9: return (uint)ConsoleKey.F9; case KeyCode.F10: return (uint)ConsoleKey.F10; case KeyCode.F11: return (uint)ConsoleKey.F11; case KeyCode.F12: return (uint)ConsoleKey.F12; case KeyCode.F13: return (uint)ConsoleKey.F13; case KeyCode.F14: return (uint)ConsoleKey.F14; case KeyCode.F15: return (uint)ConsoleKey.F15; case KeyCode.F16: return (uint)ConsoleKey.F16; case KeyCode.F17: return (uint)ConsoleKey.F17; case KeyCode.F18: return (uint)ConsoleKey.F18; case KeyCode.F19: return (uint)ConsoleKey.F19; case KeyCode.F20: return (uint)ConsoleKey.F20; case KeyCode.F21: return (uint)ConsoleKey.F21; case KeyCode.F22: return (uint)ConsoleKey.F22; case KeyCode.F23: return (uint)ConsoleKey.F23; case KeyCode.F24: return (uint)ConsoleKey.F24; case KeyCode.Tab | KeyCode.ShiftMask: return (uint)ConsoleKey.Tab; } isConsoleKey = false; return (uint)keyValue; } /// /// Maps a to a . /// /// The console key. /// The or the . public static KeyCode MapConsoleKeyInfoToKeyCode (ConsoleKeyInfo consoleKeyInfo) { KeyCode keyCode; switch (consoleKeyInfo.Key) { case ConsoleKey.Enter: keyCode = KeyCode.Enter; break; case ConsoleKey.Delete: keyCode = KeyCode.Delete; break; case ConsoleKey.UpArrow: keyCode = KeyCode.CursorUp; break; case ConsoleKey.DownArrow: keyCode = KeyCode.CursorDown; break; case ConsoleKey.LeftArrow: keyCode = KeyCode.CursorLeft; break; case ConsoleKey.RightArrow: keyCode = KeyCode.CursorRight; break; case ConsoleKey.PageUp: keyCode = KeyCode.PageUp; break; case ConsoleKey.PageDown: keyCode = KeyCode.PageDown; break; case ConsoleKey.Home: keyCode = KeyCode.Home; break; case ConsoleKey.End: keyCode = KeyCode.End; break; case ConsoleKey.Insert: keyCode = KeyCode.Insert; break; case ConsoleKey.F1: keyCode = KeyCode.F1; break; case ConsoleKey.F2: keyCode = KeyCode.F2; break; case ConsoleKey.F3: keyCode = KeyCode.F3; break; case ConsoleKey.F4: keyCode = KeyCode.F4; break; case ConsoleKey.F5: keyCode = KeyCode.F5; break; case ConsoleKey.F6: keyCode = KeyCode.F6; break; case ConsoleKey.F7: keyCode = KeyCode.F7; break; case ConsoleKey.F8: keyCode = KeyCode.F8; break; case ConsoleKey.F9: keyCode = KeyCode.F9; break; case ConsoleKey.F10: keyCode = KeyCode.F10; break; case ConsoleKey.F11: keyCode = KeyCode.F11; break; case ConsoleKey.F12: keyCode = KeyCode.F12; break; case ConsoleKey.F13: keyCode = KeyCode.F13; break; case ConsoleKey.F14: keyCode = KeyCode.F14; break; case ConsoleKey.F15: keyCode = KeyCode.F15; break; case ConsoleKey.F16: keyCode = KeyCode.F16; break; case ConsoleKey.F17: keyCode = KeyCode.F17; break; case ConsoleKey.F18: keyCode = KeyCode.F18; break; case ConsoleKey.F19: keyCode = KeyCode.F19; break; case ConsoleKey.F20: keyCode = KeyCode.F20; break; case ConsoleKey.F21: keyCode = KeyCode.F21; break; case ConsoleKey.F22: keyCode = KeyCode.F22; break; case ConsoleKey.F23: keyCode = KeyCode.F23; break; case ConsoleKey.F24: keyCode = KeyCode.F24; break; case ConsoleKey.Tab: keyCode = KeyCode.Tab; break; default: keyCode = (KeyCode)consoleKeyInfo.KeyChar; break; } keyCode |= MapToKeyCodeModifiers (consoleKeyInfo.Modifiers, keyCode); return keyCode; } /// /// Maps a to a . /// /// The console modifiers. /// The key code. /// The with or the public static KeyCode MapToKeyCodeModifiers (ConsoleModifiers modifiers, KeyCode key) { var keyMod = new KeyCode (); if ((modifiers & ConsoleModifiers.Shift) != 0) { keyMod = KeyCode.ShiftMask; } if ((modifiers & ConsoleModifiers.Control) != 0) { keyMod |= KeyCode.CtrlMask; } if ((modifiers & ConsoleModifiers.Alt) != 0) { keyMod |= KeyCode.AltMask; } return keyMod != KeyCode.Null ? keyMod | key : key; } /// /// Generated from winuser.h. See https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes /// public enum VK : ushort { /// /// Left mouse button. /// LBUTTON = 0x01, /// /// Right mouse button. /// RBUTTON = 0x02, /// /// Control-break processing. /// CANCEL = 0x03, /// /// Middle mouse button (three-button mouse). /// MBUTTON = 0x04, /// /// X1 mouse button. /// XBUTTON1 = 0x05, /// /// X2 mouse button. /// XBUTTON2 = 0x06, /// /// BACKSPACE key. /// BACK = 0x08, /// /// TAB key. /// TAB = 0x09, /// /// CLEAR key. /// CLEAR = 0x0C, /// /// ENTER key. /// RETURN = 0x0D, /// /// SHIFT key. /// SHIFT = 0x10, /// /// CTRL key. /// CONTROL = 0x11, /// /// ALT key. /// MENU = 0x12, /// /// PAUSE key. /// PAUSE = 0x13, /// /// CAPS LOCK key. /// CAPITAL = 0x14, /// /// IME Kana mode. /// KANA = 0x15, /// /// IME Hangul mode. /// HANGUL = 0x15, /// /// IME Junja mode. /// JUNJA = 0x17, /// /// IME final mode. /// FINAL = 0x18, /// /// IME Hanja mode. /// HANJA = 0x19, /// /// IME Kanji mode. /// KANJI = 0x19, /// /// ESC key. /// ESCAPE = 0x1B, /// /// IME convert. /// CONVERT = 0x1C, /// /// IME nonconvert. /// NONCONVERT = 0x1D, /// /// IME accept. /// ACCEPT = 0x1E, /// /// IME mode change request. /// MODECHANGE = 0x1F, /// /// SPACEBAR. /// SPACE = 0x20, /// /// PAGE UP key. /// PRIOR = 0x21, /// /// PAGE DOWN key. /// NEXT = 0x22, /// /// END key. /// END = 0x23, /// /// HOME key. /// HOME = 0x24, /// /// LEFT ARROW key. /// LEFT = 0x25, /// /// UP ARROW key. /// UP = 0x26, /// /// RIGHT ARROW key. /// RIGHT = 0x27, /// /// DOWN ARROW key. /// DOWN = 0x28, /// /// SELECT key. /// SELECT = 0x29, /// /// PRINT key. /// PRINT = 0x2A, /// /// EXECUTE key /// EXECUTE = 0x2B, /// /// PRINT SCREEN key /// SNAPSHOT = 0x2C, /// /// INS key /// INSERT = 0x2D, /// /// DEL key /// DELETE = 0x2E, /// /// HELP key /// HELP = 0x2F, /// /// Left Windows key (Natural keyboard) /// LWIN = 0x5B, /// /// Right Windows key (Natural keyboard) /// RWIN = 0x5C, /// /// Applications key (Natural keyboard) /// APPS = 0x5D, /// /// Computer Sleep key /// SLEEP = 0x5F, /// /// Numeric keypad 0 key /// NUMPAD0 = 0x60, /// /// Numeric keypad 1 key /// NUMPAD1 = 0x61, /// /// Numeric keypad 2 key /// NUMPAD2 = 0x62, /// /// Numeric keypad 3 key /// NUMPAD3 = 0x63, /// /// Numeric keypad 4 key /// NUMPAD4 = 0x64, /// /// Numeric keypad 5 key /// NUMPAD5 = 0x65, /// /// Numeric keypad 6 key /// NUMPAD6 = 0x66, /// /// Numeric keypad 7 key /// NUMPAD7 = 0x67, /// /// Numeric keypad 8 key /// NUMPAD8 = 0x68, /// /// Numeric keypad 9 key /// NUMPAD9 = 0x69, /// /// Multiply key /// MULTIPLY = 0x6A, /// /// Add key /// ADD = 0x6B, /// /// Separator key /// SEPARATOR = 0x6C, /// /// Subtract key /// SUBTRACT = 0x6D, /// /// Decimal key /// DECIMAL = 0x6E, /// /// Divide key /// DIVIDE = 0x6F, /// /// F1 key /// F1 = 0x70, /// /// F2 key /// F2 = 0x71, /// /// F3 key /// F3 = 0x72, /// /// F4 key /// F4 = 0x73, /// /// F5 key /// F5 = 0x74, /// /// F6 key /// F6 = 0x75, /// /// F7 key /// F7 = 0x76, /// /// F8 key /// F8 = 0x77, /// /// F9 key /// F9 = 0x78, /// /// F10 key /// F10 = 0x79, /// /// F11 key /// F11 = 0x7A, /// /// F12 key /// F12 = 0x7B, /// /// F13 key /// F13 = 0x7C, /// /// F14 key /// F14 = 0x7D, /// /// F15 key /// F15 = 0x7E, /// /// F16 key /// F16 = 0x7F, /// /// F17 key /// F17 = 0x80, /// /// F18 key /// F18 = 0x81, /// /// F19 key /// F19 = 0x82, /// /// F20 key /// F20 = 0x83, /// /// F21 key /// F21 = 0x84, /// /// F22 key /// F22 = 0x85, /// /// F23 key /// F23 = 0x86, /// /// F24 key /// F24 = 0x87, /// /// NUM LOCK key /// NUMLOCK = 0x90, /// /// SCROLL LOCK key /// SCROLL = 0x91, /// /// NEC PC-9800 kbd definition: '=' key on numpad /// OEM_NEC_EQUAL = 0x92, /// /// Fujitsu/OASYS kbd definition: 'Dictionary' key /// OEM_FJ_JISHO = 0x92, /// /// Fujitsu/OASYS kbd definition: 'Unregister word' key /// OEM_FJ_MASSHOU = 0x93, /// /// Fujitsu/OASYS kbd definition: 'Register word' key /// OEM_FJ_TOUROKU = 0x94, /// /// Fujitsu/OASYS kbd definition: 'Left OYAYUBI' key /// OEM_FJ_LOYA = 0x95, /// /// Fujitsu/OASYS kbd definition: 'Right OYAYUBI' key /// OEM_FJ_ROYA = 0x96, /// /// Left SHIFT key /// LSHIFT = 0xA0, /// /// Right SHIFT key /// RSHIFT = 0xA1, /// /// Left CONTROL key /// LCONTROL = 0xA2, /// /// Right CONTROL key /// RCONTROL = 0xA3, /// /// Left MENU key (Left Alt key) /// LMENU = 0xA4, /// /// Right MENU key (Right Alt key) /// RMENU = 0xA5, /// /// Browser Back key /// BROWSER_BACK = 0xA6, /// /// Browser Forward key /// BROWSER_FORWARD = 0xA7, /// /// Browser Refresh key /// BROWSER_REFRESH = 0xA8, /// /// Browser Stop key /// BROWSER_STOP = 0xA9, /// /// Browser Search key /// BROWSER_SEARCH = 0xAA, /// /// Browser Favorites key /// BROWSER_FAVORITES = 0xAB, /// /// Browser Home key /// BROWSER_HOME = 0xAC, /// /// Volume Mute key /// VOLUME_MUTE = 0xAD, /// /// Volume Down key /// VOLUME_DOWN = 0xAE, /// /// Volume Up key /// VOLUME_UP = 0xAF, /// /// Next Track key /// MEDIA_NEXT_TRACK = 0xB0, /// /// Previous Track key /// MEDIA_PREV_TRACK = 0xB1, /// /// Stop Media key /// MEDIA_STOP = 0xB2, /// /// Play/Pause Media key /// MEDIA_PLAY_PAUSE = 0xB3, /// /// Start Mail key /// LAUNCH_MAIL = 0xB4, /// /// Select Media key /// LAUNCH_MEDIA_SELECT = 0xB5, /// /// Start Application 1 key /// LAUNCH_APP1 = 0xB6, /// /// Start Application 2 key /// LAUNCH_APP2 = 0xB7, /// /// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ';:' key /// OEM_1 = 0xBA, /// /// For any country/region, the '+' key /// OEM_PLUS = 0xBB, /// /// For any country/region, the ',' key /// OEM_COMMA = 0xBC, /// /// For any country/region, the '-' key /// OEM_MINUS = 0xBD, /// /// For any country/region, the '.' key /// OEM_PERIOD = 0xBE, /// /// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '/?' key /// OEM_2 = 0xBF, /// /// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '`~' key /// OEM_3 = 0xC0, /// /// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '[{' key /// OEM_4 = 0xDB, /// /// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '\|' key /// OEM_5 = 0xDC, /// /// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ']}' key /// OEM_6 = 0xDD, /// /// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key /// OEM_7 = 0xDE, /// /// Used for miscellaneous characters; it can vary by keyboard. /// OEM_8 = 0xDF, /// /// 'AX' key on Japanese AX kbd /// OEM_AX = 0xE1, /// /// Either the angle bracket key or the backslash key on the RT 102-key keyboard /// OEM_102 = 0xE2, /// /// Help key on ICO /// ICO_HELP = 0xE3, /// /// 00 key on ICO /// ICO_00 = 0xE4, /// /// Process key /// PROCESSKEY = 0xE5, /// /// Clear key on ICO /// ICO_CLEAR = 0xE6, /// /// Packet key to be used to pass Unicode characters as if they were keystrokes /// PACKET = 0xE7, /// /// Reset key /// OEM_RESET = 0xE9, /// /// Jump key /// OEM_JUMP = 0xEA, /// /// PA1 key /// OEM_PA1 = 0xEB, /// /// PA2 key /// OEM_PA2 = 0xEC, /// /// PA3 key /// OEM_PA3 = 0xED, /// /// WsCtrl key /// OEM_WSCTRL = 0xEE, /// /// CuSel key /// OEM_CUSEL = 0xEF, /// /// Attn key /// OEM_ATTN = 0xF0, /// /// Finish key /// OEM_FINISH = 0xF1, /// /// Copy key /// OEM_COPY = 0xF2, /// /// Auto key /// OEM_AUTO = 0xF3, /// /// Enlw key /// OEM_ENLW = 0xF4, /// /// BackTab key /// OEM_BACKTAB = 0xF5, /// /// Attn key /// ATTN = 0xF6, /// /// CrSel key /// CRSEL = 0xF7, /// /// ExSel key /// EXSEL = 0xF8, /// /// Erase EOF key /// EREOF = 0xF9, /// /// Play key /// PLAY = 0xFA, /// /// Zoom key /// ZOOM = 0xFB, /// /// Reserved /// NONAME = 0xFC, /// /// PA1 key /// PA1 = 0xFD, /// /// Clear key /// OEM_CLEAR = 0xFE } // BUGBUG: This database makes no sense. It is not possible to map a VK code to a character without knowing the keyboard layout // It should be deleted. static HashSet _scanCodes = new HashSet { new (1, VK.ESCAPE, 0, '\u001B'), // Escape new (1, VK.ESCAPE, ConsoleModifiers.Shift, '\u001B'), new (2, (VK)'1', 0, '1'), // D1 new (2, (VK)'1', ConsoleModifiers.Shift, '!'), new (3, (VK)'2', 0, '2'), // D2 new (3, (VK)'2', ConsoleModifiers.Shift, '\"'), // BUGBUG: This is true for Portugese keyboard, but not ENG (@) or DEU (") new (3, (VK)'2', ConsoleModifiers.Alt | ConsoleModifiers.Control, '@'), new (4, (VK)'3', 0, '3'), // D3 new (4, (VK)'3', ConsoleModifiers.Shift, '#'), new (4, (VK)'3', ConsoleModifiers.Alt | ConsoleModifiers.Control, '£'), new (5, (VK)'4', 0, '4'), // D4 new (5, (VK)'4', ConsoleModifiers.Shift, '$'), new (5, (VK)'4', ConsoleModifiers.Alt | ConsoleModifiers.Control, '§'), new (6, (VK)'5', 0, '5'), // D5 new (6, (VK)'5', ConsoleModifiers.Shift, '%'), new (6, (VK)'5', ConsoleModifiers.Alt | ConsoleModifiers.Control, '€'), new (7, (VK)'6', 0, '6'), // D6 new (7, (VK)'6', ConsoleModifiers.Shift, '&'), new (8, (VK)'7', 0, '7'), // D7 new (8, (VK)'7', ConsoleModifiers.Shift, '/'), new (8, (VK)'7', ConsoleModifiers.Alt | ConsoleModifiers.Control, '{'), new (9, (VK)'8', 0, '8'), // D8 new (9, (VK)'8', ConsoleModifiers.Shift, '('), new (9, (VK)'8', ConsoleModifiers.Alt | ConsoleModifiers.Control, '['), new (10, (VK)'9', 0, '9'), // D9 new (10, (VK)'9', ConsoleModifiers.Shift, ')'), new (10, (VK)'9', ConsoleModifiers.Alt | ConsoleModifiers.Control, ']'), new (11, (VK)'0', 0, '0'), // D0 new (11, (VK)'0', ConsoleModifiers.Shift, '='), new (11, (VK)'0', ConsoleModifiers.Alt | ConsoleModifiers.Control, '}'), new (12, VK.OEM_4, 0, '\''), // Oem4 new (12, VK.OEM_4, ConsoleModifiers.Shift, '?'), new (13, VK.OEM_6, 0, '+'), // Oem6 new (13, VK.OEM_6, ConsoleModifiers.Shift, '*'), new (14, VK.BACK, 0, '\u0008'), // Backspace new (14, VK.BACK, ConsoleModifiers.Shift, '\u0008'), new (15, VK.TAB, 0, '\u0009'), // Tab new (15, VK.TAB, ConsoleModifiers.Shift, '\u000F'), new (16, (VK)'Q', 0, 'q'), // Q new (16, (VK)'Q', ConsoleModifiers.Shift, 'Q'), new (17, (VK)'W', 0, 'w'), // W new (17, (VK)'W', ConsoleModifiers.Shift, 'W'), new (18, (VK)'E', 0, 'e'), // E new (18, (VK)'E', ConsoleModifiers.Shift, 'E'), new (19, (VK)'R', 0, 'r'), // R new (19, (VK)'R', ConsoleModifiers.Shift, 'R'), new (20, (VK)'T', 0, 't'), // T new (20, (VK)'T', ConsoleModifiers.Shift, 'T'), new (21, (VK)'Y', 0, 'y'), // Y new (21, (VK)'Y', ConsoleModifiers.Shift, 'Y'), new (22, (VK)'U', 0, 'u'), // U new (22, (VK)'U', ConsoleModifiers.Shift, 'U'), new (23, (VK)'I', 0, 'i'), // I new (23, (VK)'I', ConsoleModifiers.Shift, 'I'), new (24, (VK)'O', 0, 'o'), // O new (24, (VK)'O', ConsoleModifiers.Shift, 'O'), new (25, (VK)'P', 0, 'p'), // P new (25, (VK)'P', ConsoleModifiers.Shift, 'P'), new (26, VK.OEM_PLUS, 0, '+'), // OemPlus new (26, VK.OEM_PLUS, ConsoleModifiers.Shift, '*'), new (26, VK.OEM_PLUS, ConsoleModifiers.Alt | ConsoleModifiers.Control, '¨'), new (27, VK.OEM_1, 0, '´'), // Oem1 new (27, VK.OEM_1, ConsoleModifiers.Shift, '`'), new (28, VK.RETURN, 0, '\u000D'), // Enter new (28, VK.RETURN, ConsoleModifiers.Shift, '\u000D'), new (29, VK.CONTROL, 0, '\0'), // Control new (29, VK.CONTROL, ConsoleModifiers.Shift, '\0'), new (30, (VK)'A', 0, 'a'), // A new (30, (VK)'A', ConsoleModifiers.Shift, 'A'), new (31, (VK)'S', 0, 's'), // S new (31, (VK)'S', ConsoleModifiers.Shift, 'S'), new (32, (VK)'D', 0, 'd'), // D new (32, (VK)'D', ConsoleModifiers.Shift, 'D'), new (33, (VK)'F', 0, 'f'), // F new (33, (VK)'F', ConsoleModifiers.Shift, 'F'), new (34, (VK)'G', 0, 'g'), // G new (34, (VK)'G', ConsoleModifiers.Shift, 'G'), new (35, (VK)'H', 0, 'h'), // H new (35, (VK)'H', ConsoleModifiers.Shift, 'H'), new (36, (VK)'J', 0, 'j'), // J new (36, (VK)'J', ConsoleModifiers.Shift, 'J'), new (37, (VK)'K', 0, 'k'), // K new (37, (VK)'K', ConsoleModifiers.Shift, 'K'), new (38, (VK)'L', 0, 'l'), // L new (38, (VK)'L', ConsoleModifiers.Shift, 'L'), new (39, VK.OEM_3, 0, '`'), // Oem3 (Backtick/Grave) new (39, VK.OEM_3, ConsoleModifiers.Shift, '~'), new (40, VK.OEM_7, 0, '\''), // Oem7 (Single Quote) new (40, VK.OEM_7, ConsoleModifiers.Shift, '\"'), new (41, VK.OEM_5, 0, '\\'), // Oem5 (Backslash) new (41, VK.OEM_5, ConsoleModifiers.Shift, '|'), new (42, VK.LSHIFT, 0, '\0'), // Left Shift new (42, VK.LSHIFT, ConsoleModifiers.Shift, '\0'), new (43, VK.OEM_2, 0, '/'), // Oem2 (Forward Slash) new (43, VK.OEM_2, ConsoleModifiers.Shift, '?'), new (44, (VK)'Z', 0, 'z'), // Z new (44, (VK)'Z', ConsoleModifiers.Shift, 'Z'), new (45, (VK)'X', 0, 'x'), // X new (45, (VK)'X', ConsoleModifiers.Shift, 'X'), new (46, (VK)'C', 0, 'c'), // C new (46, (VK)'C', ConsoleModifiers.Shift, 'C'), new (47, (VK)'V', 0, 'v'), // V new (47, (VK)'V', ConsoleModifiers.Shift, 'V'), new (48, (VK)'B', 0, 'b'), // B new (48, (VK)'B', ConsoleModifiers.Shift, 'B'), new (49, (VK)'N', 0, 'n'), // N new (49, (VK)'N', ConsoleModifiers.Shift, 'N'), new (50, (VK)'M', 0, 'm'), // M new (50, (VK)'M', ConsoleModifiers.Shift, 'M'), new (51, VK.OEM_COMMA, 0, ','), // OemComma new (51, VK.OEM_COMMA, ConsoleModifiers.Shift, '<'), new (52, VK.OEM_PERIOD, 0, '.'), // OemPeriod new (52, VK.OEM_PERIOD, ConsoleModifiers.Shift, '>'), new (53, VK.OEM_MINUS, 0, '-'), // OemMinus new (53, VK.OEM_MINUS, ConsoleModifiers.Shift, '_'), new (54, VK.RSHIFT, 0, '\0'), // Right Shift new (54, VK.RSHIFT, ConsoleModifiers.Shift, '\0'), new (55, VK.PRINT, 0, '\0'), // Print Screen new (55, VK.PRINT, ConsoleModifiers.Shift, '\0'), new (56, VK.LMENU, 0, '\0'), // Alt new (56, VK.LMENU, ConsoleModifiers.Shift, '\0'), new (57, VK.SPACE, 0, ' '), // Spacebar new (57, VK.SPACE, ConsoleModifiers.Shift, ' '), new (58, VK.CAPITAL, 0, '\0'), // Caps Lock new (58, VK.CAPITAL, ConsoleModifiers.Shift, '\0'), new (59, VK.F1, 0, '\0'), // F1 new (59, VK.F1, ConsoleModifiers.Shift, '\0'), new (60, VK.F2, 0, '\0'), // F2 new (60, VK.F2, ConsoleModifiers.Shift, '\0'), new (61, VK.F3, 0, '\0'), // F3 new (61, VK.F3, ConsoleModifiers.Shift, '\0'), new (62, VK.F4, 0, '\0'), // F4 new (62, VK.F4, ConsoleModifiers.Shift, '\0'), new (63, VK.F5, 0, '\0'), // F5 new (63, VK.F5, ConsoleModifiers.Shift, '\0'), new (64, VK.F6, 0, '\0'), // F6 new (64, VK.F6, ConsoleModifiers.Shift, '\0'), new (65, VK.F7, 0, '\0'), // F7 new (65, VK.F7, ConsoleModifiers.Shift, '\0'), new (66, VK.F8, 0, '\0'), // F8 new (66, VK.F8, ConsoleModifiers.Shift, '\0'), new (67, VK.F9, 0, '\0'), // F9 new (67, VK.F9, ConsoleModifiers.Shift, '\0'), new (68, VK.F10, 0, '\0'), // F10 new (68, VK.F10, ConsoleModifiers.Shift, '\0'), new (69, VK.NUMLOCK, 0, '\0'), // Num Lock new (69, VK.NUMLOCK, ConsoleModifiers.Shift, '\0'), new (70, VK.SCROLL, 0, '\0'), // Scroll Lock new (70, VK.SCROLL, ConsoleModifiers.Shift, '\0'), new (71, VK.HOME, 0, '\0'), // Home new (71, VK.HOME, ConsoleModifiers.Shift, '\0'), new (72, VK.UP, 0, '\0'), // Up Arrow new (72, VK.UP, ConsoleModifiers.Shift, '\0'), new (73, VK.PRIOR, 0, '\0'), // Page Up new (73, VK.PRIOR, ConsoleModifiers.Shift, '\0'), new (74, VK.SUBTRACT, 0, '-'), // Subtract (Num Pad '-') new (74, VK.SUBTRACT, ConsoleModifiers.Shift, '-'), new (75, VK.LEFT, 0, '\0'), // Left Arrow new (75, VK.LEFT, ConsoleModifiers.Shift, '\0'), new (76, VK.CLEAR, 0, '\0'), // Center key (Num Pad 5 with Num Lock off) new (76, VK.CLEAR, ConsoleModifiers.Shift, '\0'), new (77, VK.RIGHT, 0, '\0'), // Right Arrow new (77, VK.RIGHT, ConsoleModifiers.Shift, '\0'), new (78, VK.ADD, 0, '+'), // Add (Num Pad '+') new (78, VK.ADD, ConsoleModifiers.Shift, '+'), new (79, VK.END, 0, '\0'), // End new (79, VK.END, ConsoleModifiers.Shift, '\0'), new (80, VK.DOWN, 0, '\0'), // Down Arrow new (80, VK.DOWN, ConsoleModifiers.Shift, '\0'), new (81, VK.NEXT, 0, '\0'), // Page Down new (81, VK.NEXT, ConsoleModifiers.Shift, '\0'), new (82, VK.INSERT, 0, '\0'), // Insert new (82, VK.INSERT, ConsoleModifiers.Shift, '\0'), new (83, VK.DELETE, 0, '\0'), // Delete new (83, VK.DELETE, ConsoleModifiers.Shift, '\0'), new (86, VK.OEM_102, 0, '<'), // OEM 102 (Typically '<' or '|' key next to Left Shift) new (86, VK.OEM_102, ConsoleModifiers.Shift, '>'), new (87, VK.F11, 0, '\0'), // F11 new (87, VK.F11, ConsoleModifiers.Shift, '\0'), new (88, VK.F12, 0, '\0'), // F12 new (88, VK.F12, ConsoleModifiers.Shift, '\0') }; /// /// Decode a that is using . /// /// The console key info. /// The decoded or the . /// If it's a the may be /// a or a value. /// public static ConsoleKeyInfo DecodeVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) { if (consoleKeyInfo.Key != ConsoleKey.Packet) { return consoleKeyInfo; } return GetConsoleKeyInfoFromKeyChar (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out _); } /// /// Encode the with the /// if the first a byte length, otherwise only the KeyChar is considered and searched on the database. /// /// The console key info. /// The encoded KeyChar with the Key if both can be shifted, otherwise only the KeyChar. /// This is useful to use with the . public static char EncodeKeyCharForVKPacket (ConsoleKeyInfo consoleKeyInfo) { char keyChar = consoleKeyInfo.KeyChar; ConsoleKey consoleKey = consoleKeyInfo.Key; if (keyChar != 0 && consoleKeyInfo.KeyChar < byte.MaxValue && consoleKey == ConsoleKey.None) { // try to get the ConsoleKey var scode = _scanCodes.FirstOrDefault ((e) => e.UnicodeChar == keyChar); if (scode != null) { consoleKey = (ConsoleKey)scode.VirtualKey; } } if (keyChar < byte.MaxValue && consoleKey != ConsoleKey.None) { keyChar = (char)(consoleKeyInfo.KeyChar << 8 | (byte)consoleKey); } return keyChar; } }