diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadStdinUnbuffered.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadStdinUnbuffered.cs index 844e669682b1a..b56e66ef7dee1 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadStdinUnbuffered.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadStdinUnbuffered.cs @@ -12,7 +12,7 @@ internal static partial class Sys internal static unsafe partial int ReadStdin(byte* buffer, int bufferSize); [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_InitializeConsoleBeforeRead")] - internal static partial void InitializeConsoleBeforeRead(byte minChars = 1, byte decisecondsTimeout = 0); + internal static partial void InitializeConsoleBeforeRead([MarshalAs(UnmanagedType.Bool)] bool distinguishNewLines, byte minChars = 1, byte decisecondsTimeout = 0); [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_UninitializeConsoleAfterRead")] internal static partial void UninitializeConsoleAfterRead(); diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SetSignalForBreak.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SetSignalForBreak.cs index a235e8e133a01..8ad4eba93c917 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SetSignalForBreak.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SetSignalForBreak.cs @@ -12,6 +12,6 @@ internal static partial class Sys internal static partial int GetSignalForBreak(); [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_SetSignalForBreak")] - internal static partial int SetSignalForBreak(int signalForBreak); + internal static partial int SetSignalForBreak(int signalForBreak, [MarshalAs(UnmanagedType.Bool)] bool distinguishNewLines); } } diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.StdinReady.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.StdinReady.cs index ea191c81900e5..a5016d463c922 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.StdinReady.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.StdinReady.cs @@ -9,6 +9,6 @@ internal static partial class Sys { [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_StdinReady")] [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool StdinReady(); + internal static partial bool StdinReady([MarshalAs(UnmanagedType.Bool)] bool distinguishNewLines); } } diff --git a/src/libraries/Common/src/System/Console/ConsoleUtils.cs b/src/libraries/Common/src/System/Console/ConsoleUtils.cs index 2cec17fb2e637..551a719018e17 100644 --- a/src/libraries/Common/src/System/Console/ConsoleUtils.cs +++ b/src/libraries/Common/src/System/Console/ConsoleUtils.cs @@ -10,6 +10,8 @@ internal static partial class ConsoleUtils /// Whether to output ansi color strings. private static volatile int s_emitAnsiColorCodes = -1; + private static volatile int s_useNet6KeyParser = -1; + /// Get whether to emit ANSI color codes. public static bool EmitAnsiColorCodes { @@ -49,5 +51,31 @@ public static bool EmitAnsiColorCodes return enabled; } } + + internal static bool UseNet6KeyParser + { + get + { + int useNet6KeyParser = s_useNet6KeyParser; + + if (useNet6KeyParser == -1) + { + useNet6KeyParser = s_useNet6KeyParser = GetNet6CompatReadKeySetting() ? 1 : 0; + } + + return useNet6KeyParser == 1; + + static bool GetNet6CompatReadKeySetting() + { + if (AppContext.TryGetSwitch("System.Console.UseNet6CompatReadKey", out bool fileConfig)) + { + return fileConfig; + } + + string? envVar = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_CONSOLE_USENET6COMPATREADKEY"); + return envVar is not null && (envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase)); + } + } + } } } diff --git a/src/libraries/System.Console/src/System.Console.csproj b/src/libraries/System.Console/src/System.Console.csproj index 806daf70c3e4a..ea763aa3a4631 100644 --- a/src/libraries/System.Console/src/System.Console.csproj +++ b/src/libraries/System.Console/src/System.Console.csproj @@ -158,11 +158,18 @@ + + + + + + + Gets the lazily-initialized terminal information for the terminal. + public static TerminalFormatStrings TerminalFormatStringsInstance { get { return s_terminalFormatStringsInstance.Value; } } + private static readonly Lazy s_terminalFormatStringsInstance = new(() => new TerminalFormatStrings(TermInfo.DatabaseFactory.ReadActiveDatabase())); + public static Stream OpenStandardInput() { return new UnixConsoleStream(Interop.CheckIo(Interop.Sys.Dup(Interop.Sys.FileDescriptors.STDIN_FILENO)), FileAccess.Read, @@ -149,7 +153,7 @@ public static bool TreatControlCAsInput if (!Console.IsInputRedirected) { EnsureConsoleInitialized(); - if (Interop.Sys.SetSignalForBreak(Convert.ToInt32(!value)) == 0) + if (Interop.Sys.SetSignalForBreak(Convert.ToInt32(!value), distinguishNewLines: !ConsoleUtils.UseNet6KeyParser) == 0) throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo()); } } @@ -198,7 +202,7 @@ public static string Title if (Console.IsOutputRedirected) return; - string? titleFormat = TerminalFormatStrings.Instance.Title; + string? titleFormat = TerminalFormatStringsInstance.Title; if (!string.IsNullOrEmpty(titleFormat)) { string ansiStr = TermInfo.ParameterizedStrings.Evaluate(titleFormat, value); @@ -211,7 +215,7 @@ public static void Beep() { if (!Console.IsOutputRedirected) { - WriteStdoutAnsiString(TerminalFormatStrings.Instance.Bell, mayChangeCursorPosition: false); + WriteStdoutAnsiString(TerminalFormatStringsInstance.Bell, mayChangeCursorPosition: false); } } @@ -224,7 +228,7 @@ public static void Clear() { if (!Console.IsOutputRedirected) { - WriteStdoutAnsiString(TerminalFormatStrings.Instance.Clear); + WriteStdoutAnsiString(TerminalFormatStringsInstance.Clear); } } @@ -242,7 +246,7 @@ public static void SetCursorPosition(int left, int top) return; } - string? cursorAddressFormat = TerminalFormatStrings.Instance.CursorAddress; + string? cursorAddressFormat = TerminalFormatStringsInstance.CursorAddress; if (!string.IsNullOrEmpty(cursorAddressFormat)) { string ansiStr = TermInfo.ParameterizedStrings.Evaluate(cursorAddressFormat, top, left); @@ -372,8 +376,8 @@ private static void GetWindowSize(out int width, out int height) } else { - s_windowWidth = TerminalFormatStrings.Instance.Columns; - s_windowHeight = TerminalFormatStrings.Instance.Lines; + s_windowWidth = TerminalFormatStringsInstance.Columns; + s_windowHeight = TerminalFormatStringsInstance.Lines; } } width = s_windowWidth; @@ -399,8 +403,8 @@ public static bool CursorVisible if (!Console.IsOutputRedirected) { WriteStdoutAnsiString(value ? - TerminalFormatStrings.Instance.CursorVisible : - TerminalFormatStrings.Instance.CursorInvisible); + TerminalFormatStringsInstance.CursorVisible : + TerminalFormatStringsInstance.CursorInvisible); } } } @@ -475,7 +479,7 @@ internal static bool TryGetCursorPosition(out int left, out int top, bool reinit // involved in reading/writing, such as when accessing a remote system. We also extend // the timeout on the very first request to 15 seconds, to account for potential latency // before we know if we will receive a response. - Interop.Sys.InitializeConsoleBeforeRead(minChars: (byte)(s_everReceivedCursorPositionResponse ? 1 : 0), decisecondsTimeout: (byte)(s_firstCursorPositionRequest ? 100 : 10)); + Interop.Sys.InitializeConsoleBeforeRead(distinguishNewLines: !ConsoleUtils.UseNet6KeyParser, minChars: (byte)(s_everReceivedCursorPositionResponse ? 1 : 0), decisecondsTimeout: (byte)(s_firstCursorPositionRequest ? 100 : 10)); try { // Write out the cursor position report request. @@ -550,7 +554,7 @@ internal static bool TryGetCursorPosition(out int left, out int top, bool reinit { if (reinitializeForRead) { - Interop.Sys.InitializeConsoleBeforeRead(); + Interop.Sys.InitializeConsoleBeforeRead(distinguishNewLines: !ConsoleUtils.UseNet6KeyParser); } else { @@ -784,10 +788,10 @@ private static void WriteSetColorString(bool foreground, ConsoleColor color) } // We haven't yet computed a format string. Compute it, use it, then cache it. - string? formatString = foreground ? TerminalFormatStrings.Instance.Foreground : TerminalFormatStrings.Instance.Background; + string? formatString = foreground ? TerminalFormatStringsInstance.Foreground : TerminalFormatStringsInstance.Background; if (!string.IsNullOrEmpty(formatString)) { - int maxColors = TerminalFormatStrings.Instance.MaxColors; // often 8 or 16; 0 is invalid + int maxColors = TerminalFormatStringsInstance.MaxColors; // often 8 or 16; 0 is invalid if (maxColors > 0) { // The values of the ConsoleColor enums unfortunately don't map to the @@ -831,62 +835,20 @@ private static void WriteResetColorString() { if (ConsoleUtils.EmitAnsiColorCodes) { - WriteStdoutAnsiString(TerminalFormatStrings.Instance.Reset); + WriteStdoutAnsiString(TerminalFormatStringsInstance.Reset); } } /// Cache of the format strings for foreground/background and ConsoleColor. private static readonly string[,] s_fgbgAndColorStrings = new string[2, 16]; // 2 == fg vs bg, 16 == ConsoleColor values - public static bool TryGetSpecialConsoleKey(char[] givenChars, int startIndex, int endIndex, out ConsoleKeyInfo key, out int keyLength) - { - int unprocessedCharCount = endIndex - startIndex; - - // First process special control character codes. These override anything from terminfo. - if (unprocessedCharCount > 0) - { - // Is this an erase / backspace? - char c = givenChars[startIndex]; - if (c != s_posixDisableValue && c == s_veraseCharacter) - { - key = new ConsoleKeyInfo(c, ConsoleKey.Backspace, shift: false, alt: false, control: false); - keyLength = 1; - return true; - } - } - - // Then process terminfo mappings. - int minRange = TerminalFormatStrings.Instance.MinKeyFormatLength; - if (unprocessedCharCount >= minRange) - { - int maxRange = Math.Min(unprocessedCharCount, TerminalFormatStrings.Instance.MaxKeyFormatLength); - - for (int i = maxRange; i >= minRange; i--) - { - var currentString = new ReadOnlyMemory(givenChars, startIndex, i); - - // Check if the string prefix matches. - if (TerminalFormatStrings.Instance.KeyFormatToConsoleKey.TryGetValue(currentString, out key)) - { - keyLength = currentString.Length; - return true; - } - } - } - - // Otherwise, not a known special console key. - key = default(ConsoleKeyInfo); - keyLength = 0; - return false; - } - /// Whether keypad_xmit has already been written out to the terminal. private static volatile bool s_initialized; /// Value used to indicate that a special character code isn't available. internal static byte s_posixDisableValue; /// Special control character code used to represent an erase (backspace). - private static byte s_veraseCharacter; + internal static byte s_veraseCharacter; /// Special control character that represents the end of a line. internal static byte s_veolCharacter; /// Special control character that represents the end of a line. @@ -921,7 +883,7 @@ private static unsafe void EnsureInitializedCore() // the native lib later to handle signals that require re-entering the mode. if (!Console.IsOutputRedirected) { - string? keypadXmit = TerminalFormatStrings.Instance.KeypadXmit; + string? keypadXmit = TerminalFormatStringsInstance.KeypadXmit; if (keypadXmit != null) { Interop.Sys.SetKeypadXmit(keypadXmit); @@ -956,243 +918,6 @@ private static unsafe void EnsureInitializedCore() } } - /// Provides format strings and related information for use with the current terminal. - internal sealed class TerminalFormatStrings - { - /// Gets the lazily-initialized terminal information for the terminal. - public static TerminalFormatStrings Instance { get { return s_instance.Value; } } - private static readonly Lazy s_instance = new Lazy(() => new TerminalFormatStrings(TermInfo.Database.ReadActiveDatabase())); - - /// The format string to use to change the foreground color. - public readonly string? Foreground; - /// The format string to use to change the background color. - public readonly string? Background; - /// The format string to use to reset the foreground and background colors. - public readonly string? Reset; - /// The maximum number of colors supported by the terminal. - public readonly int MaxColors; - /// The number of columns in a format. - public readonly int Columns; - /// The number of lines in a format. - public readonly int Lines; - /// The format string to use to make cursor visible. - public readonly string? CursorVisible; - /// The format string to use to make cursor invisible - public readonly string? CursorInvisible; - /// The format string to use to set the window title. - public readonly string? Title; - /// The format string to use for an audible bell. - public readonly string? Bell; - /// The format string to use to clear the terminal. - public readonly string? Clear; - /// The format string to use to set the position of the cursor. - public readonly string? CursorAddress; - /// The format string to use to move the cursor to the left. - public readonly string? CursorLeft; - /// The format string to use to clear to the end of line. - public readonly string? ClrEol; - /// The ANSI-compatible string for the Cursor Position report request. - /// - /// This should really be in user string 7 in the terminfo file, but some terminfo databases - /// are missing it. As this is defined to be supported by any ANSI-compatible terminal, - /// we assume it's available; doing so means CursorTop/Left will work even if the terminfo database - /// doesn't contain it (as appears to be the case with e.g. screen and tmux on Ubuntu), at the risk - /// of outputting the sequence on some terminal that's not compatible. - /// - public const string CursorPositionReport = "\x1B[6n"; - /// - /// The dictionary of keystring to ConsoleKeyInfo. - /// Only some members of the ConsoleKeyInfo are used; in particular, the actual char is ignored. - /// - public readonly Dictionary, ConsoleKeyInfo> KeyFormatToConsoleKey = - new Dictionary, ConsoleKeyInfo>(new ReadOnlyMemoryContentComparer()); - - /// Max key length - public readonly int MaxKeyFormatLength; - /// Min key length - public readonly int MinKeyFormatLength; - /// The ANSI string used to enter "application" / "keypad transmit" mode. - public readonly string? KeypadXmit; - - public TerminalFormatStrings(TermInfo.Database? db) - { - if (db == null) - return; - - KeypadXmit = db.GetString(TermInfo.WellKnownStrings.KeypadXmit); - Foreground = db.GetString(TermInfo.WellKnownStrings.SetAnsiForeground); - Background = db.GetString(TermInfo.WellKnownStrings.SetAnsiBackground); - Reset = db.GetString(TermInfo.WellKnownStrings.OrigPairs) ?? db.GetString(TermInfo.WellKnownStrings.OrigColors); - Bell = db.GetString(TermInfo.WellKnownStrings.Bell); - Clear = db.GetString(TermInfo.WellKnownStrings.Clear); - Columns = db.GetNumber(TermInfo.WellKnownNumbers.Columns); - Lines = db.GetNumber(TermInfo.WellKnownNumbers.Lines); - CursorVisible = db.GetString(TermInfo.WellKnownStrings.CursorVisible); - CursorInvisible = db.GetString(TermInfo.WellKnownStrings.CursorInvisible); - CursorAddress = db.GetString(TermInfo.WellKnownStrings.CursorAddress); - CursorLeft = db.GetString(TermInfo.WellKnownStrings.CursorLeft); - ClrEol = db.GetString(TermInfo.WellKnownStrings.ClrEol); - - Title = GetTitle(db); - - Debug.WriteLineIf(db.GetString(TermInfo.WellKnownStrings.CursorPositionReport) != CursorPositionReport, - "Getting the cursor position will only work if the terminal supports the CPR sequence," + - "but the terminfo database does not contain an entry for it."); - - int maxColors = db.GetNumber(TermInfo.WellKnownNumbers.MaxColors); - MaxColors = // normalize to either the full range of all ANSI colors, just the dark ones, or none - maxColors >= 16 ? 16 : - maxColors >= 8 ? 8 : - 0; - - AddKey(db, TermInfo.WellKnownStrings.KeyF1, ConsoleKey.F1); - AddKey(db, TermInfo.WellKnownStrings.KeyF2, ConsoleKey.F2); - AddKey(db, TermInfo.WellKnownStrings.KeyF3, ConsoleKey.F3); - AddKey(db, TermInfo.WellKnownStrings.KeyF4, ConsoleKey.F4); - AddKey(db, TermInfo.WellKnownStrings.KeyF5, ConsoleKey.F5); - AddKey(db, TermInfo.WellKnownStrings.KeyF6, ConsoleKey.F6); - AddKey(db, TermInfo.WellKnownStrings.KeyF7, ConsoleKey.F7); - AddKey(db, TermInfo.WellKnownStrings.KeyF8, ConsoleKey.F8); - AddKey(db, TermInfo.WellKnownStrings.KeyF9, ConsoleKey.F9); - AddKey(db, TermInfo.WellKnownStrings.KeyF10, ConsoleKey.F10); - AddKey(db, TermInfo.WellKnownStrings.KeyF11, ConsoleKey.F11); - AddKey(db, TermInfo.WellKnownStrings.KeyF12, ConsoleKey.F12); - AddKey(db, TermInfo.WellKnownStrings.KeyF13, ConsoleKey.F13); - AddKey(db, TermInfo.WellKnownStrings.KeyF14, ConsoleKey.F14); - AddKey(db, TermInfo.WellKnownStrings.KeyF15, ConsoleKey.F15); - AddKey(db, TermInfo.WellKnownStrings.KeyF16, ConsoleKey.F16); - AddKey(db, TermInfo.WellKnownStrings.KeyF17, ConsoleKey.F17); - AddKey(db, TermInfo.WellKnownStrings.KeyF18, ConsoleKey.F18); - AddKey(db, TermInfo.WellKnownStrings.KeyF19, ConsoleKey.F19); - AddKey(db, TermInfo.WellKnownStrings.KeyF20, ConsoleKey.F20); - AddKey(db, TermInfo.WellKnownStrings.KeyF21, ConsoleKey.F21); - AddKey(db, TermInfo.WellKnownStrings.KeyF22, ConsoleKey.F22); - AddKey(db, TermInfo.WellKnownStrings.KeyF23, ConsoleKey.F23); - AddKey(db, TermInfo.WellKnownStrings.KeyF24, ConsoleKey.F24); - AddKey(db, TermInfo.WellKnownStrings.KeyBackspace, ConsoleKey.Backspace); - AddKey(db, TermInfo.WellKnownStrings.KeyBackTab, ConsoleKey.Tab, shift: true, alt: false, control: false); - AddKey(db, TermInfo.WellKnownStrings.KeyBegin, ConsoleKey.Home); - AddKey(db, TermInfo.WellKnownStrings.KeyClear, ConsoleKey.Clear); - AddKey(db, TermInfo.WellKnownStrings.KeyDelete, ConsoleKey.Delete); - AddKey(db, TermInfo.WellKnownStrings.KeyDown, ConsoleKey.DownArrow); - AddKey(db, TermInfo.WellKnownStrings.KeyEnd, ConsoleKey.End); - AddKey(db, TermInfo.WellKnownStrings.KeyEnter, ConsoleKey.Enter); - AddKey(db, TermInfo.WellKnownStrings.KeyHelp, ConsoleKey.Help); - AddKey(db, TermInfo.WellKnownStrings.KeyHome, ConsoleKey.Home); - AddKey(db, TermInfo.WellKnownStrings.KeyInsert, ConsoleKey.Insert); - AddKey(db, TermInfo.WellKnownStrings.KeyLeft, ConsoleKey.LeftArrow); - AddKey(db, TermInfo.WellKnownStrings.KeyPageDown, ConsoleKey.PageDown); - AddKey(db, TermInfo.WellKnownStrings.KeyPageUp, ConsoleKey.PageUp); - AddKey(db, TermInfo.WellKnownStrings.KeyPrint, ConsoleKey.Print); - AddKey(db, TermInfo.WellKnownStrings.KeyRight, ConsoleKey.RightArrow); - AddKey(db, TermInfo.WellKnownStrings.KeyScrollForward, ConsoleKey.PageDown, shift: true, alt: false, control: false); - AddKey(db, TermInfo.WellKnownStrings.KeyScrollReverse, ConsoleKey.PageUp, shift: true, alt: false, control: false); - AddKey(db, TermInfo.WellKnownStrings.KeySBegin, ConsoleKey.Home, shift: true, alt: false, control: false); - AddKey(db, TermInfo.WellKnownStrings.KeySDelete, ConsoleKey.Delete, shift: true, alt: false, control: false); - AddKey(db, TermInfo.WellKnownStrings.KeySHome, ConsoleKey.Home, shift: true, alt: false, control: false); - AddKey(db, TermInfo.WellKnownStrings.KeySelect, ConsoleKey.Select); - AddKey(db, TermInfo.WellKnownStrings.KeySLeft, ConsoleKey.LeftArrow, shift: true, alt: false, control: false); - AddKey(db, TermInfo.WellKnownStrings.KeySPrint, ConsoleKey.Print, shift: true, alt: false, control: false); - AddKey(db, TermInfo.WellKnownStrings.KeySRight, ConsoleKey.RightArrow, shift: true, alt: false, control: false); - AddKey(db, TermInfo.WellKnownStrings.KeyUp, ConsoleKey.UpArrow); - AddPrefixKey(db, "kLFT", ConsoleKey.LeftArrow); - AddPrefixKey(db, "kRIT", ConsoleKey.RightArrow); - AddPrefixKey(db, "kUP", ConsoleKey.UpArrow); - AddPrefixKey(db, "kDN", ConsoleKey.DownArrow); - AddPrefixKey(db, "kDC", ConsoleKey.Delete); - AddPrefixKey(db, "kEND", ConsoleKey.End); - AddPrefixKey(db, "kHOM", ConsoleKey.Home); - AddPrefixKey(db, "kNXT", ConsoleKey.PageDown); - AddPrefixKey(db, "kPRV", ConsoleKey.PageUp); - - if (KeyFormatToConsoleKey.Count > 0) - { - MaxKeyFormatLength = int.MinValue; - MinKeyFormatLength = int.MaxValue; - - foreach (KeyValuePair, ConsoleKeyInfo> entry in KeyFormatToConsoleKey) - { - if (entry.Key.Length > MaxKeyFormatLength) - { - MaxKeyFormatLength = entry.Key.Length; - } - if (entry.Key.Length < MinKeyFormatLength) - { - MinKeyFormatLength = entry.Key.Length; - } - } - } - } - - private static string GetTitle(TermInfo.Database db) - { - // Try to get the format string from tsl/fsl and use it if they're available - string? tsl = db.GetString(TermInfo.WellKnownStrings.ToStatusLine); - string? fsl = db.GetString(TermInfo.WellKnownStrings.FromStatusLine); - if (tsl != null && fsl != null) - { - return tsl + "%p1%s" + fsl; - } - - string term = db.Term; - if (term == null) - { - return string.Empty; - } - - if (term.StartsWith("xterm", StringComparison.Ordinal)) // normalize all xterms to enable easier matching - { - term = "xterm"; - } - - switch (term) - { - case "aixterm": - case "dtterm": - case "linux": - case "rxvt": - case "xterm": - return "\x1B]0;%p1%s\x07"; - case "cygwin": - return "\x1B];%p1%s\x07"; - case "konsole": - return "\x1B]30;%p1%s\x07"; - case "screen": - return "\x1Bk%p1%s\x1B"; - default: - return string.Empty; - } - } - - private void AddKey(TermInfo.Database db, TermInfo.WellKnownStrings keyId, ConsoleKey key) - { - AddKey(db, keyId, key, shift: false, alt: false, control: false); - } - - private void AddKey(TermInfo.Database db, TermInfo.WellKnownStrings keyId, ConsoleKey key, bool shift, bool alt, bool control) - { - ReadOnlyMemory keyFormat = db.GetString(keyId).AsMemory(); - if (!keyFormat.IsEmpty) - KeyFormatToConsoleKey[keyFormat] = new ConsoleKeyInfo('\0', key, shift, alt, control); - } - - private void AddPrefixKey(TermInfo.Database db, string extendedNamePrefix, ConsoleKey key) - { - AddKey(db, extendedNamePrefix + "3", key, shift: false, alt: true, control: false); - AddKey(db, extendedNamePrefix + "4", key, shift: true, alt: true, control: false); - AddKey(db, extendedNamePrefix + "5", key, shift: false, alt: false, control: true); - AddKey(db, extendedNamePrefix + "6", key, shift: true, alt: false, control: true); - AddKey(db, extendedNamePrefix + "7", key, shift: false, alt: false, control: true); - } - - private void AddKey(TermInfo.Database db, string extendedName, ConsoleKey key, bool shift, bool alt, bool control) - { - ReadOnlyMemory keyFormat = db.GetExtendedString(extendedName).AsMemory(); - if (!keyFormat.IsEmpty) - KeyFormatToConsoleKey[keyFormat] = new ConsoleKeyInfo('\0', key, shift, alt, control); - } - } - /// Reads data from the file descriptor into the buffer. /// The file descriptor. /// The buffer to read into. @@ -1428,14 +1153,5 @@ public override void Flush() base.Flush(); } } - - private sealed class ReadOnlyMemoryContentComparer : IEqualityComparer> - { - public bool Equals(ReadOnlyMemory x, ReadOnlyMemory y) => - x.Span.SequenceEqual(y.Span); - - public int GetHashCode(ReadOnlyMemory obj) => - string.GetHashCode(obj.Span); - } } } diff --git a/src/libraries/System.Console/src/System/IO/KeyParser.cs b/src/libraries/System.Console/src/System/IO/KeyParser.cs new file mode 100644 index 0000000000000..d54e0800a42b9 --- /dev/null +++ b/src/libraries/System.Console/src/System/IO/KeyParser.cs @@ -0,0 +1,392 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.IO; + +internal static class KeyParser +{ + private const char Escape = '\u001B'; + private const char Delete = '\u007F'; + private const char VtSequenceEndTag = '~'; + private const char ModifierSeparator = ';'; + private const int MinimalSequenceLength = 3; + private const int SequencePrefixLength = 2; // ^[[ ("^[" stands for Escape) + + internal static ConsoleKeyInfo Parse(char[] buffer, TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter, ref int startIndex, int endIndex) + { + int length = endIndex - startIndex; + Debug.Assert(length > 0); + + // VERASE overrides anything from Terminfo. Both settings can be different for Linux and macOS. + if (buffer[startIndex] != posixDisableValue && buffer[startIndex] == veraseCharacter) + { + // the original char is preserved on purpose (backward compat + consistency) + return new ConsoleKeyInfo(buffer[startIndex++], ConsoleKey.Backspace, false, false, false); + } + + // Escape Sequences start with Escape. But some terminals like PuTTY and rxvt prepend Escape to express that for given sequence Alt was pressed. + if (length >= MinimalSequenceLength + 1 && buffer[startIndex] == Escape && buffer[startIndex + 1] == Escape) + { + startIndex++; + if (TryParseTerminalInputSequence(buffer, terminalFormatStrings, out ConsoleKeyInfo parsed, ref startIndex, endIndex)) + { + return new ConsoleKeyInfo(parsed.KeyChar, parsed.Key, (parsed.Modifiers & ConsoleModifiers.Shift) != 0, alt: true, (parsed.Modifiers & ConsoleModifiers.Control) != 0); + } + startIndex--; + } + else if (length >= MinimalSequenceLength && TryParseTerminalInputSequence(buffer, terminalFormatStrings, out ConsoleKeyInfo parsed, ref startIndex, endIndex)) + { + return parsed; + } + + if (length == 2 && buffer[startIndex] == Escape && buffer[startIndex + 1] != Escape) + { + startIndex++; // skip the Escape + return ParseFromSingleChar(buffer[startIndex++], isAlt: true); + } + + return ParseFromSingleChar(buffer[startIndex++], isAlt: false); + } + + private static bool TryParseTerminalInputSequence(char[] buffer, TerminalFormatStrings terminalFormatStrings, out ConsoleKeyInfo parsed, ref int startIndex, int endIndex) + { + ReadOnlySpan input = buffer.AsSpan(startIndex, endIndex - startIndex); + parsed = default; + + // sequences start with either "^[[" or "^[O". "^[" stands for Escape (27). + if (input.Length < MinimalSequenceLength || input[0] != Escape || (input[1] != '[' && input[1] != 'O')) + { + return false; + } + + Dictionary, ConsoleKeyInfo> terminfoDb = terminalFormatStrings.KeyFormatToConsoleKey; // the most important source of truth + ConsoleModifiers modifiers = 0; + ConsoleKey key; + + // Is it a three character sequence? (examples: '^[[H' (Home), '^[OP' (F1)) + if (input[1] == 'O' || char.IsAsciiLetter(input[2]) || input.Length == MinimalSequenceLength) + { + if (!terminfoDb.TryGetValue(buffer.AsMemory(startIndex, MinimalSequenceLength), out parsed)) + { + // All terminals which use "^[O{letter}" escape sequences don't define conflicting mappings. + // Example: ^[OH either means Home or simply is not used by given terminal. + // But with "^[[{character}" sequences, there are conflicts between rxvt and SCO. + // Example: "^[[a" is Shift+UpArrow for rxvt and Shift+F3 for SCO. + (key, modifiers) = input[1] == 'O' || terminalFormatStrings.IsRxvtTerm + ? MapKeyIdOXterm(input[2], terminalFormatStrings.IsRxvtTerm) + : MapSCO(input[2]); + + if (key == default) + { + return false; // it was not a known sequence + } + + char keyChar = key switch + { + ConsoleKey.Enter => '\r', // "^[OM" should produce new line character (was not previously mapped this way) + ConsoleKey.Add => '+', + ConsoleKey.Subtract => '-', + ConsoleKey.Divide => '/', + ConsoleKey.Multiply => '*', + _ => default + }; + parsed = Create(keyChar, key, modifiers); + } + + startIndex += MinimalSequenceLength; + return true; + } + + // Is it a four character sequence used by Linux Console or PuTTy configured to emulate it? (examples: '^[[[A' (F1), '^[[[B' (F2)) + if (input[1] == '[' && input[2] == '[' && char.IsBetween(input[3], 'A', 'E')) + { + if (!terminfoDb.TryGetValue(buffer.AsMemory(startIndex, 4), out parsed)) + { + parsed = new ConsoleKeyInfo(default, ConsoleKey.F1 + input[3] - 'A', false, false, false); + } + + startIndex += 4; + return true; + } + + // If sequence does not start with a letter, it must start with one or two digits that represent the Sequence Number + int digitCount = !char.IsBetween(input[SequencePrefixLength], '1', '9') // not using IsAsciiDigit as 0 is invalid + ? 0 + : char.IsDigit(input[SequencePrefixLength + 1]) ? 2 : 1; + + if (digitCount == 0 // it does not start with a digit, it's not a sequence + || SequencePrefixLength + digitCount >= input.Length) // it's too short to be a complete sequence + { + parsed = default; + return false; + } + + if (IsSequenceEndTag(input[SequencePrefixLength + digitCount])) + { + // it's a VT Sequence like ^[[11~ or rxvt like ^[[11^ + int sequenceLength = SequencePrefixLength + digitCount + 1; + if (!terminfoDb.TryGetValue(buffer.AsMemory(startIndex, sequenceLength), out parsed)) + { + key = MapEscapeSequenceNumber(byte.Parse(input.Slice(SequencePrefixLength, digitCount))); + if (key == default) + { + return false; // it was not a known sequence + } + + if (IsRxvtModifier(input[SequencePrefixLength + digitCount])) + { + modifiers = MapRxvtModifiers(input[SequencePrefixLength + digitCount]); + } + + parsed = Create(default, key, modifiers); + } + + startIndex += sequenceLength; + return true; + } + + // If Sequence Number is not followed by the VT Seqence End Tag, + // it can be followed only by a Modifier Separator, Modifier (2-8) and Key ID or VT Sequence End Tag. + if (input[SequencePrefixLength + digitCount] is not ModifierSeparator + || SequencePrefixLength + digitCount + 2 >= input.Length + || !char.IsBetween(input[SequencePrefixLength + digitCount + 1], '2', '8') + || (!char.IsAsciiLetterUpper(input[SequencePrefixLength + digitCount + 2]) && input[SequencePrefixLength + digitCount + 2] is not VtSequenceEndTag)) + { + return false; + } + + modifiers = MapXtermModifiers(input[SequencePrefixLength + digitCount + 1]); + + key = input[SequencePrefixLength + digitCount + 2] is VtSequenceEndTag + ? MapEscapeSequenceNumber(byte.Parse(input.Slice(SequencePrefixLength, digitCount))) + : MapKeyIdOXterm(input[SequencePrefixLength + digitCount + 2], terminalFormatStrings.IsRxvtTerm).key; + + if (key == default) + { + return false; + } + + startIndex += SequencePrefixLength + digitCount + 3; // 3 stands for separator, modifier and end tag or id + parsed = Create(default, key, modifiers); + return true; + + // maps "^[O{character}" for all Terminals and "^[[{character}" for rxvt Terminals + static (ConsoleKey key, ConsoleModifiers modifiers) MapKeyIdOXterm(char character, bool isRxvt) + => character switch + { + 'A' or 'x' => (ConsoleKey.UpArrow, 0), // lowercase used by rxvt + 'a' => (ConsoleKey.UpArrow, ConsoleModifiers.Shift), // rxvt + 'B' or 'r' => (ConsoleKey.DownArrow, 0), // lowercase used by rxv + 'b' => (ConsoleKey.DownArrow, ConsoleModifiers.Shift), // used by rxvt + 'C' or 'v' => (ConsoleKey.RightArrow, 0), // lowercase used by rxv + 'c' => (ConsoleKey.RightArrow, ConsoleModifiers.Shift), // used by rxvt + 'D' or 't' => (ConsoleKey.LeftArrow, 0), // lowercase used by rxv + 'd' => (ConsoleKey.LeftArrow, ConsoleModifiers.Shift), // used by rxvt + 'E' => (ConsoleKey.NoName, 0), // ^[OE maps to Begin, but we don't have such Key. To reproduce press Num5. + 'F' or 'q' => (ConsoleKey.End, 0), + 'H' => (ConsoleKey.Home, 0), + 'j' => (ConsoleKey.Multiply, 0), // used by both xterm and rxvt + 'k' => (ConsoleKey.Add, 0), // used by both xterm and rxvt + 'm' => (ConsoleKey.Subtract, 0), // used by both xterm and rxvt + 'M' => (ConsoleKey.Enter, 0), // used by xterm, rxvt (they have it Terminfo) and tmux (no record in Terminfo) + 'n' => (ConsoleKey.Delete, 0), // rxvt + 'o' => (ConsoleKey.Divide, 0), // used by both xterm and rxvt + 'P' => (ConsoleKey.F1, 0), + 'p' => (ConsoleKey.Insert, 0), // rxvt + 'Q' => (ConsoleKey.F2, 0), + 'R' => (ConsoleKey.F3, 0), + 'S' => (ConsoleKey.F4, 0), + 's' => (ConsoleKey.PageDown, 0), // rxvt + 'T' => (ConsoleKey.F5, 0), // VT 100+ + 'U' => (ConsoleKey.F6, 0), // VT 100+ + 'u' => (ConsoleKey.NoName, 0), // it should be Begin, but we don't have such (press Num5 in rxvt to reproduce) + 'V' => (ConsoleKey.F7, 0), // VT 100+ + 'W' => (ConsoleKey.F8, 0), // VT 100+ + 'w' when isRxvt => (ConsoleKey.Home, 0), + 'w' when !isRxvt => (ConsoleKey.End, 0), + 'X' => (ConsoleKey.F9, 0), // VT 100+ + 'Y' => (ConsoleKey.F10, 0), // VT 100+ + 'y' => (ConsoleKey.PageUp, 0), // rxvt + 'Z' => (ConsoleKey.F11, 0), // VT 100+ + '[' => (ConsoleKey.F12, 0), // VT 100+ + _ => default + }; + + // maps "^[[{character}" for SCO terminals, based on https://vt100.net/docs/vt510-rm/chapter6.html + static (ConsoleKey key, ConsoleModifiers modifiers) MapSCO(char character) + => character switch + { + 'A' => (ConsoleKey.UpArrow, 0), + 'B' => (ConsoleKey.DownArrow, 0), + 'C' => (ConsoleKey.RightArrow, 0), + 'D' => (ConsoleKey.LeftArrow, 0), + 'F' => (ConsoleKey.End, 0), + 'G' => (ConsoleKey.PageDown, 0), + 'H' => (ConsoleKey.Home, 0), + 'I' => (ConsoleKey.PageUp, 0), + _ when char.IsBetween(character, 'M', 'X') => (ConsoleKey.F1 + character - 'M', 0), + _ when char.IsBetween(character, 'Y', 'Z') => (ConsoleKey.F1 + character - 'Y', ConsoleModifiers.Shift), + _ when char.IsBetween(character, 'a', 'j') => (ConsoleKey.F3 + character - 'a', ConsoleModifiers.Shift), + _ when char.IsBetween(character, 'k', 'v') => (ConsoleKey.F1 + character - 'k', ConsoleModifiers.Control), + _ when char.IsBetween(character, 'w', 'z') => (ConsoleKey.F1 + character - 'w', ConsoleModifiers.Control | ConsoleModifiers.Shift), + '@' => (ConsoleKey.F5, ConsoleModifiers.Control | ConsoleModifiers.Shift), + '[' => (ConsoleKey.F6, ConsoleModifiers.Control | ConsoleModifiers.Shift), + '<' or '\\' => (ConsoleKey.F7, ConsoleModifiers.Control | ConsoleModifiers.Shift), // the Spec says <, PuTTy uses \. + ']' => (ConsoleKey.F8, ConsoleModifiers.Control | ConsoleModifiers.Shift), + '^' => (ConsoleKey.F9, ConsoleModifiers.Control | ConsoleModifiers.Shift), + '_' => (ConsoleKey.F10, ConsoleModifiers.Control | ConsoleModifiers.Shift), + '`' => (ConsoleKey.F11, ConsoleModifiers.Control | ConsoleModifiers.Shift), + '{' => (ConsoleKey.F12, ConsoleModifiers.Control | ConsoleModifiers.Shift), + _ => default + }; + + // based on https://en.wikipedia.org/wiki/ANSI_escape_code#Fe_Escape_sequences + static ConsoleKey MapEscapeSequenceNumber(byte number) + => number switch + { + 1 or 7 => ConsoleKey.Home, + 2 => ConsoleKey.Insert, + 3 => ConsoleKey.Delete, + 4 or 8 => ConsoleKey.End, + 5 => ConsoleKey.PageUp, + 6 => ConsoleKey.PageDown, + // Limitation: 10 is mapped to F0, ConsoleKey does not define it so it's not supported. + 11 => ConsoleKey.F1, + 12 => ConsoleKey.F2, + 13 => ConsoleKey.F3, + 14 => ConsoleKey.F4, + 15 => ConsoleKey.F5, + 17 => ConsoleKey.F6, + 18 => ConsoleKey.F7, + 19 => ConsoleKey.F8, + 20 => ConsoleKey.F9, + 21 => ConsoleKey.F10, + 23 => ConsoleKey.F11, + 24 => ConsoleKey.F12, + 25 => ConsoleKey.F13, + 26 => ConsoleKey.F14, + 28 => ConsoleKey.F15, + 29 => ConsoleKey.F16, + 31 => ConsoleKey.F17, + 32 => ConsoleKey.F18, + 33 => ConsoleKey.F19, + 34 => ConsoleKey.F20, + // 9, 16, 22, 27, 30 and 35 have no mapping + _ => default + }; + + // based on https://www.xfree86.org/current/ctlseqs.html + static ConsoleModifiers MapXtermModifiers(char modifier) + => modifier switch + { + '2' => ConsoleModifiers.Shift, + '3' => ConsoleModifiers.Alt, + '4' => ConsoleModifiers.Shift | ConsoleModifiers.Alt, + '5' => ConsoleModifiers.Control, + '6' => ConsoleModifiers.Shift | ConsoleModifiers.Control, + '7' => ConsoleModifiers.Alt | ConsoleModifiers.Control, + '8' => ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control, + _ => default + }; + + static bool IsSequenceEndTag(char character) => character is VtSequenceEndTag || IsRxvtModifier(character); + + static bool IsRxvtModifier(char character) => MapRxvtModifiers(character) != default; + + static ConsoleModifiers MapRxvtModifiers(char modifier) + => modifier switch + { + '^' => ConsoleModifiers.Control, + '$' => ConsoleModifiers.Shift, + '@' => ConsoleModifiers.Control | ConsoleModifiers.Shift, + _ => default + }; + + static ConsoleKeyInfo Create(char keyChar, ConsoleKey key, ConsoleModifiers modifiers) + => new(keyChar, key, (modifiers & ConsoleModifiers.Shift) != 0, (modifiers & ConsoleModifiers.Alt) != 0, (modifiers & ConsoleModifiers.Control) != 0); + } + + private static ConsoleKeyInfo ParseFromSingleChar(char single, bool isAlt) + { + bool isShift = false, isCtrl = false; + char keyChar = single; + + ConsoleKey key = single switch + { + '\b' => ConsoleKey.Backspace, + '\t' => ConsoleKey.Tab, + '\r' or '\n' => ConsoleKey.Enter, + ' ' => ConsoleKey.Spacebar, + Escape => ConsoleKey.Escape, // Ctrl+[ and Ctrl+3 are also mapped to 27. Limitation: Ctrl+[ and Ctrl+3 can't be mapped. + Delete => ConsoleKey.Backspace, // Ctrl+8 and Backspace are mapped to 127 (ASCII Delete key). Limitation: Ctrl+8 can't be mapped. + '*' => ConsoleKey.Multiply, // We can't distinguish Dx+Shift and Multiply (Numeric Keypad). Limitation: Shift+Dx can't be mapped. + '/' => ConsoleKey.Divide, // We can't distinguish OemX and Divide (Numeric Keypad). Limitation: OemX keys can't be mapped. + '-' => ConsoleKey.Subtract, // We can't distinguish OemMinus and Subtract (Numeric Keypad). Limitation: OemMinus can't be mapped. + '+' => ConsoleKey.Add, // We can't distinguish OemPlus and Add (Numeric Keypad). Limitation: OemPlus can't be mapped. + '=' => default, // '+' is not mapped to OemPlus, so `=` is not mapped to Shift+OemPlus. Limitation: Shift+OemPlus can't be mapped. + '!' or '@' or '#' or '$' or '%' or '^' or '&' or '&' or '*' or '(' or ')' => default, // We can't make assumptions about keyboard layout neither read it. Limitation: Shift+Dx keys can't be mapped. + ',' => ConsoleKey.OemComma, // was not previously mapped this way + '.' => ConsoleKey.OemPeriod, // was not previously mapped this way + _ when char.IsAsciiLetterLower(single) => ConsoleKey.A + single - 'a', + _ when char.IsAsciiLetterUpper(single) => UppercaseCharacter(single, out isShift), + _ when char.IsAsciiDigit(single) => ConsoleKey.D0 + single - '0', // We can't distinguish DX and Ctrl+DX as they produce same values. Limitation: Ctrl+DX can't be mapped. + _ when char.IsBetween(single, (char)1, (char)26) => ControlAndLetterPressed(single, out keyChar, out isCtrl), + _ when char.IsBetween(single, (char)28, (char)31) => ControlAndDigitPressed(single, out keyChar, out isCtrl), + '\u0000' => ControlAndDigitPressed(single, out keyChar, out isCtrl), + _ => default + }; + + if (single is '\b' or '\n') + { + isCtrl = true; // Ctrl+Backspace is mapped to '\b' (8), Ctrl+Enter to '\n' (10) + } + + if (isAlt) + { + isAlt = key != default; // two char sequences starting with Escape are Alt+$Key only when we can recognize the key + } + + return new ConsoleKeyInfo(keyChar, key, isShift, isAlt, isCtrl); + + static ConsoleKey UppercaseCharacter(char single, out bool isShift) + { + // Previous implementation assumed that all uppercase characters were typed using Shift. + // Limitation: Caps Lock+(a-z) is always mapped to Shift+(a-z). + isShift = true; + return ConsoleKey.A + single - 'A'; + } + + static ConsoleKey ControlAndLetterPressed(char single, out char keyChar, out bool isCtrl) + { + // Ctrl+(a-z) characters are mapped to values from 1 to 26. + // Ctrl+H is mapped to 8, which also maps to Ctrl+Backspace. + // Ctrl+I is mapped to 9, which also maps to Tab. + // Ctrl+J is mapped to 10, which also maps to Ctrl+Enter ('\n'). + // Ctrl+M is mapped to 13, which also maps to Enter ('\r'). + // Limitation: Ctrl+H, Ctrl+I, Ctrl+J and Crl+M can't be mapped. More: https://unix.stackexchange.com/questions/563469/conflict-ctrl-i-with-tab-in-normal-mode + Debug.Assert(single != 'b' && single != '\t' && single != '\n' && single != '\r'); + + isCtrl = true; + keyChar = default; // we could use the letter here, but it's impossible to distinguish upper vs lowercase (and Windows doesn't do it as well) + return ConsoleKey.A + single - 1; + } + + static ConsoleKey ControlAndDigitPressed(char single, out char keyChar, out bool isCtrl) + { + // Ctrl+(D3-D7) characters are mapped to values from 27 to 31. Escape is also mapped to 27. + // Limitation: Ctrl+(D1, D3, D8, D9 and D0) can't be mapped. + Debug.Assert(single == default || char.IsBetween(single, (char)28, (char)31)); + + isCtrl = true; + keyChar = default; // consistent with Windows + return single switch + { + '\u0000' => ConsoleKey.D2, // was not previously mapped this way + _ => ConsoleKey.D4 + single - 28 + }; + } + } +} diff --git a/src/libraries/System.Console/src/System/IO/Net6KeyParser.cs b/src/libraries/System.Console/src/System/IO/Net6KeyParser.cs new file mode 100644 index 0000000000000..7420978e566e4 --- /dev/null +++ b/src/libraries/System.Console/src/System/IO/Net6KeyParser.cs @@ -0,0 +1,180 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.IO; + +internal static class Net6KeyParser +{ + internal static ConsoleKeyInfo Parse(char[] buffer, TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter, ref int startIndex, int endIndex) + { + MapBufferToConsoleKey(buffer, terminalFormatStrings, posixDisableValue, veraseCharacter, out ConsoleKey key, + out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, endIndex); + + // Replace the '\n' char for Enter by '\r' to match Windows behavior. + if (key == ConsoleKey.Enter && ch == '\n') + { + ch = '\r'; + } + + return new ConsoleKeyInfo(ch, key, isShift, isAlt, isCtrl); + } + + private static bool MapBufferToConsoleKey(char[] buffer, TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter, + out ConsoleKey key, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref int startIndex, int endIndex) + { + // Try to get the special key match from the TermInfo static information. + if (TryGetSpecialConsoleKey(buffer, startIndex, endIndex, terminalFormatStrings, posixDisableValue, veraseCharacter, out ConsoleKeyInfo keyInfo, out int keyLength)) + { + key = keyInfo.Key; + isShift = (keyInfo.Modifiers & ConsoleModifiers.Shift) != 0; + isAlt = (keyInfo.Modifiers & ConsoleModifiers.Alt) != 0; + isCtrl = (keyInfo.Modifiers & ConsoleModifiers.Control) != 0; + + ch = ((keyLength == 1) ? buffer[startIndex] : '\0'); // ignore keyInfo.KeyChar + startIndex += keyLength; + return true; + } + + // Check if we can match Esc + combination and guess if alt was pressed. + if (buffer[startIndex] == (char)0x1B && // Alt is send as an escape character + endIndex - startIndex >= 2) // We have at least two characters to read + { + startIndex++; + if (MapBufferToConsoleKey(buffer, terminalFormatStrings, posixDisableValue, veraseCharacter, out key, out ch, out isShift, out _, out isCtrl, ref startIndex, endIndex)) + { + isAlt = true; + return true; + } + else + { + // We could not find a matching key here so, Alt+ combination assumption is in-correct. + // The current key needs to be marked as Esc key. + // Also, we do not increment _startIndex as we already did it. + key = ConsoleKey.Escape; + ch = (char)0x1B; + isAlt = false; + return true; + } + } + + // Try reading the first char in the buffer and interpret it as a key. + ch = buffer[startIndex++]; + key = GetKeyFromCharValue(ch, out isShift, out isCtrl); + isAlt = false; + return key != default(ConsoleKey); + } + + private static bool TryGetSpecialConsoleKey(char[] givenChars, int startIndex, int endIndex, + TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter, out ConsoleKeyInfo key, out int keyLength) + { + int unprocessedCharCount = endIndex - startIndex; + + // First process special control character codes. These override anything from terminfo. + if (unprocessedCharCount > 0) + { + // Is this an erase / backspace? + char c = givenChars[startIndex]; + if (c != posixDisableValue && c == veraseCharacter) + { + key = new ConsoleKeyInfo(c, ConsoleKey.Backspace, shift: false, alt: false, control: false); + keyLength = 1; + return true; + } + } + + // Then process terminfo mappings. + int minRange = terminalFormatStrings.MinKeyFormatLength; + if (unprocessedCharCount >= minRange) + { + int maxRange = Math.Min(unprocessedCharCount, terminalFormatStrings.MaxKeyFormatLength); + + for (int i = maxRange; i >= minRange; i--) + { + var currentString = new ReadOnlyMemory(givenChars, startIndex, i); + + // Check if the string prefix matches. + if (terminalFormatStrings.KeyFormatToConsoleKey.TryGetValue(currentString, out key)) + { + keyLength = currentString.Length; + return true; + } + } + } + + // Otherwise, not a known special console key. + key = default(ConsoleKeyInfo); + keyLength = 0; + return false; + } + + private static ConsoleKey GetKeyFromCharValue(char x, out bool isShift, out bool isCtrl) + { + isShift = false; + isCtrl = false; + + switch (x) + { + case '\b': + return ConsoleKey.Backspace; + + case '\t': + return ConsoleKey.Tab; + + case '\n': + case '\r': + return ConsoleKey.Enter; + + case (char)(0x1B): + return ConsoleKey.Escape; + + case '*': + return ConsoleKey.Multiply; + + case '+': + return ConsoleKey.Add; + + case '-': + return ConsoleKey.Subtract; + + case '/': + return ConsoleKey.Divide; + + case (char)(0x7F): + return ConsoleKey.Delete; + + case ' ': + return ConsoleKey.Spacebar; + + default: + // 1. Ctrl A to Ctrl Z. + if (char.IsBetween(x, (char)1, (char)26)) + { + isCtrl = true; + return ConsoleKey.A + x - 1; + } + + // 2. Numbers from 0 to 9. + if (char.IsAsciiDigit(x)) + { + return ConsoleKey.D0 + x - '0'; + } + + //3. A to Z + if (char.IsAsciiLetterUpper(x)) + { + isShift = true; + return ConsoleKey.A + (x - 'A'); + } + + // 4. a to z. + if (char.IsAsciiLetterLower(x)) + { + return ConsoleKey.A + (x - 'a'); + } + + break; + } + + return default(ConsoleKey); + } +} diff --git a/src/libraries/System.Console/src/System/IO/StdInReader.cs b/src/libraries/System.Console/src/System/IO/StdInReader.cs index e20465b226567..7b0105c9de087 100644 --- a/src/libraries/System.Console/src/System/IO/StdInReader.cs +++ b/src/libraries/System.Console/src/System/IO/StdInReader.cs @@ -151,7 +151,7 @@ private bool ReadLineCore(bool consumeKeys) // Don't carry over chars from previous ReadLine call. _readLineSB.Clear(); - Interop.Sys.InitializeConsoleBeforeRead(); + Interop.Sys.InitializeConsoleBeforeRead(distinguishNewLines: !ConsoleUtils.UseNet6KeyParser); try { // Read key-by-key until we've read a line. @@ -205,7 +205,7 @@ private bool ReadLineCore(bool consumeKeys) if (ConsolePal.TryGetCursorPosition(out int left, out int top, reinitializeForRead: true) && left == 0 && top > 0) { - s_clearToEol ??= ConsolePal.TerminalFormatStrings.Instance.ClrEol ?? string.Empty; + s_clearToEol ??= ConsolePal.TerminalFormatStringsInstance.ClrEol ?? string.Empty; // Move to end of previous line ConsolePal.SetCursorPosition(ConsolePal.WindowWidth - 1, top - 1); @@ -216,7 +216,7 @@ private bool ReadLineCore(bool consumeKeys) { if (s_moveLeftString == null) { - string? moveLeft = ConsolePal.TerminalFormatStrings.Instance.CursorLeft; + string? moveLeft = ConsolePal.TerminalFormatStringsInstance.CursorLeft; s_moveLeftString = !string.IsNullOrEmpty(moveLeft) ? moveLeft + " " + moveLeft : string.Empty; } @@ -301,125 +301,6 @@ private static bool IsEol(char c) (c == ConsolePal.s_veolCharacter || c == ConsolePal.s_veol2Character || c == ConsolePal.s_veofCharacter); } - internal static ConsoleKey GetKeyFromCharValue(char x, out bool isShift, out bool isCtrl) - { - isShift = false; - isCtrl = false; - - switch (x) - { - case '\b': - return ConsoleKey.Backspace; - - case '\t': - return ConsoleKey.Tab; - - case '\n': - case '\r': - return ConsoleKey.Enter; - - case (char)(0x1B): - return ConsoleKey.Escape; - - case '*': - return ConsoleKey.Multiply; - - case '+': - return ConsoleKey.Add; - - case '-': - return ConsoleKey.Subtract; - - case '/': - return ConsoleKey.Divide; - - case (char)(0x7F): - return ConsoleKey.Delete; - - case ' ': - return ConsoleKey.Spacebar; - - default: - // 1. Ctrl A to Ctrl Z. - if (char.IsBetween(x, (char)1, (char)26)) - { - isCtrl = true; - return ConsoleKey.A + x - 1; - } - - // 2. Numbers from 0 to 9. - if (char.IsAsciiDigit(x)) - { - return ConsoleKey.D0 + x - '0'; - } - - //3. A to Z - if (char.IsAsciiLetterUpper(x)) - { - isShift = true; - return ConsoleKey.A + (x - 'A'); - } - - // 4. a to z. - if (char.IsAsciiLetterLower(x)) - { - return ConsoleKey.A + (x - 'a'); - } - - break; - } - - return default(ConsoleKey); - } - - internal bool MapBufferToConsoleKey(out ConsoleKey key, out char ch, out bool isShift, out bool isAlt, out bool isCtrl) - { - Debug.Assert(!IsUnprocessedBufferEmpty()); - - // Try to get the special key match from the TermInfo static information. - ConsoleKeyInfo keyInfo; - int keyLength; - if (ConsolePal.TryGetSpecialConsoleKey(_unprocessedBufferToBeRead, _startIndex, _endIndex, out keyInfo, out keyLength)) - { - key = keyInfo.Key; - isShift = (keyInfo.Modifiers & ConsoleModifiers.Shift) != 0; - isAlt = (keyInfo.Modifiers & ConsoleModifiers.Alt) != 0; - isCtrl = (keyInfo.Modifiers & ConsoleModifiers.Control) != 0; - - ch = ((keyLength == 1) ? _unprocessedBufferToBeRead[_startIndex] : '\0'); // ignore keyInfo.KeyChar - _startIndex += keyLength; - return true; - } - - // Check if we can match Esc + combination and guess if alt was pressed. - if (_unprocessedBufferToBeRead[_startIndex] == (char)0x1B && // Alt is send as an escape character - _endIndex - _startIndex >= 2) // We have at least two characters to read - { - _startIndex++; - if (MapBufferToConsoleKey(out key, out ch, out isShift, out _, out isCtrl)) - { - isAlt = true; - return true; - } - else - { - // We could not find a matching key here so, Alt+ combination assumption is in-correct. - // The current key needs to be marked as Esc key. - // Also, we do not increment _startIndex as we already did it. - key = ConsoleKey.Escape; - ch = (char)0x1B; - isAlt = false; - return true; - } - } - - // Try reading the first char in the buffer and interpret it as a key. - ch = _unprocessedBufferToBeRead[_startIndex++]; - key = GetKeyFromCharValue(ch, out isShift, out isCtrl); - isAlt = false; - return key != default(ConsoleKey); - } - /// /// Try to intercept the key pressed. /// @@ -446,13 +327,10 @@ private unsafe ConsoleKeyInfo ReadKey() { Debug.Assert(_availableKeys.Count == 0); - Interop.Sys.InitializeConsoleBeforeRead(); + bool useNet6KeyParser = ConsoleUtils.UseNet6KeyParser; + Interop.Sys.InitializeConsoleBeforeRead(distinguishNewLines: !useNet6KeyParser); try { - ConsoleKey key; - char ch; - bool isAlt, isCtrl, isShift; - if (IsUnprocessedBufferEmpty()) { // Read in bytes @@ -476,15 +354,9 @@ private unsafe ConsoleKeyInfo ReadKey() } } - MapBufferToConsoleKey(out key, out ch, out isShift, out isAlt, out isCtrl); - - // Replace the '\n' char for Enter by '\r' to match Windows behavior. - if (key == ConsoleKey.Enter && ch == '\n') - { - ch = '\r'; - } - - return new ConsoleKeyInfo(ch, key, isShift, isAlt, isCtrl); + return useNet6KeyParser + ? Net6KeyParser.Parse(_unprocessedBufferToBeRead, ConsolePal.TerminalFormatStringsInstance, ConsolePal.s_posixDisableValue, ConsolePal.s_veraseCharacter, ref _startIndex, _endIndex) + : KeyParser.Parse(_unprocessedBufferToBeRead, ConsolePal.TerminalFormatStringsInstance, ConsolePal.s_posixDisableValue, ConsolePal.s_veraseCharacter, ref _startIndex, _endIndex); } finally { @@ -493,6 +365,6 @@ private unsafe ConsoleKeyInfo ReadKey() } /// Gets whether there's input waiting on stdin. - internal static bool StdinReady => Interop.Sys.StdinReady(); + internal static bool StdinReady => Interop.Sys.StdinReady(distinguishNewLines: !ConsoleUtils.UseNet6KeyParser); } } diff --git a/src/libraries/System.Console/src/System/TermInfo.Database.cs b/src/libraries/System.Console/src/System/TermInfo.Database.cs new file mode 100644 index 0000000000000..de09ea072c3d8 --- /dev/null +++ b/src/libraries/System.Console/src/System/TermInfo.Database.cs @@ -0,0 +1,310 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace System; + +internal static partial class TermInfo +{ + /// Provides a terminfo database. + internal sealed class Database + { + /// The name of the terminfo file. + private readonly string _term; + /// Raw data of the database instance. + private readonly byte[] _data; + + /// The number of bytes in the names section of the database. + private readonly int _nameSectionNumBytes; + /// The number of bytes in the Booleans section of the database. + private readonly int _boolSectionNumBytes; + /// The number of integers in the numbers section of the database. + private readonly int _numberSectionNumInts; + /// The number of offsets in the strings section of the database. + private readonly int _stringSectionNumOffsets; + /// The number of bytes in the strings table of the database. + private readonly int _stringTableNumBytes; + /// Whether or not to read the number section as 32-bit integers. + private readonly bool _readAs32Bit; + /// The size of the integers on the number section. + private readonly int _sizeOfInt; + + /// Extended / user-defined entries in the terminfo database. + private readonly Dictionary _extendedStrings; + + /// Initializes the database instance. + /// The name of the terminal. + /// The data from the terminfo file. + internal Database(string term, byte[] data) + { + _term = term; + _data = data; + + const int MagicLegacyNumber = 0x11A; // magic number octal 0432 for legacy ncurses terminfo + const int Magic32BitNumber = 0x21E; // magic number octal 01036 for new ncruses terminfo + short magic = ReadInt16(data, 0); + _readAs32Bit = + magic == MagicLegacyNumber ? false : + magic == Magic32BitNumber ? true : + throw new InvalidOperationException(SR.Format(SR.IO_TermInfoInvalidMagicNumber, "O" + Convert.ToString(magic, 8))); // magic number was not recognized. Printing the magic number in octal. + _sizeOfInt = (_readAs32Bit) ? 4 : 2; + + _nameSectionNumBytes = ReadInt16(data, 2); + _boolSectionNumBytes = ReadInt16(data, 4); + _numberSectionNumInts = ReadInt16(data, 6); + _stringSectionNumOffsets = ReadInt16(data, 8); + _stringTableNumBytes = ReadInt16(data, 10); + if (_nameSectionNumBytes < 0 || + _boolSectionNumBytes < 0 || + _numberSectionNumInts < 0 || + _stringSectionNumOffsets < 0 || + _stringTableNumBytes < 0) + { + throw new InvalidOperationException(SR.IO_TermInfoInvalid); + } + + // In addition to the main section of bools, numbers, and strings, there is also + // an "extended" section. This section contains additional entries that don't + // have well-known indices, and are instead named mappings. As such, we parse + // all of this data now rather than on each request, as the mapping is fairly complicated. + // This function relies on the data stored above, so it's the last thing we run. + // (Note that the extended section also includes other Booleans and numbers, but we don't + // have any need for those now, so we don't parse them.) + int extendedBeginning = RoundUpToEven(StringsTableOffset + _stringTableNumBytes); + _extendedStrings = ParseExtendedStrings(data, extendedBeginning, _readAs32Bit) ?? new Dictionary(); + } + + /// The name of the associated terminfo, if any. + public string Term { get { return _term; } } + + /// The offset into data where the names section begins. + private const int NamesOffset = 12; // comes right after the header, which is always 12 bytes + + /// The offset into data where the Booleans section begins. + private int BooleansOffset { get { return NamesOffset + _nameSectionNumBytes; } } // after the names section + + /// The offset into data where the numbers section begins. + private int NumbersOffset { get { return RoundUpToEven(BooleansOffset + _boolSectionNumBytes); } } // after the Booleans section, at an even position + + /// + /// The offset into data where the string offsets section begins. We index into this section + /// to find the location within the strings table where a string value exists. + /// + private int StringOffsetsOffset { get { return NumbersOffset + (_numberSectionNumInts * _sizeOfInt); } } + + /// The offset into data where the string table exists. + private int StringsTableOffset { get { return StringOffsetsOffset + (_stringSectionNumOffsets * 2); } } + + /// Gets a string from the strings section by the string's well-known index. + /// The index of the string to find. + /// The string if it's in the database; otherwise, null. + public string? GetString(WellKnownStrings stringTableIndex) + { + int index = (int)stringTableIndex; + Debug.Assert(index >= 0); + + if (index >= _stringSectionNumOffsets) + { + // Some terminfo files may not contain enough entries to actually + // have the requested one. + return null; + } + + int tableIndex = ReadInt16(_data, StringOffsetsOffset + (index * 2)); + if (tableIndex == -1) + { + // Some terminfo files may have enough entries, but may not actually + // have it filled in for this particular string. + return null; + } + + return ReadString(_data, StringsTableOffset + tableIndex); + } + + /// Gets a string from the extended strings section. + /// The name of the string as contained in the extended names section. + /// The string if it's in the database; otherwise, null. + public string? GetExtendedString(string name) + { + Debug.Assert(name != null); + + string? value; + return _extendedStrings.TryGetValue(name, out value) ? + value : + null; + } + + /// Gets a number from the numbers section by the number's well-known index. + /// The index of the string to find. + /// The number if it's in the database; otherwise, -1. + public int GetNumber(WellKnownNumbers numberIndex) + { + int index = (int)numberIndex; + Debug.Assert(index >= 0); + + if (index >= _numberSectionNumInts) + { + // Some terminfo files may not contain enough entries to actually + // have the requested one. + return -1; + } + + return ReadInt(_data, NumbersOffset + (index * _sizeOfInt), _readAs32Bit); + } + + /// Parses the extended string information from the terminfo data. + /// + /// A dictionary of the name to value mapping. As this section of the terminfo isn't as well + /// defined as the earlier portions, and may not even exist, the parsing is more lenient about + /// errors, returning an empty collection rather than throwing. + /// + private static Dictionary? ParseExtendedStrings(byte[] data, int extendedBeginning, bool readAs32Bit) + { + const int ExtendedHeaderSize = 10; + int sizeOfIntValuesInBytes = (readAs32Bit) ? 4 : 2; + if (extendedBeginning + ExtendedHeaderSize >= data.Length) + { + // Exit out as there's no extended information. + return null; + } + + // Read in extended counts, and exit out if we got any incorrect info + int extendedBoolCount = ReadInt16(data, extendedBeginning); + int extendedNumberCount = ReadInt16(data, extendedBeginning + (2 * 1)); + int extendedStringCount = ReadInt16(data, extendedBeginning + (2 * 2)); + int extendedStringNumOffsets = ReadInt16(data, extendedBeginning + (2 * 3)); + int extendedStringTableByteSize = ReadInt16(data, extendedBeginning + (2 * 4)); + if (extendedBoolCount < 0 || + extendedNumberCount < 0 || + extendedStringCount < 0 || + extendedStringNumOffsets < 0 || + extendedStringTableByteSize < 0) + { + // The extended header contained invalid data. Bail. + return null; + } + + // Skip over the extended bools. We don't need them now and can add this in later + // if needed. Also skip over extended numbers, for the same reason. + + // Get the location where the extended string offsets begin. These point into + // the extended string table. + int extendedOffsetsStart = + extendedBeginning + // go past the normal data + ExtendedHeaderSize + // and past the extended header + RoundUpToEven(extendedBoolCount) + // and past all of the extended Booleans + (extendedNumberCount * sizeOfIntValuesInBytes); // and past all of the extended numbers + + // Get the location where the extended string table begins. This area contains + // null-terminated strings. + int extendedStringTableStart = + extendedOffsetsStart + + (extendedStringCount * 2) + // and past all of the string offsets + ((extendedBoolCount + extendedNumberCount + extendedStringCount) * 2); // and past all of the name offsets + + // Get the location where the extended string table ends. We shouldn't read past this. + int extendedStringTableEnd = + extendedStringTableStart + + extendedStringTableByteSize; + + if (extendedStringTableEnd > data.Length) + { + // We don't have enough data to parse everything. Bail. + return null; + } + + // Now we need to parse all of the extended string values. These aren't necessarily + // "in order", meaning the offsets aren't guaranteed to be increasing. Instead, we parse + // the offsets in order, pulling out each string it references and storing them into our + // results list in the order of the offsets. + var values = new List(extendedStringCount); + int lastEnd = 0; + for (int i = 0; i < extendedStringCount; i++) + { + int offset = extendedStringTableStart + ReadInt16(data, extendedOffsetsStart + (i * 2)); + if (offset < 0 || offset >= data.Length) + { + // If the offset is invalid, bail. + return null; + } + + // Add the string + int end = FindNullTerminator(data, offset); + values.Add(Encoding.ASCII.GetString(data, offset, end - offset)); + + // Keep track of where the last string ends. The name strings will come after that. + lastEnd = Math.Max(end, lastEnd); + } + + // Now parse all of the names. + var names = new List(extendedBoolCount + extendedNumberCount + extendedStringCount); + for (int pos = lastEnd + 1; pos < extendedStringTableEnd; pos++) + { + int end = FindNullTerminator(data, pos); + names.Add(Encoding.ASCII.GetString(data, pos, end - pos)); + pos = end; + } + + // The names are in order for the Booleans, then the numbers, and then the strings. + // Skip over the bools and numbers, and associate the names with the values. + var extendedStrings = new Dictionary(extendedStringCount); + for (int iName = extendedBoolCount + extendedNumberCount, iValue = 0; + iName < names.Count && iValue < values.Count; + iName++, iValue++) + { + extendedStrings.Add(names[iName], values[iValue]); + } + + return extendedStrings; + } + + private static int RoundUpToEven(int i) { return i % 2 == 1 ? i + 1 : i; } + + /// Read a 16-bit or 32-bit value from the buffer starting at the specified position. + /// The buffer from which to read. + /// The position at which to read. + /// Whether or not to read value as 32-bit. Will read as 16-bit if set to false. + /// The value read. + private static int ReadInt(byte[] buffer, int pos, bool readAs32Bit) => + readAs32Bit ? ReadInt32(buffer, pos) : ReadInt16(buffer, pos); + + /// Read a 16-bit value from the buffer starting at the specified position. + /// The buffer from which to read. + /// The position at which to read. + /// The 16-bit value read. + private static short ReadInt16(byte[] buffer, int pos) + { + return unchecked((short) + ((((int)buffer[pos + 1]) << 8) | + ((int)buffer[pos] & 0xff))); + } + + /// Read a 32-bit value from the buffer starting at the specified position. + /// The buffer from which to read. + /// The position at which to read. + /// The 32-bit value read. + private static int ReadInt32(byte[] buffer, int pos) + => BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(pos)); + + /// Reads a string from the buffer starting at the specified position. + /// The buffer from which to read. + /// The position at which to read. + /// The string read from the specified position. + private static string ReadString(byte[] buffer, int pos) + { + int end = FindNullTerminator(buffer, pos); + return Encoding.ASCII.GetString(buffer, pos, end - pos); + } + + /// Finds the null-terminator for a string that begins at the specified position. + private static int FindNullTerminator(byte[] buffer, int pos) + { + int i = buffer.AsSpan(pos).IndexOf((byte)'\0'); + return i >= 0 ? pos + i : buffer.Length; + } + } +} diff --git a/src/libraries/System.Console/src/System/TermInfo.DatabaseFactory.cs b/src/libraries/System.Console/src/System/TermInfo.DatabaseFactory.cs new file mode 100644 index 0000000000000..844475e4851d5 --- /dev/null +++ b/src/libraries/System.Console/src/System/TermInfo.DatabaseFactory.cs @@ -0,0 +1,129 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Microsoft.Win32.SafeHandles; + +namespace System; + +internal static partial class TermInfo +{ + internal sealed class DatabaseFactory + { + /// + /// The default locations in which to search for terminfo databases. + /// This is the ordering of well-known locations used by ncurses. + /// + private static readonly string[] _terminfoLocations = new string[] { + "/etc/terminfo", + "/lib/terminfo", + "/usr/share/terminfo", + "/usr/share/misc/terminfo", + "/usr/local/share/terminfo" + }; + + /// Read the database for the current terminal as specified by the "TERM" environment variable. + /// The database, or null if it could not be found. + internal static Database? ReadActiveDatabase() + { + string? term = Environment.GetEnvironmentVariable("TERM"); + return !string.IsNullOrEmpty(term) ? ReadDatabase(term) : null; + } + + /// Read the database for the specified terminal. + /// The identifier for the terminal. + /// The database, or null if it could not be found. + private static Database? ReadDatabase(string term) + { + // This follows the same search order as prescribed by ncurses. + Database? db; + + // First try a location specified in the TERMINFO environment variable. + string? terminfo = Environment.GetEnvironmentVariable("TERMINFO"); + if (!string.IsNullOrWhiteSpace(terminfo) && (db = ReadDatabase(term, terminfo)) != null) + { + return db; + } + + // Then try in the user's home directory. + string? home = PersistedFiles.GetHomeDirectory(); + if (!string.IsNullOrWhiteSpace(home) && (db = ReadDatabase(term, home + "/.terminfo")) != null) + { + return db; + } + + // Then try a set of well-known locations. + foreach (string terminfoLocation in _terminfoLocations) + { + if ((db = ReadDatabase(term, terminfoLocation)) != null) + { + return db; + } + } + + // Couldn't find one + return null; + } + + /// Attempt to open as readonly the specified file path. + /// The path to the file to open. + /// If successful, the opened file descriptor; otherwise, -1. + /// true if the file was successfully opened; otherwise, false. + private static bool TryOpen(string filePath, [NotNullWhen(true)] out SafeFileHandle? fd) + { + fd = Interop.Sys.Open(filePath, Interop.Sys.OpenFlags.O_RDONLY | Interop.Sys.OpenFlags.O_CLOEXEC, 0); + if (fd.IsInvalid) + { + // Don't throw in this case, as we'll be polling multiple locations looking for the file. + fd.Dispose(); + fd = null; + return false; + } + + return true; + } + + /// Read the database for the specified terminal from the specified directory. + /// The identifier for the terminal. + /// The path to the directory containing terminfo database files. + /// The database, or null if it could not be found. + private static Database? ReadDatabase(string? term, string? directoryPath) + { + if (string.IsNullOrEmpty(term) || string.IsNullOrEmpty(directoryPath)) + { + return null; + } + + Span stackBuffer = stackalloc char[256]; + SafeFileHandle? fd; + if (!TryOpen(string.Create(null, stackBuffer, $"{directoryPath}/{term[0]}/{term}"), out fd) && // /directory/termFirstLetter/term (Linux) + !TryOpen(string.Create(null, stackBuffer, $"{directoryPath}/{(int)term[0]:X}/{term}"), out fd)) // /directory/termFirstLetterAsHex/term (Mac) + { + return null; + } + + using (fd) + { + // Read in all of the terminfo data + long termInfoLength = Interop.CheckIo(Interop.Sys.LSeek(fd, 0, Interop.Sys.SeekWhence.SEEK_END)); // jump to the end to get the file length + Interop.CheckIo(Interop.Sys.LSeek(fd, 0, Interop.Sys.SeekWhence.SEEK_SET)); // reset back to beginning + const int MaxTermInfoLength = 4096; // according to the term and tic man pages, 4096 is the terminfo file size max + const int HeaderLength = 12; + if (termInfoLength <= HeaderLength || termInfoLength > MaxTermInfoLength) + { + throw new InvalidOperationException(SR.IO_TermInfoInvalid); + } + + byte[] data = new byte[(int)termInfoLength]; + if (ConsolePal.Read(fd, data) != data.Length) + { + throw new InvalidOperationException(SR.IO_TermInfoInvalid); + } + + // Create the database from the data + return new Database(term, data); + } + } + } +} diff --git a/src/libraries/System.Console/src/System/TermInfo.WellKnownNumbers.cs b/src/libraries/System.Console/src/System/TermInfo.WellKnownNumbers.cs new file mode 100644 index 0000000000000..f8cd8d51e49fb --- /dev/null +++ b/src/libraries/System.Console/src/System/TermInfo.WellKnownNumbers.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System; + +internal static partial class TermInfo +{ + internal enum WellKnownNumbers + { + Columns = 0, + Lines = 2, + MaxColors = 13, + } +} diff --git a/src/libraries/System.Console/src/System/TermInfo.WellKnownStrings.cs b/src/libraries/System.Console/src/System/TermInfo.WellKnownStrings.cs new file mode 100644 index 0000000000000..20502b3b5f8c4 --- /dev/null +++ b/src/libraries/System.Console/src/System/TermInfo.WellKnownStrings.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System; + +internal static partial class TermInfo +{ + internal enum WellKnownStrings + { + Bell = 1, + Clear = 5, + ClrEol = 6, + CursorAddress = 10, + CursorLeft = 14, + CursorPositionReport = 294, + OrigPairs = 297, + OrigColors = 298, + SetAnsiForeground = 359, + SetAnsiBackground = 360, + CursorInvisible = 13, + CursorVisible = 16, + FromStatusLine = 47, + ToStatusLine = 135, + KeyBackspace = 55, + KeyClear = 57, + KeyDelete = 59, + KeyDown = 61, + KeyF1 = 66, + KeyF10 = 67, + KeyF2 = 68, + KeyF3 = 69, + KeyF4 = 70, + KeyF5 = 71, + KeyF6 = 72, + KeyF7 = 73, + KeyF8 = 74, + KeyF9 = 75, + KeyHome = 76, + KeyInsert = 77, + KeyLeft = 79, + KeyPageDown = 81, + KeyPageUp = 82, + KeyRight = 83, + KeyScrollForward = 84, + KeyScrollReverse = 85, + KeyUp = 87, + KeypadXmit = 89, + KeyBackTab = 148, + KeyBegin = 158, + KeyEnd = 164, + KeyEnter = 165, + KeyHelp = 168, + KeyPrint = 176, + KeySBegin = 186, + KeySDelete = 191, + KeySelect = 193, + KeySHelp = 198, + KeySHome = 199, + KeySLeft = 201, + KeySPrint = 207, + KeySRight = 210, + KeyF11 = 216, + KeyF12 = 217, + KeyF13 = 218, + KeyF14 = 219, + KeyF15 = 220, + KeyF16 = 221, + KeyF17 = 222, + KeyF18 = 223, + KeyF19 = 224, + KeyF20 = 225, + KeyF21 = 226, + KeyF22 = 227, + KeyF23 = 228, + KeyF24 = 229, + } +} diff --git a/src/libraries/System.Console/src/System/TermInfo.cs b/src/libraries/System.Console/src/System/TermInfo.cs index d44e99afa1c24..0241e77bf43fc 100644 --- a/src/libraries/System.Console/src/System/TermInfo.cs +++ b/src/libraries/System.Console/src/System/TermInfo.cs @@ -1,509 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers.Binary; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Text; -using Microsoft.Win32.SafeHandles; namespace System { /// Provides access to and processing of a terminfo database. - internal static class TermInfo + internal static partial class TermInfo { - internal enum WellKnownNumbers - { - Columns = 0, - Lines = 2, - MaxColors = 13, - } - - internal enum WellKnownStrings - { - Bell = 1, - Clear = 5, - ClrEol = 6, - CursorAddress = 10, - CursorLeft = 14, - CursorPositionReport = 294, - OrigPairs = 297, - OrigColors = 298, - SetAnsiForeground = 359, - SetAnsiBackground = 360, - CursorInvisible = 13, - CursorVisible = 16, - FromStatusLine = 47, - ToStatusLine = 135, - KeyBackspace = 55, - KeyClear = 57, - KeyDelete = 59, - KeyDown = 61, - KeyF1 = 66, - KeyF10 = 67, - KeyF2 = 68, - KeyF3 = 69, - KeyF4 = 70, - KeyF5 = 71, - KeyF6 = 72, - KeyF7 = 73, - KeyF8 = 74, - KeyF9 = 75, - KeyHome = 76, - KeyInsert = 77, - KeyLeft = 79, - KeyPageDown = 81, - KeyPageUp = 82, - KeyRight = 83, - KeyScrollForward = 84, - KeyScrollReverse = 85, - KeyUp = 87, - KeypadXmit = 89, - KeyBackTab = 148, - KeyBegin = 158, - KeyEnd = 164, - KeyEnter = 165, - KeyHelp = 168, - KeyPrint = 176, - KeySBegin = 186, - KeySDelete = 191, - KeySelect = 193, - KeySHelp = 198, - KeySHome = 199, - KeySLeft = 201, - KeySPrint = 207, - KeySRight = 210, - KeyF11 = 216, - KeyF12 = 217, - KeyF13 = 218, - KeyF14 = 219, - KeyF15 = 220, - KeyF16 = 221, - KeyF17 = 222, - KeyF18 = 223, - KeyF19 = 224, - KeyF20 = 225, - KeyF21 = 226, - KeyF22 = 227, - KeyF23 = 228, - KeyF24 = 229, - } - - /// Provides a terminfo database. - internal sealed class Database - { - /// The name of the terminfo file. - private readonly string _term; - /// Raw data of the database instance. - private readonly byte[] _data; - - /// The number of bytes in the names section of the database. - private readonly int _nameSectionNumBytes; - /// The number of bytes in the Booleans section of the database. - private readonly int _boolSectionNumBytes; - /// The number of integers in the numbers section of the database. - private readonly int _numberSectionNumInts; - /// The number of offsets in the strings section of the database. - private readonly int _stringSectionNumOffsets; - /// The number of bytes in the strings table of the database. - private readonly int _stringTableNumBytes; - /// Whether or not to read the number section as 32-bit integers. - private readonly bool _readAs32Bit; - /// The size of the integers on the number section. - private readonly int _sizeOfInt; - - /// Extended / user-defined entries in the terminfo database. - private readonly Dictionary _extendedStrings; - - /// Initializes the database instance. - /// The name of the terminal. - /// The data from the terminfo file. - private Database(string term, byte[] data) - { - _term = term; - _data = data; - - const int MagicLegacyNumber = 0x11A; // magic number octal 0432 for legacy ncurses terminfo - const int Magic32BitNumber = 0x21E; // magic number octal 01036 for new ncruses terminfo - short magic = ReadInt16(data, 0); - _readAs32Bit = - magic == MagicLegacyNumber ? false : - magic == Magic32BitNumber ? true : - throw new InvalidOperationException(SR.Format(SR.IO_TermInfoInvalidMagicNumber, "O" + Convert.ToString(magic, 8))); // magic number was not recognized. Printing the magic number in octal. - _sizeOfInt = (_readAs32Bit) ? 4 : 2; - - _nameSectionNumBytes = ReadInt16(data, 2); - _boolSectionNumBytes = ReadInt16(data, 4); - _numberSectionNumInts = ReadInt16(data, 6); - _stringSectionNumOffsets = ReadInt16(data, 8); - _stringTableNumBytes = ReadInt16(data, 10); - if (_nameSectionNumBytes < 0 || - _boolSectionNumBytes < 0 || - _numberSectionNumInts < 0 || - _stringSectionNumOffsets < 0 || - _stringTableNumBytes < 0) - { - throw new InvalidOperationException(SR.IO_TermInfoInvalid); - } - - // In addition to the main section of bools, numbers, and strings, there is also - // an "extended" section. This section contains additional entries that don't - // have well-known indices, and are instead named mappings. As such, we parse - // all of this data now rather than on each request, as the mapping is fairly complicated. - // This function relies on the data stored above, so it's the last thing we run. - // (Note that the extended section also includes other Booleans and numbers, but we don't - // have any need for those now, so we don't parse them.) - int extendedBeginning = RoundUpToEven(StringsTableOffset + _stringTableNumBytes); - _extendedStrings = ParseExtendedStrings(data, extendedBeginning, _readAs32Bit) ?? new Dictionary(); - } - - /// The name of the associated terminfo, if any. - public string Term { get { return _term; } } - - /// Read the database for the current terminal as specified by the "TERM" environment variable. - /// The database, or null if it could not be found. - internal static Database? ReadActiveDatabase() - { - string? term = Environment.GetEnvironmentVariable("TERM"); - return !string.IsNullOrEmpty(term) ? ReadDatabase(term) : null; - } - - /// - /// The default locations in which to search for terminfo databases. - /// This is the ordering of well-known locations used by ncurses. - /// - private static readonly string[] _terminfoLocations = new string[] { - "/etc/terminfo", - "/lib/terminfo", - "/usr/share/terminfo", - "/usr/share/misc/terminfo", - "/usr/local/share/terminfo" - }; - - /// Read the database for the specified terminal. - /// The identifier for the terminal. - /// The database, or null if it could not be found. - private static Database? ReadDatabase(string term) - { - // This follows the same search order as prescribed by ncurses. - Database? db; - - // First try a location specified in the TERMINFO environment variable. - string? terminfo = Environment.GetEnvironmentVariable("TERMINFO"); - if (!string.IsNullOrWhiteSpace(terminfo) && (db = ReadDatabase(term, terminfo)) != null) - { - return db; - } - - // Then try in the user's home directory. - string? home = PersistedFiles.GetHomeDirectory(); - if (!string.IsNullOrWhiteSpace(home) && (db = ReadDatabase(term, home + "/.terminfo")) != null) - { - return db; - } - - // Then try a set of well-known locations. - foreach (string terminfoLocation in _terminfoLocations) - { - if ((db = ReadDatabase(term, terminfoLocation)) != null) - { - return db; - } - } - - // Couldn't find one - return null; - } - - /// Attempt to open as readonly the specified file path. - /// The path to the file to open. - /// If successful, the opened file descriptor; otherwise, -1. - /// true if the file was successfully opened; otherwise, false. - private static bool TryOpen(string filePath, [NotNullWhen(true)] out SafeFileHandle? fd) - { - fd = Interop.Sys.Open(filePath, Interop.Sys.OpenFlags.O_RDONLY | Interop.Sys.OpenFlags.O_CLOEXEC, 0); - if (fd.IsInvalid) - { - // Don't throw in this case, as we'll be polling multiple locations looking for the file. - fd.Dispose(); - fd = null; - return false; - } - - return true; - } - - /// Read the database for the specified terminal from the specified directory. - /// The identifier for the terminal. - /// The path to the directory containing terminfo database files. - /// The database, or null if it could not be found. - private static Database? ReadDatabase(string? term, string? directoryPath) - { - if (string.IsNullOrEmpty(term) || string.IsNullOrEmpty(directoryPath)) - { - return null; - } - - Span stackBuffer = stackalloc char[256]; - SafeFileHandle? fd; - if (!TryOpen(string.Create(null, stackBuffer, $"{directoryPath}/{term[0]}/{term}"), out fd) && // /directory/termFirstLetter/term (Linux) - !TryOpen(string.Create(null, stackBuffer, $"{directoryPath}/{(int)term[0]:X}/{term}"), out fd)) // /directory/termFirstLetterAsHex/term (Mac) - { - return null; - } - - using (fd) - { - // Read in all of the terminfo data - long termInfoLength = Interop.CheckIo(Interop.Sys.LSeek(fd, 0, Interop.Sys.SeekWhence.SEEK_END)); // jump to the end to get the file length - Interop.CheckIo(Interop.Sys.LSeek(fd, 0, Interop.Sys.SeekWhence.SEEK_SET)); // reset back to beginning - const int MaxTermInfoLength = 4096; // according to the term and tic man pages, 4096 is the terminfo file size max - const int HeaderLength = 12; - if (termInfoLength <= HeaderLength || termInfoLength > MaxTermInfoLength) - { - throw new InvalidOperationException(SR.IO_TermInfoInvalid); - } - - byte[] data = new byte[(int)termInfoLength]; - if (ConsolePal.Read(fd, data) != data.Length) - { - throw new InvalidOperationException(SR.IO_TermInfoInvalid); - } - - // Create the database from the data - return new Database(term, data); - } - } - - /// The offset into data where the names section begins. - private const int NamesOffset = 12; // comes right after the header, which is always 12 bytes - - /// The offset into data where the Booleans section begins. - private int BooleansOffset { get { return NamesOffset + _nameSectionNumBytes; } } // after the names section - - /// The offset into data where the numbers section begins. - private int NumbersOffset { get { return RoundUpToEven(BooleansOffset + _boolSectionNumBytes); } } // after the Booleans section, at an even position - - /// - /// The offset into data where the string offsets section begins. We index into this section - /// to find the location within the strings table where a string value exists. - /// - private int StringOffsetsOffset { get { return NumbersOffset + (_numberSectionNumInts * _sizeOfInt); } } - - /// The offset into data where the string table exists. - private int StringsTableOffset { get { return StringOffsetsOffset + (_stringSectionNumOffsets * 2); } } - - /// Gets a string from the strings section by the string's well-known index. - /// The index of the string to find. - /// The string if it's in the database; otherwise, null. - public string? GetString(WellKnownStrings stringTableIndex) - { - int index = (int)stringTableIndex; - Debug.Assert(index >= 0); - - if (index >= _stringSectionNumOffsets) - { - // Some terminfo files may not contain enough entries to actually - // have the requested one. - return null; - } - - int tableIndex = ReadInt16(_data, StringOffsetsOffset + (index * 2)); - if (tableIndex == -1) - { - // Some terminfo files may have enough entries, but may not actually - // have it filled in for this particular string. - return null; - } - - return ReadString(_data, StringsTableOffset + tableIndex); - } - - /// Gets a string from the extended strings section. - /// The name of the string as contained in the extended names section. - /// The string if it's in the database; otherwise, null. - public string? GetExtendedString(string name) - { - Debug.Assert(name != null); - - string? value; - return _extendedStrings.TryGetValue(name, out value) ? - value : - null; - } - - /// Gets a number from the numbers section by the number's well-known index. - /// The index of the string to find. - /// The number if it's in the database; otherwise, -1. - public int GetNumber(WellKnownNumbers numberIndex) - { - int index = (int)numberIndex; - Debug.Assert(index >= 0); - - if (index >= _numberSectionNumInts) - { - // Some terminfo files may not contain enough entries to actually - // have the requested one. - return -1; - } - - return ReadInt(_data, NumbersOffset + (index * _sizeOfInt), _readAs32Bit); - } - - /// Parses the extended string information from the terminfo data. - /// - /// A dictionary of the name to value mapping. As this section of the terminfo isn't as well - /// defined as the earlier portions, and may not even exist, the parsing is more lenient about - /// errors, returning an empty collection rather than throwing. - /// - private static Dictionary? ParseExtendedStrings(byte[] data, int extendedBeginning, bool readAs32Bit) - { - const int ExtendedHeaderSize = 10; - int sizeOfIntValuesInBytes = (readAs32Bit) ? 4 : 2; - if (extendedBeginning + ExtendedHeaderSize >= data.Length) - { - // Exit out as there's no extended information. - return null; - } - - // Read in extended counts, and exit out if we got any incorrect info - int extendedBoolCount = ReadInt16(data, extendedBeginning); - int extendedNumberCount = ReadInt16(data, extendedBeginning + (2 * 1)); - int extendedStringCount = ReadInt16(data, extendedBeginning + (2 * 2)); - int extendedStringNumOffsets = ReadInt16(data, extendedBeginning + (2 * 3)); - int extendedStringTableByteSize = ReadInt16(data, extendedBeginning + (2 * 4)); - if (extendedBoolCount < 0 || - extendedNumberCount < 0 || - extendedStringCount < 0 || - extendedStringNumOffsets < 0 || - extendedStringTableByteSize < 0) - { - // The extended header contained invalid data. Bail. - return null; - } - - // Skip over the extended bools. We don't need them now and can add this in later - // if needed. Also skip over extended numbers, for the same reason. - - // Get the location where the extended string offsets begin. These point into - // the extended string table. - int extendedOffsetsStart = - extendedBeginning + // go past the normal data - ExtendedHeaderSize + // and past the extended header - RoundUpToEven(extendedBoolCount) + // and past all of the extended Booleans - (extendedNumberCount * sizeOfIntValuesInBytes); // and past all of the extended numbers - - // Get the location where the extended string table begins. This area contains - // null-terminated strings. - int extendedStringTableStart = - extendedOffsetsStart + - (extendedStringCount * 2) + // and past all of the string offsets - ((extendedBoolCount + extendedNumberCount + extendedStringCount) * 2); // and past all of the name offsets - - // Get the location where the extended string table ends. We shouldn't read past this. - int extendedStringTableEnd = - extendedStringTableStart + - extendedStringTableByteSize; - - if (extendedStringTableEnd > data.Length) - { - // We don't have enough data to parse everything. Bail. - return null; - } - - // Now we need to parse all of the extended string values. These aren't necessarily - // "in order", meaning the offsets aren't guaranteed to be increasing. Instead, we parse - // the offsets in order, pulling out each string it references and storing them into our - // results list in the order of the offsets. - var values = new List(extendedStringCount); - int lastEnd = 0; - for (int i = 0; i < extendedStringCount; i++) - { - int offset = extendedStringTableStart + ReadInt16(data, extendedOffsetsStart + (i * 2)); - if (offset < 0 || offset >= data.Length) - { - // If the offset is invalid, bail. - return null; - } - - // Add the string - int end = FindNullTerminator(data, offset); - values.Add(Encoding.ASCII.GetString(data, offset, end - offset)); - - // Keep track of where the last string ends. The name strings will come after that. - lastEnd = Math.Max(end, lastEnd); - } - - // Now parse all of the names. - var names = new List(extendedBoolCount + extendedNumberCount + extendedStringCount); - for (int pos = lastEnd + 1; pos < extendedStringTableEnd; pos++) - { - int end = FindNullTerminator(data, pos); - names.Add(Encoding.ASCII.GetString(data, pos, end - pos)); - pos = end; - } - - // The names are in order for the Booleans, then the numbers, and then the strings. - // Skip over the bools and numbers, and associate the names with the values. - var extendedStrings = new Dictionary(extendedStringCount); - for (int iName = extendedBoolCount + extendedNumberCount, iValue = 0; - iName < names.Count && iValue < values.Count; - iName++, iValue++) - { - extendedStrings.Add(names[iName], values[iValue]); - } - - return extendedStrings; - } - - private static int RoundUpToEven(int i) { return i % 2 == 1 ? i + 1 : i; } - - /// Read a 16-bit or 32-bit value from the buffer starting at the specified position. - /// The buffer from which to read. - /// The position at which to read. - /// Whether or not to read value as 32-bit. Will read as 16-bit if set to false. - /// The value read. - private static int ReadInt(byte[] buffer, int pos, bool readAs32Bit) => - readAs32Bit ? ReadInt32(buffer, pos) : ReadInt16(buffer, pos); - - /// Read a 16-bit value from the buffer starting at the specified position. - /// The buffer from which to read. - /// The position at which to read. - /// The 16-bit value read. - private static short ReadInt16(byte[] buffer, int pos) - { - return unchecked((short) - ((((int)buffer[pos + 1]) << 8) | - ((int)buffer[pos] & 0xff))); - } - - /// Read a 32-bit value from the buffer starting at the specified position. - /// The buffer from which to read. - /// The position at which to read. - /// The 32-bit value read. - private static int ReadInt32(byte[] buffer, int pos) - => BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(pos)); - - /// Reads a string from the buffer starting at the specified position. - /// The buffer from which to read. - /// The position at which to read. - /// The string read from the specified position. - private static string ReadString(byte[] buffer, int pos) - { - int end = FindNullTerminator(buffer, pos); - return Encoding.ASCII.GetString(buffer, pos, end - pos); - } - - /// Finds the null-terminator for a string that begins at the specified position. - private static int FindNullTerminator(byte[] buffer, int pos) - { - int i = buffer.AsSpan(pos).IndexOf((byte)'\0'); - return i >= 0 ? pos + i : buffer.Length; - } - } - /// Provides support for evaluating parameterized terminfo database format strings. internal static class ParameterizedStrings { diff --git a/src/libraries/System.Console/src/System/TerminalFormatStrings.cs b/src/libraries/System.Console/src/System/TerminalFormatStrings.cs new file mode 100644 index 0000000000000..897f67c733e8b --- /dev/null +++ b/src/libraries/System.Console/src/System/TerminalFormatStrings.cs @@ -0,0 +1,256 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; + +namespace System; + +/// Provides format strings and related information for use with the current terminal. +internal sealed class TerminalFormatStrings +{ + /// The format string to use to change the foreground color. + public readonly string? Foreground; + /// The format string to use to change the background color. + public readonly string? Background; + /// The format string to use to reset the foreground and background colors. + public readonly string? Reset; + /// The maximum number of colors supported by the terminal. + public readonly int MaxColors; + /// The number of columns in a format. + public readonly int Columns; + /// The number of lines in a format. + public readonly int Lines; + /// The format string to use to make cursor visible. + public readonly string? CursorVisible; + /// The format string to use to make cursor invisible + public readonly string? CursorInvisible; + /// The format string to use to set the window title. + public readonly string? Title; + /// The format string to use for an audible bell. + public readonly string? Bell; + /// The format string to use to clear the terminal. + public readonly string? Clear; + /// The format string to use to set the position of the cursor. + public readonly string? CursorAddress; + /// The format string to use to move the cursor to the left. + public readonly string? CursorLeft; + /// The format string to use to clear to the end of line. + public readonly string? ClrEol; + /// The ANSI-compatible string for the Cursor Position report request. + /// + /// This should really be in user string 7 in the terminfo file, but some terminfo databases + /// are missing it. As this is defined to be supported by any ANSI-compatible terminal, + /// we assume it's available; doing so means CursorTop/Left will work even if the terminfo database + /// doesn't contain it (as appears to be the case with e.g. screen and tmux on Ubuntu), at the risk + /// of outputting the sequence on some terminal that's not compatible. + /// + public const string CursorPositionReport = "\x1B[6n"; + /// + /// The dictionary of keystring to ConsoleKeyInfo. + /// Only some members of the ConsoleKeyInfo are used; in particular, the actual char is ignored. + /// + public readonly Dictionary, ConsoleKeyInfo> KeyFormatToConsoleKey = + new Dictionary, ConsoleKeyInfo>(new ReadOnlyMemoryContentComparer()); + + /// Max key length + public readonly int MaxKeyFormatLength; + /// Min key length + public readonly int MinKeyFormatLength; + /// The ANSI string used to enter "application" / "keypad transmit" mode. + public readonly string? KeypadXmit; + /// Indicates that it was created out of rxvt TERM + public readonly bool IsRxvtTerm; + + public TerminalFormatStrings(TermInfo.Database? db) + { + if (db == null) + return; + + KeypadXmit = db.GetString(TermInfo.WellKnownStrings.KeypadXmit); + Foreground = db.GetString(TermInfo.WellKnownStrings.SetAnsiForeground); + Background = db.GetString(TermInfo.WellKnownStrings.SetAnsiBackground); + Reset = db.GetString(TermInfo.WellKnownStrings.OrigPairs) ?? db.GetString(TermInfo.WellKnownStrings.OrigColors); + Bell = db.GetString(TermInfo.WellKnownStrings.Bell); + Clear = db.GetString(TermInfo.WellKnownStrings.Clear); + Columns = db.GetNumber(TermInfo.WellKnownNumbers.Columns); + Lines = db.GetNumber(TermInfo.WellKnownNumbers.Lines); + CursorVisible = db.GetString(TermInfo.WellKnownStrings.CursorVisible); + CursorInvisible = db.GetString(TermInfo.WellKnownStrings.CursorInvisible); + CursorAddress = db.GetString(TermInfo.WellKnownStrings.CursorAddress); + CursorLeft = db.GetString(TermInfo.WellKnownStrings.CursorLeft); + ClrEol = db.GetString(TermInfo.WellKnownStrings.ClrEol); + + IsRxvtTerm = !string.IsNullOrEmpty(db.Term) && db.Term.Contains("rxvt", StringComparison.OrdinalIgnoreCase); + Title = GetTitle(db); + + Debug.WriteLineIf(db.GetString(TermInfo.WellKnownStrings.CursorPositionReport) != CursorPositionReport, + "Getting the cursor position will only work if the terminal supports the CPR sequence," + + "but the terminfo database does not contain an entry for it."); + + int maxColors = db.GetNumber(TermInfo.WellKnownNumbers.MaxColors); + MaxColors = // normalize to either the full range of all ANSI colors, just the dark ones, or none + maxColors >= 16 ? 16 : + maxColors >= 8 ? 8 : + 0; + + AddKey(db, TermInfo.WellKnownStrings.KeyF1, ConsoleKey.F1); + AddKey(db, TermInfo.WellKnownStrings.KeyF2, ConsoleKey.F2); + AddKey(db, TermInfo.WellKnownStrings.KeyF3, ConsoleKey.F3); + AddKey(db, TermInfo.WellKnownStrings.KeyF4, ConsoleKey.F4); + AddKey(db, TermInfo.WellKnownStrings.KeyF5, ConsoleKey.F5); + AddKey(db, TermInfo.WellKnownStrings.KeyF6, ConsoleKey.F6); + AddKey(db, TermInfo.WellKnownStrings.KeyF7, ConsoleKey.F7); + AddKey(db, TermInfo.WellKnownStrings.KeyF8, ConsoleKey.F8); + AddKey(db, TermInfo.WellKnownStrings.KeyF9, ConsoleKey.F9); + AddKey(db, TermInfo.WellKnownStrings.KeyF10, ConsoleKey.F10); + AddKey(db, TermInfo.WellKnownStrings.KeyF11, ConsoleKey.F11); + AddKey(db, TermInfo.WellKnownStrings.KeyF12, ConsoleKey.F12); + AddKey(db, TermInfo.WellKnownStrings.KeyF13, ConsoleKey.F13); + AddKey(db, TermInfo.WellKnownStrings.KeyF14, ConsoleKey.F14); + AddKey(db, TermInfo.WellKnownStrings.KeyF15, ConsoleKey.F15); + AddKey(db, TermInfo.WellKnownStrings.KeyF16, ConsoleKey.F16); + AddKey(db, TermInfo.WellKnownStrings.KeyF17, ConsoleKey.F17); + AddKey(db, TermInfo.WellKnownStrings.KeyF18, ConsoleKey.F18); + AddKey(db, TermInfo.WellKnownStrings.KeyF19, ConsoleKey.F19); + AddKey(db, TermInfo.WellKnownStrings.KeyF20, ConsoleKey.F20); + AddKey(db, TermInfo.WellKnownStrings.KeyF21, ConsoleKey.F21); + AddKey(db, TermInfo.WellKnownStrings.KeyF22, ConsoleKey.F22); + AddKey(db, TermInfo.WellKnownStrings.KeyF23, ConsoleKey.F23); + AddKey(db, TermInfo.WellKnownStrings.KeyF24, ConsoleKey.F24); + AddKey(db, TermInfo.WellKnownStrings.KeyBackspace, ConsoleKey.Backspace); + AddKey(db, TermInfo.WellKnownStrings.KeyBackTab, ConsoleKey.Tab, shift: true, alt: false, control: false); + AddKey(db, TermInfo.WellKnownStrings.KeyBegin, ConsoleKey.Home); + AddKey(db, TermInfo.WellKnownStrings.KeyClear, ConsoleKey.Clear); + AddKey(db, TermInfo.WellKnownStrings.KeyDelete, ConsoleKey.Delete); + AddKey(db, TermInfo.WellKnownStrings.KeyDown, ConsoleKey.DownArrow); + AddKey(db, TermInfo.WellKnownStrings.KeyEnd, ConsoleKey.End); + AddKey(db, TermInfo.WellKnownStrings.KeyEnter, ConsoleKey.Enter); + AddKey(db, TermInfo.WellKnownStrings.KeyHelp, ConsoleKey.Help); + AddKey(db, TermInfo.WellKnownStrings.KeyHome, ConsoleKey.Home); + AddKey(db, TermInfo.WellKnownStrings.KeyInsert, ConsoleKey.Insert); + AddKey(db, TermInfo.WellKnownStrings.KeyLeft, ConsoleKey.LeftArrow); + AddKey(db, TermInfo.WellKnownStrings.KeyPageDown, ConsoleKey.PageDown); + AddKey(db, TermInfo.WellKnownStrings.KeyPageUp, ConsoleKey.PageUp); + AddKey(db, TermInfo.WellKnownStrings.KeyPrint, ConsoleKey.Print); + AddKey(db, TermInfo.WellKnownStrings.KeyRight, ConsoleKey.RightArrow); + AddKey(db, TermInfo.WellKnownStrings.KeyScrollForward, ConsoleKey.PageDown, shift: true, alt: false, control: false); + AddKey(db, TermInfo.WellKnownStrings.KeyScrollReverse, ConsoleKey.PageUp, shift: true, alt: false, control: false); + AddKey(db, TermInfo.WellKnownStrings.KeySBegin, ConsoleKey.Home, shift: true, alt: false, control: false); + AddKey(db, TermInfo.WellKnownStrings.KeySDelete, ConsoleKey.Delete, shift: true, alt: false, control: false); + AddKey(db, TermInfo.WellKnownStrings.KeySHome, ConsoleKey.Home, shift: true, alt: false, control: false); + AddKey(db, TermInfo.WellKnownStrings.KeySelect, ConsoleKey.Select); + AddKey(db, TermInfo.WellKnownStrings.KeySLeft, ConsoleKey.LeftArrow, shift: true, alt: false, control: false); + AddKey(db, TermInfo.WellKnownStrings.KeySPrint, ConsoleKey.Print, shift: true, alt: false, control: false); + AddKey(db, TermInfo.WellKnownStrings.KeySRight, ConsoleKey.RightArrow, shift: true, alt: false, control: false); + AddKey(db, TermInfo.WellKnownStrings.KeyUp, ConsoleKey.UpArrow); + AddPrefixKey(db, "kLFT", ConsoleKey.LeftArrow); + AddPrefixKey(db, "kRIT", ConsoleKey.RightArrow); + AddPrefixKey(db, "kUP", ConsoleKey.UpArrow); + AddPrefixKey(db, "kDN", ConsoleKey.DownArrow); + AddPrefixKey(db, "kDC", ConsoleKey.Delete); + AddPrefixKey(db, "kEND", ConsoleKey.End); + AddPrefixKey(db, "kHOM", ConsoleKey.Home); + AddPrefixKey(db, "kNXT", ConsoleKey.PageDown); + AddPrefixKey(db, "kPRV", ConsoleKey.PageUp); + + if (KeyFormatToConsoleKey.Count > 0) + { + MaxKeyFormatLength = int.MinValue; + MinKeyFormatLength = int.MaxValue; + + foreach (KeyValuePair, ConsoleKeyInfo> entry in KeyFormatToConsoleKey) + { + if (entry.Key.Length > MaxKeyFormatLength) + { + MaxKeyFormatLength = entry.Key.Length; + } + if (entry.Key.Length < MinKeyFormatLength) + { + MinKeyFormatLength = entry.Key.Length; + } + } + } + } + + private static string GetTitle(TermInfo.Database db) + { + // Try to get the format string from tsl/fsl and use it if they're available + string? tsl = db.GetString(TermInfo.WellKnownStrings.ToStatusLine); + string? fsl = db.GetString(TermInfo.WellKnownStrings.FromStatusLine); + if (tsl != null && fsl != null) + { + return tsl + "%p1%s" + fsl; + } + + string term = db.Term; + if (term == null) + { + return string.Empty; + } + + if (term.StartsWith("xterm", StringComparison.Ordinal)) // normalize all xterms to enable easier matching + { + term = "xterm"; + } + else if (term.StartsWith("screen", StringComparison.Ordinal)) // normalize all tmux configs + { + term = "screen"; + } + + switch (term) + { + case "aixterm": + case "dtterm": + case "linux": + case "rxvt": + case "xterm": + return "\x1B]0;%p1%s\x07"; + case "cygwin": + return "\x1B];%p1%s\x07"; + case "konsole": + return "\x1B]30;%p1%s\x07"; + case "screen": + return "\x1Bk%p1%s\x1B"; + default: + return string.Empty; + } + } + + private void AddKey(TermInfo.Database db, TermInfo.WellKnownStrings keyId, ConsoleKey key) + { + AddKey(db, keyId, key, shift: false, alt: false, control: false); + } + + private void AddKey(TermInfo.Database db, TermInfo.WellKnownStrings keyId, ConsoleKey key, bool shift, bool alt, bool control) + { + ReadOnlyMemory keyFormat = db.GetString(keyId).AsMemory(); + if (!keyFormat.IsEmpty) + KeyFormatToConsoleKey[keyFormat] = new ConsoleKeyInfo(key == ConsoleKey.Enter ? '\r' : '\0', key, shift, alt, control); + } + + private void AddPrefixKey(TermInfo.Database db, string extendedNamePrefix, ConsoleKey key) + { + AddKey(db, extendedNamePrefix + "3", key, shift: false, alt: true, control: false); + AddKey(db, extendedNamePrefix + "4", key, shift: true, alt: true, control: false); + AddKey(db, extendedNamePrefix + "5", key, shift: false, alt: false, control: true); + AddKey(db, extendedNamePrefix + "6", key, shift: true, alt: false, control: true); + AddKey(db, extendedNamePrefix + "7", key, shift: false, alt: false, control: true); + } + + private void AddKey(TermInfo.Database db, string extendedName, ConsoleKey key, bool shift, bool alt, bool control) + { + ReadOnlyMemory keyFormat = db.GetExtendedString(extendedName).AsMemory(); + if (!keyFormat.IsEmpty) + KeyFormatToConsoleKey[keyFormat] = new ConsoleKeyInfo('\0', key, shift, alt, control); + } + + private sealed class ReadOnlyMemoryContentComparer : IEqualityComparer> + { + public bool Equals(ReadOnlyMemory x, ReadOnlyMemory y) => + x.Span.SequenceEqual(y.Span); + + public int GetHashCode(ReadOnlyMemory obj) => + string.GetHashCode(obj.Span); + } +} diff --git a/src/libraries/System.Console/tests/KeyParserTests.cs b/src/libraries/System.Console/tests/KeyParserTests.cs new file mode 100644 index 0000000000000..f5cebdeeff17e --- /dev/null +++ b/src/libraries/System.Console/tests/KeyParserTests.cs @@ -0,0 +1,1866 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Xunit; + +namespace System.Tests; + +public class KeyParserTests +{ + private static readonly TerminalData[] Terminals = + { + new XTermData(), + new GNOMETerminalData(), + new LinuxConsole(), + new PuTTYData_xterm(), + new PuTTYData_linux(), + new PuTTYData_putty(), + new WindowsTerminalData(), + new TmuxData(), + new Tmux256ColorData(), + new RxvtUnicode(), + }; + + private static IEnumerable<(char ch, ConsoleKey key)> AsciiKeys + { + get + { + yield return (' ', ConsoleKey.Spacebar); + yield return ('\t', ConsoleKey.Tab); + yield return ('\r', ConsoleKey.Enter); + + yield return ('+', ConsoleKey.Add); + yield return ('-', ConsoleKey.Subtract); + yield return ('*', ConsoleKey.Multiply); + yield return ('/', ConsoleKey.Divide); + + yield return ('.', ConsoleKey.OemPeriod); + yield return (',', ConsoleKey.OemComma); + + yield return ('\u001B', ConsoleKey.Escape); + + for (char i = '0'; i <= '9'; i++) + { + yield return (i, ConsoleKey.D0 + i - '0'); + } + for (char i = 'a'; i <= 'z'; i++) + { + yield return (i, ConsoleKey.A + i - 'a'); + } + for (char i = 'A'; i <= 'Z'; i++) + { + yield return (i, ConsoleKey.A + i - 'A'); + } + } + } + + public static IEnumerable AsciiCharactersArguments + => Terminals.SelectMany(terminal => AsciiKeys.Select(tuple => new object[] { terminal, tuple.ch, tuple.key })); + + [Theory] + [MemberData(nameof(AsciiCharactersArguments))] + public void AsciiCharacters(TerminalData terminalData, char input, ConsoleKey expectedKey) + { + ConsoleKeyInfo consoleKeyInfo = Parse(new[] { input }, terminalData.TerminalDb, terminalData.Verase, 1); + + Assert.Equal(input, consoleKeyInfo.KeyChar); + Assert.Equal(expectedKey, consoleKeyInfo.Key); + Assert.Equal(char.IsAsciiLetterUpper(input) ? ConsoleModifiers.Shift : 0, consoleKeyInfo.Modifiers); + } + + public static IEnumerable RecordedScenarios + { + get + { + foreach (TerminalData terminalData in Terminals) + { + foreach ((byte[] bytes, ConsoleKeyInfo cki) in terminalData.RecordedScenarios) + { + yield return new object[] { terminalData, bytes, cki }; + } + } + + // PuTTY has multiple settings that can customize key mappings. + // 1. Connection => Data => Terminal details => Terminal-type string: this string controls the TERM env var. + // The default value is xterm. Users can set it to putty, linux or any other known TERM. + // For all these different TERMs we have different Terminfo databases. + // On top of that, other Terminal settings can be applied (listed and tested below). + // These settings often use different byte representation than stated in Terminfo. + // Example: Terminfo says that F1 should be represented with X, but by using some other setting it's represented with Y. + // That is why here we test their combinations. + // From the implementation perspective, we test the Terminfo fallback code path (no mapping found). + foreach (TerminalData putty in new TerminalData[] { new PuTTYData_xterm(), new PuTTYData_linux(), new PuTTYData_putty() }) + { + // 2. Terminal => Keyboard => The Home and End keys + // 2a: Standard + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.StandardHomeAndEndKeys) + { + yield return new object[] { putty, bytes, cki }; + } + // 2b: rxvt + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.RxvtHomeAndEndKeys) + { + yield return new object[] { putty, bytes, cki }; + } + + // 3. Terminal => Keyboard => The function keys and keypad + // 3a: ESC[n~ + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.ESCnFunctionKeys) + { + yield return new object[] { putty, bytes, cki }; + } + // 3b: Linux + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.LinuxFunctionKeys) + { + yield return new object[] { putty, bytes, cki }; + } + // 3c: Xterm R6 + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.XtermR6FunctionKeys) + { + yield return new object[] { putty, bytes, cki }; + } + // 3d: VT 400 + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.VT400FunctionKeys) + { + yield return new object[] { putty, bytes, cki }; + } + // 3e: VT 100+ + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.VT100FunctionKeys) + { + yield return new object[] { putty, bytes, cki }; + } + // 3f: SCO + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.SCOFunctionKeys) + { + yield return new object[] { putty, bytes, cki }; + } + // 3g: Xterm 216+ + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.Xterm216FunctionKeys) + { + yield return new object[] { putty, bytes, cki }; + } + + // 4: Terminal => Keyboard => Shift/Ctrl/Alt with arrow keys + // 4a: Ctrl toggles app mode + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.CtrlTogglesAppModeArrows) + { + yield return new object[] { putty, bytes, cki }; + } + // 4b: xterm-style bitmap does not work as expected in application mode + // as it produces same data as the setting above, so we don't test it + } + + // I was not able to find any SCO terminal other than PuTTy which allows to emulate it + foreach (TerminalData putty in new TerminalData[] { new PuTTYData_xterm(), new PuTTYData_linux() }) + { + foreach ((byte[] bytes, ConsoleKeyInfo cki) in SCO.HomeKeys.Concat(SCO.ArrowKeys)) + { + yield return new object[] { putty, bytes, cki }; + } + } + } + } + + [Theory] + [MemberData(nameof(RecordedScenarios))] + public void KeysAreProperlyMapped(TerminalData terminalData, byte[] recordedBytes, ConsoleKeyInfo expected) + { + char[] encoded = terminalData.ConsoleEncoding.GetString(recordedBytes).ToCharArray(); + + ConsoleKeyInfo actual = Parse(encoded, terminalData.TerminalDb, terminalData.Verase, encoded.Length); + + Assert.Equal(expected.Key, actual.Key); + Assert.Equal(expected.Modifiers, actual.Modifiers); + Assert.Equal(expected.KeyChar, actual.KeyChar); + } + + private static IEnumerable<(string chars, ConsoleKey key)> VTSequences + { + get + { + yield return (GetString(1), ConsoleKey.Home); + yield return (GetString(2), ConsoleKey.Insert); + yield return (GetString(3), ConsoleKey.Delete); + yield return (GetString(4), ConsoleKey.End); + yield return (GetString(5), ConsoleKey.PageUp); + yield return (GetString(6), ConsoleKey.PageDown); + yield return (GetString(7), ConsoleKey.Home); + yield return (GetString(8), ConsoleKey.End); + yield return (GetString(11), ConsoleKey.F1); + yield return (GetString(12), ConsoleKey.F2); + yield return (GetString(13), ConsoleKey.F3); + yield return (GetString(14), ConsoleKey.F4); + yield return (GetString(15), ConsoleKey.F5); + yield return (GetString(17), ConsoleKey.F6); + yield return (GetString(18), ConsoleKey.F7); + yield return (GetString(19), ConsoleKey.F8); + yield return (GetString(20), ConsoleKey.F9); + yield return (GetString(21), ConsoleKey.F10); + yield return (GetString(23), ConsoleKey.F11); + yield return (GetString(24), ConsoleKey.F12); + yield return (GetString(25), ConsoleKey.F13); + yield return (GetString(26), ConsoleKey.F14); + yield return (GetString(28), ConsoleKey.F15); + yield return (GetString(29), ConsoleKey.F16); + yield return (GetString(31), ConsoleKey.F17); + yield return (GetString(32), ConsoleKey.F18); + yield return (GetString(33), ConsoleKey.F19); + yield return (GetString(34), ConsoleKey.F20); + + static string GetString(int i) => $"\u001B[{i}~"; + } + } + + public static IEnumerable VTSequencesArguments + => Terminals.SelectMany(terminal => VTSequences.Select(tuple => new object[] { terminal, tuple.chars, tuple.key })); + + [Theory] + [MemberData(nameof(VTSequencesArguments))] + public void VTSequencesAreProperlyMapped(TerminalData terminalData, string input, ConsoleKey expectedKey) + { + if (terminalData is RxvtUnicode && input == "\u001B[4~" && expectedKey == ConsoleKey.End) + { + expectedKey = ConsoleKey.Select; // rxvt binds this key to Select in Terminfo and uses "^[[8~" for End key + } + + ConsoleKeyInfo consoleKeyInfo = Parse(input.ToCharArray(), terminalData.TerminalDb, terminalData.Verase, input.Length); + + Assert.Equal(expectedKey, consoleKeyInfo.Key); + Assert.Equal(default, consoleKeyInfo.KeyChar); + Assert.Equal(default, consoleKeyInfo.Modifiers); + } + + private static IEnumerable<(string chars, ConsoleKey key)> ThreeCharactersKeysRxvt + { + get + { + yield return ("\u001BOa", ConsoleKey.UpArrow); + yield return ("\u001BOb", ConsoleKey.DownArrow); + yield return ("\u001BOc", ConsoleKey.RightArrow); + yield return ("\u001BOd", ConsoleKey.LeftArrow); + } + } + + [Fact] + public void ExtendedStringCodePath() + { + RxvtUnicode terminalData = new RxvtUnicode(); + + foreach ((string input, ConsoleKey expectedKey) in ThreeCharactersKeysRxvt) + { + ConsoleKeyInfo consoleKeyInfo = Parse(input.ToCharArray(), terminalData.TerminalDb, terminalData.Verase, input.Length); + + Assert.Equal(expectedKey, consoleKeyInfo.Key); + Assert.Equal(default, consoleKeyInfo.KeyChar); + Assert.Equal(ConsoleModifiers.Control, consoleKeyInfo.Modifiers); + } + } + + private static IEnumerable<(string chars, ConsoleKeyInfo[] keys)> EdgeCaseScenarios + { + get + { + // Backspace + yield return (new string((char)127, 1), new[] { new ConsoleKeyInfo((char)127, ConsoleKey.Backspace, false, false, false) }); + // Ctrl+Backspace + yield return ("\b", new[] { new ConsoleKeyInfo('\b', ConsoleKey.Backspace, false, false, true) }); + // Alt+Backspace + yield return ("\u001B\u007F", new[] { new ConsoleKeyInfo((char)0x7F, ConsoleKey.Backspace, false, true, false) }); + // Ctrl+Alt+Backspace + yield return ("\u001B\b", new[] { new ConsoleKeyInfo('\b', ConsoleKey.Backspace, false, true, true) }); + // Enter + yield return ("\r", new[] { new ConsoleKeyInfo('\r', ConsoleKey.Enter, false, false, false) }); + // Ctrl+Enter + yield return ("\n", new[] { new ConsoleKeyInfo('\n', ConsoleKey.Enter, false, false, true) }); + + // Escape key pressed multiple times + for (int i = 1; i <= 5; i++) + { + yield return (new string('\u001B', i), Enumerable.Repeat(new ConsoleKeyInfo('\u001B', ConsoleKey.Escape, false, false, false), i).ToArray()); + } + + // Home key (^[[H) followed by H key + yield return ("\u001B[HH", new[] + { + new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false), + new ConsoleKeyInfo('H', ConsoleKey.H, true, false, false) + }); + + // escape sequence (F12 '^[[24~') followed by an extra tylde: + yield return ($"\u001B[24~~", new[] + { + new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false), + new ConsoleKeyInfo('~', default, false, false, false), + }); + + // Invalid escape sequences: + // Invalid modifiers (valid values are <2, 8>) + foreach (int invalidModifier in new[] { 0, 1, 9 }) + { + yield return ($"\u001B[1;{invalidModifier}H", new[] + { + new ConsoleKeyInfo('\u001B', ConsoleKey.Escape, false, false, false), + new ConsoleKeyInfo('[', default, false, false, false), + new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false), + new ConsoleKeyInfo(';', default, false, false, false), + new ConsoleKeyInfo((char)('0' + invalidModifier), ConsoleKey.D0 + invalidModifier, false, false, false), + new ConsoleKeyInfo('H', ConsoleKey.H, true, false, false) + }); + } + // Invalid ID (valid values are <1, 34> except of 9, 16, 22, 27, 30 and 35) + foreach (int invalidId in new[] { 16, 22, 27, 30, 35, 36, 77, 99 }) + { + yield return ($"\u001B[{invalidId}~", new[] + { + new ConsoleKeyInfo('\u001B', ConsoleKey.Escape, false, false, false), + new ConsoleKeyInfo('[', default, false, false, false), + new ConsoleKeyInfo((char)('0' + invalidId / 10), ConsoleKey.D0 + invalidId / 10, false, false, false), + new ConsoleKeyInfo((char)('0' + invalidId % 10), ConsoleKey.D0 + invalidId % 10, false, false, false), + new ConsoleKeyInfo('~', default, false, false, false), + }); + } + // too long ID (more than 2 digits) + yield return ($"\u001B[111~", new[] + { + new ConsoleKeyInfo('\u001B', ConsoleKey.Escape, false, false, false), + new ConsoleKeyInfo('[', default, false, false, false), + new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false), + new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false), + new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false), + new ConsoleKeyInfo('~', default, false, false, false), + }); + // missing closing tag (tylde): + yield return ($"\u001B[24", new[] + { + new ConsoleKeyInfo('\u001B', ConsoleKey.Escape, false, false, false), + new ConsoleKeyInfo('[', default, false, false, false), + new ConsoleKeyInfo('2', ConsoleKey.D2, false, false, false), + new ConsoleKeyInfo('4', ConsoleKey.D4, false, false, false), + }); + } + } + + public static IEnumerable EdgeCaseScenariosArguments + => Terminals.SelectMany(terminal => EdgeCaseScenarios.Select(tuple => new object[] { terminal, tuple.chars, tuple.keys })); + + [Theory] + [MemberData(nameof(EdgeCaseScenariosArguments))] + public void EdgeCasesAreProperlyHandled(TerminalData terminalData, string input, ConsoleKeyInfo[] expectedKeys) + { + int startIndex = 0; + char[] chars = input.ToCharArray(); + + foreach (ConsoleKeyInfo expectedKey in expectedKeys) + { + ConsoleKeyInfo parsed = KeyParser.Parse(chars, terminalData.TerminalDb, 0, terminalData.Verase, ref startIndex, chars.Length); + + Assert.Equal(expectedKey.Key, parsed.Key); + Assert.Equal(expectedKey.KeyChar, parsed.KeyChar); + Assert.Equal(expectedKey.Modifiers, parsed.Modifiers); + } + + Assert.Equal(chars.Length, startIndex); + } + + [Theory] + [MemberData(nameof(AsciiCharactersArguments))] + public void VeraseIsRespected(TerminalData terminalData, char input, ConsoleKey ifNotVerase) + { + ConsoleKeyInfo consoleKeyInfo = Parse(new[] { input }, terminalData.TerminalDb, (byte)input, 1); + + Assert.Equal(input, consoleKeyInfo.KeyChar); + Assert.Equal(ConsoleKey.Backspace, consoleKeyInfo.Key); + Assert.NotEqual(ifNotVerase, consoleKeyInfo.Key); + Assert.Equal((ConsoleModifiers)0, consoleKeyInfo.Modifiers); + } + + [Fact] + public void NewLineEscapeSequenceProducesCharacter() + { + XTermData xTerm = new(); + + ConsoleKeyInfo consoleKeyInfo = Parse("\u001BOM".ToCharArray(), xTerm.TerminalDb, xTerm.Verase, 3); + + Assert.Equal(ConsoleKey.Enter, consoleKeyInfo.Key); + Assert.Equal('\r', consoleKeyInfo.KeyChar); + Assert.Equal((ConsoleModifiers)0, consoleKeyInfo.Modifiers); + } + + [Fact] + public void BackTabEscapeSequence() + { + XTermData xTerm = new(); + + ConsoleKeyInfo consoleKeyInfo = Parse("\u001B[Z".ToCharArray(), xTerm.TerminalDb, xTerm.Verase, 3); + + Assert.Equal(ConsoleKey.Tab, consoleKeyInfo.Key); + Assert.Equal(default, consoleKeyInfo.KeyChar); + Assert.Equal(ConsoleModifiers.Shift, consoleKeyInfo.Modifiers); + } + + private static ConsoleKeyInfo Parse(char[] chars, TerminalFormatStrings terminalFormatStrings, byte verase, int expectedStartIndex) + { + int startIndex = 0; + + ConsoleKeyInfo parsed = KeyParser.Parse(chars, terminalFormatStrings, 0, verase, ref startIndex, chars.Length); + + Assert.Equal(expectedStartIndex, startIndex); + + return parsed; + } +} + +public abstract class TerminalData +{ + private TerminalFormatStrings? _terminalDb; + private Encoding? _consoleEncoding; + + protected abstract string EncodingCharset { get; } + protected abstract string Term { get; } + protected abstract string EncodedTerminalDb { get; } + internal abstract byte Verase { get; } + internal abstract IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios { get; } + + internal TerminalFormatStrings TerminalDb => _terminalDb ??= + new TerminalFormatStrings(new TermInfo.Database(Term, Convert.FromBase64String(EncodedTerminalDb))); + + internal Encoding ConsoleEncoding => _consoleEncoding ??= (string.IsNullOrEmpty(EncodingCharset) ? Encoding.Default : Encoding.GetEncoding(EncodingCharset)).RemovePreamble(); +} + +// Below you can find test data recorded with https://github.com/adamsitnik/ReadKey +// The idea behind is to be able to verify parser changes without the need of manual verification for every known Terminal. + +// Ubuntu 18.04 x64 +public class GNOMETerminalData : TerminalData +{ + protected override string EncodingCharset => "utf-8"; + protected override string Term => "xterm-256color"; + internal override byte Verase => 127; + protected override string EncodedTerminalDb => "GgElACYADwCdAQIGeHRlcm0tMjU2Y29sb3J8eHRlcm0gd2l0aCAyNTYgY29sb3JzAAABAAABAAAAAQAAAAABAQAAAAAAAAABAAABAAEBAAAAAAAAAAABAFAACAAYAP//////////////////////////AAH/fwAABAAGAAgAGQAeACYAKgAuAP//OQBKAEwAUABXAP//WQBmAP//agBuAHgAfAD/////gACEAIkAjgD//6AApQCqAP//rwC0ALkAvgDHAMsA0gD//+QA6QDvAPUA////////BwH///////8ZAf//HQH///////8fAf//JAH//////////ygBLAEyATYBOgE+AUQBSgFQAVYBXAFgAf//ZQH//2kBbgFzAXcBfgH//4UBiQGRAf////////////////////////////+ZAaIB/////6sBtAG9AcYBzwHYAeEB6gHzAfwB////////BQIJAg4CEwInAjAC/////0ICRQJQAlMCVQJYArUC//+4Av///////////////7oC//////////++Av//8wL/////9wL9Av////////////////////////////8DAwcD//////////////////////////////////////////////////////////////////8LA/////8SA///////////GQMgAycD/////y4D//81A////////zwD/////////////0MDSQNPA1YDXQNkA2sDcwN7A4MDiwOTA5sDowOrA7IDuQPAA8cDzwPXA98D5wPvA/cD/wMHBA4EFQQcBCMEKwQzBDsEQwRLBFMEWwRjBGoEcQR4BH8EhwSPBJcEnwSnBK8EtwS/BMYEzQTUBP/////////////////////////////////////////////////////////////ZBOQE6QT8BAAFCQUQBf////////////////////////////9uBf///////////////////////3MF////////////////////////////////////////////////////////////////////////////////////////eQX///////99BbwF//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wF/wUbW1oABwANABtbJWklcDElZDslcDIlZHIAG1szZwAbW0gbWzJKABtbSwAbW0oAG1slaSVwMSVkRwAbWyVpJXAxJWQ7JXAyJWRIAAoAG1tIABtbPzI1bAAIABtbPzEybBtbPzI1aAAbW0MAG1tBABtbPzEyOzI1aAAbW1AAG1tNABsoMAAbWzVtABtbMW0AG1s/MTA0OWgbWzIyOzA7MHQAG1sybQAbWzRoABtbOG0AG1s3bQAbWzdtABtbNG0AG1slcDElZFgAGyhCABsoQhtbbQAbWz8xMDQ5bBtbMjM7MDswdAAbWzRsABtbMjdtABtbMjRtABtbPzVoJDwxMDAvPhtbPzVsABtbIXAbWz8zOzRsG1s0bBs+ABtbTAB/ABtbM34AG09CABtPUAAbWzIxfgAbT1EAG09SABtPUwAbWzE1fgAbWzE3fgAbWzE4fgAbWzE5fgAbWzIwfgAbT0gAG1syfgAbT0QAG1s2fgAbWzV+ABtPQwAbWzE7MkIAG1sxOzJBABtPQQAbWz8xbBs+ABtbPzFoGz0AG1s/MTAzNGwAG1s/MTAzNGgAG1slcDElZFAAG1slcDElZE0AG1slcDElZEIAG1slcDElZEAAG1slcDElZFMAG1slcDElZEwAG1slcDElZEQAG1slcDElZEMAG1slcDElZFQAG1slcDElZEEAG1tpABtbNGkAG1s1aQAlcDElYxtbJXAyJXsxfSUtJWRiABtjG10xMDQHABtbIXAbWz8zOzRsG1s0bBs+ABs4ABtbJWklcDElZGQAGzcACgAbTQAlPyVwOSV0GygwJWUbKEIlOxtbMCU/JXA2JXQ7MSU7JT8lcDUldDsyJTslPyVwMiV0OzQlOyU/JXAxJXAzJXwldDs3JTslPyVwNCV0OzUlOyU/JXA3JXQ7OCU7bQAbSAAJABtPRQBgYGFhZmZnZ2lpampra2xsbW1ubm9vcHBxcXJyc3N0dHV1dnZ3d3h4eXl6ent7fHx9fX5+ABtbWgAbWz83aAAbWz83bAAbT0YAG09NABtbMzsyfgAbWzE7MkYAG1sxOzJIABtbMjsyfgAbWzE7MkQAG1s2OzJ+ABtbNTsyfgAbWzE7MkMAG1syM34AG1syNH4AG1sxOzJQABtb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"; // /lib/terminfo/x/xterm-256color + + internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios + { + get + { + yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, true, false, false)); + yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); + yield return (new byte[] { 1 }, new ConsoleKeyInfo(default, ConsoleKey.A, false, false, true)); + yield return (new byte[] { 27, 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, true, false)); + yield return (new byte[] { 27, 1 }, new ConsoleKeyInfo(default, ConsoleKey.A, false, true, true)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); + yield return (new byte[] { 27, 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, true, false)); + yield return (new byte[] { 33 }, new ConsoleKeyInfo('!', default, false, false, false)); + yield return (new byte[] { 50 }, new ConsoleKeyInfo('2', ConsoleKey.D2, false, false, false)); + yield return (new byte[] { 0 }, new ConsoleKeyInfo(default, ConsoleKey.D2, false, false, true)); + yield return (new byte[] { 27, 50 }, new ConsoleKeyInfo('2', ConsoleKey.D2, false, true, false)); + yield return (new byte[] { 64 }, new ConsoleKeyInfo('@', default, false, false, false)); + yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', default, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); + yield return (new byte[] { 27, 61 }, new ConsoleKeyInfo('=', default, false, false, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, false, false, false)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)127, ConsoleKey.Backspace, false, false, false)); // verase + yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)127, ConsoleKey.Backspace, false, true, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); + yield return (new byte[] { 45 }, new ConsoleKeyInfo('-', ConsoleKey.Subtract, false, false, false)); + yield return (new byte[] { 27, 79, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); + yield return (new byte[] { 27, 79, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, false)); // F1 + yield return (new byte[] { 27, 79, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, false)); // F2 + yield return (new byte[] { 27, 79, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, false)); // F3 + yield return (new byte[] { 27, 79, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, false)); // F4 + yield return (new byte[] { 27, 91, 49, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, false)); // F5 + yield return (new byte[] { 27, 91, 49, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, false)); // F6 + yield return (new byte[] { 27, 91, 49, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, false)); // F7 + yield return (new byte[] { 27, 91, 49, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, false)); // F8 + yield return (new byte[] { 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, false)); // F9 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // F12 + yield return (new byte[] { 27, 91, 49, 59, 53, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, true)); // Ctrl+F1 + yield return (new byte[] { 27, 91, 49, 59, 53, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, true)); // Ctrl+F2 + yield return (new byte[] { 27, 91, 49, 59, 53, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, true)); // Ctrl+F3 + yield return (new byte[] { 27, 91, 49, 59, 53, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, true)); // Ctrl+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, true)); // Ctrl+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, true)); // Ctrl+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, true)); // Ctrl+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, true)); // Ctrl+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, true)); // Ctrl+F9 + yield return (new byte[] { 27, 91, 50, 49, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, true)); // Ctrl+F10 + yield return (new byte[] { 27, 91, 50, 51, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, true)); // Ctrl+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); // Ctrl+F12 + yield return (new byte[] { 27, 91, 50, 48, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, true, false)); // Alt+F9 + yield return (new byte[] { 27, 91, 50, 51, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, true, false)); // Alt+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); // Alt+F12 + yield return (new byte[] { 27, 91, 49, 59, 50, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, true, false, false)); // Shift+F1 + yield return (new byte[] { 27, 91, 49, 59, 50, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, true, false, false)); // Shift+F2 + yield return (new byte[] { 27, 91, 49, 59, 50, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, true, false, false)); // Shift+F3 + yield return (new byte[] { 27, 91, 49, 59, 50, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, true, false, false)); // Shift+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, true, false, false)); // Shift+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, true, false, false)); // Shift+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, true, false, false)); // Shift+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, true, false, false)); // Shift+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, true, false, false)); // Shift+F9 + yield return (new byte[] { 27, 91, 50, 51, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, true, false, false)); // Shift+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); // Shift+F12 + yield return (new byte[] { 27, 91, 49, 59, 56, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, true, true, true)); // Ctrl+Alt+Shift+F1 + yield return (new byte[] { 27, 91, 49, 59, 56, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, true, true, true)); // Ctrl+Alt+Shift+F2 + yield return (new byte[] { 27, 91, 49, 59, 56, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, true, true, true)); // Ctrl+Alt+Shift+F3 + yield return (new byte[] { 27, 91, 49, 59, 56, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, true, true, true)); // Ctrl+Alt+Shift+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, true, true, true)); // Ctrl+Alt+Shift+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, true, true, true)); // Ctrl+Alt+Shift+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, true, true, true)); // Ctrl+Alt+Shift+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, true, true, true)); // Ctrl+Alt+Shift+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, true, true, true)); // Ctrl+Alt+Shift+F9 + yield return (new byte[] { 27, 91, 50, 49, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, true, true, true)); // Ctrl+Alt+Shift+F10 + yield return (new byte[] { 27, 91, 50, 51, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, true, true, true)); // Ctrl+Alt+Shift+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); // Ctrl+Alt+Shift+F12 + yield return (new byte[] { 27, 79, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); // Home + yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); // Ctrl+Home + yield return (new byte[] { 27, 91, 49, 59, 51, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); // Alt+Home + yield return (new byte[] { 27, 91, 49, 59, 55, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); // Ctrl+Alt+Home + yield return (new byte[] { 27, 79, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + yield return (new byte[] { 27, 91, 49, 59, 53, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, true)); // Ctrl+End + yield return (new byte[] { 27, 91, 49, 59, 51, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, true, false)); // Alt+End + yield return (new byte[] { 27, 91, 49, 59, 55, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, true, true)); // Ctrl+Alt+End + yield return (new byte[] { 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, false)); // PageUp + yield return (new byte[] { 27, 91, 53, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, true)); // Ctrl+PageUp + yield return (new byte[] { 27, 91, 53, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, true, false)); // Alt+PageUp + yield return (new byte[] { 27, 91, 53, 59, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, true, true)); // Ctrl+Alt+PageUp + yield return (new byte[] { 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, false)); // PageDown + yield return (new byte[] { 27, 91, 54, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, true)); // Ctrl+PageDown + yield return (new byte[] { 27, 91, 54, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, true, false)); // Alt+PageDown + yield return (new byte[] { 27, 91, 54, 59, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, true, true)); // Ctrl+Alt+PageDown + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); // LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); // Ctrl+LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); // Alt+LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, true, false, false)); // Shift+LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 52, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, true, true, false)); // Shift+Alt+LeftArrow + yield return (new byte[] { 27, 79, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false)); // UpArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, true)); // Ctrl+UpArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, true, false)); // Alt+UpArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, true, false, false)); // Shift+UpArrow + yield return (new byte[] { 27, 91, 49, 59, 52, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, true, true, false)); // Shift+Alt+UpArrow + yield return (new byte[] { 27, 79, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false)); // DownArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, true)); // Ctrl+DownArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, true, false)); // Alt+DownArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, true, false, false)); // Shift+DownArrow + yield return (new byte[] { 27, 91, 49, 59, 52, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, true, true, false)); // Shift+Alt+DownArrow + yield return (new byte[] { 27, 79, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false)); // RightArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, true)); // Ctrl+RightArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, true, false)); // Alt+RightArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, true, false, false)); // Shift+RightArrow + yield return (new byte[] { 27, 91, 49, 59, 52, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, true, true, false)); // Shift+Alt+RightArrow + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); // Insert + yield return (new byte[] { 27, 91, 50, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, true, false)); // Alt+Insert + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); // Delete + yield return (new byte[] { 27, 91, 51, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, true)); // Ctrl+Delete + yield return (new byte[] { 27, 91, 51, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, true, false)); // Alt+Delete + yield return (new byte[] { 27, 91, 51, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, false, false)); // Shift+Delete + yield return (new byte[] { 27, 91, 51, 59, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, false, true)); // Ctrl+Shift+Delete + yield return (new byte[] { 27, 91, 51, 59, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, true, false)); // Alt+Shift+Delete + yield return (new byte[] { 27, 91, 51, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, true, true)); // Ctrl+Alt+Shift+Delete + // Numeric Keypad + yield return (new byte[] { 48 }, new ConsoleKeyInfo('0', ConsoleKey.D0, false, false, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); + yield return (new byte[] { 50 }, new ConsoleKeyInfo('2', ConsoleKey.D2, false, false, false)); + yield return (new byte[] { 51 }, new ConsoleKeyInfo('3', ConsoleKey.D3, false, false, false)); + yield return (new byte[] { 52 }, new ConsoleKeyInfo('4', ConsoleKey.D4, false, false, false)); + yield return (new byte[] { 53 }, new ConsoleKeyInfo('5', ConsoleKey.D5, false, false, false)); + yield return (new byte[] { 54 }, new ConsoleKeyInfo('6', ConsoleKey.D6, false, false, false)); + yield return (new byte[] { 55 }, new ConsoleKeyInfo('7', ConsoleKey.D7, false, false, false)); + yield return (new byte[] { 56 }, new ConsoleKeyInfo('8', ConsoleKey.D8, false, false, false)); + yield return (new byte[] { 57 }, new ConsoleKeyInfo('9', ConsoleKey.D9, false, false, false)); + yield return (new byte[] { 47 }, new ConsoleKeyInfo('/', ConsoleKey.Divide, false, false, false)); + yield return (new byte[] { 42 }, new ConsoleKeyInfo('*', ConsoleKey.Multiply, false, false, false)); + yield return (new byte[] { 45 }, new ConsoleKeyInfo('-', ConsoleKey.Subtract, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); + yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); // Enter (using Numeric Keypad)) + yield return (new byte[] { 46 }, new ConsoleKeyInfo('.', ConsoleKey.OemPeriod, false, false, false)); // . (period using Numeric Keypad)) + // Num Lock toggle + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); // Insert + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); // Delete + yield return (new byte[] { 27, 79, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + yield return (new byte[] { 27, 79, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false)); // Down Arrow + yield return (new byte[] { 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, false)); // Page Down + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); // Left Arrow + yield return (new byte[] { 27, 79, 69 }, new ConsoleKeyInfo(default, ConsoleKey.NoName, false, false, false)); // Begin (5) + yield return (new byte[] { 27, 79, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false)); // Right Arrow + yield return (new byte[] { 27, 79, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); // Home + yield return (new byte[] { 27, 79, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false)); // Up Arrow + yield return (new byte[] { 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, false)); // Page Up + yield return (new byte[] { 27, 79, 111 }, new ConsoleKeyInfo('/', ConsoleKey.Divide, false, false, false)); // / (divide sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 106 }, new ConsoleKeyInfo('*', ConsoleKey.Multiply, false, false, false)); // * (multiply sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 109 }, new ConsoleKeyInfo('-', ConsoleKey.Subtract, false, false, false)); // - (minus sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 107 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); // + (plus sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 77 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); // Enter (using Numeric Keypad)) + } + } +} + +// Ubuntu 18.04 x64 +public class XTermData : TerminalData +{ + protected override string EncodingCharset => "utf-8"; + protected override string Term => "xterm"; + internal override byte Verase => 127; + protected override string EncodedTerminalDb => "GgEpACYADwCdAbgFeHRlcm18eHRlcm0tZGViaWFufFgxMSB0ZXJtaW5hbCBlbXVsYXRvcgAAAQAAAQAAAAEAAAAAAQEAAAAAAAAAAQAAAQAAAQAAAAAAAAAAAQBQAAgAGAD//////////////////////////wgAQAAAAAQABgAIABkAHgAmACoALgD//zkASgBMAFAAVwD//1kAZgD//2oAbgB4AHwA/////4AAhACJAI4A//+gAKUAqgD//68AtAC5AL4AxwDLANIA///kAOkA7wD1AP///////wcB////////GQH//x0B////////HwH//yQB//////////8oASwBMgE2AToBPgFEAUoBUAFWAVwBYAH//2UB//9pAW4BcwF3AX4B//+FAYkBkQH/////////////////////////////mQGiAf////+rAbQBvQHGAc8B2AHhAeoB8wH8Af///////wUCCQIOAhMCJwIqAv////88Aj8CSgJNAk8CUgKvAv//sgL///////////////+0Av//////////uAL//+0C//////EC9wL//////////////////////////////QIBA///////////////////////////////////////////////////////////////////BQP/////DAP//////////xMDGgMhA/////8oA///LwP///////82A/////////////89A0MDSQNQA1cDXgNlA20DdQN9A4UDjQOVA50DpQOsA7MDugPBA8kD0QPZA+ED6QPxA/kDAQQIBA8EFgQdBCUELQQ1BD0ERQRNBFUEXQRkBGsEcgR5BIEEiQSRBJkEoQSpBLEEuQTABMcEzgT/////////////////////////////////////////////////////////////0wTeBOME9gT6BP//////////AwVJBf//////////////////jwX///////////////////////+UBf///////////////////////////////////////////////////////////////////////////////////////5oF////////ngWoBf//////////////////////////////////////////////////////////////////////////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"; // /lib/terminfo/x/xterm + + internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios + { + get + { + yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, true, false, false)); + yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); + yield return (new byte[] { 1 }, new ConsoleKeyInfo(default, ConsoleKey.A, false, false, true)); + yield return (new byte[] { 195, 161 }, new ConsoleKeyInfo('\u00E1', default, false, false, false)); + yield return (new byte[] { 194, 129 }, new ConsoleKeyInfo('\u0081', default, false, false, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); + yield return (new byte[] { 194, 177 }, new ConsoleKeyInfo('\u00B1', default, false, false, false)); + yield return (new byte[] { 33 }, new ConsoleKeyInfo('!', default, false, false, false)); + yield return (new byte[] { 50 }, new ConsoleKeyInfo('2', ConsoleKey.D2, false, false, false)); + yield return (new byte[] { 0 }, new ConsoleKeyInfo(default, ConsoleKey.D2, false, false, true)); + yield return (new byte[] { 194, 178 }, new ConsoleKeyInfo('\u00B2', default, false, false, false)); + yield return (new byte[] { 64 }, new ConsoleKeyInfo('@', default, false, false, false)); + yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', default, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); + yield return (new byte[] { 194, 189 }, new ConsoleKeyInfo('\u00BD', default, false, false, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, false, false, false)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)127, ConsoleKey.Backspace, false, false, false)); // verase + yield return (new byte[] { 195, 191 }, new ConsoleKeyInfo('\u00FF', default, false, false, false)); + yield return (new byte[] { 194, 136 }, new ConsoleKeyInfo('\u0088', default, false, false, false)); + yield return (new byte[] { 27, 79, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); + yield return (new byte[] { 45 }, new ConsoleKeyInfo('-', ConsoleKey.Subtract, false, false, false)); + + yield return (new byte[] { 27, 79, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, false)); // F1 + yield return (new byte[] { 27, 79, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, false)); // F2 + yield return (new byte[] { 27, 79, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, false)); // F3 + yield return (new byte[] { 27, 79, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, false)); // F4 + yield return (new byte[] { 27, 91, 49, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, false)); // F5 + yield return (new byte[] { 27, 91, 49, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, false)); // F6 + yield return (new byte[] { 27, 91, 49, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, false)); // F7 + yield return (new byte[] { 27, 91, 49, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, false)); // F8 + yield return (new byte[] { 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, false)); // F9 + yield return (new byte[] { 27, 91, 50, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, false)); // F10 + yield return (new byte[] { 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, false)); // F11 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // F12 + yield return (new byte[] { 27, 91, 49, 59, 53, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, true)); // Ctrl+F1 + yield return (new byte[] { 27, 91, 49, 59, 53, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, true)); // Ctrl+F2 + yield return (new byte[] { 27, 91, 49, 59, 53, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, true)); // Ctrl+F3 + yield return (new byte[] { 27, 91, 49, 59, 53, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, true)); // Ctrl+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, true)); // Ctrl+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, true)); // Ctrl+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, true)); // Ctrl+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, true)); // Ctrl+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, true)); // Ctrl+F9 + yield return (new byte[] { 27, 91, 50, 49, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, true)); // Ctrl+F10 + yield return (new byte[] { 27, 91, 50, 51, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, true)); // Ctrl+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); // Ctrl+F12 + yield return (new byte[] { 27, 91, 49, 59, 51, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, true, false)); // Alt+F3 + yield return (new byte[] { 27, 91, 50, 48, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, true, false)); // Alt+F9 + yield return (new byte[] { 27, 91, 50, 51, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, true, false)); // Alt+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); // Alt+F12 + yield return (new byte[] { 27, 91, 49, 59, 50, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, true, false, false)); // Shift+F1 + yield return (new byte[] { 27, 91, 49, 59, 50, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, true, false, false)); // Shift+F2 + yield return (new byte[] { 27, 91, 49, 59, 50, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, true, false, false)); // Shift+F3 + yield return (new byte[] { 27, 91, 49, 59, 50, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, true, false, false)); // Shift+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, true, false, false)); // Shift+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, true, false, false)); // Shift+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, true, false, false)); // Shift+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, true, false, false)); // Shift+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, true, false, false)); // Shift+F9 + yield return (new byte[] { 27, 91, 50, 49, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, true, false, false)); // Shift+F10 + yield return (new byte[] { 27, 91, 50, 51, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, true, false, false)); // Shift+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); // Shift+F12 + yield return (new byte[] { 27, 91, 49, 59, 56, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, true, true, true)); // Ctrl+Alt+Shift+F1 + yield return (new byte[] { 27, 91, 49, 59, 56, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, true, true, true)); // Ctrl+Alt+Shift+F2 + yield return (new byte[] { 27, 91, 49, 59, 56, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, true, true, true)); // Ctrl+Alt+Shift+F3 + yield return (new byte[] { 27, 91, 49, 59, 56, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, true, true, true)); // Ctrl+Alt+Shift+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, true, true, true)); // Ctrl+Alt+Shift+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, true, true, true)); // Ctrl+Alt+Shift+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, true, true, true)); // Ctrl+Alt+Shift+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, true, true, true)); // Ctrl+Alt+Shift+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, true, true, true)); // Ctrl+Alt+Shift+F9 + yield return (new byte[] { 27, 91, 50, 49, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, true, true, true)); // Ctrl+Alt+Shift+F10 + yield return (new byte[] { 27, 91, 50, 51, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, true, true, true)); // Ctrl+Alt+Shift+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); // Ctrl+Alt+Shift+F12 + yield return (new byte[] { 27, 79, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); // Home + yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); // Ctrl+Home + yield return (new byte[] { 27, 91, 49, 59, 51, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); // Alt+Home + yield return (new byte[] { 27, 91, 49, 59, 55, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); // Ctrl+Alt+Home + yield return (new byte[] { 27, 79, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + yield return (new byte[] { 27, 91, 49, 59, 53, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, true)); // Ctrl+End + yield return (new byte[] { 27, 91, 49, 59, 51, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, true, false)); // Alt+End + yield return (new byte[] { 27, 91, 49, 59, 55, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, true, true)); // Ctrl+Alt+End + yield return (new byte[] { 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, false)); // PageUp + yield return (new byte[] { 27, 91, 53, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, true)); // Ctrl+PageUp + yield return (new byte[] { 27, 91, 53, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, true, false)); // Alt+PageUp + yield return (new byte[] { 27, 91, 53, 59, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, true, true)); // Ctrl+Alt+PageUp + yield return (new byte[] { 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, false)); // PageDown + yield return (new byte[] { 27, 91, 54, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, true)); // Ctrl+PageDown + yield return (new byte[] { 27, 91, 54, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, true, false)); // Alt+PageDown + yield return (new byte[] { 27, 91, 54, 59, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, true, true)); // Ctrl+Alt+PageDown + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); // LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); // Ctrl+LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); // Alt+LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, true, false, false)); // Shift+LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 52, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, true, true, false)); // Shift+Alt+LeftArrow + yield return (new byte[] { 27, 79, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false)); // UpArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, true)); // Ctrl+UpArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, true, false)); // Alt+UpArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, true, false, false)); // Shift+UpArrow + yield return (new byte[] { 27, 91, 49, 59, 52, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, true, true, false)); // Shift+Alt+UpArrow + yield return (new byte[] { 27, 79, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false)); // DownArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, true)); // Ctrl+DownArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, true, false)); // Alt+DownArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, true, false, false)); // Shift+DownArrow + yield return (new byte[] { 27, 91, 49, 59, 52, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, true, true, false)); // Shift+Alt+DownArrow + yield return (new byte[] { 27, 79, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false)); // RightArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, true)); // Ctrl+RightArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, true, false)); // Alt+RightArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, true, false, false)); // Shift+RightArrow + yield return (new byte[] { 27, 91, 49, 59, 52, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, true, true, false)); // Shift+Alt+RightArrow + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); // Insert + yield return (new byte[] { 27, 91, 50, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, true, false)); // Alt+Insert + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); // Delete + yield return (new byte[] { 27, 91, 51, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, true)); // Ctrl+Delete + yield return (new byte[] { 27, 91, 51, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, true, false)); // Alt+Delete + yield return (new byte[] { 27, 91, 51, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, false, false)); // Shift+Delete + yield return (new byte[] { 27, 91, 51, 59, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, false, true)); // Ctrl+Shift+Delete + yield return (new byte[] { 27, 91, 51, 59, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, true, false)); // Alt+Shift+Delete + yield return (new byte[] { 27, 91, 51, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, true, true)); // Ctrl+Alt+Shift+Delete + // Numeric Keypad + yield return (new byte[] { 48 }, new ConsoleKeyInfo('0', ConsoleKey.D0, false, false, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); + yield return (new byte[] { 50 }, new ConsoleKeyInfo('2', ConsoleKey.D2, false, false, false)); + yield return (new byte[] { 51 }, new ConsoleKeyInfo('3', ConsoleKey.D3, false, false, false)); + yield return (new byte[] { 52 }, new ConsoleKeyInfo('4', ConsoleKey.D4, false, false, false)); + yield return (new byte[] { 53 }, new ConsoleKeyInfo('5', ConsoleKey.D5, false, false, false)); + yield return (new byte[] { 54 }, new ConsoleKeyInfo('6', ConsoleKey.D6, false, false, false)); + yield return (new byte[] { 55 }, new ConsoleKeyInfo('7', ConsoleKey.D7, false, false, false)); + yield return (new byte[] { 56 }, new ConsoleKeyInfo('8', ConsoleKey.D8, false, false, false)); + yield return (new byte[] { 57 }, new ConsoleKeyInfo('9', ConsoleKey.D9, false, false, false)); + yield return (new byte[] { 47 }, new ConsoleKeyInfo('/', ConsoleKey.Divide, false, false, false)); + yield return (new byte[] { 42 }, new ConsoleKeyInfo('*', ConsoleKey.Multiply, false, false, false)); + yield return (new byte[] { 45 }, new ConsoleKeyInfo('-', ConsoleKey.Subtract, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); + yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); // Enter (using Numeric Keypad)) + yield return (new byte[] { 46 }, new ConsoleKeyInfo('.', ConsoleKey.OemPeriod, false, false, false)); // . (period using Numeric Keypad)) + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); // Insert + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); // Delete + yield return (new byte[] { 27, 79, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + yield return (new byte[] { 27, 79, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false)); // Down Arrow + yield return (new byte[] { 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, false)); // Page Down + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); // Left Arrow + yield return (new byte[] { 27, 79, 69 }, new ConsoleKeyInfo(default, ConsoleKey.NoName, false, false, false)); // Begin (5) + yield return (new byte[] { 27, 79, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false)); // Right Arrow + yield return (new byte[] { 27, 79, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); // Home + yield return (new byte[] { 27, 79, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false)); // Up Arrow + yield return (new byte[] { 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, false)); // Page Up + yield return (new byte[] { 27, 79, 111 }, new ConsoleKeyInfo('/', ConsoleKey.Divide, false, false, false)); // / (divide sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 106 }, new ConsoleKeyInfo('*', ConsoleKey.Multiply, false, false, false)); // * (multiply sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 109 }, new ConsoleKeyInfo('-', ConsoleKey.Subtract, false, false, false)); // - (minus sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 107 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); // + (plus sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 77 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); // Enter (using Numeric Keypad)) + } + } +} + +// Ubuntu 18.04 x64 (Press Ctrl+ALt+F6 to enter the text console, then switch back to GUI by pressing Ctrl+ALt+F1) +public class LinuxConsole : TerminalData +{ + protected override string EncodingCharset => "utf-8"; + protected override string Term => "linux"; + internal override byte Verase => 127; + protected override string EncodedTerminalDb => "GgEUAB0AEAB9AUUDbGludXh8bGludXggY29uc29sZQAAAQAAAQEAAAAAAAAAAQEAAAAAAAEAAAAAAAABAQD//wgA/////////////////////////////wgAQAASAP//AAACAAQAFQAaACEAJQApAP//NABFAEcASwBXAP//WQBlAP//aQBtAHkAfQD/////gQCDAIgA/////40AkgD/////lwCcAKEApgCvALEA/////7YAuwDBAMcA////////////////2QDdAP//4QD////////jAP//6AD//////////+wA8QD3APwAAQEGAQsBEQEXAR0BIwEoAf//LQH//zEBNgE7Af///////z8B////////////////////////////////////////QwH//0YBTwFYAWEB//9qAXMBfAH//4UB//////////////////+OAf///////5QBlwGiAaUBpwGqAQEC//8EAv///////////////wYC//////////8KAv//TQL/////UQJXAv////9dAv////////////////////9hAv//////////////////////////////////////////////////ZgL//////////////////////////////////////////////////////////////////////////////////2gCbgJ0AnoCgAKGAowCkgKYAp4C//////////////////////////////////////////////////////////////////////////////////////////////////////////////////+kAv////////////////////////////////////////////////////////////+pArQCuQK/AsMCzALQAv//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////IQP///////8lAy8D////////////////////////////////////////////////OQM/AwcADQAbWyVpJXAxJWQ7JXAyJWRyABtbM2cAG1tIG1tKABtbSwAbW0oAG1slaSVwMSVkRwAbWyVpJXAxJWQ7JXAyJWRIAAoAG1tIABtbPzI1bBtbPzFjAAgAG1s/MjVoG1s/MGMAG1tDABtbQQAbWz8yNWgbWz84YwAbW1AAG1tNAA4AG1s1bQAbWzFtABtbMm0AG1s0aAAbWzdtABtbN20AG1s0bQAbWyVwMSVkWAAPABtbbQ8AG1s0bAAbWzI3bQAbWzI0bQAbWz81aCQ8MjAwLz4bWz81bAAbW0AAG1tMAH8AG1szfgAbW0IAG1tbQQAbWzIxfgAbW1tCABtbW0MAG1tbRAAbW1tFABtbMTd+ABtbMTh+ABtbMTl+ABtbMjB+ABtbMX4AG1syfgAbW0QAG1s2fgAbWzV+ABtbQwAbW0EADQoAG1slcDElZFAAG1slcDElZE0AG1slcDElZEIAG1slcDElZEAAG1slcDElZEwAG1slcDElZEQAG1slcDElZEMAG1slcDElZEEAG2MbXVIAGzgAG1slaSVwMSVkZAAbNwAKABtNABtbMDsxMCU/JXAxJXQ7NyU7JT8lcDIldDs0JTslPyVwMyV0OzclOyU/JXA0JXQ7NSU7JT8lcDUldDsyJTslPyVwNiV0OzElO20lPyVwOSV0DiVlDyU7ABtIAAkAG1tHACsrLCwtLS4uMDBfX2BgYWFmZmdnaGhpaWpqa2tsbG1tbm5vb3BwcXFycnNzdHR1dXZ2d3d4eHl5enp7e3x8fWN+fgAbW1oAG1s/N2gAG1s/N2wAGykwABtbNH4AGgAbWzIzfgAbWzI0fgAbWzI1fgAbWzI2fgAbWzI4fgAbWzI5fgAbWzMxfgAbWzMyfgAbWzMzfgAbWzM0fgAbWzFLABtbJWklZDslZFIAG1s2bgAbWz82YwAbW2MAG1szOTs0OW0AG11SABtdUCVwMSV4JXAyJXsyNTV9JSolezEwMDB9JS8lMDJ4JXAzJXsyNTV9JSolezEwMDB9JS8lMDJ4JXA0JXsyNTV9JSolezEwMDB9JS8lMDJ4ABtbTQAbWzMlcDElZG0AG1s0JXAxJWRtABtbMTFtABtbMTBtAAADAAEACwAaADwAAQAAAAEA//8AAP///////////////////////wAAAwAGAAkADAAPABIAFQAYAB4AJAAoACwAMAA0ABtbM0oAQVgARzAAWFQAVTgARTAARTMAUzAAWE0Aa0VORDUAa0hPTTUAa2EyAGtiMQBrYjMAa2MyAHhtAA=="; // /lib/terminfo/l/linux + + internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios + { + get + { + yield return (new byte[] { 27, 91, 91, 65 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, false)); // F1 + yield return (new byte[] { 27, 91, 91, 66 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, false)); // F2 + yield return (new byte[] { 27, 91, 91, 67 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, false)); // F3 + yield return (new byte[] { 27, 91, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, false)); // F4 + yield return (new byte[] { 27, 91, 91, 69 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, false)); // F5 + yield return (new byte[] { 27, 91, 49, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, false)); // F6 + yield return (new byte[] { 27, 91, 49, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, false)); // F7 + yield return (new byte[] { 27, 91, 49, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, false)); // F8 + yield return (new byte[] { 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, false)); // F9 + yield return (new byte[] { 27, 91, 50, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, false)); // F10 + yield return (new byte[] { 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, false)); // F11 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // F12 + yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); // Home + yield return (new byte[] { 27, 91, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + yield return (new byte[] { 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, false)); // PageUp + yield return (new byte[] { 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, false)); // PageDown + yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); // LeftArrow + yield return (new byte[] { 27, 91, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false)); // UpArrow + yield return (new byte[] { 27, 91, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false)); // DownArrow + yield return (new byte[] { 27, 91, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false)); // RightArrow + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); // Insert + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); // Delete + // Numeric Keypad + yield return (new byte[] { 48 }, new ConsoleKeyInfo('0', ConsoleKey.D0, false, false, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); + yield return (new byte[] { 50 }, new ConsoleKeyInfo('2', ConsoleKey.D2, false, false, false)); + yield return (new byte[] { 51 }, new ConsoleKeyInfo('3', ConsoleKey.D3, false, false, false)); + yield return (new byte[] { 52 }, new ConsoleKeyInfo('4', ConsoleKey.D4, false, false, false)); + yield return (new byte[] { 53 }, new ConsoleKeyInfo('5', ConsoleKey.D5, false, false, false)); + yield return (new byte[] { 54 }, new ConsoleKeyInfo('6', ConsoleKey.D6, false, false, false)); + yield return (new byte[] { 55 }, new ConsoleKeyInfo('7', ConsoleKey.D7, false, false, false)); + yield return (new byte[] { 56 }, new ConsoleKeyInfo('8', ConsoleKey.D8, false, false, false)); + yield return (new byte[] { 57 }, new ConsoleKeyInfo('9', ConsoleKey.D9, false, false, false)); + yield return (new byte[] { 47 }, new ConsoleKeyInfo('/', ConsoleKey.Divide, false, false, false)); + yield return (new byte[] { 42 }, new ConsoleKeyInfo('*', ConsoleKey.Multiply, false, false, false)); + yield return (new byte[] { 45 }, new ConsoleKeyInfo('-', ConsoleKey.Subtract, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); + yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); // Enter (using Numeric Keypad)) + yield return (new byte[] { 46 }, new ConsoleKeyInfo('.', ConsoleKey.OemPeriod, false, false, false)); // . (period using Numeric Keypad)) + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); // Insert + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); // Delete + yield return (new byte[] { 27, 91, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + yield return (new byte[] { 27, 91, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false)); // Down Arrow + yield return (new byte[] { 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, false)); // Page Down + yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); // Left Arrow + yield return (new byte[] { 27, 91, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false)); // Right Arrow + yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); // Home + yield return (new byte[] { 27, 91, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false)); // Up Arrow + yield return (new byte[] { 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, false)); // Page Up + yield return (new byte[] { 47 }, new ConsoleKeyInfo('/', ConsoleKey.Divide, false, false, false)); // / (divide sign using Numeric Keypad)) + yield return (new byte[] { 42 }, new ConsoleKeyInfo('*', ConsoleKey.Multiply, false, false, false)); // * (multiply sign using Numeric Keypad)) + yield return (new byte[] { 45 }, new ConsoleKeyInfo('-', ConsoleKey.Subtract, false, false, false)); // - (minus sign using Numeric Keypad)) + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); // + (plus sign using Numeric Keypad)) + yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); // Enter (using Numeric Keypad)) + } + } +} + +// Windows 11 machine connected via PuTTY to Ubuntu 20.04 arm64 machine using TERM=xterm (default) +public class PuTTYData_xterm : TerminalData +{ + protected override string EncodingCharset => ""; + protected override string Term => "xterm"; + internal override byte Verase => 127; + protected override string EncodedTerminalDb => "GgEpACYADwCdAaQFeHRlcm18eHRlcm0tZGViaWFufFgxMSB0ZXJtaW5hbCBlbXVsYXRvcgAAAQAAAQAAAAEAAAAAAQEAAAAAAAAAAQAAAQAAAQAAAAAAAAAAAQBQAAgAGAD//////////////////////////wgAQAAAAAQABgAIABkAHgAmACoALgD//zkASgBMAFAAVwD//1kAZgD//2oAbgB4AHwA/////4AAhACJAI4A//+gAKUAqgD//68AtAC5AL4AxwDLANIA///kAOkA7wD1AP///////wcB////////GQH//x0B////////HwH//yQB//////////8oASwBMgE2AToBPgFEAUoBUAFWAVwBYAH//2UB//9pAW4BcwF3AX4B//+FAYkBkQH/////////////////////////////mQGiAf////+rAbQBvQHGAc8B2AHhAeoB8wH8Af///////wUCCQIOAv//EwIWAv////8oAisCNgI5AjsCPgKbAv//ngL///////////////+gAv//////////pAL//9kC/////90C4wL/////////////////////////////6QLtAv//////////////////////////////////////////////////////////////////8QL/////+AL///////////8CBgMNA/////8UA///GwP///////8iA/////////////8pAy8DNQM8A0MDSgNRA1kDYQNpA3EDeQOBA4kDkQOYA58DpgOtA7UDvQPFA80D1QPdA+UD7QP0A/sDAgQJBBEEGQQhBCkEMQQ5BEEESQRQBFcEXgRlBG0EdQR9BIUEjQSVBJ0EpQSsBLMEugT/////////////////////////////////////////////////////////////vwTKBM8E4gTmBP//////////7wQ1Bf//////////////////ewX///////////////////////+ABf///////////////////////////////////////////////////////////////////////////////////////4YF////////igWUBf//////////////////////////////////////////////////////////////////////////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"; // /lib/terminfo/x/xterm + + internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios => Array.Empty<(byte[], ConsoleKeyInfo)>(); +} + +// Windows 11 machine connected via PuTTY to Ubuntu 20.04 arm64 machine using TERM=linux +public class PuTTYData_linux : TerminalData +{ + protected override string EncodingCharset => ""; + protected override string Term => "linux"; + internal override byte Verase => 127; + protected override string EncodedTerminalDb => "GgEUAB0AEAB9AUUDbGludXh8bGludXggY29uc29sZQAAAQAAAQEAAAAAAAAAAQEAAAAAAAEAAAAAAAABAQD//wgA/////////////////////////////wgAQAASAP//AAACAAQAFQAaACEAJQApAP//NABFAEcASwBXAP//WQBlAP//aQBtAHkAfQD/////gQCDAIgA/////40AkgD/////lwCcAKEApgCvALEA/////7YAuwDBAMcA////////////////2QDdAP//4QD////////jAP//6AD//////////+wA8QD3APwAAQEGAQsBEQEXAR0BIwEoAf//LQH//zEBNgE7Af///////z8B////////////////////////////////////////QwH//0YBTwFYAWEB//9qAXMBfAH//4UB//////////////////+OAf///////5QBlwGiAaUBpwGqAQEC//8EAv///////////////wYC//////////8KAv//TQL/////UQJXAv////9dAv////////////////////9hAv//////////////////////////////////////////////////ZgL//////////////////////////////////////////////////////////////////////////////////2gCbgJ0AnoCgAKGAowCkgKYAp4C//////////////////////////////////////////////////////////////////////////////////////////////////////////////////+kAv////////////////////////////////////////////////////////////+pArQCuQK/AsMCzALQAv//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////IQP///////8lAy8D////////////////////////////////////////////////OQM/AwcADQAbWyVpJXAxJWQ7JXAyJWRyABtbM2cAG1tIG1tKABtbSwAbW0oAG1slaSVwMSVkRwAbWyVpJXAxJWQ7JXAyJWRIAAoAG1tIABtbPzI1bBtbPzFjAAgAG1s/MjVoG1s/MGMAG1tDABtbQQAbWz8yNWgbWz84YwAbW1AAG1tNAA4AG1s1bQAbWzFtABtbMm0AG1s0aAAbWzdtABtbN20AG1s0bQAbWyVwMSVkWAAPABtbbQ8AG1s0bAAbWzI3bQAbWzI0bQAbWz81aCQ8MjAwLz4bWz81bAAbW0AAG1tMAH8AG1szfgAbW0IAG1tbQQAbWzIxfgAbW1tCABtbW0MAG1tbRAAbW1tFABtbMTd+ABtbMTh+ABtbMTl+ABtbMjB+ABtbMX4AG1syfgAbW0QAG1s2fgAbWzV+ABtbQwAbW0EADQoAG1slcDElZFAAG1slcDElZE0AG1slcDElZEIAG1slcDElZEAAG1slcDElZEwAG1slcDElZEQAG1slcDElZEMAG1slcDElZEEAG2MbXVIAGzgAG1slaSVwMSVkZAAbNwAKABtNABtbMDsxMCU/JXAxJXQ7NyU7JT8lcDIldDs0JTslPyVwMyV0OzclOyU/JXA0JXQ7NSU7JT8lcDUldDsyJTslPyVwNiV0OzElO20lPyVwOSV0DiVlDyU7ABtIAAkAG1tHACsrLCwtLS4uMDBfX2BgYWFmZmdnaGhpaWpqa2tsbG1tbm5vb3BwcXFycnNzdHR1dXZ2d3d4eHl5enp7e3x8fWN+fgAbW1oAG1s/N2gAG1s/N2wAGykwABtbNH4AGgAbWzIzfgAbWzI0fgAbWzI1fgAbWzI2fgAbWzI4fgAbWzI5fgAbWzMxfgAbWzMyfgAbWzMzfgAbWzM0fgAbWzFLABtbJWklZDslZFIAG1s2bgAbWz82YwAbW2MAG1szOTs0OW0AG11SABtdUCVwMSV4JXAyJXsyNTV9JSolezEwMDB9JS8lMDJ4JXAzJXsyNTV9JSolezEwMDB9JS8lMDJ4JXA0JXsyNTV9JSolezEwMDB9JS8lMDJ4ABtbTQAbWzMlcDElZG0AG1s0JXAxJWRtABtbMTFtABtbMTBtAAABAAEAAQAEAA4AAQABAAAAAAADAAYAG1szSgBBWABVOABFMwA="; // /lib/terminfo/l/linux + + internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios => Array.Empty<(byte[], ConsoleKeyInfo)>(); +} + +// Windows 11 machine connected via PuTTY to Ubuntu 20.04 arm64 machine using TERM=putty +public class PuTTYData_putty : TerminalData +{ + protected override string EncodingCharset => ""; + protected override string Term => "putty"; + internal override byte Verase => 127; + protected override string EncodedTerminalDb => "GgEeAB0AEAB9AXwEcHV0dHl8UHVUVFkgdGVybWluYWwgZW11bGF0b3IAAQEAAAEAAAAAAQAAAAEBAAAAAAABAAAAAAAAAQEA//8IAP////////////////////////////8IAEAAFgAAAAQABgAIABkAHgAlACkALQD//zgASQBMAFAAVwD//1kAYAD//2QA//9nAGsAbwD//3UAdwB8AIEA/////4gA/////40AkgCXAJwApQCnAKwA//+3ALwAwgDIAP//2gD//9wA/////////gD//wIB////////BAH//wkB//////////8NARMBGQEfASUBKwExATcBPQFDAUkBTgH//1MB//9XAVwBYQFlAWkB//9tAXEBeQH//////////////////////////////////4EB//+EAY0BlgH//58BqAGxAboBwwHMAf/////////////////////VAf/////2AfkBBAIHAgkCDAJUAv//VwJZAv////////////9eAv//////////YgL//5UC/////5kCnwL/////pQL/////////////////////rAL//////////////////////////////////////////////////7EC//////////////////////////////////////////+zAv////////////////////+3Av////////////+7AsECxwLNAtMC2QLfAuUC6wLxAv//////////////////////////////////////////////////////////////////////////////////////////////////////////////////9wL//////////////////////////////////////////////////////////////AIHAwwDEgMWAx8DIwP//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////3QD////////eAOCA////////4wDkgOYA/////////////////////////////+eA3AEdgQbW1oABwANABtbJWklcDElZDslcDIlZHIAG1szZwAbW0gbW0oAG1tLABtbSgAbWyVpJXAxJWRHABtbJWklcDElZDslcDIlZEgAG0QAG1tIABtbPzI1bAAIABtbPzI1aAAbW0MAG00AG1tQABtbTQAbXTA7BwAOABtbNW0AG1sxbQAbWz80N2gAG1s0aAAbWzdtABtbN20AG1s0bQAbWyVwMSVkWAAPABtbbQ8AG1syShtbPzQ3bAAbWzRsABtbMjdtABtbMjRtABtbPzVoJDwxMDAvPhtbPzVsAAcAGzcbW3IbW20bWz83aBtbPzE7NDs2bBtbNGwbOBs+G11SABtbTAB/ABtbM34AG09CABtbMTF+ABtbMjF+ABtbMTJ+ABtbMTN+ABtbMTR+ABtbMTV+ABtbMTd+ABtbMTh+ABtbMTl+ABtbMjB+ABtbMX4AG1syfgAbT0QAG1s2fgAbWzV+ABtPQwAbW0IAG1tBABtPQQAbWz8xbBs+ABtbPzFoGz0ADQoAG1slcDElZFAAG1slcDElZE0AG1slcDElZEIAG1slcDElZFMAG1slcDElZEwAG1slcDElZEQAG1slcDElZEMAG1slcDElZFQAG1slcDElZEEAGzwbWyJwG1s1MDs2InAbYxtbPzNsG11SG1s/MTAwMGwAGzgAG1slaSVwMSVkZAAbNwAKABtNABtbMCU/JXAxJXA2JXwldDsxJTslPyVwMiV0OzQlOyU/JXAxJXAzJXwldDs3JTslPyVwNCV0OzUlO20lPyVwOSV0DiVlDyU7ABtIAAkAG10wOwAbW0cAYGBhYWZmZ2dqamtrbGxtbW5ub29wcHFxcnJzc3R0dXV2dnd3eHh5eXp6e3t8fH19fn4AG1taABtbPzdoABtbPzdsABsoQhspMAAbWzR+ABoAG1tEABtbQwAbWzIzfgAbWzI0fgAbWzI1fgAbWzI2fgAbWzI4fgAbWzI5fgAbWzMxfgAbWzMyfgAbWzMzfgAbWzM0fgAbWzFLABtbJWklZDslZFIAG1s2bgAbWz82YwAbW2MAG1szOTs0OW0AG11SABtdUCVwMSV4JXAyJXsyNTV9JSolezEwMDB9JS8lMDJ4JXAzJXsyNTV9JSolezEwMDB9JS8lMDJ4JXA0JXsyNTV9JSolezEwMDB9JS8lMDJ4ABtbPAAbWzMlcDElZG0AG1s0JXAxJWRtABtbMTBtABtbMTFtABtbMTJtACU/JXAxJXs4fSU9JXQbJSVH4peYGyUlQCVlJXAxJXsxMH0lPSV0GyUlR+KXmRslJUAlZSVwMSV7MTJ9JT0ldBslJUfimYAbJSVAJWUlcDElezEzfSU9JXQbJSVH4pmqGyUlQCVlJXAxJXsxNH0lPSV0GyUlR+KZqxslJUAlZSVwMSV7MTV9JT0ldBslJUfimLwbJSVAJWUlcDElezI3fSU9JXQbJSVH4oaQGyUlQCVlJXAxJXsxNTV9JT0ldBslJUfggqIbJSVAJWUlcDElYyU7ABtbMTFtABtbMTBtAAEAAQAEAAoAYQABAAEAAAAFAAoAKgAAAAMABgAJAAwADwAbWzNKABtdMDsAG1s/MTAwNjsxMDAwJT8lcDElezF9JT0ldGglZWwlOwAbWzwlaSVwMyVkOyVwMSVkOyVwMiVkOyU/JXA0JXRNJWVtJTsAWFQAVTgARTMAVFMAWE0AeG0A"; // /usr/share/terminfo/p/putty + + internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios => Array.Empty<(byte[], ConsoleKeyInfo)>(); +} + +// Windows (11) Terminal connected via SSH to Ubuntu 20.04 arm64 +public class WindowsTerminalData : TerminalData +{ + protected override string EncodingCharset => ""; + protected override string Term => "xterm-256color"; + internal override byte Verase => 127; + protected override string EncodedTerminalDb => "HgIlACYADwCdAe4FeHRlcm0tMjU2Y29sb3J8eHRlcm0gd2l0aCAyNTYgY29sb3JzAAABAAABAAAAAQAAAAABAQAAAAAAAAABAAABAAEBAAAAAAAAAAABAFAAAAAIAAAAGAAAAP////////////////////////////////////////////////////8AAQAAAAABAAAABAAGAAgAGQAeACYAKgAuAP//OQBKAEwAUABXAP//WQBmAP//agBuAHgAfAD/////gACEAIkAjgD//6AApQCqAP//rwC0ALkAvgDHAMsA0gD//+QA6QDvAPUA////////BwH///////8ZAf//HQH///////8fAf//JAH//////////ygBLAEyATYBOgE+AUQBSgFQAVYBXAFgAf//ZQH//2kBbgFzAXcBfgH//4UBiQGRAf////////////////////////////+ZAaIB/////6sBtAG9AcYBzwHYAeEB6gHzAfwB////////BQIJAg4C//8TAhwC/////y4CMQI8Aj8CQQJEAqEC//+kAv///////////////6YC//////////+qAv//3wL/////4wLpAv/////////////////////////////vAvMC///////////////////////////////////////////////////////////////////3Av/////+Av//////////BQMMAxMD/////xoD//8hA////////ygD/////////////y8DNQM7A0IDSQNQA1cDXwNnA28DdwN/A4cDjwOXA54DpQOsA7MDuwPDA8sD0wPbA+MD6wPzA/oDAQQIBA8EFwQfBCcELwQ3BD8ERwRPBFYEXQRkBGsEcwR7BIMEiwSTBJsEowSrBLIEuQTABP/////////////////////////////////////////////////////////////FBNAE1QToBOwE9QT8BP////////////////////////////9aBf///////////////////////18F////////////////////////////////////////////////////////////////////////////////////////ZQX///////9pBagF/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+gF6wUbW1oABwANABtbJWklcDElZDslcDIlZHIAG1szZwAbW0gbWzJKABtbSwAbW0oAG1slaSVwMSVkRwAbWyVpJXAxJWQ7JXAyJWRIAAoAG1tIABtbPzI1bAAIABtbPzEybBtbPzI1aAAbW0MAG1tBABtbPzEyOzI1aAAbW1AAG1tNABsoMAAbWzVtABtbMW0AG1s/MTA0OWgbWzIyOzA7MHQAG1sybQAbWzRoABtbOG0AG1s3bQAbWzdtABtbNG0AG1slcDElZFgAGyhCABsoQhtbbQAbWz8xMDQ5bBtbMjM7MDswdAAbWzRsABtbMjdtABtbMjRtABtbPzVoJDwxMDAvPhtbPzVsABtbIXAbWz8zOzRsG1s0bBs+ABtbTAB/ABtbM34AG09CABtPUAAbWzIxfgAbT1EAG09SABtPUwAbWzE1fgAbWzE3fgAbWzE4fgAbWzE5fgAbWzIwfgAbT0gAG1syfgAbT0QAG1s2fgAbWzV+ABtPQwAbWzE7MkIAG1sxOzJBABtPQQAbWz8xbBs+ABtbPzFoGz0AG1s/MTAzNGwAG1s/MTAzNGgAG1slcDElZFAAG1slcDElZE0AG1slcDElZEIAG1slcDElZEAAG1slcDElZFMAG1slcDElZEwAG1slcDElZEQAG1slcDElZEMAG1slcDElZFQAG1slcDElZEEAG1tpABtbNGkAG1s1aQAbYxtdMTA0BwAbWyFwG1s/Mzs0bBtbNGwbPgAbOAAbWyVpJXAxJWRkABs3AAoAG00AJT8lcDkldBsoMCVlGyhCJTsbWzAlPyVwNiV0OzElOyU/JXA1JXQ7MiU7JT8lcDIldDs0JTslPyVwMSVwMyV8JXQ7NyU7JT8lcDQldDs1JTslPyVwNyV0OzglO20AG0gACQAbT0UAYGBhYWZmZ2dpaWpqa2tsbG1tbm5vb3BwcXFycnNzdHR1dXZ2d3d4eHl5enp7e3x8fX1+fgAbW1oAG1s/N2gAG1s/N2wAG09GABtPTQAbWzM7Mn4AG1sxOzJGABtbMTsySAAbWzI7Mn4AG1sxOzJEABtbNjsyfgAbWzU7Mn4AG1sxOzJDABtbMjN+ABtbMjR+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"; // /lib/terminfo/x/xterm-256color + + internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios + { + get + { + yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, true, false, false)); + yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); + yield return (new byte[] { 1 }, new ConsoleKeyInfo(default, ConsoleKey.A, false, false, true)); + yield return (new byte[] { 27, 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, true, false)); + yield return (new byte[] { 27, 1 }, new ConsoleKeyInfo(default, ConsoleKey.A, false, true, true)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); + yield return (new byte[] { 27, 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, true, false)); + yield return (new byte[] { 33 }, new ConsoleKeyInfo('!', default, false, false, false)); + yield return (new byte[] { 50 }, new ConsoleKeyInfo('2', ConsoleKey.D2, false, false, false)); + yield return (new byte[] { 27, 50 }, new ConsoleKeyInfo('2', ConsoleKey.D2, false, true, false)); + yield return (new byte[] { 64 }, new ConsoleKeyInfo('@', default, false, false, false)); + yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', default, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); + yield return (new byte[] { 27, 61 }, new ConsoleKeyInfo('=', default, false, false, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, false, false, false)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)127, ConsoleKey.Backspace, false, false, false)); // verase + yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)127, ConsoleKey.Backspace, false, true, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); + yield return (new byte[] { 45 }, new ConsoleKeyInfo('-', ConsoleKey.Subtract, false, false, false)); + + yield return (new byte[] { 27, 79, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, false)); // F1 + yield return (new byte[] { 27, 79, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, false)); // F2 + yield return (new byte[] { 27, 79, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, false)); // F3 + yield return (new byte[] { 27, 79, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, false)); // F4 + yield return (new byte[] { 27, 91, 49, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, false)); // F5 + yield return (new byte[] { 27, 91, 49, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, false)); // F6 + yield return (new byte[] { 27, 91, 49, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, false)); // F7 + yield return (new byte[] { 27, 91, 49, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, false)); // F8 + yield return (new byte[] { 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, false)); // F9 + yield return (new byte[] { 27, 91, 50, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, false)); // F10 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // F12 + yield return (new byte[] { 27, 91, 49, 59, 53, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, true)); // Ctrl+F1 + yield return (new byte[] { 27, 91, 49, 59, 53, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, true)); // Ctrl+F2 + yield return (new byte[] { 27, 91, 49, 59, 53, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, true)); // Ctrl+F3 + yield return (new byte[] { 27, 91, 49, 59, 53, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, true)); // Ctrl+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, true)); // Ctrl+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, true)); // Ctrl+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, true)); // Ctrl+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, true)); // Ctrl+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, true)); // Ctrl+F9 + yield return (new byte[] { 27, 91, 50, 49, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, true)); // Ctrl+F10 + yield return (new byte[] { 27, 91, 50, 51, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, true)); // Ctrl+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); // Ctrl+F12 + yield return (new byte[] { 27, 91, 49, 59, 51, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, true, false)); // Alt+F1 + yield return (new byte[] { 27, 91, 49, 59, 51, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, true, false)); // Alt+F2 + yield return (new byte[] { 27, 91, 49, 59, 51, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, true, false)); // Alt+F3 + yield return (new byte[] { 27, 91, 49, 53, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, true, false)); // Alt+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, true, false)); // Alt+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, true, false)); // Alt+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, true, false)); // Alt+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, true, false)); // Alt+F9 + yield return (new byte[] { 27, 91, 50, 49, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, true, false)); // Alt+F10 + yield return (new byte[] { 27, 91, 50, 51, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, true, false)); // Alt+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); // Alt+F12 + yield return (new byte[] { 27, 91, 49, 59, 50, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, true, false, false)); // Shift+F1 + yield return (new byte[] { 27, 91, 49, 59, 50, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, true, false, false)); // Shift+F2 + yield return (new byte[] { 27, 91, 49, 59, 50, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, true, false, false)); // Shift+F3 + yield return (new byte[] { 27, 91, 49, 59, 50, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, true, false, false)); // Shift+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, true, false, false)); // Shift+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, true, false, false)); // Shift+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, true, false, false)); // Shift+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, true, false, false)); // Shift+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, true, false, false)); // Shift+F9 + yield return (new byte[] { 27, 91, 50, 49, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, true, false, false)); // Shift+F10 + yield return (new byte[] { 27, 91, 50, 51, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, true, false, false)); // Shift+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); // Shift+F12 + yield return (new byte[] { 27, 91, 49, 59, 56, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, true, true, true)); // Ctrl+Alt+Shift+F1 + yield return (new byte[] { 27, 91, 49, 59, 56, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, true, true, true)); // Ctrl+Alt+Shift+F2 + yield return (new byte[] { 27, 91, 49, 59, 56, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, true, true, true)); // Ctrl+Alt+Shift+F3 + yield return (new byte[] { 27, 91, 49, 59, 56, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, true, true, true)); // Ctrl+Alt+Shift+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, true, true, true)); // Ctrl+Alt+Shift+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, true, true, true)); // Ctrl+Alt+Shift+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, true, true, true)); // Ctrl+Alt+Shift+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, true, true, true)); // Ctrl+Alt+Shift+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, true, true, true)); // Ctrl+Alt+Shift+F9 + yield return (new byte[] { 27, 91, 50, 49, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, true, true, true)); // Ctrl+Alt+Shift+F10 + yield return (new byte[] { 27, 91, 50, 51, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, true, true, true)); // Ctrl+Alt+Shift+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); // Ctrl+Alt+Shift+F12 + yield return (new byte[] { 27, 79, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); // Home + yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); // Ctrl+Home + yield return (new byte[] { 27, 91, 49, 59, 51, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); // Alt+Home + yield return (new byte[] { 27, 91, 49, 59, 55, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); // Ctrl+Alt+Home + yield return (new byte[] { 27, 79, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + yield return (new byte[] { 27, 91, 49, 59, 53, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, true)); // Ctrl+End + yield return (new byte[] { 27, 91, 49, 59, 51, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, true, false)); // Alt+End + yield return (new byte[] { 27, 91, 49, 59, 55, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, true, true)); // Ctrl+Alt+End + yield return (new byte[] { 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, false)); // PageUp + yield return (new byte[] { 27, 91, 53, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, true)); // Ctrl+PageUp + yield return (new byte[] { 27, 91, 53, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, true, false)); // Alt+PageUp + yield return (new byte[] { 27, 91, 53, 59, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, true, true)); // Ctrl+Alt+PageUp + yield return (new byte[] { 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, false)); // PageDown + yield return (new byte[] { 27, 91, 54, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, true)); // Ctrl+PageDown + yield return (new byte[] { 27, 91, 54, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, true, false)); // Alt+PageDown + yield return (new byte[] { 27, 91, 54, 59, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, true, true)); // Ctrl+Alt+PageDown + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); // LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); // Ctrl+LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); // Alt+LeftArrow + yield return (new byte[] { 27, 79, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false)); // UpArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, true)); // Ctrl+UpArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, true, false)); // Alt+UpArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, true, false, false)); // Shift+UpArrow + yield return (new byte[] { 27, 79, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false)); // DownArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, true)); // Ctrl+DownArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, true, false)); // Alt+DownArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, true, false, false)); // Shift+DownArrow + yield return (new byte[] { 27, 79, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false)); // RightArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, true)); // Ctrl+RightArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, true, false)); // Alt+RightArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, true, false, false)); // Shift+RightArrow + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); // Insert + yield return (new byte[] { 27, 91, 50, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, true, false)); // Alt+Insert + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); // Delete + yield return (new byte[] { 27, 91, 51, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, true)); // Ctrl+Delete + yield return (new byte[] { 27, 91, 51, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, true, false)); // Alt+Delete + yield return (new byte[] { 27, 91, 51, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, false, false)); // Shift+Delete + yield return (new byte[] { 27, 91, 51, 59, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, false, true)); // Ctrl+Shift+Delete + yield return (new byte[] { 27, 91, 51, 59, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, true, false)); // Alt+Shift+Delete + yield return (new byte[] { 27, 91, 51, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, true, true)); // Ctrl+Alt+Shift+Delete + } + } +} + +// Ubuntu 18.04 x64 +public class RxvtUnicode : TerminalData +{ + protected override string EncodingCharset => "utf-8"; + protected override string Term => "rxvt-unicode-256color"; + internal override byte Verase => 127; + protected override string EncodedTerminalDb => "GgFOAB0AHwBwASkFcnh2dC11bmljb2RlLTI1NmNvbG9yfHJ4dnQtdW5pY29kZSB0ZXJtaW5hbCB3aXRoIDI1NiBjb2xvcnMgKFggV2luZG93IFN5c3RlbSkAAQEAAAEBAAABAQAAAAEBAAAAAAABAAEAAAEAAQEAUAAIABgAAAD///////////////////////8AAf9/AAD/////////////////////////////////////BQD//wAAAgAEABUAGgAiACYAKgD//zUARgBIAEwAUwD//1UAYgD//2YAagB0AHgAfAD//4IAhgCLAJAA/////5kA/////54AowCoAK0AtgC6AMEA///NANIA2ADeAP//7wDxAPYA/////y4BMgH//zYB////////OAH//z0B//9BAf////9GAUwBUgFYAV4BZAFqAXABdgF8AYIBhwH//4wB//+QAZUBmgH///////+eAaIBpQH///////////////////////////////////////+oAbEBugHDAcwB1QHeAecB8AH5Af///////wICBgILAv//EAITAv////9HAkoCVQJYAloCXQKuAv//sQKzAv///////7gCvALAAsQCyAL/////zAL//w0D/////xEDFwP/////HQP/////////////////////HgMjA///JwP/////////////////////////////////////////////////////////////LAP//zEDNgP/////OwP//0ADRQNKA/////9OA///UwP///////9YA/////////////9cA2IDaANuA3QDegOAA4YDjAOSA///////////////////////////////////////////////////////////////////////////////////////////////////////////////////mAP/////////////////////////////////////////////////////////////nQOoA60DtQO5A///wgP/////JgSKBP//////////////////7gT////////////////////////zBP////////////////////////////////////////////////////////////////////////////////////////kE/////////QQLBf///////xkFHQUhBSUFBwANABtbJWklcDElZDslcDIlZHIAG1szZwAbW0gbWzJKABtbSwAbW0oAG1slaSVwMSVkRwAbWyVpJXAxJWQ7JXAyJWRIAAoAG1tIABtbPzI1bAAIABtbPzEybBtbPzI1aAAbW0MAG1tBABtbPzEyOzI1aAAbW1AAG1tNABtdMjsHABsoMAAbWzVtABtbMW0AG1s/MTA0OWgAG1s0aAAbWzdtABtbN20AG1s0bQAbWyVwMSVkWAAbKEIAG1ttGyhCABtbchtbPzEwNDlsABtbNGwAG1syN20AG1syNG0AG1s/NWgkPDIwLz4bWz81bAAHABtbIXAAG1tyG1ttG1syShtbPzc7MjVoG1s/MTszOzQ7NTs2Ozk7NjY7MTAwMDsxMDAxOzEwNDlsG1s0bAAbW0AAG1tMAH8AG1szfgAbW0IAG1s4XgAbWzExfgAbWzIxfgAbWzEyfgAbWzEzfgAbWzE0fgAbWzE1fgAbWzE3fgAbWzE4fgAbWzE5fgAbWzIwfgAbWzd+ABtbMn4AG1tEABtbNn4AG1s1fgAbW0MAG1tBABs+ABs9ABtbJXAxJWRQABtbJXAxJWRNABtbJXAxJWRCABtbJXAxJWRAABtbJXAxJWRTABtbJXAxJWRMABtbJXAxJWREABtbJXAxJWRDABtbJXAxJWRUABtbJXAxJWRBABtbaQAbWzRpABtbNWkAG2MAG1tyG1ttG1s/NzsyNWgbWz8xOzM7NDs1OzY7OTs2NjsxMDAwOzEwMDE7MTA0OWwbWzRsABs4ABtbJWklcDElZGQAGzcACgAbTQAbWyU/JXA2JXQ7MSU7JT8lcDIldDs0JTslPyVwMSVwMyV8JXQ7NyU7JT8lcDQldDs1JTslPyVwNyV0OzglO20lPyVwOSV0GygwJWUbKEIlOwAbSAAJABtdMjsAG093ABtPeQAbT3UAG09xABtPcwBgYGFhZmZnZ2pqa2tsbG1tbm5vb3BwcXFycnNzdHR1dXZ2d3d4eHl5enp7e3x8fX1+fi1BLkIrQyxEMEVoRmlHABtbWgAbWz83aAAbWz83bAAAG1s4fgAbT00AG1sxfgAbWzMkABtbNH4AG1s4JAAbWzEkABtbNyQAG1syJAAbW2QAG1s2JAAbWzUkABtbYwAbWzIzfgAbWzI0fgAbWzI1fgAbWzI2fgAbWzI4fgAbWzI5fgAbWzMxfgAbWzMyfgAbWzMzfgAbWzM0fgAbWzFLABtbJWklZDslZFIAG1s2bgAbWz8xOzJjABtbYwAbWzM5OzQ5bQAbXTQ7JXAxJWQ7cmdiOiVwMiV7NjU1MzV9JSolezEwMDB9JS8lNC40WC8lcDMlezY1NTM1fSUqJXsxMDAwfSUvJTQuNFgvJXA0JXs2NTUzNX0lKiV7MTAwMH0lLyU0LjRYG1wAJT8lcDElezd9JT4ldBtbMzg7NTslcDElZG0lZRtbMyU/JXAxJXsxfSU9JXQ0JWUlcDElezN9JT0ldDYlZSVwMSV7NH0lPSV0MSVlJXAxJXs2fSU9JXQzJWUlcDElZCU7bSU7ACU/JXAxJXs3fSU+JXQbWzQ4OzU7JXAxJWRtJWUbWzQlPyVwMSV7MX0lPSV0NCVlJXAxJXszfSU9JXQ2JWUlcDElezR9JT0ldDElZSVwMSV7Nn0lPSV0MyVlJXAxJWQlO20lOwAbWzNtABtbMjNtABtbTQAbWzM4OzU7JXAxJWRtABtbNDg7NTslcDElZG0AGyhCABsoMAAbKkIAGytCAAAAAAAAFAAoAMwAAAAFAAoADgASABcAHAAhACYAKwAwADUAOgA+AEMASABNAFIAVgBaAAAABQAKAA4AEwAZAB8AJQArADEANwA8AEEARwBNAFMAWQBfAGUAaQAbWzNeABtbM0AAG1tiABtPYgAbWzheABtbOEAAG1sxXgAbWzFAABtbN14AG1s3QAAbWzJeABtbMkAAG09kABtbNl4AG1s2QAAbWzVeABtbNUAAG09jABtbYQAbT2EAa0RDNQBrREM2AGtETgBrRE41AGtFTkQ1AGtFTkQ2AGtGTkQ1AGtGTkQ2AGtIT001AGtIT002AGtJQzUAa0lDNgBrTEZUNQBrTlhUNQBrTlhUNgBrUFJWNQBrUFJWNgBrUklUNQBrVVAAa1VQNQA="; // /lib/terminfo/r/rxvt-unicode-256color + + internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios + { + get + { + yield return (new byte[] { 27, 91, 49, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, false)); // F1 + yield return (new byte[] { 27, 91, 49, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, false)); // F2 + yield return (new byte[] { 27, 91, 49, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, false)); // F3 + yield return (new byte[] { 27, 91, 49, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, false)); // F4 + yield return (new byte[] { 27, 91, 49, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, false)); // F5 + yield return (new byte[] { 27, 91, 49, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, false)); // F6 + yield return (new byte[] { 27, 91, 49, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, false)); // F7 + yield return (new byte[] { 27, 91, 49, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, false)); // F8 + yield return (new byte[] { 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, false)); // F9 + yield return (new byte[] { 27, 91, 50, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, false)); // F10 + yield return (new byte[] { 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, false)); // F11 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // F12 + yield return (new byte[] { 27, 91, 49, 49, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, true)); // Ctrl+F1 + yield return (new byte[] { 27, 91, 49, 50, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, true)); // Ctrl+F2 + yield return (new byte[] { 27, 91, 49, 51, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, true)); // Ctrl+F3 + yield return (new byte[] { 27, 91, 49, 52, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, true)); // Ctrl+F4 + yield return (new byte[] { 27, 91, 49, 53, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, true)); // Ctrl+F5 + yield return (new byte[] { 27, 91, 49, 55, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, true)); // Ctrl+F6 + yield return (new byte[] { 27, 91, 49, 56, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, true)); // Ctrl+F7 + yield return (new byte[] { 27, 91, 49, 57, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, true)); // Ctrl+F8 + yield return (new byte[] { 27, 91, 50, 48, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, true)); // Ctrl+F9 + yield return (new byte[] { 27, 91, 50, 49, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, true)); // Ctrl+F10 + yield return (new byte[] { 27, 91, 50, 51, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, true)); // Ctrl+F11 + yield return (new byte[] { 27, 91, 50, 52, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); // Ctrl+F12 + yield return (new byte[] { 27, 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, true, false)); // Alt+F9 + yield return (new byte[] { 27, 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, true, false)); // Alt+F11 + yield return (new byte[] { 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, false)); // Shift+F1=F11 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // Shift+F2=F12 + yield return (new byte[] { 27, 91, 50, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F13, false, false, false)); // Shift+F3=F13 + yield return (new byte[] { 27, 91, 50, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F14, false, false, false)); // Shift+F4=F14 + yield return (new byte[] { 27, 91, 50, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F15, false, false, false)); // Shift+F5=F14 + yield return (new byte[] { 27, 91, 50, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F16, false, false, false)); // Shift+F6=F16 + yield return (new byte[] { 27, 91, 51, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F17, false, false, false)); // Shift+F7=F17 + yield return (new byte[] { 27, 91, 51, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F18, false, false, false)); // Shift+F8=F18 + yield return (new byte[] { 27, 91, 51, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F19, false, false, false)); // Shift+F9=F19 + yield return (new byte[] { 27, 91, 51, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F20, false, false, false)); // Shift+F10=F20 + yield return (new byte[] { 27, 91, 50, 51, 36 }, new ConsoleKeyInfo(default, ConsoleKey.F11, true, false, false)); // Shift+F11 + yield return (new byte[] { 27, 91, 50, 52, 36 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); // Shift+F12 + yield return (new byte[] { 27, 27, 91, 50, 51, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, true, true)); // Ctrl+Alt+Shift+F1=Ctrl+Alt+F11 + yield return (new byte[] { 27, 27, 91, 50, 52, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, true)); // Ctrl+Alt+Shift+F2=Ctrl+Alt+F12 + yield return (new byte[] { 27, 27, 91, 50, 53, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F13, false, true, true)); // Ctrl+Alt+Shift+F3=Ctrl+Alt+F13 + yield return (new byte[] { 27, 27, 91, 50, 54, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F14, false, true, true)); // Ctrl+Alt+Shift+F4=Ctrl+Alt+F14 + yield return (new byte[] { 27, 27, 91, 50, 56, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F15, false, true, true)); // Ctrl+Alt+Shift+F5=Ctrl+Alt+F15 + yield return (new byte[] { 27, 27, 91, 50, 57, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F16, false, true, true)); // Ctrl+Alt+Shift+F6=Ctrl+Alt+F16 + yield return (new byte[] { 27, 27, 91, 51, 49, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F17, false, true, true)); // Ctrl+Alt+Shift+F7=Ctrl+Alt+F17 + yield return (new byte[] { 27, 27, 91, 51, 50, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F18, false, true, true)); // Ctrl+Alt+Shift+F8=Ctrl+Alt+F18 + yield return (new byte[] { 27, 27, 91, 51, 51, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F19, false, true, true)); // Ctrl+Alt+Shift+F9=Ctrl+Alt+F19 + yield return (new byte[] { 27, 27, 91, 51, 52, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F20, false, true, true)); // Ctrl+Alt+Shift+F10=Ctrl+Alt+F20 + yield return (new byte[] { 27, 27, 91, 50, 51, 64 }, new ConsoleKeyInfo(default, ConsoleKey.F11, true, true, true)); // Ctrl+Alt+Shift+F11 + yield return (new byte[] { 27, 27, 91, 50, 52, 64 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); // Ctrl+Alt+Shift+F12 + yield return (new byte[] { 27, 91, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); // Home + yield return (new byte[] { 27, 91, 55, 94 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); // Ctrl+Home + yield return (new byte[] { 27, 27, 91, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); // Alt+Home + yield return (new byte[] { 27, 27, 91, 55, 94 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); // Ctrl+Alt+Home + yield return (new byte[] { 27, 91, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + yield return (new byte[] { 27, 91, 56, 94 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, true)); // Ctrl+End + yield return (new byte[] { 27, 27, 91, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, true, false)); // Alt+End + yield return (new byte[] { 27, 27, 91, 56, 94 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, true, true)); // Ctrl+Alt+End + yield return (new byte[] { 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, false)); // PageUp + yield return (new byte[] { 27, 91, 53, 94 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, true)); // Ctrl+PageUp + yield return (new byte[] { 27, 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, true, false)); // Alt+PageUp + yield return (new byte[] { 27, 27, 91, 53, 94 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, true, true)); // Ctrl+Alt+PageUp + yield return (new byte[] { 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, false)); // PageDown + yield return (new byte[] { 27, 91, 54, 94 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, true)); // Ctrl+PageDown + yield return (new byte[] { 27, 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, true, false)); // Alt+PageDown + yield return (new byte[] { 27, 27, 91, 54, 94 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, true, true)); // Ctrl+Alt+PageDown + yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); // LeftArrow + yield return (new byte[] { 27, 79, 100 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); // Ctrl+LeftArrow + yield return (new byte[] { 27, 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); // Alt+LeftArrow + yield return (new byte[] { 27, 91, 100 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, true, false, false)); // Shift+LeftArrow + yield return (new byte[] { 27, 27, 91, 100 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, true, true, false)); // Shift+Alt+LeftArrow + yield return (new byte[] { 27, 91, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false)); // UpArrow + yield return (new byte[] { 27, 79, 97 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, true)); // Ctrl+UpArrow + yield return (new byte[] { 27, 27, 91, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, true, false)); // Alt+UpArrow + yield return (new byte[] { 27, 91, 97 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, true, false, false)); // Shift+UpArrow + yield return (new byte[] { 27, 27, 91, 97 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, true, true, false)); // Shift+Alt+UpArrow + yield return (new byte[] { 27, 91, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false)); // DownArrow + yield return (new byte[] { 27, 79, 98 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, true)); // Ctrl+DownArrow + yield return (new byte[] { 27, 27, 91, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, true, false)); // Alt+DownArrow + yield return (new byte[] { 27, 91, 98 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, true, false, false)); // Shift+DownArrow + yield return (new byte[] { 27, 27, 91, 98 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, true, true, false)); // Shift+Alt+DownArrow + yield return (new byte[] { 27, 91, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false)); // RightArrow + yield return (new byte[] { 27, 79, 99 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, true)); // Ctrl+RightArrow + yield return (new byte[] { 27, 27, 91, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, true, false)); // Alt+RightArrow + yield return (new byte[] { 27, 91, 99 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, true, false, false)); // Shift+RightArrow + yield return (new byte[] { 27, 27, 91, 99 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, true, true, false)); // Shift+Alt+RightArrow + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); // Insert + yield return (new byte[] { 27, 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, true, false)); // Alt+Insert + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); // Delete + yield return (new byte[] { 27, 91, 51, 94 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, true)); // Ctrl+Delete + yield return (new byte[] { 27, 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, true, false)); // Alt+Delete + yield return (new byte[] { 27, 91, 51, 36 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, false, false)); // Shift+Delete + yield return (new byte[] { 27, 91, 51, 64 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, false, true)); // Ctrl+Shift+Delete + yield return (new byte[] { 27, 27, 91, 51, 36 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, true, false)); // Alt+Shift+Delete + yield return (new byte[] { 27, 27, 91, 51, 64 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, true, true)); // Ctrl+Alt+Shift+Delete + // Numeric Keypad + yield return (new byte[] { 48 }, new ConsoleKeyInfo('0', ConsoleKey.D0, false, false, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); + yield return (new byte[] { 50 }, new ConsoleKeyInfo('2', ConsoleKey.D2, false, false, false)); + yield return (new byte[] { 51 }, new ConsoleKeyInfo('3', ConsoleKey.D3, false, false, false)); + yield return (new byte[] { 52 }, new ConsoleKeyInfo('4', ConsoleKey.D4, false, false, false)); + yield return (new byte[] { 53 }, new ConsoleKeyInfo('5', ConsoleKey.D5, false, false, false)); + yield return (new byte[] { 54 }, new ConsoleKeyInfo('6', ConsoleKey.D6, false, false, false)); + yield return (new byte[] { 55 }, new ConsoleKeyInfo('7', ConsoleKey.D7, false, false, false)); + yield return (new byte[] { 56 }, new ConsoleKeyInfo('8', ConsoleKey.D8, false, false, false)); + yield return (new byte[] { 57 }, new ConsoleKeyInfo('9', ConsoleKey.D9, false, false, false)); + yield return (new byte[] { 47 }, new ConsoleKeyInfo('/', ConsoleKey.Divide, false, false, false)); + yield return (new byte[] { 42 }, new ConsoleKeyInfo('*', ConsoleKey.Multiply, false, false, false)); + yield return (new byte[] { 45 }, new ConsoleKeyInfo('-', ConsoleKey.Subtract, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); + yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); // Enter (using Numeric Keypad)) + yield return (new byte[] { 46 }, new ConsoleKeyInfo('.', ConsoleKey.OemPeriod, false, false, false)); // . (period using Numeric Keypad)) + yield return (new byte[] { 27, 79, 112 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); // Insert + yield return (new byte[] { 27, 79, 110 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); // Delete + yield return (new byte[] { 27, 79, 113 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + yield return (new byte[] { 27, 79, 114 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false)); // Down Arrow + yield return (new byte[] { 27, 79, 115 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, false)); // Page Down + yield return (new byte[] { 27, 79, 116 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); // Left Arrow + yield return (new byte[] { 27, 79, 117 }, new ConsoleKeyInfo(default, ConsoleKey.NoName, false, false, false)); // Begin (5) + yield return (new byte[] { 27, 79, 118 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false)); // Right Arrow + yield return (new byte[] { 27, 79, 119 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); // Home + yield return (new byte[] { 27, 79, 120 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false)); // Up Arrow + yield return (new byte[] { 27, 79, 121 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, false)); // Page Up + yield return (new byte[] { 27, 79, 111 }, new ConsoleKeyInfo('/', ConsoleKey.Divide, false, false, false)); // / (divide sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 106 }, new ConsoleKeyInfo('*', ConsoleKey.Multiply, false, false, false)); // * (multiply sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 109 }, new ConsoleKeyInfo('-', ConsoleKey.Subtract, false, false, false)); // - (minus sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 107 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); // + (plus sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 77 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); // Enter (using Numeric Keypad)) + } + } +} + +// Ubuntu 18.04 x64 +public class TmuxData : TerminalData +{ + protected override string EncodingCharset => "utf-8"; + protected override string Term => "screen"; + internal override byte Verase => 127; + protected override string EncodedTerminalDb => "GgEqACsAEABpAZkCc2NyZWVufFZUIDEwMC9BTlNJIFgzLjY0IHZpcnR1YWwgdGVybWluYWwAAAEAAAEAAAABAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAQBQAAgAGAD//////////////////////////wgAQAD+/wAABAAGAAgAGQAeACUAKQAtAP//OABJAEsATwBWAP//WABkAP//aABrAHEAdQD/////eQB7AIAAhQD//44AkwD/////mACdAKIA//+nAKkArgD//7cAvADCAMgA////////ywD////////PAP//0wD////////VAP//2gD//////////94A4gDoAOwA8AD0APoAAAEGAQwBEgEXAf//HAH//yABJQEqAf///////y4BMgE6Af//////////////////////////////////QgH//0UBTgFXAWABaQFyAXsBhAH//40B/////////////////////5YB/////6cBqgG1AbgBugG9AREC//8UAv////////////////////////////8WAv//VwL///////////////9bAv////////////////////9iAv///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2cCbQL///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9zAv///////////////////////////////////////////////////////////////////////3gC////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////gQL///////+FAo8CG1taAAcADQAbWyVpJXAxJWQ7JXAyJWRyABtbM2cAG1tIG1tKABtbSwAbW0oAG1slaSVwMSVkRwAbWyVpJXAxJWQ7JXAyJWRIAAoAG1tIABtbPzI1bAAIABtbMzRoG1s/MjVoABtbQwAbTQAbWzM0bAAbW1AAG1tNAA4AG1s1bQAbWzFtABtbPzEwNDloABtbMm0AG1s0aAAbWzdtABtbM20AG1s0bQAPABtbbQ8AG1s/MTA0OWwAG1s0bAAbWzIzbQAbWzI0bQAbZwAbKTAAG1tMAH8AG1szfgAbT0IAG09QABtbMjF+ABtPUQAbT1IAG09TABtbMTV+ABtbMTd+ABtbMTh+ABtbMTl+ABtbMjB+ABtbMX4AG1syfgAbT0QAG1s2fgAbWzV+ABtPQwAbT0EAG1s/MWwbPgAbWz8xaBs9ABtFABtbJXAxJWRQABtbJXAxJWRNABtbJXAxJWRCABtbJXAxJWRAABtbJXAxJWRTABtbJXAxJWRMABtbJXAxJWREABtbJXAxJWRDABtbJXAxJWRBABtjG1s/MTAwMGwbWz8yNWgAGzgAG1slaSVwMSVkZAAbNwAKABtNABtbMCU/JXA2JXQ7MSU7JT8lcDEldDszJTslPyVwMiV0OzQlOyU/JXAzJXQ7NyU7JT8lcDQldDs1JTslPyVwNSV0OzIlO20lPyVwOSV0DiVlDyU7ABtIAAkAKyssLC0tLi4wMGBgYWFmZmdnaGhpaWpqa2tsbG1tbm5vb3BwcXFycnNzdHR1dXZ2d3d4eHl5enp7e3x8fX1+fgAbW1oAGyhCGykwABtbNH4AG1syM34AG1syNH4AG1sxSwAbWzM5OzQ5bQAbW00AG1szJXAxJWRtABtbNCVwMSVkbQAAAwABAAsAGgBDAAEBAAABAAAA//8EAP////////////////////8AAAMABgAJAAwADwASABUAGAAeACQAKAAsADAANAAbKEIAGyglcDElYwBBWABHMABYVABVOABFMABFMwBTMABYTQBrRU5ENQBrSE9NNQBrYTIAa2IxAGtiMwBrYzIAeG0A"; // /lib/terminfo/s/screen + + internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios + { + get + { + yield return (new byte[] { 27, 79, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, false)); // F1 + yield return (new byte[] { 27, 79, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, false)); // F2 + yield return (new byte[] { 27, 79, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, false)); // F3 + yield return (new byte[] { 27, 79, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, false)); // F4 + yield return (new byte[] { 27, 91, 49, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, false)); // F5 + yield return (new byte[] { 27, 91, 49, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, false)); // F6 + yield return (new byte[] { 27, 91, 49, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, false)); // F7 + yield return (new byte[] { 27, 91, 49, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, false)); // F8 + yield return (new byte[] { 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, false)); // F9 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // F12 + yield return (new byte[] { 27, 91, 49, 59, 53, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, true)); // Ctrl+F1 + yield return (new byte[] { 27, 91, 49, 59, 53, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, true)); // Ctrl+F2 + yield return (new byte[] { 27, 91, 49, 59, 53, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, true)); // Ctrl+F3 + yield return (new byte[] { 27, 91, 49, 59, 53, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, true)); // Ctrl+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, true)); // Ctrl+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, true)); // Ctrl+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, true)); // Ctrl+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, true)); // Ctrl+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, true)); // Ctrl+F9 + yield return (new byte[] { 27, 91, 50, 49, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, true)); // Ctrl+F10 + yield return (new byte[] { 27, 91, 50, 51, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, true)); // Ctrl+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); // Ctrl+F12 + yield return (new byte[] { 27, 91, 49, 59, 51, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, true, false)); // Alt+F3 + yield return (new byte[] { 27, 91, 50, 48, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, true, false)); // Alt+F9 + yield return (new byte[] { 27, 91, 50, 51, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, true, false)); // Alt+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); // Alt+F12 + yield return (new byte[] { 27, 91, 49, 59, 50, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, true, false, false)); // Shift+F1 + yield return (new byte[] { 27, 91, 49, 59, 50, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, true, false, false)); // Shift+F2 + yield return (new byte[] { 27, 91, 49, 59, 50, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, true, false, false)); // Shift+F3 + yield return (new byte[] { 27, 91, 49, 59, 50, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, true, false, false)); // Shift+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, true, false, false)); // Shift+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, true, false, false)); // Shift+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, true, false, false)); // Shift+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, true, false, false)); // Shift+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, true, false, false)); // Shift+F9 + yield return (new byte[] { 27, 91, 50, 51, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, true, false, false)); // Shift+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); // Shift+F12 + yield return (new byte[] { 27, 91, 49, 59, 56, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, true, true, true)); // Ctrl+Alt+Shift+F1 + yield return (new byte[] { 27, 91, 49, 59, 56, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, true, true, true)); // Ctrl+Alt+Shift+F2 + yield return (new byte[] { 27, 91, 49, 59, 56, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, true, true, true)); // Ctrl+Alt+Shift+F3 + yield return (new byte[] { 27, 91, 49, 59, 56, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, true, true, true)); // Ctrl+Alt+Shift+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, true, true, true)); // Ctrl+Alt+Shift+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, true, true, true)); // Ctrl+Alt+Shift+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, true, true, true)); // Ctrl+Alt+Shift+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, true, true, true)); // Ctrl+Alt+Shift+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, true, true, true)); // Ctrl+Alt+Shift+F9 + yield return (new byte[] { 27, 91, 50, 49, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, true, true, true)); // Ctrl+Alt+Shift+F10 + yield return (new byte[] { 27, 91, 50, 51, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, true, true, true)); // Ctrl+Alt+Shift+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); // Ctrl+Alt+Shift+F12 + yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); // Home + yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); // Ctrl+Home + yield return (new byte[] { 27, 91, 49, 59, 51, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); // Alt+Home + yield return (new byte[] { 27, 91, 49, 59, 55, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); // Ctrl+Alt+Home + yield return (new byte[] { 27, 91, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + yield return (new byte[] { 27, 91, 49, 59, 53, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, true)); // Ctrl+End + yield return (new byte[] { 27, 91, 49, 59, 51, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, true, false)); // Alt+End + yield return (new byte[] { 27, 91, 49, 59, 55, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, true, true)); // Ctrl+Alt+End + yield return (new byte[] { 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, false)); // PageUp + yield return (new byte[] { 27, 91, 53, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, true)); // Ctrl+PageUp + yield return (new byte[] { 27, 91, 53, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, true, false)); // Alt+PageUp + yield return (new byte[] { 27, 91, 53, 59, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, true, true)); // Ctrl+Alt+PageUp + yield return (new byte[] { 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, false)); // PageDown + yield return (new byte[] { 27, 91, 54, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, true)); // Ctrl+PageDown + yield return (new byte[] { 27, 91, 54, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, true, false)); // Alt+PageDown + yield return (new byte[] { 27, 91, 54, 59, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, true, true)); // Ctrl+Alt+PageDown + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); // LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); // Ctrl+LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); // Alt+LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, true, false, false)); // Shift+LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 52, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, true, true, false)); // Shift+Alt+LeftArrow + yield return (new byte[] { 27, 79, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false)); // UpArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, true)); // Ctrl+UpArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, true, false)); // Alt+UpArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, true, false, false)); // Shift+UpArrow + yield return (new byte[] { 27, 91, 49, 59, 52, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, true, true, false)); // Shift+Alt+UpArrow + yield return (new byte[] { 27, 79, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false)); // DownArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, true)); // Ctrl+DownArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, true, false)); // Alt+DownArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, true, false, false)); // Shift+DownArrow + yield return (new byte[] { 27, 91, 49, 59, 52, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, true, true, false)); // Shift+Alt+DownArrow + yield return (new byte[] { 27, 79, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false)); // RightArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, true)); // Ctrl+RightArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, true, false)); // Alt+RightArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, true, false, false)); // Shift+RightArrow + yield return (new byte[] { 27, 91, 49, 59, 52, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, true, true, false)); // Shift+Alt+RightArrow + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); // Insert + yield return (new byte[] { 27, 91, 50, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, true, false)); // Alt+Insert + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); // Delete + yield return (new byte[] { 27, 91, 51, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, true)); // Ctrl+Delete + yield return (new byte[] { 27, 91, 51, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, true, false)); // Alt+Delete + yield return (new byte[] { 27, 91, 51, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, false, false)); // Shift+Delete + yield return (new byte[] { 27, 91, 51, 59, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, false, true)); // Ctrl+Shift+Delete + yield return (new byte[] { 27, 91, 51, 59, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, true, false)); // Alt+Shift+Delete + yield return (new byte[] { 27, 91, 51, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, true, true)); // Ctrl+Alt+Shift+Delete + // Numeric Keypad + yield return (new byte[] { 48 }, new ConsoleKeyInfo('0', ConsoleKey.D0, false, false, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); + yield return (new byte[] { 50 }, new ConsoleKeyInfo('2', ConsoleKey.D2, false, false, false)); + yield return (new byte[] { 51 }, new ConsoleKeyInfo('3', ConsoleKey.D3, false, false, false)); + yield return (new byte[] { 52 }, new ConsoleKeyInfo('4', ConsoleKey.D4, false, false, false)); + yield return (new byte[] { 53 }, new ConsoleKeyInfo('5', ConsoleKey.D5, false, false, false)); + yield return (new byte[] { 54 }, new ConsoleKeyInfo('6', ConsoleKey.D6, false, false, false)); + yield return (new byte[] { 55 }, new ConsoleKeyInfo('7', ConsoleKey.D7, false, false, false)); + yield return (new byte[] { 56 }, new ConsoleKeyInfo('8', ConsoleKey.D8, false, false, false)); + yield return (new byte[] { 57 }, new ConsoleKeyInfo('9', ConsoleKey.D9, false, false, false)); + yield return (new byte[] { 47 }, new ConsoleKeyInfo('/', ConsoleKey.Divide, false, false, false)); + yield return (new byte[] { 42 }, new ConsoleKeyInfo('*', ConsoleKey.Multiply, false, false, false)); + yield return (new byte[] { 45 }, new ConsoleKeyInfo('-', ConsoleKey.Subtract, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); + yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); // Enter (using Numeric Keypad)) + yield return (new byte[] { 46 }, new ConsoleKeyInfo('.', ConsoleKey.OemPeriod, false, false, false)); // . (period using Numeric Keypad)) + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); // Insert + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); // Delete + yield return (new byte[] { 27, 91, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + yield return (new byte[] { 27, 79, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false)); // Down Arrow + yield return (new byte[] { 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, false)); // Page Down + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); // Left Arrow + yield return (new byte[] { 27, 79, 69 }, new ConsoleKeyInfo(default, ConsoleKey.NoName, false, false, false)); // Begin (5) + yield return (new byte[] { 27, 79, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false)); // Right Arrow + yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); // Home + yield return (new byte[] { 27, 79, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false)); // Up Arrow + yield return (new byte[] { 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, false)); // Page Up + yield return (new byte[] { 27, 79, 111 }, new ConsoleKeyInfo('/', ConsoleKey.Divide, false, false, false)); // / (divide sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 106 }, new ConsoleKeyInfo('*', ConsoleKey.Multiply, false, false, false)); // * (multiply sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 109 }, new ConsoleKeyInfo('-', ConsoleKey.Subtract, false, false, false)); // - (minus sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 107 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); // + (plus sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 77 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); // Enter (using Numeric Keypad)) + } + } +} + +// Ubuntu 18.04 x64 Tmux configured to use 256 colours (https://github.com/tmux/tmux/wiki/FAQ) started from GNOME Terminal using `tmux new` command +public class Tmux256ColorData : TerminalData +{ + protected override string EncodingCharset => "utf-8"; + protected override string Term => "screen-256color"; + internal override byte Verase => 127; + protected override string EncodedTerminalDb => "GgErACsADwBpAQQDc2NyZWVuLTI1NmNvbG9yfEdOVSBTY3JlZW4gd2l0aCAyNTYgY29sb3JzAAABAAABAAAAAQAAAAABAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAFQAAgAGAD//////////////////////////wAB/38AAAQABgAIABkAHgAlACkALQD//zgASQBLAE8AVgD//1gAZAD//2gAawBxAHUA/////3kAewCAAIUA//+OAJMA/////5gAnQCiAP//pwCpAK4A//+3ALwAwgDIAP///////8sA////////zwD//9MA////////1QD//9oA///////////eAOIA6ADsAPAA9AD6AAABBgEMARIBFwH//xwB//8gASUBKgH///////8uATIBOgH//////////////////////////////////0IB//9FAU4BVwFgAWkBcgF7AYQB//+NAf////////////////////+WAf////+nAaoBtQG4AboBvQERAv//FAL/////////////////////////////FgL//1cC////////////////WwL/////////////////////YgL///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9nAm0C////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////cwL///////////////////////////////////////////////////////////////////////94Av///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////4EC////////hQLEAhtbWgAHAA0AG1slaSVwMSVkOyVwMiVkcgAbWzNnABtbSBtbSgAbW0sAG1tKABtbJWklcDElZEcAG1slaSVwMSVkOyVwMiVkSAAKABtbSAAbWz8yNWwACAAbWzM0aBtbPzI1aAAbW0MAG00AG1szNGwAG1tQABtbTQAOABtbNW0AG1sxbQAbWz8xMDQ5aAAbWzJtABtbNGgAG1s3bQAbWzNtABtbNG0ADwAbW20PABtbPzEwNDlsABtbNGwAG1syM20AG1syNG0AG2cAGykwABtbTAB/ABtbM34AG09CABtPUAAbWzIxfgAbT1EAG09SABtPUwAbWzE1fgAbWzE3fgAbWzE4fgAbWzE5fgAbWzIwfgAbWzF+ABtbMn4AG09EABtbNn4AG1s1fgAbT0MAG09BABtbPzFsGz4AG1s/MWgbPQAbRQAbWyVwMSVkUAAbWyVwMSVkTQAbWyVwMSVkQgAbWyVwMSVkQAAbWyVwMSVkUwAbWyVwMSVkTAAbWyVwMSVkRAAbWyVwMSVkQwAbWyVwMSVkQQAbYxtbPzEwMDBsG1s/MjVoABs4ABtbJWklcDElZGQAGzcACgAbTQAbWzAlPyVwNiV0OzElOyU/JXAxJXQ7MyU7JT8lcDIldDs0JTslPyVwMyV0OzclOyU/JXA0JXQ7NSU7JT8lcDUldDsyJTttJT8lcDkldA4lZQ8lOwAbSAAJACsrLCwtLS4uMDBgYGFhZmZnZ2hoaWlqamtrbGxtbW5ub29wcHFxcnJzc3R0dXV2dnd3eHh5eXp6e3t8fH19fn4AG1taABsoQhspMAAbWzR+ABtbMjN+ABtbMjR+ABtbMUsAG1szOTs0OW0AG1tNABtbJT8lcDElezh9JTwldDMlcDElZCVlJXAxJXsxNn0lPCV0OSVwMSV7OH0lLSVkJWUzODs1OyVwMSVkJTttABtbJT8lcDElezh9JTwldDQlcDElZCVlJXAxJXsxNn0lPCV0MTAlcDElezh9JS0lZCVlNDg7NTslcDElZCU7bQADAAEAIABEALYAAQEAAAEAAAAEAP///////////////////////////////////////////////////////////////////////////////wAAAwAGAAkADAAPABIAFQAaAB8AJAApAC4AMgA3ADwAQQBGAEsAUQBXAF0AYwBpAG8AdQB7AIEAhwCNAJMAlwCbAJ8AowCnABsoQgAbKCVwMSVjAEFYAEcwAFhUAFU4AEUwAFMwAFhNAGtEQzMAa0RDNABrREM1AGtEQzYAa0RDNwBrRE4Aa0ROMwBrRE40AGtETjUAa0RONgBrRE43AGtFTkQ1AGtIT001AGtMRlQzAGtMRlQ0AGtMRlQ1AGtMRlQ2AGtMRlQ3AGtSSVQzAGtSSVQ0AGtSSVQ1AGtSSVQ2AGtSSVQ3AGtVUABrYTIAa2IxAGtiMwBrYzIAeG0A"; // /lib/terminfo/s/screen-256color + + internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios + { + get + { + yield return (new byte[] { 27, 79, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, false)); // F1 + yield return (new byte[] { 27, 79, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, false)); // F2 + yield return (new byte[] { 27, 79, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, false)); // F3 + yield return (new byte[] { 27, 79, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, false)); // F4 + yield return (new byte[] { 27, 91, 49, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, false)); // F5 + yield return (new byte[] { 27, 91, 49, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, false)); // F6 + yield return (new byte[] { 27, 91, 49, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, false)); // F7 + yield return (new byte[] { 27, 91, 49, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, false)); // F8 + yield return (new byte[] { 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, false)); // F9 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // F12 + yield return (new byte[] { 27, 91, 49, 59, 53, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, true)); // Ctrl+F1 + yield return (new byte[] { 27, 91, 49, 59, 53, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, true)); // Ctrl+F2 + yield return (new byte[] { 27, 91, 49, 59, 53, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, true)); // Ctrl+F3 + yield return (new byte[] { 27, 91, 49, 59, 53, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, true)); // Ctrl+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, true)); // Ctrl+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, true)); // Ctrl+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, true)); // Ctrl+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, true)); // Ctrl+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, true)); // Ctrl+F9 + yield return (new byte[] { 27, 91, 50, 49, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, true)); // Ctrl+F10 + yield return (new byte[] { 27, 91, 50, 51, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, true)); // Ctrl+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); // Ctrl+F12 + yield return (new byte[] { 27, 91, 49, 59, 51, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, true, false)); // Alt+F3 + yield return (new byte[] { 27, 91, 50, 48, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, true, false)); // Alt+F9 + yield return (new byte[] { 27, 91, 50, 51, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, true, false)); // Alt+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); // Alt+F12 + yield return (new byte[] { 27, 91, 49, 59, 50, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, true, false, false)); // Shift+F1 + yield return (new byte[] { 27, 91, 49, 59, 50, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, true, false, false)); // Shift+F2 + yield return (new byte[] { 27, 91, 49, 59, 50, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, true, false, false)); // Shift+F3 + yield return (new byte[] { 27, 91, 49, 59, 50, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, true, false, false)); // Shift+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, true, false, false)); // Shift+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, true, false, false)); // Shift+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, true, false, false)); // Shift+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, true, false, false)); // Shift+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, true, false, false)); // Shift+F9 + yield return (new byte[] { 27, 91, 50, 52, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); // Shift+F12 + yield return (new byte[] { 27, 91, 49, 59, 56, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, true, true, true)); // Ctrl+Alt+Shift+F1 + yield return (new byte[] { 27, 91, 49, 59, 56, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, true, true, true)); // Ctrl+Alt+Shift+F2 + yield return (new byte[] { 27, 91, 49, 59, 56, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, true, true, true)); // Ctrl+Alt+Shift+F3 + yield return (new byte[] { 27, 91, 49, 59, 56, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, true, true, true)); // Ctrl+Alt+Shift+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, true, true, true)); // Ctrl+Alt+Shift+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, true, true, true)); // Ctrl+Alt+Shift+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, true, true, true)); // Ctrl+Alt+Shift+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, true, true, true)); // Ctrl+Alt+Shift+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, true, true, true)); // Ctrl+Alt+Shift+F9 + yield return (new byte[] { 27, 91, 50, 49, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, true, true, true)); // Ctrl+Alt+Shift+F10 + yield return (new byte[] { 27, 91, 50, 51, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, true, true, true)); // Ctrl+Alt+Shift+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); // Ctrl+Alt+Shift+F12 + yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); // Home + yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); // Ctrl+Home + yield return (new byte[] { 27, 91, 49, 59, 51, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); // Alt+Home + yield return (new byte[] { 27, 91, 49, 59, 55, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); // Ctrl+Alt+Home + yield return (new byte[] { 27, 91, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + yield return (new byte[] { 27, 91, 49, 59, 53, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, true)); // Ctrl+End + yield return (new byte[] { 27, 91, 49, 59, 51, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, true, false)); // Alt+End + yield return (new byte[] { 27, 91, 49, 59, 55, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, true, true)); // Ctrl+Alt+End + yield return (new byte[] { 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, false)); // PageUp + yield return (new byte[] { 27, 91, 53, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, true)); // Ctrl+PageUp + yield return (new byte[] { 27, 91, 53, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, true, false)); // Alt+PageUp + yield return (new byte[] { 27, 91, 53, 59, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, true, true)); // Ctrl+Alt+PageUp + yield return (new byte[] { 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, false)); // PageDown + yield return (new byte[] { 27, 91, 54, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, true)); // Ctrl+PageDown + yield return (new byte[] { 27, 91, 54, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, true, false)); // Alt+PageDown + yield return (new byte[] { 27, 91, 54, 59, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, true, true)); // Ctrl+Alt+PageDown + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); // LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); // Ctrl+LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); // Alt+LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, true, false, false)); // Shift+LeftArrow + yield return (new byte[] { 27, 91, 49, 59, 52, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, true, true, false)); // Shift+Alt+LeftArrow + yield return (new byte[] { 27, 79, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false)); // UpArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, true)); // Ctrl+UpArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, true, false)); // Alt+UpArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, true, false, false)); // Shift+UpArrow + yield return (new byte[] { 27, 91, 49, 59, 52, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, true, true, false)); // Shift+Alt+UpArrow + yield return (new byte[] { 27, 79, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false)); // DownArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, true)); // Ctrl+DownArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, true, false)); // Alt+DownArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, true, false, false)); // Shift+DownArrow + yield return (new byte[] { 27, 91, 49, 59, 52, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, true, true, false)); // Shift+Alt+DownArrow + yield return (new byte[] { 27, 79, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false)); // RightArrow + yield return (new byte[] { 27, 91, 49, 59, 53, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, true)); // Ctrl+RightArrow + yield return (new byte[] { 27, 91, 49, 59, 51, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, true, false)); // Alt+RightArrow + yield return (new byte[] { 27, 91, 49, 59, 50, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, true, false, false)); // Shift+RightArrow + yield return (new byte[] { 27, 91, 49, 59, 52, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, true, true, false)); // Shift+Alt+RightArrow + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); // Insert + yield return (new byte[] { 27, 91, 50, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, true, false)); // Alt+Insert + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); // Delete + yield return (new byte[] { 27, 91, 51, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, true)); // Ctrl+Delete + yield return (new byte[] { 27, 91, 51, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, true, false)); // Alt+Delete + yield return (new byte[] { 27, 91, 51, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, false, false)); // Shift+Delete + yield return (new byte[] { 27, 91, 51, 59, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, false, true)); // Ctrl+Shift+Delete + yield return (new byte[] { 27, 91, 51, 59, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, true, false)); // Alt+Shift+Delete + yield return (new byte[] { 27, 91, 51, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, true, true, true)); // Ctrl+Alt+Shift+Delete + yield return (new byte[] { 48 }, new ConsoleKeyInfo('0', ConsoleKey.D0, false, false, false)); // 0 (using Numeric Keypad) + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); // 1 (using Numeric Keypad) + yield return (new byte[] { 50 }, new ConsoleKeyInfo('2', ConsoleKey.D2, false, false, false)); // 2 (using Numeric Keypad) + yield return (new byte[] { 51 }, new ConsoleKeyInfo('3', ConsoleKey.D3, false, false, false)); // 3 (using Numeric Keypad) + yield return (new byte[] { 52 }, new ConsoleKeyInfo('4', ConsoleKey.D4, false, false, false)); // 4 (using Numeric Keypad) + yield return (new byte[] { 53 }, new ConsoleKeyInfo('5', ConsoleKey.D5, false, false, false)); // 5 (using Numeric Keypad) + yield return (new byte[] { 54 }, new ConsoleKeyInfo('6', ConsoleKey.D6, false, false, false)); // 6 (using Numeric Keypad) + yield return (new byte[] { 55 }, new ConsoleKeyInfo('7', ConsoleKey.D7, false, false, false)); // 7 (using Numeric Keypad) + yield return (new byte[] { 56 }, new ConsoleKeyInfo('8', ConsoleKey.D8, false, false, false)); // 8 (using Numeric Keypad) + yield return (new byte[] { 57 }, new ConsoleKeyInfo('9', ConsoleKey.D9, false, false, false)); // 9 (using Numeric Keypad) + yield return (new byte[] { 47 }, new ConsoleKeyInfo('/', ConsoleKey.Divide, false, false, false)); // / (divide sign using Numeric Keypad)) + yield return (new byte[] { 42 }, new ConsoleKeyInfo('*', ConsoleKey.Multiply, false, false, false)); // * (multiply sign using Numeric Keypad)) + yield return (new byte[] { 45 }, new ConsoleKeyInfo('-', ConsoleKey.Subtract, false, false, false)); // - (minus sign using Numeric Keypad)) + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); // + (plus sign using Numeric Keypad)) + yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); // Enter (using Numeric Keypad)) + yield return (new byte[] { 46 }, new ConsoleKeyInfo('.', ConsoleKey.OemPeriod, false, false, false)); // . (period using Numeric Keypad)) + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); // Insert + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); // Delete + yield return (new byte[] { 27, 91, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + yield return (new byte[] { 27, 79, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false)); // Down Arrow + yield return (new byte[] { 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, false)); // Page Down + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); // Left Arrow + yield return (new byte[] { 27, 79, 69 }, new ConsoleKeyInfo(default, ConsoleKey.NoName, false, false, false)); // Begin (5) + yield return (new byte[] { 27, 79, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false)); // Right Arrow + yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); // Home + yield return (new byte[] { 27, 79, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false)); // Up Arrow + yield return (new byte[] { 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, false)); // Page Up + yield return (new byte[] { 27, 79, 111 }, new ConsoleKeyInfo('/', ConsoleKey.Divide, false, false, false)); // / (divide sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 106 }, new ConsoleKeyInfo('*', ConsoleKey.Multiply, false, false, false)); // * (multiply sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 109 }, new ConsoleKeyInfo('-', ConsoleKey.Subtract, false, false, false)); // - (minus sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 107 }, new ConsoleKeyInfo('+', ConsoleKey.Add, false, false, false)); // + (plus sign using Numeric Keypad)) + yield return (new byte[] { 27, 79, 77 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); // Enter (using Numeric Keypad)) + } + } +} + +internal static class PuTTy +{ + // PuTTy: Home and End keys: Standard + internal static IEnumerable<(byte[], ConsoleKeyInfo)> StandardHomeAndEndKeys + { + get + { + yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); // Home + yield return (new byte[] { 27, 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); // Alt+Home + yield return (new byte[] { 27, 91, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + yield return (new byte[] { 27, 27, 91, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, true, false)); // Alt+End + yield return (new byte[] { 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, false)); // PageUp + yield return (new byte[] { 27, 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, true, false)); // Alt+PageUp + yield return (new byte[] { 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, false)); // PageDown + yield return (new byte[] { 27, 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, true, false)); // Alt+PageDown + } + } + + // PuTTy: Home and End keys: rxvt + internal static IEnumerable<(byte[], ConsoleKeyInfo)> RxvtHomeAndEndKeys + { + get + { + yield return (new byte[] { 27, 91, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); // Home + yield return (new byte[] { 27, 27, 91, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); // Alt+Home + yield return (new byte[] { 27, 79, 119 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + yield return (new byte[] { 27, 27, 79, 119 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, true, false)); // Alt+End + yield return (new byte[] { 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, false)); // PageUp + yield return (new byte[] { 27, 27, 91, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, true, false)); // Alt+PageUp + yield return (new byte[] { 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, false)); // PageDown + yield return (new byte[] { 27, 27, 91, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, true, false)); // Alt+PageDown + } + } + + // PuTTy: The function keys and keypad: ESC[n~ + internal static IEnumerable<(byte[], ConsoleKeyInfo)> ESCnFunctionKeys + { + get + { + yield return (new byte[] { 27, 91, 49, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, false)); // F1 + yield return (new byte[] { 27, 91, 49, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, false)); // F2 + yield return (new byte[] { 27, 91, 49, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, false)); // F3 + yield return (new byte[] { 27, 91, 49, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, false)); // F4 + yield return (new byte[] { 27, 91, 49, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, false)); // F5 + yield return (new byte[] { 27, 91, 49, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, false)); // F6 + yield return (new byte[] { 27, 91, 49, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, false)); // F7 + yield return (new byte[] { 27, 91, 49, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, false)); // F8 + yield return (new byte[] { 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, false)); // F9 + yield return (new byte[] { 27, 91, 50, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, false)); // F10 + yield return (new byte[] { 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, false)); // F11 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // F12 + yield return (new byte[] { 27, 27, 91, 49, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, true, false)); // Alt+F1 + yield return (new byte[] { 27, 27, 91, 49, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, true, false)); // Alt+F2 + yield return (new byte[] { 27, 27, 91, 49, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, true, false)); // Alt+F3 + yield return (new byte[] { 27, 27, 91, 49, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, true, false)); // Alt+F5 + yield return (new byte[] { 27, 27, 91, 49, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, true, false)); // Alt+F6 + yield return (new byte[] { 27, 27, 91, 49, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, true, false)); // Alt+F7 + yield return (new byte[] { 27, 27, 91, 49, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, true, false)); // Alt+F8 + yield return (new byte[] { 27, 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, true, false)); // Alt+F9 + yield return (new byte[] { 27, 27, 91, 50, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, true, false)); // Alt+F10 + yield return (new byte[] { 27, 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, true, false)); // Alt+F11 + yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); // Alt+F12 + yield return (new byte[] { 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, false)); // Shift+F1=F11 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // Shift+F2=F12 + yield return (new byte[] { 27, 91, 50, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F13, false, false, false)); // Shift+F3=F13 + yield return (new byte[] { 27, 91, 50, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F14, false, false, false)); // Shift+F4=F14 + yield return (new byte[] { 27, 91, 50, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F15, false, false, false)); // Shift+F5=F15 + yield return (new byte[] { 27, 91, 50, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F16, false, false, false)); // Shift+F6=F16 + yield return (new byte[] { 27, 91, 51, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F17, false, false, false)); // Shift+F7=F17 + yield return (new byte[] { 27, 91, 51, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F18, false, false, false)); // Shift+F8=F18 + yield return (new byte[] { 27, 91, 51, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F19, false, false, false)); // Shift+F9=F19 + yield return (new byte[] { 27, 91, 51, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F20, false, false, false)); // Shift+F10=F20 + } + } + + // PuTTy: The function keys and keypad: Linux + internal static IEnumerable<(byte[], ConsoleKeyInfo)> LinuxFunctionKeys + { + get + { + yield return (new byte[] { 27, 91, 91, 65 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, false)); // F1 + yield return (new byte[] { 27, 91, 91, 66 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, false)); // F2 + yield return (new byte[] { 27, 91, 91, 67 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, false)); // F3 + yield return (new byte[] { 27, 91, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, false)); // F4 + yield return (new byte[] { 27, 91, 91, 69 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, false)); // F5 + yield return (new byte[] { 27, 91, 49, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, false)); // F6 + yield return (new byte[] { 27, 91, 49, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, false)); // F7 + yield return (new byte[] { 27, 91, 49, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, false)); // F8 + yield return (new byte[] { 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, false)); // F9 + yield return (new byte[] { 27, 91, 50, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, false)); // F10 + yield return (new byte[] { 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, false)); // F11 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // F12 + yield return (new byte[] { 27, 27, 91, 91, 65 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, true, false)); // Alt+F1 + yield return (new byte[] { 27, 27, 91, 91, 66 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, true, false)); // Alt+F2 + yield return (new byte[] { 27, 27, 91, 91, 67 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, true, false)); // Alt+F3 + yield return (new byte[] { 27, 27, 91, 91, 69 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, true, false)); // Alt+F5 + yield return (new byte[] { 27, 27, 91, 49, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, true, false)); // Alt+F6 + yield return (new byte[] { 27, 27, 91, 49, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, true, false)); // Alt+F7 + yield return (new byte[] { 27, 27, 91, 49, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, true, false)); // Alt+F8 + yield return (new byte[] { 27, 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, true, false)); // Alt+F9 + yield return (new byte[] { 27, 27, 91, 50, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, true, false)); // Alt+F10 + yield return (new byte[] { 27, 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, true, false)); // Alt+F11 + yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); // Alt+F12 + yield return (new byte[] { 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, false)); // Shift+F1=F11 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // Shift+F2=F12 + yield return (new byte[] { 27, 91, 50, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F13, false, false, false)); // Shift+F3=F13 + yield return (new byte[] { 27, 91, 50, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F14, false, false, false)); // Shift+F4=F14 + yield return (new byte[] { 27, 91, 50, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F15, false, false, false)); // Shift+F5=F15 + yield return (new byte[] { 27, 91, 50, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F16, false, false, false)); // Shift+F6=F16 + yield return (new byte[] { 27, 91, 51, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F17, false, false, false)); // Shift+F7=F17 + yield return (new byte[] { 27, 91, 51, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F18, false, false, false)); // Shift+F8=F18 + yield return (new byte[] { 27, 91, 51, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F19, false, false, false)); // Shift+F9=F19 + yield return (new byte[] { 27, 91, 51, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F20, false, false, false)); // Shift+F10=F20 + } + } + + // PuTTy: The function keys and keypad: Xterm R6 + internal static IEnumerable<(byte[], ConsoleKeyInfo)> XtermR6FunctionKeys + { + get + { + yield return (new byte[] { 27, 79, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, false)); // F1 + yield return (new byte[] { 27, 79, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, false)); // F2 + yield return (new byte[] { 27, 79, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, false)); // F3 + yield return (new byte[] { 27, 79, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, false)); // F4 + yield return (new byte[] { 27, 91, 49, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, false)); // F5 + yield return (new byte[] { 27, 91, 49, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, false)); // F6 + yield return (new byte[] { 27, 91, 49, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, false)); // F7 + yield return (new byte[] { 27, 91, 49, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, false)); // F8 + yield return (new byte[] { 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, false)); // F9 + yield return (new byte[] { 27, 91, 50, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, false)); // F10 + yield return (new byte[] { 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, false)); // F11 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // F12 + yield return (new byte[] { 27, 27, 79, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, true, false)); // Alt+F1 + yield return (new byte[] { 27, 27, 79, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, true, false)); // Alt+F2 + yield return (new byte[] { 27, 27, 79, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, true, false)); // Alt+F3 + yield return (new byte[] { 27, 27, 91, 49, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, true, false)); // Alt+F5 + yield return (new byte[] { 27, 27, 91, 49, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, true, false)); // Alt+F6 + yield return (new byte[] { 27, 27, 91, 49, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, true, false)); // Alt+F7 + yield return (new byte[] { 27, 27, 91, 49, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, true, false)); // Alt+F8 + yield return (new byte[] { 27, 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, true, false)); // Alt+F9 + yield return (new byte[] { 27, 27, 91, 50, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, true, false)); // Alt+F10 + yield return (new byte[] { 27, 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, true, false)); // Alt+F11 + yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); // Alt+F12 + yield return (new byte[] { 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, false)); // Shift+F1=F11 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // Shift+F2=F12 + yield return (new byte[] { 27, 91, 50, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F13, false, false, false)); // Shift+F3=F13 + yield return (new byte[] { 27, 91, 50, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F14, false, false, false)); // Shift+F4=F14 + yield return (new byte[] { 27, 91, 50, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F15, false, false, false)); // Shift+F5=F15 + yield return (new byte[] { 27, 91, 50, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F16, false, false, false)); // Shift+F6=F16 + yield return (new byte[] { 27, 91, 51, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F17, false, false, false)); // Shift+F7=F17 + yield return (new byte[] { 27, 91, 51, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F18, false, false, false)); // Shift+F8=F18 + yield return (new byte[] { 27, 91, 51, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F19, false, false, false)); // Shift+F9=F19 + yield return (new byte[] { 27, 91, 51, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F20, false, false, false)); // Shift+F10=F20 + } + } + + // PuTTy: The function keys and keypad: VT 400 + internal static IEnumerable<(byte[], ConsoleKeyInfo)> VT400FunctionKeys + { + get + { + yield return (new byte[] { 27, 91, 49, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, false)); // F1 + yield return (new byte[] { 27, 91, 49, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, false)); // F2 + yield return (new byte[] { 27, 91, 49, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, false)); // F3 + yield return (new byte[] { 27, 91, 49, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, false)); // F4 + yield return (new byte[] { 27, 91, 49, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, false)); // F5 + yield return (new byte[] { 27, 91, 49, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, false)); // F6 + yield return (new byte[] { 27, 91, 49, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, false)); // F7 + yield return (new byte[] { 27, 91, 49, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, false)); // F8 + yield return (new byte[] { 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, false)); // F9 + yield return (new byte[] { 27, 91, 50, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, false)); // F10 + yield return (new byte[] { 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, false)); // F11 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // F12 + yield return (new byte[] { 27, 27, 91, 49, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, true, false)); // Alt+F1 + yield return (new byte[] { 27, 27, 91, 49, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, true, false)); // Alt+F2 + yield return (new byte[] { 27, 27, 91, 49, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, true, false)); // Alt+F3 + yield return (new byte[] { 27, 27, 91, 49, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, true, false)); // Alt+F5 + yield return (new byte[] { 27, 27, 91, 49, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, true, false)); // Alt+F6 + yield return (new byte[] { 27, 27, 91, 49, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, true, false)); // Alt+F7 + yield return (new byte[] { 27, 27, 91, 49, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, true, false)); // Alt+F8 + yield return (new byte[] { 27, 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, true, false)); // Alt+F9 + yield return (new byte[] { 27, 27, 91, 50, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, true, false)); // Alt+F10 + yield return (new byte[] { 27, 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, true, false)); // Alt+F11 + yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); // Alt+F12 + yield return (new byte[] { 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, false)); // Shift+F1=F11 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // Shift+F2=F12 + yield return (new byte[] { 27, 91, 50, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F13, false, false, false)); // Shift+F3=F13 + yield return (new byte[] { 27, 91, 50, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F14, false, false, false)); // Shift+F4=F14 + yield return (new byte[] { 27, 91, 50, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F15, false, false, false)); // Shift+F5=F15 + yield return (new byte[] { 27, 91, 50, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F16, false, false, false)); // Shift+F6=F16 + yield return (new byte[] { 27, 91, 51, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F17, false, false, false)); // Shift+F7=F17 + yield return (new byte[] { 27, 91, 51, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F18, false, false, false)); // Shift+F8=F18 + yield return (new byte[] { 27, 91, 51, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F19, false, false, false)); // Shift+F9=F19 + yield return (new byte[] { 27, 91, 51, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F20, false, false, false)); // Shift+F10=F20 + } + } + + // PuTTy: The function keys and keypad: VT 100+ + internal static IEnumerable<(byte[], ConsoleKeyInfo)> VT100FunctionKeys + { + get + { + yield return (new byte[] { 27, 79, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, false)); // F1 + yield return (new byte[] { 27, 79, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, false)); // F2 + yield return (new byte[] { 27, 79, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, false)); // F3 + yield return (new byte[] { 27, 79, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, false)); // F4 + yield return (new byte[] { 27, 79, 84 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, false)); // F5 + yield return (new byte[] { 27, 79, 85 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, false)); // F6 + yield return (new byte[] { 27, 79, 86 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, false)); // F7 + yield return (new byte[] { 27, 79, 87 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, false)); // F8 + yield return (new byte[] { 27, 79, 88 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, false)); // F9 + yield return (new byte[] { 27, 79, 89 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, false)); // F10 + yield return (new byte[] { 27, 79, 90 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, false)); // F11 + yield return (new byte[] { 27, 79, 91 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // F12 + yield return (new byte[] { 27, 27, 79, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, true, false)); // Alt+F1 + yield return (new byte[] { 27, 27, 79, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, true, false)); // Alt+F2 + yield return (new byte[] { 27, 27, 79, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, true, false)); // Alt+F3 + yield return (new byte[] { 27, 27, 79, 84 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, true, false)); // Alt+F5 + yield return (new byte[] { 27, 27, 79, 85 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, true, false)); // Alt+F6 + yield return (new byte[] { 27, 27, 79, 86 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, true, false)); // Alt+F7 + yield return (new byte[] { 27, 27, 79, 87 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, true, false)); // Alt+F8 + yield return (new byte[] { 27, 27, 79, 88 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, true, false)); // Alt+F9 + yield return (new byte[] { 27, 27, 79, 89 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, true, false)); // Alt+F10 + yield return (new byte[] { 27, 27, 79, 90 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, true, false)); // Alt+F11 + yield return (new byte[] { 27, 27, 79, 91 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); // Alt+F12 + yield return (new byte[] { 27, 79, 90 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, false)); // Shift+F1=F11 + yield return (new byte[] { 27, 79, 91 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // Shift+F2=F12 + yield return (new byte[] { 27, 91, 50, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F13, false, false, false)); // Shift+F3=F13 + yield return (new byte[] { 27, 91, 50, 54, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F14, false, false, false)); // Shift+F4=F14 + yield return (new byte[] { 27, 91, 50, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F15, false, false, false)); // Shift+F5=F15 + yield return (new byte[] { 27, 91, 50, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F16, false, false, false)); // Shift+F6=F16 + yield return (new byte[] { 27, 91, 51, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F17, false, false, false)); // Shift+F7=F17 + yield return (new byte[] { 27, 91, 51, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F18, false, false, false)); // Shift+F8=F18 + yield return (new byte[] { 27, 91, 51, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F19, false, false, false)); // Shift+F9=F19 + yield return (new byte[] { 27, 91, 51, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F20, false, false, false)); // Shift+F10=F20 + } + } + + // PuTTy: The function keys and keypad: SCO + internal static IEnumerable<(byte[], ConsoleKeyInfo)> SCOFunctionKeys + { + get + { + yield return (new byte[] { 27, 91, 77 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, false)); // F1 + yield return (new byte[] { 27, 91, 78 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, false)); // F2 + yield return (new byte[] { 27, 91, 79 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, false)); // F3 + yield return (new byte[] { 27, 91, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, false)); // F4 + yield return (new byte[] { 27, 91, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, false)); // F5 + yield return (new byte[] { 27, 91, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, false)); // F6 + yield return (new byte[] { 27, 91, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, false)); // F7 + yield return (new byte[] { 27, 91, 84 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, false)); // F8 + yield return (new byte[] { 27, 91, 85 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, false)); // F9 + yield return (new byte[] { 27, 91, 86 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, false)); // F10 + yield return (new byte[] { 27, 91, 87 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, false)); // F11 + yield return (new byte[] { 27, 91, 88 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // F12 + yield return (new byte[] { 27, 91, 107 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, true)); // Ctrl+F1 + yield return (new byte[] { 27, 91, 108 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, true)); // Ctrl+F2 + yield return (new byte[] { 27, 91, 109 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, true)); // Ctrl+F3 + yield return (new byte[] { 27, 91, 110 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, true)); // Ctrl+F4 + yield return (new byte[] { 27, 91, 111 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, true)); // Ctrl+F5 + yield return (new byte[] { 27, 91, 112 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, true)); // Ctrl+F6 + yield return (new byte[] { 27, 91, 113 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, true)); // Ctrl+F7 + yield return (new byte[] { 27, 91, 114 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, true)); // Ctrl+F8 + yield return (new byte[] { 27, 91, 115 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, true)); // Ctrl+F9 + yield return (new byte[] { 27, 91, 116 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, true)); // Ctrl+F10 + yield return (new byte[] { 27, 91, 117 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, true)); // Ctrl+F11 + yield return (new byte[] { 27, 91, 118 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); // Ctrl+F12 + yield return (new byte[] { 27, 27, 91, 77 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, true, false)); // Alt+F1 + yield return (new byte[] { 27, 27, 91, 78 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, true, false)); // Alt+F2 + yield return (new byte[] { 27, 27, 91, 79 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, true, false)); // Alt+F3 + yield return (new byte[] { 27, 27, 91, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, true, false)); // Alt+F5 + yield return (new byte[] { 27, 27, 91, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, true, false)); // Alt+F6 + yield return (new byte[] { 27, 27, 91, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, true, false)); // Alt+F7 + yield return (new byte[] { 27, 27, 91, 84 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, true, false)); // Alt+F8 + yield return (new byte[] { 27, 27, 91, 85 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, true, false)); // Alt+F9 + yield return (new byte[] { 27, 27, 91, 86 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, true, false)); // Alt+F10 + yield return (new byte[] { 27, 27, 91, 87 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, true, false)); // Alt+F11 + yield return (new byte[] { 27, 27, 91, 88 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); // Alt+F12 + yield return (new byte[] { 27, 91, 89 }, new ConsoleKeyInfo(default, ConsoleKey.F1, true, false, false)); // Shift+F1 + // { 27, 91, 90 } is not supported in this case as Terminfo binds it to Shift+Tab (Backtab) + // yield return (new byte[] { 27, 91, 90 }, new ConsoleKeyInfo(default, ConsoleKey.F2, true, false, false)); // Shift+F2 + yield return (new byte[] { 27, 91, 97 }, new ConsoleKeyInfo(default, ConsoleKey.F3, true, false, false)); // Shift+F3 + yield return (new byte[] { 27, 91, 98 }, new ConsoleKeyInfo(default, ConsoleKey.F4, true, false, false)); // Shift+F4 + yield return (new byte[] { 27, 91, 99 }, new ConsoleKeyInfo(default, ConsoleKey.F5, true, false, false)); // Shift+F5 + yield return (new byte[] { 27, 91, 100 }, new ConsoleKeyInfo(default, ConsoleKey.F6, true, false, false)); // Shift+F6 + yield return (new byte[] { 27, 91, 101 }, new ConsoleKeyInfo(default, ConsoleKey.F7, true, false, false)); // Shift+F7 + yield return (new byte[] { 27, 91, 102 }, new ConsoleKeyInfo(default, ConsoleKey.F8, true, false, false)); // Shift+F8 + yield return (new byte[] { 27, 91, 103 }, new ConsoleKeyInfo(default, ConsoleKey.F9, true, false, false)); // Shift+F9 + yield return (new byte[] { 27, 91, 104 }, new ConsoleKeyInfo(default, ConsoleKey.F10, true, false, false)); // Shift+F10 + yield return (new byte[] { 27, 91, 105 }, new ConsoleKeyInfo(default, ConsoleKey.F11, true, false, false)); // Shift+F11 + yield return (new byte[] { 27, 91, 106 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); // Shift+F12 + yield return (new byte[] { 27, 91, 119 }, new ConsoleKeyInfo(default, ConsoleKey.F1, true, false, true)); // Ctrl+Shift+F1 + yield return (new byte[] { 27, 91, 120 }, new ConsoleKeyInfo(default, ConsoleKey.F2, true, false, true)); // Ctrl+Shift+F2 + yield return (new byte[] { 27, 91, 121 }, new ConsoleKeyInfo(default, ConsoleKey.F3, true, false, true)); // Ctrl+Shift+F3 + yield return (new byte[] { 27, 91, 122 }, new ConsoleKeyInfo(default, ConsoleKey.F4, true, false, true)); // Ctrl+Shift+F4 + yield return (new byte[] { 27, 91, 64 }, new ConsoleKeyInfo(default, ConsoleKey.F5, true, false, true)); // Ctrl+Shift+F5 + yield return (new byte[] { 27, 91, 91 }, new ConsoleKeyInfo(default, ConsoleKey.F6, true, false, true)); // Ctrl+Shift+F6 + yield return (new byte[] { 27, 91, 92 }, new ConsoleKeyInfo(default, ConsoleKey.F7, true, false, true)); // Ctrl+Shift+F7 + yield return (new byte[] { 27, 91, 93 }, new ConsoleKeyInfo(default, ConsoleKey.F8, true, false, true)); // Ctrl+Shift+F8 + yield return (new byte[] { 27, 91, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F9, true, false, true)); // Ctrl+Shift+F9 + yield return (new byte[] { 27, 91, 95 }, new ConsoleKeyInfo(default, ConsoleKey.F10, true, false, true)); // Ctrl+Shift+F10 + yield return (new byte[] { 27, 91, 96 }, new ConsoleKeyInfo(default, ConsoleKey.F11, true, false, true)); // Ctrl+Shift+F11 + yield return (new byte[] { 27, 91, 123 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, true)); // Ctrl+Shift+F12 + yield return (new byte[] { 27, 27, 91, 119 }, new ConsoleKeyInfo(default, ConsoleKey.F1, true, true, true)); // Ctrl+Alt+Shift+F1 + yield return (new byte[] { 27, 27, 91, 120 }, new ConsoleKeyInfo(default, ConsoleKey.F2, true, true, true)); // Ctrl+Alt+Shift+F2 + yield return (new byte[] { 27, 27, 91, 121 }, new ConsoleKeyInfo(default, ConsoleKey.F3, true, true, true)); // Ctrl+Alt+Shift+F3 + yield return (new byte[] { 27, 27, 91, 64 }, new ConsoleKeyInfo(default, ConsoleKey.F5, true, true, true)); // Ctrl+Alt+Shift+F5 + yield return (new byte[] { 27, 27, 91, 91 }, new ConsoleKeyInfo(default, ConsoleKey.F6, true, true, true)); // Ctrl+Alt+Shift+F6 + yield return (new byte[] { 27, 27, 91, 92 }, new ConsoleKeyInfo(default, ConsoleKey.F7, true, true, true)); // Ctrl+Alt+Shift+F7 + yield return (new byte[] { 27, 27, 91, 93 }, new ConsoleKeyInfo(default, ConsoleKey.F8, true, true, true)); // Ctrl+Alt+Shift+F8 + yield return (new byte[] { 27, 27, 91, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F9, true, true, true)); // Ctrl+Alt+Shift+F9 + yield return (new byte[] { 27, 27, 91, 95 }, new ConsoleKeyInfo(default, ConsoleKey.F10, true, true, true)); // Ctrl+Alt+Shift+F10 + yield return (new byte[] { 27, 27, 91, 96 }, new ConsoleKeyInfo(default, ConsoleKey.F11, true, true, true)); // Ctrl+Alt+Shift+F11 + yield return (new byte[] { 27, 27, 91, 123 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); // Ctrl+Alt+Shift+F12 + } + } + + // PuTTy: The function keys and keypad: Xterm 216+ + internal static IEnumerable<(byte[], ConsoleKeyInfo)> Xterm216FunctionKeys + { + get + { + yield return (new byte[] { 27, 79, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, false)); // F1 + yield return (new byte[] { 27, 79, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, false)); // F2 + yield return (new byte[] { 27, 79, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, false)); // F3 + yield return (new byte[] { 27, 79, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, false)); // F4 + yield return (new byte[] { 27, 91, 49, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, false)); // F5 + yield return (new byte[] { 27, 91, 49, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, false)); // F6 + yield return (new byte[] { 27, 91, 49, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, false)); // F7 + yield return (new byte[] { 27, 91, 49, 57, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, false)); // F8 + yield return (new byte[] { 27, 91, 50, 48, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, false)); // F9 + yield return (new byte[] { 27, 91, 50, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, false)); // F10 + yield return (new byte[] { 27, 91, 50, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, false)); // F11 + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); // F12 + yield return (new byte[] { 27, 91, 49, 59, 53, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, false, true)); // Ctrl+F1 + yield return (new byte[] { 27, 91, 49, 59, 53, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, false, true)); // Ctrl+F2 + yield return (new byte[] { 27, 91, 49, 59, 53, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, false, true)); // Ctrl+F3 + yield return (new byte[] { 27, 91, 49, 59, 53, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, false, false, true)); // Ctrl+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, false, true)); // Ctrl+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, false, true)); // Ctrl+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, false, true)); // Ctrl+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, false, true)); // Ctrl+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, false, true)); // Ctrl+F9 + yield return (new byte[] { 27, 91, 50, 49, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, false, true)); // Ctrl+F10 + yield return (new byte[] { 27, 91, 50, 51, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, false, true)); // Ctrl+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); // Ctrl+F12 + yield return (new byte[] { 27, 27, 91, 49, 59, 51, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, false, true, false)); // Alt+F1 + yield return (new byte[] { 27, 27, 91, 49, 59, 51, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, false, true, false)); // Alt+F2 + yield return (new byte[] { 27, 27, 91, 49, 59, 51, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, false, true, false)); // Alt+F3 + yield return (new byte[] { 27, 27, 91, 49, 53, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, false, true, false)); // Alt+F5 + yield return (new byte[] { 27, 27, 91, 49, 55, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, false, true, false)); // Alt+F6 + yield return (new byte[] { 27, 27, 91, 49, 56, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, false, true, false)); // Alt+F7 + yield return (new byte[] { 27, 27, 91, 49, 57, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, false, true, false)); // Alt+F8 + yield return (new byte[] { 27, 27, 91, 50, 48, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, false, true, false)); // Alt+F9 + yield return (new byte[] { 27, 27, 91, 50, 49, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, false, true, false)); // Alt+F10 + yield return (new byte[] { 27, 27, 91, 50, 51, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, false, true, false)); // Alt+F11 + yield return (new byte[] { 27, 27, 91, 50, 52, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); // Alt+F12 + yield return (new byte[] { 27, 91, 49, 59, 50, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, true, false, false)); // Shift+F1 + yield return (new byte[] { 27, 91, 49, 59, 50, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, true, false, false)); // Shift+F2 + yield return (new byte[] { 27, 91, 49, 59, 50, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, true, false, false)); // Shift+F3 + yield return (new byte[] { 27, 91, 49, 59, 50, 83 }, new ConsoleKeyInfo(default, ConsoleKey.F4, true, false, false)); // Shift+F4 + yield return (new byte[] { 27, 91, 49, 53, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, true, false, false)); // Shift+F5 + yield return (new byte[] { 27, 91, 49, 55, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, true, false, false)); // Shift+F6 + yield return (new byte[] { 27, 91, 49, 56, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, true, false, false)); // Shift+F7 + yield return (new byte[] { 27, 91, 49, 57, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, true, false, false)); // Shift+F8 + yield return (new byte[] { 27, 91, 50, 48, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, true, false, false)); // Shift+F9 + yield return (new byte[] { 27, 91, 50, 49, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, true, false, false)); // Shift+F10 + yield return (new byte[] { 27, 91, 50, 51, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, true, false, false)); // Shift+F11 + yield return (new byte[] { 27, 91, 50, 52, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); // Shift+F12 + yield return (new byte[] { 27, 27, 91, 49, 59, 56, 80 }, new ConsoleKeyInfo(default, ConsoleKey.F1, true, true, true)); // Ctrl+Alt+Shift+F1 + yield return (new byte[] { 27, 27, 91, 49, 59, 56, 81 }, new ConsoleKeyInfo(default, ConsoleKey.F2, true, true, true)); // Ctrl+Alt+Shift+F2 + yield return (new byte[] { 27, 27, 91, 49, 59, 56, 82 }, new ConsoleKeyInfo(default, ConsoleKey.F3, true, true, true)); // Ctrl+Alt+Shift+F3 + yield return (new byte[] { 27, 27, 91, 49, 53, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F5, true, true, true)); // Ctrl+Alt+Shift+F5 + yield return (new byte[] { 27, 27, 91, 49, 55, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F6, true, true, true)); // Ctrl+Alt+Shift+F6 + yield return (new byte[] { 27, 27, 91, 49, 56, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F7, true, true, true)); // Ctrl+Alt+Shift+F7 + yield return (new byte[] { 27, 27, 91, 49, 57, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F8, true, true, true)); // Ctrl+Alt+Shift+F8 + yield return (new byte[] { 27, 27, 91, 50, 48, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F9, true, true, true)); // Ctrl+Alt+Shift+F9 + yield return (new byte[] { 27, 27, 91, 50, 49, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F10, true, true, true)); // Ctrl+Alt+Shift+F10 + yield return (new byte[] { 27, 27, 91, 50, 51, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F11, true, true, true)); // Ctrl+Alt+Shift+F11 + yield return (new byte[] { 27, 27, 91, 50, 52, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); // Ctrl+Alt+Shift+F12 + } + } + + // PuTTY: Shift/Ctrl/Alt with arrow keys: Ctrl toggles app mode + internal static IEnumerable<(byte[], ConsoleKeyInfo)> CtrlTogglesAppModeArrows + { + get + { + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); // LeftArrow + yield return (new byte[] { 27, 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); // Alt+LeftArrow + yield return (new byte[] { 27, 79, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false)); // UpArrow + yield return (new byte[] { 27, 27, 79, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, true, false)); // Alt+UpArrow + yield return (new byte[] { 27, 79, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false)); // DownArrow + yield return (new byte[] { 27, 27, 79, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, true, false)); // Alt+DownArrow + yield return (new byte[] { 27, 79, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false)); // RightArrow + yield return (new byte[] { 27, 27, 79, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, true, false)); // Alt+RightArrow + // Ctrl and Shift key modifiers are not supported for PuTTy arrow keys as they use mappings that contradict data stored in Terminfo. + // example: PuTTy configured with "putty" Terminal-type string sets TERM=putty. + // "putty" Terminfo says that "27, 91, 68" (\[[D) stored under KeySLeft is LeftArrow with Shift, + // but PuTTy itself uses it for Ctrl+LeftArrow. + } + } +} + +public static class SCO +{ + internal static IEnumerable<(byte[], ConsoleKeyInfo)> ArrowKeys + { + get + { + yield return (new byte[] { 27, 91, 65 }, new ConsoleKeyInfo(default, ConsoleKey.UpArrow, false, false, false)); // UpArrow + yield return (new byte[] { 27, 91, 66 }, new ConsoleKeyInfo(default, ConsoleKey.DownArrow, false, false, false)); // DownArrow + yield return (new byte[] { 27, 91, 67 }, new ConsoleKeyInfo(default, ConsoleKey.RightArrow, false, false, false)); // LeftArrow + yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); // RightArrow + } + } + + internal static IEnumerable<(byte[], ConsoleKeyInfo)> HomeKeys + { + get + { + yield return (new byte[] { 27, 91, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); // Home + yield return (new byte[] { 27, 91, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + yield return (new byte[] { 27, 91, 73 }, new ConsoleKeyInfo(default, ConsoleKey.PageUp, false, false, false)); // PageUp + yield return (new byte[] { 27, 91, 71 }, new ConsoleKeyInfo(default, ConsoleKey.PageDown, false, false, false)); // PageDown + } + } +} diff --git a/src/libraries/System.Console/tests/System.Console.Tests.csproj b/src/libraries/System.Console/tests/System.Console.Tests.csproj index 83b9a72b587c5..311abe1aaf873 100644 --- a/src/libraries/System.Console/tests/System.Console.Tests.csproj +++ b/src/libraries/System.Console/tests/System.Console.Tests.csproj @@ -4,6 +4,7 @@ true $(NetCoreAppCurrent);$(NetCoreAppCurrent)-windows true + ..\src\Resources\Strings.resx @@ -33,6 +34,14 @@ + + + + + + + + (TerminfoLocationsField, null, Type.GetType(TerminfoDatabaseType + SystemConsoleFullNameSuffix)); + string[] locations = GetFieldValueOnObject(TerminfoLocationsField, null, typeof(Console).GetTypeInfo().Assembly.GetType(TerminfoDatabaseFactoryType)); foreach (string location in locations) { if (!Directory.Exists(location)) @@ -66,13 +62,12 @@ public void VerifyInstalledTermInfosParse() [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests TermInfo public void VerifyTermInfoSupportsNewAndLegacyNcurses() { - MethodInfo readDbMethod = Type.GetType(TerminfoDatabaseType + SystemConsoleFullNameSuffix).GetTypeInfo().GetDeclaredMethods(ReadDatabaseMethod).Where(m => m.GetParameters().Count() == 2).Single(); + MethodInfo readDbMethod = typeof(Console).GetTypeInfo().Assembly.GetType(TerminfoDatabaseFactoryType).GetTypeInfo().GetDeclaredMethods(ReadDatabaseMethod).Where(m => m.GetParameters().Count() == 2).Single(); readDbMethod.Invoke(null, new object[] { "xterm", "ncursesFormats" }); // This will throw InvalidOperationException in case we don't support the legacy format readDbMethod.Invoke(null, new object[] { "screen-256color", "ncursesFormats" }); // This will throw InvalidOperationException if we can't parse the new format } [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/72833", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))] [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests TermInfo [InlineData("xterm-256color", "\u001B\u005B\u00330m", "\u001B\u005B\u00340m", 0)] [InlineData("xterm-256color", "\u001B\u005B\u00331m", "\u001B\u005B\u00341m", 1)] @@ -126,37 +121,37 @@ public void TryingToLoadTermThatDoesNotExistDoesNotThrow() private object ReadTermInfoDatabase(string term) { - MethodInfo readDbMethod = Type.GetType(TerminfoDatabaseType + SystemConsoleFullNameSuffix).GetTypeInfo().GetDeclaredMethods(ReadDatabaseMethod).Where(m => m.GetParameters().Count() == 1).Single(); + MethodInfo readDbMethod = typeof(Console).GetTypeInfo().Assembly.GetType(TerminfoDatabaseFactoryType).GetTypeInfo().GetDeclaredMethods(ReadDatabaseMethod).Where(m => m.GetParameters().Count() == 1).Single(); return readDbMethod.Invoke(null, new object[] { term }); } private object CreateTermColorInfo(object db) { - return Type.GetType(TerminalFormatStringsType + SystemConsoleFullNameSuffix).GetTypeInfo().DeclaredConstructors + return typeof(Console).GetTypeInfo().Assembly.GetType(TerminalFormatStringsType).GetTypeInfo().DeclaredConstructors .Where(c => c.GetParameters().Count() == 1).Single().Invoke(new object[] { db }); } private string GetForegroundFormat(object colorInfo) { - return GetFieldValueOnObject(ForegroundFormatField, colorInfo, Type.GetType(TerminalFormatStringsType + SystemConsoleFullNameSuffix)); + return GetFieldValueOnObject(ForegroundFormatField, colorInfo, typeof(Console).GetTypeInfo().Assembly.GetType(TerminalFormatStringsType)); } private string GetBackgroundFormat(object colorInfo) { - return GetFieldValueOnObject(BackgroundFormatField, colorInfo, Type.GetType(TerminalFormatStringsType + SystemConsoleFullNameSuffix)); + return GetFieldValueOnObject(BackgroundFormatField, colorInfo, typeof(Console).GetTypeInfo().Assembly.GetType(TerminalFormatStringsType)); } private int GetMaxColors(object colorInfo) { - return GetFieldValueOnObject(MaxColorsField, colorInfo, Type.GetType(TerminalFormatStringsType + SystemConsoleFullNameSuffix)); + return GetFieldValueOnObject(MaxColorsField, colorInfo, typeof(Console).GetTypeInfo().Assembly.GetType(TerminalFormatStringsType)); } private string GetResetFormat(object colorInfo) { - return GetFieldValueOnObject(ResetFormatField, colorInfo, Type.GetType(TerminalFormatStringsType + SystemConsoleFullNameSuffix)); + return GetFieldValueOnObject(ResetFormatField, colorInfo, typeof(Console).GetTypeInfo().Assembly.GetType(TerminalFormatStringsType)); } - private T GetFieldValueOnObject(string name, object instance, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] Type baseType) + private T GetFieldValueOnObject(string name, object instance, Type baseType) { return (T)baseType.GetTypeInfo().GetDeclaredField(name).GetValue(instance); } @@ -165,7 +160,7 @@ private object CreateFormatParam(object o) { Assert.True((o.GetType() == typeof(int)) || (o.GetType() == typeof(string))); - TypeInfo ti = Type.GetType(FormatParamType + SystemConsoleFullNameSuffix).GetTypeInfo(); + TypeInfo ti = typeof(Console).GetTypeInfo().Assembly.GetType(FormatParamType).GetTypeInfo(); ConstructorInfo ci = null; foreach (ConstructorInfo c in ti.DeclaredConstructors) @@ -189,8 +184,8 @@ private object CreateFormatParam(object o) private string EvaluateParameterizedStrings(string format, params object[] parameters) { - Type formatArrayType = Type.GetType(FormatParamType + SystemConsoleFullNameSuffix).MakeArrayType(); - MethodInfo mi = Type.GetType(ParameterizedStringsType + SystemConsoleFullNameSuffix).GetTypeInfo() + Type formatArrayType = typeof(Console).GetTypeInfo().Assembly.GetType(FormatParamType).MakeArrayType(); + MethodInfo mi = typeof(Console).GetTypeInfo().Assembly.GetType(ParameterizedStringsType).GetTypeInfo() .GetDeclaredMethods(EvaluateMethod).First(m => m.GetParameters()[1].ParameterType.IsArray); // Create individual FormatParams @@ -210,4 +205,4 @@ private string EvaluateParameterizedStrings(string format, params object[] param return (string)mi.Invoke(null, evalParams); } -} +} \ No newline at end of file diff --git a/src/native/libs/System.Native/pal_console.c b/src/native/libs/System.Native/pal_console.c index 1fd55aa3d0430..00f58890a4ba0 100644 --- a/src/native/libs/System.Native/pal_console.c +++ b/src/native/libs/System.Native/pal_console.c @@ -149,7 +149,7 @@ static bool TcSetAttr(struct termios* termios, bool blockIfBackground) return rv; } -static bool ConfigureTerminal(bool signalForBreak, bool forChild, uint8_t minChars, uint8_t decisecondsTimeout, bool blockIfBackground) +static bool ConfigureTerminal(bool signalForBreak, bool forChild, uint8_t minChars, uint8_t decisecondsTimeout, bool blockIfBackground, bool distinguishNewLines) { if (!g_hasTty) { @@ -168,7 +168,15 @@ static bool ConfigureTerminal(bool signalForBreak, bool forChild, uint8_t minCha if (!forChild) { - termios.c_iflag &= (uint32_t)(~(IXON | IXOFF)); + if (distinguishNewLines) + { + termios.c_iflag &= (uint32_t)(~(IXON | IXOFF | ICRNL | INLCR | IGNCR)); + } + else + { + termios.c_iflag &= (uint32_t)(~(IXON | IXOFF)); + } + termios.c_lflag &= (uint32_t)(~(ECHO | ICANON | IEXTEN)); } @@ -212,13 +220,13 @@ void UninitializeTerminal() } } -void SystemNative_InitializeConsoleBeforeRead(uint8_t minChars, uint8_t decisecondsTimeout) +void SystemNative_InitializeConsoleBeforeRead(int32_t distinguishNewLines, uint8_t minChars, uint8_t decisecondsTimeout) { if (pthread_mutex_lock(&g_lock) == 0) { g_reading = true; - ConfigureTerminal(g_signalForBreak, /* forChild */ false, minChars, decisecondsTimeout, /* blockIfBackground */ true); + ConfigureTerminal(g_signalForBreak, /* forChild */ false, minChars, decisecondsTimeout, /* blockIfBackground */ true, distinguishNewLines); pthread_mutex_unlock(&g_lock); } @@ -256,7 +264,7 @@ void SystemNative_ConfigureTerminalForChildProcess(int32_t childUsesTerminal) // Avoid configuring the terminal: only change terminal settings when our process has changed them. if (g_terminalConfigured) { - ConfigureTerminal(g_signalForBreak, /* forChild */ childUsesTerminal, /* minChars */ 1, /* decisecondsTimeout */ 0, /* blockIfBackground */ false); + ConfigureTerminal(g_signalForBreak, /* forChild */ childUsesTerminal, /* minChars */ 1, /* decisecondsTimeout */ 0, /* blockIfBackground */ false, /* distinguishNewLines */ false); } // Redo "Application mode" when there are no more children using the terminal. @@ -364,9 +372,9 @@ void SystemNative_GetControlCharacters( } } -int32_t SystemNative_StdinReady() +int32_t SystemNative_StdinReady(int32_t distinguishNewLines) { - SystemNative_InitializeConsoleBeforeRead(/* minChars */ 1, /* decisecondsTimeout */ 0); + SystemNative_InitializeConsoleBeforeRead(distinguishNewLines, /* minChars */ 1, /* decisecondsTimeout */ 0); struct pollfd fd = { .fd = STDIN_FILENO, .events = POLLIN }; int rv = poll(&fd, 1, 0) > 0 ? 1 : 0; SystemNative_UninitializeConsoleAfterRead(); @@ -394,7 +402,7 @@ int32_t SystemNative_GetSignalForBreak() return g_signalForBreak; } -int32_t SystemNative_SetSignalForBreak(int32_t signalForBreak) +int32_t SystemNative_SetSignalForBreak(int32_t signalForBreak, int32_t distinguishNewLines) { assert(signalForBreak == 0 || signalForBreak == 1); @@ -402,7 +410,7 @@ int32_t SystemNative_SetSignalForBreak(int32_t signalForBreak) if (pthread_mutex_lock(&g_lock) == 0) { - if (ConfigureTerminal(signalForBreak, /* forChild */ false, /* minChars */ 1, /* decisecondsTimeout */ 0, /* blockIfBackground */ true)) + if (ConfigureTerminal(signalForBreak, /* forChild */ false, /* minChars */ 1, /* decisecondsTimeout */ 0, /* blockIfBackground */ true, distinguishNewLines)) { g_signalForBreak = signalForBreak; rv = 1; diff --git a/src/native/libs/System.Native/pal_console.h b/src/native/libs/System.Native/pal_console.h index f0c230a30d4ca..ca837c55bc557 100644 --- a/src/native/libs/System.Native/pal_console.h +++ b/src/native/libs/System.Native/pal_console.h @@ -86,12 +86,12 @@ PALEXPORT void SystemNative_GetControlCharacters( /** * Returns 1 if any input is waiting on stdin; otherwise, 0. */ -PALEXPORT int32_t SystemNative_StdinReady(void); +PALEXPORT int32_t SystemNative_StdinReady(int32_t distinguishNewLines); /** * Configures the terminal for System.Console Read. */ -PALEXPORT void SystemNative_InitializeConsoleBeforeRead(uint8_t minChars, uint8_t decisecondsTimeout); +PALEXPORT void SystemNative_InitializeConsoleBeforeRead(int32_t distinguishNewLines, uint8_t minChars, uint8_t decisecondsTimeout); /** * Configures the terminal after System.Console Read. @@ -121,7 +121,7 @@ PALEXPORT int32_t SystemNative_GetSignalForBreak(void); * * Returns 1 on success, 0 on failure, in which case errno is set. */ -PALEXPORT int32_t SystemNative_SetSignalForBreak(int32_t signalForBreak); +PALEXPORT int32_t SystemNative_SetSignalForBreak(int32_t signalForBreak, int32_t distinguishNewLines); typedef enum {