From d929235e485ab63334abab2b572773bc320f347d Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 28 Jun 2022 19:54:46 +0200 Subject: [PATCH 01/40] move WellKnownStrings to a separate file --- .../System.Console/src/System.Console.csproj | 1 + .../src/System/TermInfo.WellKnownStrings.cs | 77 +++++++++++++++++++ .../System.Console/src/System/TermInfo.cs | 72 +---------------- .../tests/System.Console.Tests.csproj | 1 + 4 files changed, 80 insertions(+), 71 deletions(-) create mode 100644 src/libraries/System.Console/src/System/TermInfo.WellKnownStrings.cs diff --git a/src/libraries/System.Console/src/System.Console.csproj b/src/libraries/System.Console/src/System.Console.csproj index f9303bfe1dbaa..d3a3aceff1018 100644 --- a/src/libraries/System.Console/src/System.Console.csproj +++ b/src/libraries/System.Console/src/System.Console.csproj @@ -161,6 +161,7 @@ + Provides access to and processing of a terminfo database. - internal static class TermInfo + internal static partial class TermInfo { internal enum WellKnownNumbers { @@ -21,76 +21,6 @@ internal enum WellKnownNumbers 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 { diff --git a/src/libraries/System.Console/tests/System.Console.Tests.csproj b/src/libraries/System.Console/tests/System.Console.Tests.csproj index 3592a18092ad2..1d32fe2415f6c 100644 --- a/src/libraries/System.Console/tests/System.Console.Tests.csproj +++ b/src/libraries/System.Console/tests/System.Console.Tests.csproj @@ -49,5 +49,6 @@ Link="Common\Interop\Windows\Interop.Libraries.cs" /> + From c8887ecdb382f62bd1873470a27d5df3aa8e8d9f Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 28 Jun 2022 20:02:53 +0200 Subject: [PATCH 02/40] move Database to separate file --- .../System.Console/src/System.Console.csproj | 1 + .../src/System/TermInfo.Database.cs | 427 ++++++++++++++++++ .../System.Console/src/System/TermInfo.cs | 416 ----------------- 3 files changed, 428 insertions(+), 416 deletions(-) create mode 100644 src/libraries/System.Console/src/System/TermInfo.Database.cs diff --git a/src/libraries/System.Console/src/System.Console.csproj b/src/libraries/System.Console/src/System.Console.csproj index d3a3aceff1018..5ccab4f124ee6 100644 --- a/src/libraries/System.Console/src/System.Console.csproj +++ b/src/libraries/System.Console/src/System.Console.csproj @@ -162,6 +162,7 @@ Link="Common\System\Console\ConsoleUtils.cs" /> + 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 = 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; + } + } +} diff --git a/src/libraries/System.Console/src/System/TermInfo.cs b/src/libraries/System.Console/src/System/TermInfo.cs index 0c1b9dcfa3a1b..5d71cd92a4ed5 100644 --- a/src/libraries/System.Console/src/System/TermInfo.cs +++ b/src/libraries/System.Console/src/System/TermInfo.cs @@ -1,13 +1,9 @@ // 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 { @@ -21,418 +17,6 @@ internal enum WellKnownNumbers MaxColors = 13, } - /// 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 = 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 { From cf77107555871c2c51240c259f5a2c71ebaa9de6 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 28 Jun 2022 20:11:00 +0200 Subject: [PATCH 03/40] move factory logic to DatabaseFactory so the test project does not need to reference all sys-calls used for reading files --- .../System.Console/src/System.Console.csproj | 1 + .../src/System/ConsolePal.Unix.cs | 2 +- .../src/System/TermInfo.Database.cs | 119 +--------------- .../src/System/TermInfo.DatabaseFactory.cs | 128 ++++++++++++++++++ 4 files changed, 131 insertions(+), 119 deletions(-) create mode 100644 src/libraries/System.Console/src/System/TermInfo.DatabaseFactory.cs diff --git a/src/libraries/System.Console/src/System.Console.csproj b/src/libraries/System.Console/src/System.Console.csproj index 5ccab4f124ee6..20dc10d9ee1d4 100644 --- a/src/libraries/System.Console/src/System.Console.csproj +++ b/src/libraries/System.Console/src/System.Console.csproj @@ -163,6 +163,7 @@ + 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())); + private static readonly Lazy s_instance = new Lazy(() => new TerminalFormatStrings(TermInfo.DatabaseFactory.ReadActiveDatabase())); /// The format string to use to change the foreground color. public readonly string? Foreground; diff --git a/src/libraries/System.Console/src/System/TermInfo.Database.cs b/src/libraries/System.Console/src/System/TermInfo.Database.cs index 3bce5f599b437..de09ea072c3d8 100644 --- a/src/libraries/System.Console/src/System/TermInfo.Database.cs +++ b/src/libraries/System.Console/src/System/TermInfo.Database.cs @@ -4,10 +4,7 @@ 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; @@ -42,7 +39,7 @@ internal sealed class Database /// Initializes the database instance. /// The name of the terminal. /// The data from the terminfo file. - private Database(string term, byte[] data) + internal Database(string term, byte[] data) { _term = term; _data = data; @@ -84,120 +81,6 @@ private Database(string term, byte[] data) /// 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 = 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 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..5f4f8222cead9 --- /dev/null +++ b/src/libraries/System.Console/src/System/TermInfo.DatabaseFactory.cs @@ -0,0 +1,128 @@ +// 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 = 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); + } + } + } +} From c6f3a603c54885af6af8b8911241dbe795c1d3ec Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 28 Jun 2022 20:12:58 +0200 Subject: [PATCH 04/40] move WellKnownNumbers to separate file --- .../System.Console/src/System.Console.csproj | 1 + .../src/System/TermInfo.WellKnownNumbers.cs | 14 ++++++++++++++ .../System.Console/src/System/TermInfo.cs | 7 ------- 3 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 src/libraries/System.Console/src/System/TermInfo.WellKnownNumbers.cs diff --git a/src/libraries/System.Console/src/System.Console.csproj b/src/libraries/System.Console/src/System.Console.csproj index 20dc10d9ee1d4..03f7908b731a2 100644 --- a/src/libraries/System.Console/src/System.Console.csproj +++ b/src/libraries/System.Console/src/System.Console.csproj @@ -164,6 +164,7 @@ + Provides access to and processing of a terminfo database. internal static partial class TermInfo { - internal enum WellKnownNumbers - { - Columns = 0, - Lines = 2, - MaxColors = 13, - } - /// Provides support for evaluating parameterized terminfo database format strings. internal static class ParameterizedStrings { From acd4f24fd54274d4f15c9448c2b0182dbf6b835b Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 28 Jun 2022 20:26:41 +0200 Subject: [PATCH 05/40] include Database in the test project --- src/libraries/System.Console/tests/System.Console.Tests.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libraries/System.Console/tests/System.Console.Tests.csproj b/src/libraries/System.Console/tests/System.Console.Tests.csproj index 1d32fe2415f6c..3629617e9cfa5 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 @@ -50,5 +51,7 @@ + + From f5e3641aef55be47d3db322b991d3d18c9404e2e Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 28 Jun 2022 20:32:02 +0200 Subject: [PATCH 06/40] move TerminalFormatStrings to a separate file --- .../System.Console/src/System.Console.csproj | 1 + .../ConsolePal.TerminalFormatStrings.cs | 256 ++++++++++++++++++ .../src/System/ConsolePal.Unix.cs | 248 +---------------- 3 files changed, 258 insertions(+), 247 deletions(-) create mode 100644 src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs diff --git a/src/libraries/System.Console/src/System.Console.csproj b/src/libraries/System.Console/src/System.Console.csproj index 03f7908b731a2..991003fd6011d 100644 --- a/src/libraries/System.Console/src/System.Console.csproj +++ b/src/libraries/System.Console/src/System.Console.csproj @@ -158,6 +158,7 @@ + diff --git a/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs b/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs new file mode 100644 index 0000000000000..94851c23e0526 --- /dev/null +++ b/src/libraries/System.Console/src/System/ConsolePal.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; + +internal static partial class ConsolePal +{ + /// 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.DatabaseFactory.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); + } + } + + 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/ConsolePal.Unix.cs b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs index c411e73bf3d8c..38023687af573 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs @@ -18,7 +18,7 @@ namespace System // the test infrastructure that prevent OS-specific builds of test binaries. If you // change any of the class / struct / function names, parameters, etc then you need // to also change the test class. - internal static class ConsolePal + internal static partial class ConsolePal { // StdInReader is only used when input isn't redirected and we're working // with an interactive terminal. In that case, performance isn't critical @@ -956,243 +956,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.DatabaseFactory.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 +1191,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); - } } } From d3cdb93d7e4133151fbcb91679b4eae5f0ba9d04 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 28 Jun 2022 20:42:40 +0200 Subject: [PATCH 07/40] include TerminalFormatStrings in test project --- .../ConsolePal.TerminalFormatStrings.cs | 4 --- .../src/System/ConsolePal.Unix.cs | 34 +++++++++++-------- .../src/System/IO/StdInReader.cs | 4 +-- .../tests/System.Console.Tests.csproj | 1 + 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs b/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs index 94851c23e0526..43286323abf6a 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs @@ -11,10 +11,6 @@ internal static partial class ConsolePal /// 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.DatabaseFactory.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. diff --git a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs index 38023687af573..27c21b134d297 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs @@ -39,6 +39,10 @@ internal static partial class ConsolePal private static int s_windowHeight; // Cached WindowHeight, invalid when s_windowWidth == -1. private static int s_invalidateCachedSettings = 1; // Tracks whether we should invalidate the cached settings. + /// 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, @@ -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); } } } @@ -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,7 +835,7 @@ private static void WriteResetColorString() { if (ConsoleUtils.EmitAnsiColorCodes) { - WriteStdoutAnsiString(TerminalFormatStrings.Instance.Reset); + WriteStdoutAnsiString(TerminalFormatStringsInstance.Reset); } } @@ -856,17 +860,17 @@ public static bool TryGetSpecialConsoleKey(char[] givenChars, int startIndex, in } // Then process terminfo mappings. - int minRange = TerminalFormatStrings.Instance.MinKeyFormatLength; + int minRange = TerminalFormatStringsInstance.MinKeyFormatLength; if (unprocessedCharCount >= minRange) { - int maxRange = Math.Min(unprocessedCharCount, TerminalFormatStrings.Instance.MaxKeyFormatLength); + int maxRange = Math.Min(unprocessedCharCount, TerminalFormatStringsInstance.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)) + if (TerminalFormatStringsInstance.KeyFormatToConsoleKey.TryGetValue(currentString, out key)) { keyLength = currentString.Length; return true; @@ -921,7 +925,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); diff --git a/src/libraries/System.Console/src/System/IO/StdInReader.cs b/src/libraries/System.Console/src/System/IO/StdInReader.cs index f7e2810b69953..c82bdf00e3656 100644 --- a/src/libraries/System.Console/src/System/IO/StdInReader.cs +++ b/src/libraries/System.Console/src/System/IO/StdInReader.cs @@ -207,7 +207,7 @@ private bool ReadLineCore(bool consumeKeys) { if (s_clearToEol == null) { - s_clearToEol = ConsolePal.TerminalFormatStrings.Instance.ClrEol ?? string.Empty; + s_clearToEol = ConsolePal.TerminalFormatStringsInstance.ClrEol ?? string.Empty; } // Move to end of previous line @@ -219,7 +219,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; } diff --git a/src/libraries/System.Console/tests/System.Console.Tests.csproj b/src/libraries/System.Console/tests/System.Console.Tests.csproj index 3629617e9cfa5..894e72bc3299b 100644 --- a/src/libraries/System.Console/tests/System.Console.Tests.csproj +++ b/src/libraries/System.Console/tests/System.Console.Tests.csproj @@ -53,5 +53,6 @@ + From 7ea9f507702e1ccbac31569695cde62b46fff81a Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 28 Jun 2022 20:47:12 +0200 Subject: [PATCH 08/40] fix existing tests --- src/libraries/System.Console/tests/TermInfo.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Console/tests/TermInfo.cs b/src/libraries/System.Console/tests/TermInfo.cs index cf75cbd1b5406..c3b1201d8b527 100644 --- a/src/libraries/System.Console/tests/TermInfo.cs +++ b/src/libraries/System.Console/tests/TermInfo.cs @@ -12,7 +12,7 @@ public class TermInfo { // Names of internal members accessed via reflection private const string TerminfoType = "System.TermInfo"; - private const string TerminfoDatabaseType = TerminfoType + "+Database"; + private const string TerminfoDatabaseFactoryType = TerminfoType + "+DatabaseFactory"; private const string ParameterizedStringsType = TerminfoType + "+ParameterizedStrings"; private const string FormatParamType = ParameterizedStringsType + "+FormatParam"; private const string TerminalFormatStringsType = "System.ConsolePal+TerminalFormatStrings"; @@ -30,7 +30,7 @@ public void VerifyInstalledTermInfosParse() { bool foundAtLeastOne = false; - string[] locations = GetFieldValueOnObject(TerminfoLocationsField, null, typeof(Console).GetTypeInfo().Assembly.GetType(TerminfoDatabaseType)); + string[] locations = GetFieldValueOnObject(TerminfoLocationsField, null, typeof(Console).GetTypeInfo().Assembly.GetType(TerminfoDatabaseFactoryType)); foreach (string location in locations) { if (!Directory.Exists(location)) @@ -62,7 +62,7 @@ public void VerifyInstalledTermInfosParse() [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests TermInfo public void VerifyTermInfoSupportsNewAndLegacyNcurses() { - MethodInfo readDbMethod = typeof(Console).GetTypeInfo().Assembly.GetType(TerminfoDatabaseType).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 } @@ -121,7 +121,7 @@ public void TryingToLoadTermThatDoesNotExistDoesNotThrow() private object ReadTermInfoDatabase(string term) { - MethodInfo readDbMethod = typeof(Console).GetTypeInfo().Assembly.GetType(TerminfoDatabaseType).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 }); } From dc0af6b9059b739a634e439e2db72f72e1877b82 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 29 Jun 2022 17:05:42 +0200 Subject: [PATCH 09/40] move the key mapping logic to a separate class, do NOT change it --- .../System.Console/src/System.Console.csproj | 1 + .../src/System/ConsolePal.Unix.cs | 44 +---- .../System.Console/src/System/IO/KeyMapper.cs | 167 ++++++++++++++++++ .../src/System/IO/StdInReader.cs | 122 +------------ .../tests/System.Console.Tests.csproj | 1 + 5 files changed, 172 insertions(+), 163 deletions(-) create mode 100644 src/libraries/System.Console/src/System/IO/KeyMapper.cs diff --git a/src/libraries/System.Console/src/System.Console.csproj b/src/libraries/System.Console/src/System.Console.csproj index 991003fd6011d..9a6482bce9374 100644 --- a/src/libraries/System.Console/src/System.Console.csproj +++ b/src/libraries/System.Console/src/System.Console.csproj @@ -168,6 +168,7 @@ + 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 = TerminalFormatStringsInstance.MinKeyFormatLength; - if (unprocessedCharCount >= minRange) - { - int maxRange = Math.Min(unprocessedCharCount, TerminalFormatStringsInstance.MaxKeyFormatLength); - - for (int i = maxRange; i >= minRange; i--) - { - var currentString = new ReadOnlyMemory(givenChars, startIndex, i); - - // Check if the string prefix matches. - if (TerminalFormatStringsInstance.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. diff --git a/src/libraries/System.Console/src/System/IO/KeyMapper.cs b/src/libraries/System.Console/src/System/IO/KeyMapper.cs new file mode 100644 index 0000000000000..6f50a6a8120a3 --- /dev/null +++ b/src/libraries/System.Console/src/System/IO/KeyMapper.cs @@ -0,0 +1,167 @@ +// 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 KeyMapper +{ + internal static bool MapBufferToConsoleKey(char[] buffer, ConsolePal.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, + ConsolePal.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 c82bdf00e3656..0cca6643b007d 100644 --- a/src/libraries/System.Console/src/System/IO/StdInReader.cs +++ b/src/libraries/System.Console/src/System/IO/StdInReader.cs @@ -304,125 +304,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. /// @@ -479,7 +360,8 @@ private unsafe ConsoleKeyInfo ReadKey() } } - MapBufferToConsoleKey(out key, out ch, out isShift, out isAlt, out isCtrl); + KeyMapper.MapBufferToConsoleKey(_unprocessedBufferToBeRead, ConsolePal.TerminalFormatStringsInstance, ConsolePal.s_posixDisableValue, ConsolePal.s_veraseCharacter, + out key, out ch, out isShift, out isAlt, out isCtrl, ref _startIndex, _endIndex); // Replace the '\n' char for Enter by '\r' to match Windows behavior. if (key == ConsoleKey.Enter && ch == '\n') diff --git a/src/libraries/System.Console/tests/System.Console.Tests.csproj b/src/libraries/System.Console/tests/System.Console.Tests.csproj index 894e72bc3299b..9ca7b772dde7c 100644 --- a/src/libraries/System.Console/tests/System.Console.Tests.csproj +++ b/src/libraries/System.Console/tests/System.Console.Tests.csproj @@ -54,5 +54,6 @@ + From c927091973aa7c5f29511455543ee089e188874a Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 29 Jun 2022 17:56:46 +0200 Subject: [PATCH 10/40] simple tests for ASCII characters --- .../tests/KeyMapperTests.Unix.cs | 97 +++++++++++++++++++ .../tests/System.Console.Tests.csproj | 1 + 2 files changed, 98 insertions(+) create mode 100644 src/libraries/System.Console/tests/KeyMapperTests.Unix.cs diff --git a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs new file mode 100644 index 0000000000000..990125e49f46b --- /dev/null +++ b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs @@ -0,0 +1,97 @@ +// 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.Text; +using Xunit; + +namespace System.Tests; + +public class KeyMapperTests +{ + private static readonly ConsolePal.TerminalFormatStrings db_xterm_256color = new(new TermInfo.Database("xterm-256color", Convert.FromBase64String("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 + + private static IEnumerable TerminalFormatStrings + { + get + { + yield return db_xterm_256color; + } + } + + private static IEnumerable Encodings + { + get + { + yield return Encoding.UTF8; + yield return Encoding.ASCII; + } + } + + private static IEnumerable<(char, ConsoleKey)> AsciiKeys + { + get + { + yield return (' ', ConsoleKey.Spacebar); + yield return ('\t', ConsoleKey.Tab); + yield return ('\r', ConsoleKey.Enter); + yield return ('\n', ConsoleKey.Enter); + + yield return ('+', ConsoleKey.Add); + yield return ('-', ConsoleKey.Subtract); + yield return ('*', ConsoleKey.Multiply); + yield return ('/', ConsoleKey.Divide); + + yield return ('\b', ConsoleKey.Backspace); + yield return ((char)(0x7F), ConsoleKey.Delete); + yield return ((char)(0x1B), ConsoleKey.Escape); + + for (int i = 0; i <= 9; i++) + { + yield return ((char)(48 + i), ConsoleKey.D0 + i); + } + for (int i = 0; i <= 'z' - 'a'; i++) + { + yield return ((char)(97 + i), ConsoleKey.A + i); + } + for (int i = 0; i <= 'Z' - 'A'; i++) + { + yield return ((char)(65 + i), ConsoleKey.A + i); + } + } + } + + public static IEnumerable AsciiCharactersArguments + { + get + { + foreach ((char ch, ConsoleKey key) in AsciiKeys) + { + yield return new object[] { ch, key }; + } + } + } + + [Theory] + [MemberData(nameof(AsciiCharactersArguments))] + public void AsciiCharacters(char input, ConsoleKey expectedKey) + { + ConsoleKeyInfo consoleKeyInfo = Map(new[] { input }, db_xterm_256color); + + Assert.Equal(input, consoleKeyInfo.KeyChar); + Assert.Equal(expectedKey, consoleKeyInfo.Key); + Assert.Equal((ConsoleModifiers)0, consoleKeyInfo.Modifiers); + } + + private static ConsoleKeyInfo Map(char[] chars, ConsolePal.TerminalFormatStrings terminalFormatStrings) + { + int startIndex = 0; + + Assert.True(KeyMapper.MapBufferToConsoleKey(chars, terminalFormatStrings, Byte.MinValue, Byte.MaxValue, + out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length)); + Assert.True(startIndex > 0); + + return new ConsoleKeyInfo(ch, consoleKey, isShift, isAlt, isCtrl); + } +} diff --git a/src/libraries/System.Console/tests/System.Console.Tests.csproj b/src/libraries/System.Console/tests/System.Console.Tests.csproj index 9ca7b772dde7c..95a268b721830 100644 --- a/src/libraries/System.Console/tests/System.Console.Tests.csproj +++ b/src/libraries/System.Console/tests/System.Console.Tests.csproj @@ -46,6 +46,7 @@ + Date: Tue, 5 Jul 2022 15:39:06 +0200 Subject: [PATCH 11/40] test cases for xterm and xterm-256color --- .../tests/KeyMapperTests.Unix.cs | 207 ++++++++++++++++-- .../tests/System.Console.Tests.csproj | 1 + 2 files changed, 188 insertions(+), 20 deletions(-) diff --git a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs index 990125e49f46b..cfa5e70241ffc 100644 --- a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs +++ b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs @@ -10,23 +10,46 @@ namespace System.Tests; public class KeyMapperTests { - private static readonly ConsolePal.TerminalFormatStrings db_xterm_256color = new(new TermInfo.Database("xterm-256color", Convert.FromBase64String("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 - - private static IEnumerable TerminalFormatStrings + public static IEnumerable RecordedScenarios { get { - yield return db_xterm_256color; + foreach (TerminalData terminalData in new TerminalData[] + { + new XtermData(), + new Xterm256ColorData() + }) + { + foreach ((byte[] bytes, ConsoleKeyInfo cki) in terminalData.RecordedScenarios) + { + yield return new object[] { terminalData, bytes, cki }; + } + } } } - private static IEnumerable Encodings + [Theory] + [MemberData(nameof(RecordedScenarios))] + public void KeysAreProperlyMapped(TerminalData terminalData, byte[] recordedBytes, ConsoleKeyInfo expected) { - get - { - yield return Encoding.UTF8; - yield return Encoding.ASCII; - } + char[] encoded = terminalData.ConsoleEncoding.GetString(recordedBytes).ToCharArray(); + + ConsoleKeyInfo actual = Map(encoded, terminalData.TerminalDb, terminalData.Verase); + + Assert.Equal(expected.Key, actual.Key); + Assert.Equal(expected.Modifiers, actual.Modifiers); + Assert.Equal(expected.KeyChar, actual.KeyChar); + } + + private static ConsoleKeyInfo Map(char[] chars, ConsolePal.TerminalFormatStrings terminalFormatStrings, byte verase) + { + int startIndex = 0; + + Assert.True(KeyMapper.MapBufferToConsoleKey(chars, terminalFormatStrings, 0, verase, + out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length)); + Assert.True(startIndex > 0); + + return new ConsoleKeyInfo(ch, consoleKey, isShift, isAlt, isCtrl); } private static IEnumerable<(char, ConsoleKey)> AsciiKeys @@ -66,32 +89,176 @@ public static IEnumerable AsciiCharactersArguments { get { - foreach ((char ch, ConsoleKey key) in AsciiKeys) + foreach (TerminalData terminalData in new TerminalData[] { new XtermData(), new Xterm256ColorData() }) { - yield return new object[] { ch, key }; + foreach ((char ch, ConsoleKey key) in AsciiKeys) + { + yield return new object[] { terminalData, ch, key }; + } } } } [Theory] [MemberData(nameof(AsciiCharactersArguments))] - public void AsciiCharacters(char input, ConsoleKey expectedKey) + public void AsciiCharacters(TerminalData terminalData, char input, ConsoleKey expectedKey) { - ConsoleKeyInfo consoleKeyInfo = Map(new[] { input }, db_xterm_256color); + ConsoleKeyInfo consoleKeyInfo = Map(new[] { input }, terminalData.TerminalDb, terminalData.Verase); Assert.Equal(input, consoleKeyInfo.KeyChar); Assert.Equal(expectedKey, consoleKeyInfo.Key); Assert.Equal((ConsoleModifiers)0, consoleKeyInfo.Modifiers); } +} + +public abstract class TerminalData +{ + private ConsolePal.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; } - private static ConsoleKeyInfo Map(char[] chars, ConsolePal.TerminalFormatStrings terminalFormatStrings) + internal ConsolePal.TerminalFormatStrings TerminalDb => _terminalDb ??= + new ConsolePal.TerminalFormatStrings(new TermInfo.Database(Term, Convert.FromBase64String(EncodedTerminalDb))); + + internal Encoding ConsoleEncoding => _consoleEncoding ??= Encoding.GetEncoding(EncodingCharset).RemovePreamble(); +} + +public class Xterm256ColorData : 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 { - int startIndex = 0; + get + { + yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, true, false, false)); + yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, false, false, false)); + yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); + yield return (new byte[] { 1 }, new ConsoleKeyInfo('a', 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('a', ConsoleKey.A, false, true, true)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo(default, ConsoleKey.D1, false, false, true)); + yield return (new byte[] { 27, 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, true, false)); + yield return (new byte[] { 33 }, new ConsoleKeyInfo('!', ConsoleKey.D1, true, 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('@', ConsoleKey.D2, true, false, false)); + yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.OemPlus, true, false, false)); + yield return (new byte[] { 61 }, new ConsoleKeyInfo(default, ConsoleKey.OemPlus, false, false, true)); + yield return (new byte[] { 27, 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, true, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, false, false, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, true, false, false)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, false)); + yield return (new byte[] { 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, true)); + yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); + yield return (new byte[] { 27, 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, true)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, true, false, false)); + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); + yield return (new byte[] { 27, 91, 50, 52, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); + yield return (new byte[] { 27, 91, 50, 52, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); + yield return (new byte[] { 27, 79, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); + yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); + yield return (new byte[] { 27, 91, 49, 59, 51, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); + yield return (new byte[] { 27, 91, 49, 59, 55, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); + yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); + yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, true)); + yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, true, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, false, false)); + yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, true, true)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.NumPad1, false, false, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo(default, ConsoleKey.NumPad1, false, false, true)); + 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, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); + } + } +} - Assert.True(KeyMapper.MapBufferToConsoleKey(chars, terminalFormatStrings, Byte.MinValue, Byte.MaxValue, - out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length)); - Assert.True(startIndex > 0); +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 - return new ConsoleKeyInfo(ch, consoleKey, isShift, isAlt, isCtrl); + internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios + { + get + { + yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, true, false, false)); + yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, false, false, false)); + yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); + yield return (new byte[] { 1 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, true)); + yield return (new byte[] { 195, 161 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, true, false)); + yield return (new byte[] { 194, 129 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, true, true)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo(default, ConsoleKey.D1, false, false, true)); + yield return (new byte[] { 194, 177 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, true, false)); + yield return (new byte[] { 33 }, new ConsoleKeyInfo('!', ConsoleKey.D1, true, 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('2', ConsoleKey.D2, false, true, false)); + yield return (new byte[] { 64 }, new ConsoleKeyInfo('@', ConsoleKey.D2, true, false, false)); + yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.OemPlus, true, false, false)); + yield return (new byte[] { 61 }, new ConsoleKeyInfo(default, ConsoleKey.OemPlus, false, false, true)); + yield return (new byte[] { 194, 189 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, true, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, false, false, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, true, false, false)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, false)); + yield return (new byte[] { 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, true)); + yield return (new byte[] { 195, 191 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); + yield return (new byte[] { 194, 136 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, true)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, true, false, false)); + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); + yield return (new byte[] { 27, 91, 50, 52, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); + yield return (new byte[] { 27, 91, 50, 52, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); + yield return (new byte[] { 27, 79, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); + yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); + yield return (new byte[] { 27, 91, 49, 59, 51, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); + yield return (new byte[] { 27, 91, 49, 59, 55, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); + yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); + yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, true)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, true, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, false, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, true, true)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.NumPad1, false, false, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo(default, ConsoleKey.NumPad1, false, false, true)); + 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, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); + } } } + + + diff --git a/src/libraries/System.Console/tests/System.Console.Tests.csproj b/src/libraries/System.Console/tests/System.Console.Tests.csproj index 95a268b721830..002d584996f60 100644 --- a/src/libraries/System.Console/tests/System.Console.Tests.csproj +++ b/src/libraries/System.Console/tests/System.Console.Tests.csproj @@ -56,5 +56,6 @@ + From 5ffdd67a0f670484ba548bcada62daa5d364272a Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 5 Jul 2022 18:51:08 +0200 Subject: [PATCH 12/40] add PuTTY and UXTern --- .../tests/KeyMapperTests.Unix.cs | 134 ++++++++++++++++-- 1 file changed, 126 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs index cfa5e70241ffc..97e37228b9279 100644 --- a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs +++ b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs @@ -10,15 +10,13 @@ namespace System.Tests; public class KeyMapperTests { + private static readonly TerminalData[] Terminals = { new XTermData(), new GNOMETerminalData(), new UXTermData(), new PuTTYData() }; + public static IEnumerable RecordedScenarios { get { - foreach (TerminalData terminalData in new TerminalData[] - { - new XtermData(), - new Xterm256ColorData() - }) + foreach (TerminalData terminalData in Terminals) { foreach ((byte[] bytes, ConsoleKeyInfo cki) in terminalData.RecordedScenarios) { @@ -89,7 +87,7 @@ public static IEnumerable AsciiCharactersArguments { get { - foreach (TerminalData terminalData in new TerminalData[] { new XtermData(), new Xterm256ColorData() }) + foreach (TerminalData terminalData in Terminals) { foreach ((char ch, ConsoleKey key) in AsciiKeys) { @@ -128,7 +126,7 @@ public abstract class TerminalData internal Encoding ConsoleEncoding => _consoleEncoding ??= Encoding.GetEncoding(EncodingCharset).RemovePreamble(); } -public class Xterm256ColorData : TerminalData +public class GNOMETerminalData : TerminalData { protected override string EncodingCharset => "utf-8"; protected override string Term => "xterm-256color"; @@ -194,7 +192,7 @@ public class Xterm256ColorData : TerminalData } } -public class XtermData : TerminalData +public class XTermData : TerminalData { protected override string EncodingCharset => "utf-8"; protected override string Term => "xterm"; @@ -260,5 +258,125 @@ public class XtermData : TerminalData } } +public class UXTermData : 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[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, false, false, false)); + yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); + yield return (new byte[] { 1 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, true)); + yield return (new byte[] { 195, 161 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, true, false)); + yield return (new byte[] { 194, 129 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, true, true)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo(default, ConsoleKey.D1, false, false, true)); + yield return (new byte[] { 194, 177 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, true, false)); + yield return (new byte[] { 33 }, new ConsoleKeyInfo('!', ConsoleKey.D1, true, 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('2', ConsoleKey.D2, false, true, false)); + yield return (new byte[] { 64 }, new ConsoleKeyInfo('@', ConsoleKey.D2, true, false, false)); + yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.OemPlus, true, false, false)); + yield return (new byte[] { 61 }, new ConsoleKeyInfo(default, ConsoleKey.OemPlus, false, false, true)); + yield return (new byte[] { 194, 189 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, true, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, false, false, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, true, false, false)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, false)); + yield return (new byte[] { 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, true)); + yield return (new byte[] { 195, 191 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); + yield return (new byte[] { 194, 136 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, true)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, true, false, false)); + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); + yield return (new byte[] { 27, 91, 50, 52, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); + yield return (new byte[] { 27, 91, 50, 52, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); + yield return (new byte[] { 27, 79, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); + yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); + yield return (new byte[] { 27, 91, 49, 59, 51, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); + yield return (new byte[] { 27, 91, 49, 59, 55, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); + yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); + yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, true)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, true, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, false, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, true, true)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.NumPad1, false, false, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo(default, ConsoleKey.NumPad1, false, false, true)); + 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, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); + } + } +} + +public class PuTTYData : 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 + { + get + { + yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, true, false, false)); + yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, false, false, false)); + yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); + yield return (new byte[] { 1 }, new ConsoleKeyInfo('a', 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('a', 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('!', ConsoleKey.D1, true, 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('@', ConsoleKey.D2, true, false, false)); + yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.OemPlus, true, false, false)); + yield return (new byte[] { 27, 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, true, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, false, false, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, true, false, false)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, false)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, true)); + yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); + yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, true)); + yield return (new byte[] { 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, true, false, false)); + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); + yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); + yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); + yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); + yield return (new byte[] { 27, 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); + yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); + yield return (new byte[] { 27, 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, true)); + yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, true, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, false, false)); + yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, true, true)); + } + } +} From 9230c812ce0b00de11de7d466a5c4a921637f5fa Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 6 Jul 2022 16:49:41 +0200 Subject: [PATCH 13/40] add Windows Terminal data --- .../tests/KeyMapperTests.Unix.cs | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs index 97e37228b9279..9aeedcbcd0467 100644 --- a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs +++ b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs @@ -10,7 +10,14 @@ namespace System.Tests; public class KeyMapperTests { - private static readonly TerminalData[] Terminals = { new XTermData(), new GNOMETerminalData(), new UXTermData(), new PuTTYData() }; + private static readonly TerminalData[] Terminals = + { + new XTermData(), + new GNOMETerminalData(), + new UXTermData(), + new PuTTYData(), + new WindowsTerminalData() + }; public static IEnumerable RecordedScenarios { @@ -126,6 +133,7 @@ public abstract class TerminalData internal Encoding ConsoleEncoding => _consoleEncoding ??= Encoding.GetEncoding(EncodingCharset).RemovePreamble(); } +// Ubuntu 18.04 x64 public class GNOMETerminalData : TerminalData { protected override string EncodingCharset => "utf-8"; @@ -192,6 +200,7 @@ public class GNOMETerminalData : TerminalData } } +// Ubuntu 18.04 x64 public class XTermData : TerminalData { protected override string EncodingCharset => "utf-8"; @@ -258,6 +267,7 @@ public class XTermData : TerminalData } } +// Ubuntu 18.04 x64 public class UXTermData : TerminalData { protected override string EncodingCharset => "utf-8"; @@ -324,6 +334,7 @@ public class UXTermData : TerminalData } } +// Windows 11 machine connected via PuTTY to Ubuntu 20.04 arm64 machine public class PuTTYData : TerminalData { protected override string EncodingCharset => ""; @@ -379,4 +390,67 @@ public class PuTTYData : TerminalData } } +// 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[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, false, false, false)); + yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); + yield return (new byte[] { 1 }, new ConsoleKeyInfo('a', 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('a', ConsoleKey.A, false, true, true)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo(default, ConsoleKey.D1, false, false, true)); + yield return (new byte[] { 27, 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, true, false)); + yield return (new byte[] { 33 }, new ConsoleKeyInfo('!', ConsoleKey.D1, true, 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('@', ConsoleKey.D2, true, false, false)); + yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.OemPlus, true, false, false)); + yield return (new byte[] { 27, 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, true, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, false, false, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, true, false, false)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, false)); + yield return (new byte[] { 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, true)); + yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); + yield return (new byte[] { 27, 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, true)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, true, false, false)); + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); + yield return (new byte[] { 27, 91, 50, 52, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); + yield return (new byte[] { 27, 91, 50, 52, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); + yield return (new byte[] { 27, 79, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); + yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); + yield return (new byte[] { 27, 91, 49, 59, 51, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); + yield return (new byte[] { 27, 91, 49, 59, 55, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); + yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); + yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, true)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, false, false)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.NumPad1, false, false, false)); + yield return (new byte[] { 17 }, new ConsoleKeyInfo(default, ConsoleKey.NumPad1, false, false, true)); + 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, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); + } + } +} + From 41a92bfa41def6f0d5152f45672b947a5a37c9dd Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 6 Jul 2022 18:32:25 +0200 Subject: [PATCH 14/40] add more PuTTY test cases --- .../tests/KeyMapperTests.Unix.cs | 124 +++++++++++++++++- 1 file changed, 121 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs index 9aeedcbcd0467..c2e6c70501769 100644 --- a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs +++ b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs @@ -15,7 +15,9 @@ public class KeyMapperTests new XTermData(), new GNOMETerminalData(), new UXTermData(), - new PuTTYData(), + new PuTTYData_xterm(), + new PuTTYData_linux(), + new PuTTYData_putty(), new WindowsTerminalData() }; @@ -334,8 +336,8 @@ public class UXTermData : TerminalData } } -// Windows 11 machine connected via PuTTY to Ubuntu 20.04 arm64 machine -public class PuTTYData : TerminalData +// Windows 11 machine connected via PuTTY to Ubuntu 20.04 arm64 machine using default settings ("xterm" Terminal-type setting) +public class PuTTYData_xterm : TerminalData { protected override string EncodingCharset => ""; protected override string Term => "xterm"; @@ -390,6 +392,122 @@ public class PuTTYData : TerminalData } } +// Windows 11 machine connected via PuTTY to Ubuntu 20.04 arm64 machine using "linux" Terminal-type setting +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 + { + get + { + yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, true, false, false)); + yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, false, false, false)); + yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); + yield return (new byte[] { 1 }, new ConsoleKeyInfo('a', 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('a', 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('!', ConsoleKey.D1, true, 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('@', ConsoleKey.D2, true, false, false)); + yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.OemPlus, true, false, false)); + yield return (new byte[] { 27, 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, true, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, false, false, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, true, false, false)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, false)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, true)); + yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); + yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, true)); + yield return (new byte[] { 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, true, false, false)); + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); + yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); + yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); + yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); + yield return (new byte[] { 27, 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); + yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); + yield return (new byte[] { 27, 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, true)); + yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, true, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, false, false)); + yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, true, true)); + yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.NumPad1, 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, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); + } + } +} + +// Windows 11 machine connected via PuTTY to Ubuntu 20.04 arm64 machine using "putty" Terminal-type setting +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 + { + get + { + yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, true, false, false)); + yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, false, false, false)); + yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); + yield return (new byte[] { 27, 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, true, false)); + yield return (new byte[] { 27, 1 }, new ConsoleKeyInfo('a', 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('!', ConsoleKey.D1, true, 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('@', ConsoleKey.D2, true, false, false)); + yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, false, false)); + yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.OemPlus, true, false, false)); + yield return (new byte[] { 27, 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, true, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, false, false, false)); + yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, true, false, false)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, false)); + yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, true)); + yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); + yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, true)); + yield return (new byte[] { 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, true, false, false)); + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); + yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); + yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); + yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); + yield return (new byte[] { 27, 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); + yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); + yield return (new byte[] { 27, 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, true)); + yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, true, false)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, false, false)); + yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, true, true)); + } + } +} + // Windows (11) Terminal connected via SSH to Ubuntu 20.04 arm64 public class WindowsTerminalData : TerminalData { From af92ab3bb44fcf5e19844508a987700e911f3c9c Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 6 Jul 2022 18:41:59 +0200 Subject: [PATCH 15/40] handle empty encodings --- src/libraries/System.Console/tests/KeyMapperTests.Unix.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs index c2e6c70501769..82634ebcc424a 100644 --- a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs +++ b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs @@ -132,7 +132,7 @@ public abstract class TerminalData internal ConsolePal.TerminalFormatStrings TerminalDb => _terminalDb ??= new ConsolePal.TerminalFormatStrings(new TermInfo.Database(Term, Convert.FromBase64String(EncodedTerminalDb))); - internal Encoding ConsoleEncoding => _consoleEncoding ??= Encoding.GetEncoding(EncodingCharset).RemovePreamble(); + internal Encoding ConsoleEncoding => _consoleEncoding ??= (string.IsNullOrEmpty(EncodingCharset) ? Encoding.Default : Encoding.GetEncoding(EncodingCharset)).RemovePreamble(); } // Ubuntu 18.04 x64 From 9a2ed718a70676cf7482f6f60a43c67f1c34b494 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 7 Jul 2022 12:11:39 +0200 Subject: [PATCH 16/40] make it possible to run the tests on Windows so I can use my favourite dev env --- .../tests/System.Console.Tests.csproj | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Console/tests/System.Console.Tests.csproj b/src/libraries/System.Console/tests/System.Console.Tests.csproj index 002d584996f60..cbd9479baeba2 100644 --- a/src/libraries/System.Console/tests/System.Console.Tests.csproj +++ b/src/libraries/System.Console/tests/System.Console.Tests.csproj @@ -34,6 +34,13 @@ + + + + + + + - - - - - - - From 69f12c99b0bdba5081d41f8fe407afb8bbbbf762 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 7 Jul 2022 13:47:27 +0200 Subject: [PATCH 17/40] add mappings for single characters --- .../System.Console/src/System/IO/KeyMapper.cs | 103 ++++++++ .../tests/KeyMapperTests.Unix.cs | 219 +++++++----------- 2 files changed, 182 insertions(+), 140 deletions(-) diff --git a/src/libraries/System.Console/src/System/IO/KeyMapper.cs b/src/libraries/System.Console/src/System/IO/KeyMapper.cs index 6f50a6a8120a3..602325efa5daa 100644 --- a/src/libraries/System.Console/src/System/IO/KeyMapper.cs +++ b/src/libraries/System.Console/src/System/IO/KeyMapper.cs @@ -1,10 +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.Diagnostics; + namespace System.IO; internal static class KeyMapper { + private const char Escape = '\u001B'; + private const char Delete = '\u007F'; + internal static bool MapBufferToConsoleKey(char[] buffer, ConsolePal.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) { @@ -50,6 +55,104 @@ internal static class KeyMapper return key != default(ConsoleKey); } + internal static void MapNew(char[] buffer, ConsolePal.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) + { + if (endIndex - startIndex == 1) + { + DecodeFromSingleChar(buffer[startIndex], out key, out ch, out isShift, out isCtrl); + startIndex++; + isAlt = false; + } + else if (endIndex - startIndex == 2 && buffer[startIndex] == Escape) + { + DecodeFromSingleChar(buffer[++startIndex], out key, out ch, out isShift, out isCtrl); + startIndex++; + isAlt = key != default; // two char sequences starting with Escape are Alt+$Key + } + else + { + key = default; + ch = default; + isShift = isAlt = isCtrl = false; + } + } + + private static void DecodeFromSingleChar(char single, out ConsoleKey key, out char ch, out bool isShift, out bool isCtrl) + { + isShift = isCtrl = false; + ch = single; + + key = single switch + { + // '\b' is not mapped to ConsoleKey.Backspace on purpose, as it's simply wrong mapping + '\t' => ConsoleKey.Tab, + '\r' or '\n' => ConsoleKey.Enter, + ' ' => ConsoleKey.Spacebar, + Escape => ConsoleKey.Escape, // Ctrl+[ and Ctrl+3 are also mapped to 27, but Escape is more likely to be pressed. Limitation: Ctrl+[ and Ctrl+3 can't be mapped. + Delete => ConsoleKey.Backspace, // Ctrl+8 and Backspace are mapped to 127, but Backspace is more likely to be pressed. Limitation: Ctrl+8 can't be mapped. + '*' => ConsoleKey.Multiply, // We can't distinguish D8+Shift and Multiply (Numeric Keypad). Limitation: Shift+D8 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 ch, out isCtrl), + _ when char.IsBetween(single, (char)28, (char)31) => ControlAndDigitPressed(single, out ch, out isCtrl), + '\u0000' or Delete => ControlAndDigitPressed(single, out ch, out isCtrl), + _ => default + }; + + // above we map ASCII Delete character to Backspace key, we need to map the char too + if (key == ConsoleKey.Backspace) + { + ch = '\b'; + } + + 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 ch, 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+h is more likely to be pressed. (TODO: discuss with others) + // Ctrl+i is mapped to 9, which also maps to Tab. Tab (9) is more likely to be pressed. + // Ctrl+j is mapped to 10, which also maps to Enter ('\n') and Ctrl+Enter. Enter is more likely to be pressed. + // Ctrl+m is mapped to 13, which also maps to Enter ('\r'). Enter (13) is more likely to be pressed. + // Limitation: Ctrl+i, Ctrl+j, Crl+m, Ctrl+Backspace and Ctrl+Enter can't be mapped. More: https://unix.stackexchange.com/questions/563469/conflict-ctrl-i-with-tab-in-normal-mode + Debug.Assert(single != '\t' && single != '\n' && single != '\r'); + + isCtrl = true; + ch = 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 ch, out bool isCtrl) + { + // Ctrl+(D3-D7) characters are mapped to values from 27 to 31. Escape (27) is more likely to be pressed. + // 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; + ch = default; // consistent with Windows + return single switch + { + '\u0000' => ConsoleKey.D2, // This is what PuTTY does (was not previously mapped this way) + _ => ConsoleKey.D4 + single - 28 + }; + } + } + private static bool TryGetSpecialConsoleKey(char[] givenChars, int startIndex, int endIndex, ConsolePal.TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter, out ConsoleKeyInfo key, out int keyLength) diff --git a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs index 82634ebcc424a..706609a16e95d 100644 --- a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs +++ b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs @@ -52,8 +52,10 @@ private static ConsoleKeyInfo Map(char[] chars, ConsolePal.TerminalFormatStrings { int startIndex = 0; - Assert.True(KeyMapper.MapBufferToConsoleKey(chars, terminalFormatStrings, 0, verase, - out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length)); + KeyMapper.MapNew(chars, terminalFormatStrings, 0, verase, + out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length); + //Assert.True(KeyMapper.MapBufferToConsoleKey(chars, terminalFormatStrings, 0, verase, + // out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length)); Assert.True(startIndex > 0); return new ConsoleKeyInfo(ch, consoleKey, isShift, isAlt, isCtrl); @@ -73,21 +75,22 @@ private static ConsoleKeyInfo Map(char[] chars, ConsolePal.TerminalFormatStrings yield return ('*', ConsoleKey.Multiply); yield return ('/', ConsoleKey.Divide); - yield return ('\b', ConsoleKey.Backspace); - yield return ((char)(0x7F), ConsoleKey.Delete); - yield return ((char)(0x1B), ConsoleKey.Escape); + yield return ('.', ConsoleKey.OemPeriod); + yield return (',', ConsoleKey.OemComma); - for (int i = 0; i <= 9; i++) + yield return ('\u001B', ConsoleKey.Escape); + + for (char i = '0'; i <= '9'; i++) { - yield return ((char)(48 + i), ConsoleKey.D0 + i); + yield return (i, ConsoleKey.D0 + i - '0'); } - for (int i = 0; i <= 'z' - 'a'; i++) + for (char i = 'a'; i <= 'z'; i++) { - yield return ((char)(97 + i), ConsoleKey.A + i); + yield return (i, ConsoleKey.A + i - 'a'); } - for (int i = 0; i <= 'Z' - 'A'; i++) + for (char i = 'A'; i <= 'Z'; i++) { - yield return ((char)(65 + i), ConsoleKey.A + i); + yield return (i, ConsoleKey.A + i - 'A'); } } } @@ -114,7 +117,7 @@ public void AsciiCharacters(TerminalData terminalData, char input, ConsoleKey ex Assert.Equal(input, consoleKeyInfo.KeyChar); Assert.Equal(expectedKey, consoleKeyInfo.Key); - Assert.Equal((ConsoleModifiers)0, consoleKeyInfo.Modifiers); + Assert.Equal(char.IsAsciiLetterUpper(input) ? ConsoleModifiers.Shift : 0, consoleKeyInfo.Modifiers); } } @@ -148,30 +151,23 @@ public class GNOMETerminalData : TerminalData get { yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, true, false, false)); - yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, false, false, false)); yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); - yield return (new byte[] { 1 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, true)); + 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('a', ConsoleKey.A, false, true, true)); + 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[] { 49 }, new ConsoleKeyInfo(default, ConsoleKey.D1, false, false, true)); yield return (new byte[] { 27, 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, true, false)); - yield return (new byte[] { 33 }, new ConsoleKeyInfo('!', ConsoleKey.D1, true, 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[] { 27, 50 }, new ConsoleKeyInfo('2', ConsoleKey.D2, false, true, false)); - yield return (new byte[] { 64 }, new ConsoleKeyInfo('@', ConsoleKey.D2, true, false, false)); - yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, false, false)); - yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.OemPlus, true, false, false)); - yield return (new byte[] { 61 }, new ConsoleKeyInfo(default, ConsoleKey.OemPlus, false, false, true)); - yield return (new byte[] { 27, 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, 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[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, true, false, false)); yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, false)); - yield return (new byte[] { 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, true)); yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); - yield return (new byte[] { 27, 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, true)); - yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, true, false, false)); yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); @@ -186,13 +182,10 @@ public class GNOMETerminalData : TerminalData yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, true)); - yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, false, false)); - yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, true, true)); - yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.NumPad1, false, false, false)); - yield return (new byte[] { 49 }, new ConsoleKeyInfo(default, ConsoleKey.NumPad1, false, false, true)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)10, ConsoleKey.Enter, false, false, false)); + yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); + yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)10, ConsoleKey.Enter, 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)); @@ -215,30 +208,24 @@ public class XTermData : TerminalData get { yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, true, false, false)); - yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, false, false, false)); yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); - yield return (new byte[] { 1 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, true)); + yield return (new byte[] { 1 }, new ConsoleKeyInfo(default, ConsoleKey.A, false, false, true)); yield return (new byte[] { 195, 161 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, true, false)); yield return (new byte[] { 194, 129 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, true, true)); yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); - yield return (new byte[] { 49 }, new ConsoleKeyInfo(default, ConsoleKey.D1, false, false, true)); yield return (new byte[] { 194, 177 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, true, false)); - yield return (new byte[] { 33 }, new ConsoleKeyInfo('!', ConsoleKey.D1, true, 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('2', ConsoleKey.D2, false, true, false)); - yield return (new byte[] { 64 }, new ConsoleKeyInfo('@', ConsoleKey.D2, true, false, false)); - yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, false, false)); - yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.OemPlus, true, false, false)); - yield return (new byte[] { 61 }, new ConsoleKeyInfo(default, ConsoleKey.OemPlus, false, false, true)); - yield return (new byte[] { 194, 189 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, 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[] { 194, 189 }, new ConsoleKeyInfo('=', default, false, false, false)); yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, false, false, false)); - yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, true, false, false)); yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, false)); - yield return (new byte[] { 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, true)); yield return (new byte[] { 195, 191 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); yield return (new byte[] { 194, 136 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, true)); - yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, true, false, false)); yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); @@ -253,13 +240,8 @@ public class XTermData : TerminalData yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, true)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, false, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, true, true)); - yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.NumPad1, false, false, false)); - yield return (new byte[] { 49 }, new ConsoleKeyInfo(default, ConsoleKey.NumPad1, false, false, true)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)10, ConsoleKey.Enter, 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, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); @@ -282,30 +264,24 @@ public class UXTermData : TerminalData get { yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, true, false, false)); - yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, false, false, false)); yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); - yield return (new byte[] { 1 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, true)); + yield return (new byte[] { 1 }, new ConsoleKeyInfo(default, ConsoleKey.A, false, false, true)); yield return (new byte[] { 195, 161 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, true, false)); yield return (new byte[] { 194, 129 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, true, true)); yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false)); - yield return (new byte[] { 49 }, new ConsoleKeyInfo(default, ConsoleKey.D1, false, false, true)); yield return (new byte[] { 194, 177 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, true, false)); - yield return (new byte[] { 33 }, new ConsoleKeyInfo('!', ConsoleKey.D1, true, 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('2', ConsoleKey.D2, false, true, false)); - yield return (new byte[] { 64 }, new ConsoleKeyInfo('@', ConsoleKey.D2, true, false, false)); - yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, false, false)); - yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.OemPlus, true, false, false)); - yield return (new byte[] { 61 }, new ConsoleKeyInfo(default, ConsoleKey.OemPlus, false, false, true)); - yield return (new byte[] { 194, 189 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, 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[] { 194, 189 }, new ConsoleKeyInfo('=', default, false, false, false)); yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, false, false, false)); - yield return (new byte[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, true, false, false)); yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, false)); - yield return (new byte[] { 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, true)); yield return (new byte[] { 195, 191 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); yield return (new byte[] { 194, 136 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, true)); - yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, true, false, false)); yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); @@ -320,13 +296,8 @@ public class UXTermData : TerminalData yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, true)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, false, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, true, true)); - yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.NumPad1, false, false, false)); - yield return (new byte[] { 49 }, new ConsoleKeyInfo(default, ConsoleKey.NumPad1, false, false, true)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)10, ConsoleKey.Enter, 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, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); @@ -349,28 +320,23 @@ public class PuTTYData_xterm : TerminalData get { yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, true, false, false)); - yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, false, false, false)); yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); - yield return (new byte[] { 1 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, true)); + 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('a', ConsoleKey.A, false, true, true)); + 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('!', ConsoleKey.D1, true, 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[] { 27, 50 }, new ConsoleKeyInfo('2', ConsoleKey.D2, false, true, false)); - yield return (new byte[] { 64 }, new ConsoleKeyInfo('@', ConsoleKey.D2, true, false, false)); - yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, false, false)); - yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.OemPlus, true, false, false)); - yield return (new byte[] { 27, 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, 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[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, true, false, false)); yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, false)); - yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, true)); yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); - yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, true)); - yield return (new byte[] { 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, true, false, false)); yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); @@ -383,11 +349,10 @@ public class PuTTYData_xterm : TerminalData yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); yield return (new byte[] { 27, 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, true)); - yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, false, false)); - yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, true, true)); + yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)10, ConsoleKey.Enter, false, false, false)); + yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); + yield return (new byte[] { 27, 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, true, false)); + yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)10, ConsoleKey.Enter, false, true, false)); } } } @@ -405,28 +370,23 @@ public class PuTTYData_linux : TerminalData get { yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, true, false, false)); - yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, false, false, false)); yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); - yield return (new byte[] { 1 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, true)); + 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('a', ConsoleKey.A, false, true, true)); + 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('!', ConsoleKey.D1, true, 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[] { 27, 50 }, new ConsoleKeyInfo('2', ConsoleKey.D2, false, true, false)); - yield return (new byte[] { 64 }, new ConsoleKeyInfo('@', ConsoleKey.D2, true, false, false)); - yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, false, false)); - yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.OemPlus, true, false, false)); - yield return (new byte[] { 27, 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, 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[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, true, false, false)); yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, false)); - yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, true)); yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); - yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, true)); - yield return (new byte[] { 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, true, false, false)); yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); @@ -439,12 +399,9 @@ public class PuTTYData_linux : TerminalData yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); yield return (new byte[] { 27, 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, true)); - yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, false, false)); - yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, true, true)); - yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.NumPad1, false, false, false)); + yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); + yield return (new byte[] { 27, 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, 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, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); @@ -466,27 +423,22 @@ public class PuTTYData_putty : TerminalData get { yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, true, false, false)); - yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, false, false, false)); yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); yield return (new byte[] { 27, 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, true, false)); - yield return (new byte[] { 27, 1 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, true, true)); + 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('!', ConsoleKey.D1, true, 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[] { 27, 50 }, new ConsoleKeyInfo('2', ConsoleKey.D2, false, true, false)); - yield return (new byte[] { 64 }, new ConsoleKeyInfo('@', ConsoleKey.D2, true, false, false)); - yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, false, false)); - yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.OemPlus, true, false, false)); - yield return (new byte[] { 27, 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, 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[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, true, false, false)); yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, false)); - yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, true)); yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); - yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, true)); - yield return (new byte[] { 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, true, false, false)); yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); @@ -499,11 +451,7 @@ public class PuTTYData_putty : TerminalData yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); yield return (new byte[] { 27, 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, true)); - yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, false, false)); - yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, true, true)); + yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); } } } @@ -521,28 +469,22 @@ public class WindowsTerminalData : TerminalData get { yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, true, false, false)); - yield return (new byte[] { 90 }, new ConsoleKeyInfo('Z', ConsoleKey.Z, false, false, false)); yield return (new byte[] { 97 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false)); - yield return (new byte[] { 1 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, false, true)); + 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('a', ConsoleKey.A, false, true, true)); + 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[] { 49 }, new ConsoleKeyInfo(default, ConsoleKey.D1, false, false, true)); yield return (new byte[] { 27, 49 }, new ConsoleKeyInfo('1', ConsoleKey.D1, false, true, false)); - yield return (new byte[] { 33 }, new ConsoleKeyInfo('!', ConsoleKey.D1, true, 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[] { 27, 50 }, new ConsoleKeyInfo('2', ConsoleKey.D2, false, true, false)); - yield return (new byte[] { 64 }, new ConsoleKeyInfo('@', ConsoleKey.D2, true, false, false)); - yield return (new byte[] { 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, false, false)); - yield return (new byte[] { 43 }, new ConsoleKeyInfo('+', ConsoleKey.OemPlus, true, false, false)); - yield return (new byte[] { 27, 61 }, new ConsoleKeyInfo('=', ConsoleKey.OemPlus, 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[] { 27 }, new ConsoleKeyInfo((char)27, ConsoleKey.Escape, true, false, false)); yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, false)); - yield return (new byte[] { 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, true)); yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); - yield return (new byte[] { 27, 8 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, true)); - yield return (new byte[] { 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, true, false, false)); yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); @@ -557,11 +499,8 @@ public class WindowsTerminalData : TerminalData yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, true)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, false, false)); - yield return (new byte[] { 49 }, new ConsoleKeyInfo('1', ConsoleKey.NumPad1, false, false, false)); - yield return (new byte[] { 17 }, new ConsoleKeyInfo(default, ConsoleKey.NumPad1, false, false, true)); + yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, 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, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); From 5bbac1d7998b2ae5c732f44bf0ebdaba6b4608e3 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 8 Jul 2022 17:41:29 +0200 Subject: [PATCH 18/40] add mappings for 3 characters --- .../System.Console/src/System/IO/KeyMapper.cs | 80 +++++++++++++++++-- .../tests/KeyMapperTests.Unix.cs | 51 +++++++++--- 2 files changed, 111 insertions(+), 20 deletions(-) diff --git a/src/libraries/System.Console/src/System/IO/KeyMapper.cs b/src/libraries/System.Console/src/System/IO/KeyMapper.cs index 602325efa5daa..b50198fdbdbec 100644 --- a/src/libraries/System.Console/src/System/IO/KeyMapper.cs +++ b/src/libraries/System.Console/src/System/IO/KeyMapper.cs @@ -58,24 +58,90 @@ internal static class KeyMapper internal static void MapNew(char[] buffer, ConsolePal.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) { - if (endIndex - startIndex == 1) + int length = endIndex - startIndex; + + if (length >= 3 && TryDecodeTerminalInputSequence(buffer, terminalFormatStrings, out key, out isShift, out isCtrl, out isAlt, ref startIndex, endIndex)) { - DecodeFromSingleChar(buffer[startIndex], out key, out ch, out isShift, out isCtrl); - startIndex++; - isAlt = false; + // these special keys never produce any char (Home, Arrow, F1 etc) + ch = default; + return; } - else if (endIndex - startIndex == 2 && buffer[startIndex] == Escape) + else if (length == 2 && buffer[startIndex] == Escape) { DecodeFromSingleChar(buffer[++startIndex], out key, out ch, out isShift, out isCtrl); startIndex++; isAlt = key != default; // two char sequences starting with Escape are Alt+$Key } else + { + DecodeFromSingleChar(buffer[startIndex], out key, out ch, out isShift, out isCtrl); + startIndex++; + isAlt = false; + } + } + + private static bool TryDecodeTerminalInputSequence(char[] buffer, ConsolePal.TerminalFormatStrings terminalFormatStrings, + out ConsoleKey key, out bool isShift, out bool isAlt, out bool isCtrl, ref int startIndex, int endIndex) + { + ReadOnlySpan input = buffer.AsSpan(startIndex, endIndex - startIndex); + isShift = isAlt = isCtrl = false; + key = default; + + // xterm and VT sequences start with "^[[", some xterm start with "^[O" ("^[" stands for Escape (27)) + if (input.Length < 3 || input[0] != Escape || (input[1] != '[' && input[1] != 'O')) { key = default; - ch = default; - isShift = isAlt = isCtrl = false; + return false; + } + + if (input[1] == 'O' || char.IsAsciiLetterUpper(input[2])) // xterm "^[[O" or xterm "^[[" + { + if (!TryMapUsingDatabase(buffer.AsMemory(startIndex, 3), terminalFormatStrings, ref key, ref isShift, ref isAlt, ref isCtrl)) + { + key = Map(input[2]); + Debug.Assert(key != default, $"Missing '{input.Slice(0, 3)}' mapping"); + } + startIndex += 3; + return true; + } + + if (char.IsAsciiDigit(input[2])) + { + + } + + return false; + + static bool TryMapUsingDatabase(ReadOnlyMemory inputSequence, ConsolePal.TerminalFormatStrings terminalFormatStrings, + ref ConsoleKey key, ref bool isShift, ref bool isAlt, ref bool isCtrl) + { + // Check if the string prefix matches. + if (terminalFormatStrings.KeyFormatToConsoleKey.TryGetValue(inputSequence, out ConsoleKeyInfo consoleKeyInfo)) + { + key = consoleKeyInfo.Key; + isShift = (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0; + isAlt = (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0; + isCtrl = (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0; + return true; + } + return false; } + + static ConsoleKey Map(char single) + => single switch + { + 'A' => ConsoleKey.UpArrow, + 'B' => ConsoleKey.DownArrow, + 'C' => ConsoleKey.RightArrow, + 'D' => ConsoleKey.LeftArrow, + 'F' => ConsoleKey.End, + 'H' => ConsoleKey.Home, + 'P' => ConsoleKey.F1, + 'Q' => ConsoleKey.F2, + 'R' => ConsoleKey.F3, + 'S' => ConsoleKey.F4, + _ => default + }; } private static void DecodeFromSingleChar(char single, out ConsoleKey key, out char ch, out bool isShift, out bool isCtrl) diff --git a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs index 706609a16e95d..849ea5e9f1700 100644 --- a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs +++ b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; using Xunit; @@ -61,7 +62,7 @@ private static ConsoleKeyInfo Map(char[] chars, ConsolePal.TerminalFormatStrings return new ConsoleKeyInfo(ch, consoleKey, isShift, isAlt, isCtrl); } - private static IEnumerable<(char, ConsoleKey)> AsciiKeys + private static IEnumerable<(char ch, ConsoleKey key)> AsciiKeys { get { @@ -96,18 +97,7 @@ private static ConsoleKeyInfo Map(char[] chars, ConsolePal.TerminalFormatStrings } public static IEnumerable AsciiCharactersArguments - { - get - { - foreach (TerminalData terminalData in Terminals) - { - foreach ((char ch, ConsoleKey key) in AsciiKeys) - { - yield return new object[] { terminalData, ch, key }; - } - } - } - } + => Terminals.SelectMany(terminal => AsciiKeys.Select(tuple => new object[] { terminal, tuple.ch, tuple.key })); [Theory] [MemberData(nameof(AsciiCharactersArguments))] @@ -119,6 +109,41 @@ public void AsciiCharacters(TerminalData terminalData, char input, ConsoleKey ex Assert.Equal(expectedKey, consoleKeyInfo.Key); Assert.Equal(char.IsAsciiLetterUpper(input) ? ConsoleModifiers.Shift : 0, consoleKeyInfo.Modifiers); } + + private static IEnumerable<(string chars, ConsoleKey key)> ThreeCharactersKeys + { + get + { + // "^[[" + yield return ("\u001B[H", ConsoleKey.Home); + yield return ("\u001B[F", ConsoleKey.End); + yield return ("\u001B[A", ConsoleKey.UpArrow); + yield return ("\u001B[B", ConsoleKey.DownArrow); + yield return ("\u001B[C", ConsoleKey.RightArrow); + yield return ("\u001B[D", ConsoleKey.LeftArrow); + // "^[O" + yield return ("\u001BOP", ConsoleKey.F1); + yield return ("\u001BOQ", ConsoleKey.F2); + yield return ("\u001BOR", ConsoleKey.F3); + yield return ("\u001BOS", ConsoleKey.F4); + } + } + + public static IEnumerable ThreeCharactersKeysArguments + => Terminals + .Where(t => t is not PuTTYData_putty) // different mappings (handled by KeysAreProperlyMapped test) + .SelectMany(terminal => ThreeCharactersKeys.Select(tuple => new object[] { terminal, tuple.chars, tuple.key })); + + [Theory] + [MemberData(nameof(ThreeCharactersKeysArguments))] + public void ThreeCharactersKey(TerminalData terminalData, string input, ConsoleKey expectedKey) + { + ConsoleKeyInfo consoleKeyInfo = Map(input.ToCharArray(), terminalData.TerminalDb, terminalData.Verase); + + Assert.Equal(expectedKey, consoleKeyInfo.Key); + Assert.Equal(default, consoleKeyInfo.KeyChar); + Assert.Equal(default, consoleKeyInfo.Modifiers); + } } public abstract class TerminalData From a9db7dbe18291e9b922a2f0bd50334857b188bc7 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 8 Jul 2022 19:21:54 +0200 Subject: [PATCH 19/40] fix test cases where pressing Ctrl/Alt/Shift + other key produces two bytes which represent single UTF8 character by design --- .../tests/KeyMapperTests.Unix.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs index 849ea5e9f1700..8b46140c018d0 100644 --- a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs +++ b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs @@ -235,22 +235,22 @@ public class XTermData : TerminalData 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('a', ConsoleKey.A, false, true, false)); - yield return (new byte[] { 194, 129 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, true, 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('1', ConsoleKey.D1, false, true, 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('2', ConsoleKey.D2, false, true, false)); + 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('=', default, 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)8, ConsoleKey.Backspace, false, false, false)); - yield return (new byte[] { 195, 191 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); - yield return (new byte[] { 194, 136 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, true)); + 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, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); @@ -291,22 +291,22 @@ public class UXTermData : TerminalData 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('a', ConsoleKey.A, false, true, false)); - yield return (new byte[] { 194, 129 }, new ConsoleKeyInfo('a', ConsoleKey.A, false, true, 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('1', ConsoleKey.D1, false, true, 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('2', ConsoleKey.D2, false, true, false)); + 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('=', default, 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)8, ConsoleKey.Backspace, false, false, false)); - yield return (new byte[] { 195, 191 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); - yield return (new byte[] { 194, 136 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, true)); + 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, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); From c2e02d8c5e77f629dea2dd3b2db593777f2ec4ed Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 8 Jul 2022 21:26:34 +0200 Subject: [PATCH 20/40] handle more complex sequences (with modifiers) --- .../System.Console/src/System/IO/KeyMapper.cs | 132 ++++++++++++++++-- .../tests/KeyMapperTests.Unix.cs | 69 +++++++-- 2 files changed, 179 insertions(+), 22 deletions(-) diff --git a/src/libraries/System.Console/src/System/IO/KeyMapper.cs b/src/libraries/System.Console/src/System/IO/KeyMapper.cs index b50198fdbdbec..cb8336211ac9a 100644 --- a/src/libraries/System.Console/src/System/IO/KeyMapper.cs +++ b/src/libraries/System.Console/src/System/IO/KeyMapper.cs @@ -60,13 +60,31 @@ internal static class KeyMapper { int length = endIndex - startIndex; - if (length >= 3 && TryDecodeTerminalInputSequence(buffer, terminalFormatStrings, out key, out isShift, out isCtrl, out isAlt, ref startIndex, endIndex)) + // TODO: add VERASE handling + + // Escape sequences start with Escape. But some terminals (e.g. PuTTY) use Escape to express that for given sequence Alt was pressed. + if (length >= 4 && buffer[startIndex] == Escape && buffer[startIndex + 1] == Escape) + { + startIndex++; + if (TryDecodeTerminalInputSequence(buffer, terminalFormatStrings, out key, out isShift, out _, out isCtrl, ref startIndex, endIndex)) + { + isAlt = true; + ch = default; // these special keys never produce any char (Home, Arrow, F1 etc) + return; + } + else + { + startIndex--; + } + } + else if (length >= 3 && TryDecodeTerminalInputSequence(buffer, terminalFormatStrings, out key, out isShift, out isAlt, out isCtrl, ref startIndex, endIndex)) { // these special keys never produce any char (Home, Arrow, F1 etc) ch = default; return; } - else if (length == 2 && buffer[startIndex] == Escape) + + if (length == 2 && buffer[startIndex] == Escape) { DecodeFromSingleChar(buffer[++startIndex], out key, out ch, out isShift, out isCtrl); startIndex++; @@ -90,11 +108,10 @@ internal static class KeyMapper // xterm and VT sequences start with "^[[", some xterm start with "^[O" ("^[" stands for Escape (27)) if (input.Length < 3 || input[0] != Escape || (input[1] != '[' && input[1] != 'O')) { - key = default; return false; } - if (input[1] == 'O' || char.IsAsciiLetterUpper(input[2])) // xterm "^[[O" or xterm "^[[" + if (input[1] == 'O' || char.IsAsciiLetterUpper(input[2])) // "^[O" or "^[[" { if (!TryMapUsingDatabase(buffer.AsMemory(startIndex, 3), terminalFormatStrings, ref key, ref isShift, ref isAlt, ref isCtrl)) { @@ -105,9 +122,59 @@ internal static class KeyMapper return true; } - if (char.IsAsciiDigit(input[2])) + int digitCount = 0; + ReadOnlySpan unparsed = input.Slice(2); + while (!unparsed.IsEmpty && char.IsAsciiDigit(unparsed[0])) + { + digitCount++; + unparsed = unparsed.Slice(1); + } + + if (digitCount == 0) + { + return false; + } + + if (unparsed[0] == '~') // it's a VT Sequence like ^[[11~ + { + int sequenceLength = 2 + digitCount + 1; // prefix + digit count + ~ + if (!TryMapUsingDatabase(buffer.AsMemory(startIndex, sequenceLength), terminalFormatStrings, ref key, ref isShift, ref isAlt, ref isCtrl)) + { + key = MapEscapeSequenceNumber(byte.Parse(input.Slice(2, digitCount))); + Debug.Assert(key != default, $"Missing '{input.Slice(0, sequenceLength)}' mapping"); + } + startIndex += sequenceLength; + return true; + } + + if (unparsed[0] != ';' || unparsed.Length < 2 || !char.IsDigit(unparsed[1]) || !(unparsed[2] == '~' || char.IsAsciiLetterUpper(unparsed[2]))) { + return false; + } + // after ; comes the modifiers: + ConsoleModifiers modifiers = MapModifiers(unparsed[1]); + if (char.IsAsciiLetterUpper(unparsed[2])) + { + // after the modifiers it's either a letter (key id) + key = Map(unparsed[2]); + } + else + { + // or a tylde and the whole thing is a VT Sequence like ^[[24;5~ + Debug.Assert(unparsed[2] == '~'); + int sequenceLength = 2 + digitCount + 1; // prefix + digit + key = MapEscapeSequenceNumber(byte.Parse(input.Slice(2, digitCount))); + Debug.Assert(key != default, $"Missing '{input.Slice(0, sequenceLength)}' mapping"); + } + + if (key != default) + { + startIndex += 2 + digitCount + 1 + 1; // prefix + digit count + modifier + ~ or single char + isShift = (modifiers & ConsoleModifiers.Shift) != 0; + isAlt = (modifiers & ConsoleModifiers.Alt) != 0; + isCtrl = (modifiers & ConsoleModifiers.Control) != 0; + return true; } return false; @@ -134,7 +201,7 @@ static ConsoleKey Map(char single) 'B' => ConsoleKey.DownArrow, 'C' => ConsoleKey.RightArrow, 'D' => ConsoleKey.LeftArrow, - 'F' => ConsoleKey.End, + 'F' or 'w' => ConsoleKey.End, // 'w' can be used by rxvt 'H' => ConsoleKey.Home, 'P' => ConsoleKey.F1, 'Q' => ConsoleKey.F2, @@ -142,6 +209,53 @@ static ConsoleKey Map(char single) 'S' => ConsoleKey.F4, _ => default }; + + 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 (https://en.wikipedia.org/wiki/ANSI_escape_code#Fe_Escape_sequences) + _ => default + }; + + static ConsoleModifiers MapModifiers(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 + }; } private static void DecodeFromSingleChar(char single, out ConsoleKey key, out char ch, out bool isShift, out bool isCtrl) @@ -162,7 +276,7 @@ private static void DecodeFromSingleChar(char single, out ConsoleKey key, out ch '-' => 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. + '!' 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', @@ -191,7 +305,7 @@ static ConsoleKey UppercaseCharacter(char single, out bool isShift) static ConsoleKey ControlAndLetterPressed(char single, out char ch, 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+h is more likely to be pressed. (TODO: discuss with others) + // Ctrl+h is mapped to 8, which also maps to Ctrl+Backspace. Ctrl+h is more likely to be pressed. (TODO: change it) // Ctrl+i is mapped to 9, which also maps to Tab. Tab (9) is more likely to be pressed. // Ctrl+j is mapped to 10, which also maps to Enter ('\n') and Ctrl+Enter. Enter is more likely to be pressed. // Ctrl+m is mapped to 13, which also maps to Enter ('\r'). Enter (13) is more likely to be pressed. @@ -213,7 +327,7 @@ static ConsoleKey ControlAndDigitPressed(char single, out char ch, out bool isCt ch = default; // consistent with Windows return single switch { - '\u0000' => ConsoleKey.D2, // This is what PuTTY does (was not previously mapped this way) + '\u0000' => ConsoleKey.D2, // was not previously mapped this way _ => ConsoleKey.D4 + single - 28 }; } diff --git a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs index 8b46140c018d0..174b81ea382c8 100644 --- a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs +++ b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs @@ -42,14 +42,14 @@ public void KeysAreProperlyMapped(TerminalData terminalData, byte[] recordedByte { char[] encoded = terminalData.ConsoleEncoding.GetString(recordedBytes).ToCharArray(); - ConsoleKeyInfo actual = Map(encoded, terminalData.TerminalDb, terminalData.Verase); + ConsoleKeyInfo actual = Map(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 ConsoleKeyInfo Map(char[] chars, ConsolePal.TerminalFormatStrings terminalFormatStrings, byte verase) + private static ConsoleKeyInfo Map(char[] chars, ConsolePal.TerminalFormatStrings terminalFormatStrings, byte verase, int expectedStartIndex) { int startIndex = 0; @@ -103,7 +103,7 @@ public static IEnumerable AsciiCharactersArguments [MemberData(nameof(AsciiCharactersArguments))] public void AsciiCharacters(TerminalData terminalData, char input, ConsoleKey expectedKey) { - ConsoleKeyInfo consoleKeyInfo = Map(new[] { input }, terminalData.TerminalDb, terminalData.Verase); + ConsoleKeyInfo consoleKeyInfo = Map(new[] { input }, terminalData.TerminalDb, terminalData.Verase, 1); Assert.Equal(input, consoleKeyInfo.KeyChar); Assert.Equal(expectedKey, consoleKeyInfo.Key); @@ -126,6 +126,7 @@ public void AsciiCharacters(TerminalData terminalData, char input, ConsoleKey ex yield return ("\u001BOQ", ConsoleKey.F2); yield return ("\u001BOR", ConsoleKey.F3); yield return ("\u001BOS", ConsoleKey.F4); + yield return ("\u001BOw", ConsoleKey.End); // rxvt } } @@ -138,7 +139,58 @@ public static IEnumerable ThreeCharactersKeysArguments [MemberData(nameof(ThreeCharactersKeysArguments))] public void ThreeCharactersKey(TerminalData terminalData, string input, ConsoleKey expectedKey) { - ConsoleKeyInfo consoleKeyInfo = Map(input.ToCharArray(), terminalData.TerminalDb, terminalData.Verase); + ConsoleKeyInfo consoleKeyInfo = Map(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)> 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 VTSequencesrguments + => Terminals.SelectMany(terminal => VTSequences.Select(tuple => new object[] { terminal, tuple.chars, tuple.key })); + + [Theory] + [MemberData(nameof(VTSequencesrguments))] + public void VTSequencesAreMapped(TerminalData terminalData, string input, ConsoleKey expectedKey) + { + ConsoleKeyInfo consoleKeyInfo = Map(input.ToCharArray(), terminalData.TerminalDb, terminalData.Verase, input.Length); Assert.Equal(expectedKey, consoleKeyInfo.Key); Assert.Equal(default, consoleKeyInfo.KeyChar); @@ -364,10 +416,7 @@ public class PuTTYData_xterm : TerminalData yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); - yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); - yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); yield return (new byte[] { 27, 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); @@ -414,10 +463,7 @@ public class PuTTYData_linux : TerminalData yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); - yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); - yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); yield return (new byte[] { 27, 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); @@ -466,10 +512,7 @@ public class PuTTYData_putty : TerminalData yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false)); yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); - yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); - yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); yield return (new byte[] { 27, 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); From 620d1a017ffc83d0abf79c675b735fcdc231f86e Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Sat, 9 Jul 2022 14:59:58 +0200 Subject: [PATCH 21/40] handle edge cases properly --- .../System.Console/src/System/IO/KeyMapper.cs | 147 ++++++------ .../tests/KeyMapperTests.Unix.cs | 213 +++++++++++++++--- 2 files changed, 263 insertions(+), 97 deletions(-) diff --git a/src/libraries/System.Console/src/System/IO/KeyMapper.cs b/src/libraries/System.Console/src/System/IO/KeyMapper.cs index cb8336211ac9a..0b1709959c1fa 100644 --- a/src/libraries/System.Console/src/System/IO/KeyMapper.cs +++ b/src/libraries/System.Console/src/System/IO/KeyMapper.cs @@ -9,6 +9,10 @@ internal static class KeyMapper { private const char Escape = '\u001B'; private const char Delete = '\u007F'; + private const char VTSeqenceEndTag = '~'; + private const char ModifierSeparator = ';'; + private const int MinimalSequenceLength = 3; + private const int SequencePrefixLength = 2; // ^[[ ("^[" stands for Escape) internal static bool MapBufferToConsoleKey(char[] buffer, ConsolePal.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) @@ -56,20 +60,26 @@ internal static class KeyMapper } internal static void MapNew(char[] buffer, ConsolePal.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) + out ConsoleKey key, out char character, out bool isShift, out bool isAlt, out bool isCtrl, ref int startIndex, int endIndex) { int length = endIndex - startIndex; - // TODO: add VERASE handling + // VERASE overrides anything from Terminfo + if (buffer[startIndex] != posixDisableValue && buffer[startIndex] == veraseCharacter) + { + isShift = isAlt = isCtrl = false; + character = buffer[startIndex++]; + key = ConsoleKey.Backspace; + return; + } - // Escape sequences start with Escape. But some terminals (e.g. PuTTY) use Escape to express that for given sequence Alt was pressed. - if (length >= 4 && buffer[startIndex] == Escape && buffer[startIndex + 1] == Escape) + // Escape Sequences start with Escape. But some terminals like PuTTY and rxvt use Escape to express that for given sequence Alt was pressed. + if (length >= MinimalSequenceLength + 1 && buffer[startIndex] == Escape && buffer[startIndex + 1] == Escape) { startIndex++; - if (TryDecodeTerminalInputSequence(buffer, terminalFormatStrings, out key, out isShift, out _, out isCtrl, ref startIndex, endIndex)) + if (TryParseTerminalInputSequence(buffer, terminalFormatStrings, out key, out character, out isShift, out _, out isCtrl, ref startIndex, endIndex)) { isAlt = true; - ch = default; // these special keys never produce any char (Home, Arrow, F1 etc) return; } else @@ -77,100 +87,107 @@ internal static class KeyMapper startIndex--; } } - else if (length >= 3 && TryDecodeTerminalInputSequence(buffer, terminalFormatStrings, out key, out isShift, out isAlt, out isCtrl, ref startIndex, endIndex)) + else if (length >= MinimalSequenceLength && TryParseTerminalInputSequence(buffer, terminalFormatStrings, out key, out character, out isShift, out isAlt, out isCtrl, ref startIndex, endIndex)) { - // these special keys never produce any char (Home, Arrow, F1 etc) - ch = default; return; } - if (length == 2 && buffer[startIndex] == Escape) + if (length == 2 && buffer[startIndex] == Escape && buffer[startIndex + 1] != Escape) { - DecodeFromSingleChar(buffer[++startIndex], out key, out ch, out isShift, out isCtrl); + DecodeFromSingleChar(buffer[++startIndex], out key, out character, out isShift, out isCtrl); startIndex++; isAlt = key != default; // two char sequences starting with Escape are Alt+$Key } else { - DecodeFromSingleChar(buffer[startIndex], out key, out ch, out isShift, out isCtrl); + DecodeFromSingleChar(buffer[startIndex], out key, out character, out isShift, out isCtrl); startIndex++; isAlt = false; } } - private static bool TryDecodeTerminalInputSequence(char[] buffer, ConsolePal.TerminalFormatStrings terminalFormatStrings, - out ConsoleKey key, out bool isShift, out bool isAlt, out bool isCtrl, ref int startIndex, int endIndex) + private static bool TryParseTerminalInputSequence(char[] buffer, ConsolePal.TerminalFormatStrings terminalFormatStrings, + out ConsoleKey key, out char character, out bool isShift, out bool isAlt, out bool isCtrl, ref int startIndex, int endIndex) { ReadOnlySpan input = buffer.AsSpan(startIndex, endIndex - startIndex); isShift = isAlt = isCtrl = false; + character = default; key = default; // xterm and VT sequences start with "^[[", some xterm start with "^[O" ("^[" stands for Escape (27)) - if (input.Length < 3 || input[0] != Escape || (input[1] != '[' && input[1] != 'O')) + if (input.Length < MinimalSequenceLength || input[0] != Escape || (input[1] != '[' && input[1] != 'O')) { return false; } - if (input[1] == 'O' || char.IsAsciiLetterUpper(input[2])) // "^[O" or "^[[" + // Is it a three character sequence? (examples: '^[[H' (Home), '^[OP' (F1), '^[Ow' (End)) + if (input[1] == 'O' || char.IsAsciiLetterUpper(input[2]) || input[2] == 'w') { - if (!TryMapUsingDatabase(buffer.AsMemory(startIndex, 3), terminalFormatStrings, ref key, ref isShift, ref isAlt, ref isCtrl)) + if (!TryMapUsingTerminfoDb(buffer.AsMemory(startIndex, MinimalSequenceLength), terminalFormatStrings, ref key, ref isShift, ref isAlt, ref isCtrl)) { - key = Map(input[2]); - Debug.Assert(key != default, $"Missing '{input.Slice(0, 3)}' mapping"); + key = MapKeyId(input[2]); // fallback to well known mappings + + if (key == default) + { + return false; // it was not a known sequence + } } - startIndex += 3; + character = key == ConsoleKey.Enter ? '\r' : default; // "^[OM" should produce new line character (was not previously mapped this way) + startIndex += MinimalSequenceLength; return true; } - int digitCount = 0; - ReadOnlySpan unparsed = input.Slice(2); - while (!unparsed.IsEmpty && char.IsAsciiDigit(unparsed[0])) + if (input.Length == MinimalSequenceLength) { - digitCount++; - unparsed = unparsed.Slice(1); + return false; } - if (digitCount == 0) + // 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 { return false; } - if (unparsed[0] == '~') // it's a VT Sequence like ^[[11~ + if (input[SequencePrefixLength + digitCount] is VTSeqenceEndTag) // it's a VT Sequence like ^[[11~ { - int sequenceLength = 2 + digitCount + 1; // prefix + digit count + ~ - if (!TryMapUsingDatabase(buffer.AsMemory(startIndex, sequenceLength), terminalFormatStrings, ref key, ref isShift, ref isAlt, ref isCtrl)) + int sequenceLength = SequencePrefixLength + digitCount + 1; + if (!TryMapUsingTerminfoDb(buffer.AsMemory(startIndex, sequenceLength), terminalFormatStrings, ref key, ref isShift, ref isAlt, ref isCtrl)) { - key = MapEscapeSequenceNumber(byte.Parse(input.Slice(2, digitCount))); - Debug.Assert(key != default, $"Missing '{input.Slice(0, sequenceLength)}' mapping"); + key = MapEscapeSequenceNumber(byte.Parse(input.Slice(SequencePrefixLength, digitCount))); + + if (key == default) + { + return false; // it was not a known sequence + } } startIndex += sequenceLength; return true; } - if (unparsed[0] != ';' || unparsed.Length < 2 || !char.IsDigit(unparsed[1]) || !(unparsed[2] == '~' || char.IsAsciiLetterUpper(unparsed[2]))) + // 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 Seqence 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 VTSeqenceEndTag)) { return false; } - // after ; comes the modifiers: - ConsoleModifiers modifiers = MapModifiers(unparsed[1]); - if (char.IsAsciiLetterUpper(unparsed[2])) - { - // after the modifiers it's either a letter (key id) - key = Map(unparsed[2]); - } - else - { - // or a tylde and the whole thing is a VT Sequence like ^[[24;5~ - Debug.Assert(unparsed[2] == '~'); - int sequenceLength = 2 + digitCount + 1; // prefix + digit - key = MapEscapeSequenceNumber(byte.Parse(input.Slice(2, digitCount))); - Debug.Assert(key != default, $"Missing '{input.Slice(0, sequenceLength)}' mapping"); - } + ConsoleModifiers modifiers = MapModifiers(input[SequencePrefixLength + digitCount + 1]); + + key = input[SequencePrefixLength + digitCount + 2] is VTSeqenceEndTag + ? MapEscapeSequenceNumber(byte.Parse(input.Slice(SequencePrefixLength, digitCount))) + : MapKeyId(input[SequencePrefixLength + digitCount + 2]); if (key != default) { - startIndex += 2 + digitCount + 1 + 1; // prefix + digit count + modifier + ~ or single char + startIndex += SequencePrefixLength + digitCount + 3; // 3 stands for separator, modifier and end tag or id isShift = (modifiers & ConsoleModifiers.Shift) != 0; isAlt = (modifiers & ConsoleModifiers.Alt) != 0; isCtrl = (modifiers & ConsoleModifiers.Control) != 0; @@ -179,7 +196,7 @@ internal static class KeyMapper return false; - static bool TryMapUsingDatabase(ReadOnlyMemory inputSequence, ConsolePal.TerminalFormatStrings terminalFormatStrings, + static bool TryMapUsingTerminfoDb(ReadOnlyMemory inputSequence, ConsolePal.TerminalFormatStrings terminalFormatStrings, ref ConsoleKey key, ref bool isShift, ref bool isAlt, ref bool isCtrl) { // Check if the string prefix matches. @@ -194,7 +211,7 @@ internal static class KeyMapper return false; } - static ConsoleKey Map(char single) + static ConsoleKey MapKeyId(char single) => single switch { 'A' => ConsoleKey.UpArrow, @@ -203,6 +220,7 @@ static ConsoleKey Map(char single) 'D' => ConsoleKey.LeftArrow, 'F' or 'w' => ConsoleKey.End, // 'w' can be used by rxvt 'H' => ConsoleKey.Home, + 'M' => ConsoleKey.Enter, 'P' => ConsoleKey.F1, 'Q' => ConsoleKey.F2, 'R' => ConsoleKey.F3, @@ -265,13 +283,13 @@ private static void DecodeFromSingleChar(char single, out ConsoleKey key, out ch key = single switch { - // '\b' is not mapped to ConsoleKey.Backspace on purpose, as it's simply wrong mapping + '\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, but Escape is more likely to be pressed. Limitation: Ctrl+[ and Ctrl+3 can't be mapped. - Delete => ConsoleKey.Backspace, // Ctrl+8 and Backspace are mapped to 127, but Backspace is more likely to be pressed. Limitation: Ctrl+8 can't be mapped. - '*' => ConsoleKey.Multiply, // We can't distinguish D8+Shift and Multiply (Numeric Keypad). Limitation: Shift+D8 can't be mapped. + 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. @@ -284,14 +302,13 @@ private static void DecodeFromSingleChar(char single, out ConsoleKey key, out ch _ 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 ch, out isCtrl), _ when char.IsBetween(single, (char)28, (char)31) => ControlAndDigitPressed(single, out ch, out isCtrl), - '\u0000' or Delete => ControlAndDigitPressed(single, out ch, out isCtrl), + '\u0000' => ControlAndDigitPressed(single, out ch, out isCtrl), _ => default }; - // above we map ASCII Delete character to Backspace key, we need to map the char too - if (key == ConsoleKey.Backspace) + if (single is '\b' or '\n') { - ch = '\b'; + isCtrl = true; // Ctrl+Backspace is mapped to '\b' (8), Ctrl+Enter to '\n' (10) } static ConsoleKey UppercaseCharacter(char single, out bool isShift) @@ -305,12 +322,12 @@ static ConsoleKey UppercaseCharacter(char single, out bool isShift) static ConsoleKey ControlAndLetterPressed(char single, out char ch, 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+h is more likely to be pressed. (TODO: change it) - // Ctrl+i is mapped to 9, which also maps to Tab. Tab (9) is more likely to be pressed. - // Ctrl+j is mapped to 10, which also maps to Enter ('\n') and Ctrl+Enter. Enter is more likely to be pressed. - // Ctrl+m is mapped to 13, which also maps to Enter ('\r'). Enter (13) is more likely to be pressed. - // Limitation: Ctrl+i, Ctrl+j, Crl+m, Ctrl+Backspace and Ctrl+Enter can't be mapped. More: https://unix.stackexchange.com/questions/563469/conflict-ctrl-i-with-tab-in-normal-mode - Debug.Assert(single != '\t' && single != '\n' && single != '\r'); + // Ctrl+H is mapped to 8, which also maps to Ctrl+Backspace. + // Ctrl+I is mapped to 9, which also maps to Tab. Tab (9) is more likely to be pressed. + // 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; ch = default; // we could use the letter here, but it's impossible to distinguish upper vs lowercase (and Windows doesn't do it as well) @@ -319,7 +336,7 @@ static ConsoleKey ControlAndLetterPressed(char single, out char ch, out bool isC static ConsoleKey ControlAndDigitPressed(char single, out char ch, out bool isCtrl) { - // Ctrl+(D3-D7) characters are mapped to values from 27 to 31. Escape (27) is more likely to be pressed. + // 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)); diff --git a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs index 174b81ea382c8..97f31fa2e4369 100644 --- a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs +++ b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs @@ -57,7 +57,8 @@ private static ConsoleKeyInfo Map(char[] chars, ConsolePal.TerminalFormatStrings out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length); //Assert.True(KeyMapper.MapBufferToConsoleKey(chars, terminalFormatStrings, 0, verase, // out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length)); - Assert.True(startIndex > 0); + + Assert.Equal(expectedStartIndex, startIndex); return new ConsoleKeyInfo(ch, consoleKey, isShift, isAlt, isCtrl); } @@ -69,7 +70,6 @@ private static ConsoleKeyInfo Map(char[] chars, ConsolePal.TerminalFormatStrings yield return (' ', ConsoleKey.Spacebar); yield return ('\t', ConsoleKey.Tab); yield return ('\r', ConsoleKey.Enter); - yield return ('\n', ConsoleKey.Enter); yield return ('+', ConsoleKey.Add); yield return ('-', ConsoleKey.Subtract); @@ -110,6 +110,18 @@ public void AsciiCharacters(TerminalData terminalData, char input, ConsoleKey ex Assert.Equal(char.IsAsciiLetterUpper(input) ? ConsoleModifiers.Shift : 0, consoleKeyInfo.Modifiers); } + [Theory] + [MemberData(nameof(AsciiCharactersArguments))] + public void VeraseIsRespected(TerminalData terminalData, char input, ConsoleKey ifNotVerase) + { + ConsoleKeyInfo consoleKeyInfo = Map(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); + } + private static IEnumerable<(string chars, ConsoleKey key)> ThreeCharactersKeys { get @@ -127,6 +139,8 @@ public void AsciiCharacters(TerminalData terminalData, char input, ConsoleKey ex yield return ("\u001BOR", ConsoleKey.F3); yield return ("\u001BOS", ConsoleKey.F4); yield return ("\u001BOw", ConsoleKey.End); // rxvt + + // "\u001BOM" (enter) is tested elsewhere, as it requires character verification } } @@ -196,6 +210,155 @@ public void VTSequencesAreMapped(TerminalData terminalData, string input, Consol Assert.Equal(default, consoleKeyInfo.KeyChar); Assert.Equal(default, consoleKeyInfo.Modifiers); } + + private static IEnumerable<(string chars, ConsoleKeyInfo[] keys)> TrickySequences + { + 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) }); + // Enter using "^[OM" special sequence: they char + yield return ("\u001BOM", new[] { new ConsoleKeyInfo('\r', ConsoleKey.Enter, false, false, false) }); + + // 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 short ID + foreach (char letter in Enumerable.Range('E', 'Z' - 'E')) + { + if (!(letter is 'F' or 'H' or 'M' or 'P' or 'Q' or 'R' or 'S')) // valid letters + { + yield return ($"\u001BO{letter}", new[] + { + new ConsoleKeyInfo('\u001B', ConsoleKey.Escape, false, false, false), + new ConsoleKeyInfo('O', ConsoleKey.O, true, false, false), + new ConsoleKeyInfo(letter, ConsoleKey.A + letter - 'A', true, false, false), + }); + yield return ($"\u001B[{letter}", new[] + { + new ConsoleKeyInfo('\u001B', ConsoleKey.Escape, false, false, false), + new ConsoleKeyInfo('[', default, false, false, false), + new ConsoleKeyInfo(letter, ConsoleKey.A + letter - 'A', true, false, false), + }); + } + } + foreach (char letter in Enumerable.Range('a', 'z' - 'a')) + { + if (letter is not 'w') // only valid letter + { + yield return ($"\u001BO{letter}", new[] + { + new ConsoleKeyInfo('\u001B', ConsoleKey.Escape, false, false, false), + new ConsoleKeyInfo('O', ConsoleKey.O, true, false, false), + new ConsoleKeyInfo(letter, ConsoleKey.A + letter - 'a', false, false, false), + }); + yield return ($"\u001B[{letter}", new[] + { + new ConsoleKeyInfo('\u001B', ConsoleKey.Escape, false, false, false), + new ConsoleKeyInfo('[', default, false, false, false), + new ConsoleKeyInfo(letter, ConsoleKey.A + letter - 'a', 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 TrickySequencesArguments + => Terminals.SelectMany(terminal => TrickySequences.Select(tuple => new object[] { terminal, tuple.chars, tuple.keys })); + + [Theory] + [MemberData(nameof(TrickySequencesArguments))] + public void TrickySequencesAreHandledProperly(TerminalData terminalData, string input, ConsoleKeyInfo[] expectedKeys) + { + int startIndex = 0; + char[] chars = input.ToCharArray(); + + foreach (ConsoleKeyInfo expectedKey in expectedKeys) + { + KeyMapper.MapNew(chars, terminalData.TerminalDb, 0, terminalData.Verase, + out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length); + + ConsoleKeyInfo parsed = new(ch, consoleKey, isShift, isAlt, isCtrl); + + Assert.Equal(expectedKey.Key, parsed.Key); + Assert.Equal(expectedKey.KeyChar, parsed.KeyChar); + Assert.Equal(expectedKey.Modifiers, parsed.Modifiers); + } + + Assert.Equal(chars.Length, startIndex); + } } public abstract class TerminalData @@ -243,8 +406,8 @@ public class GNOMETerminalData : TerminalData 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)8, ConsoleKey.Backspace, false, false, false)); - yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, 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[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); @@ -259,9 +422,6 @@ public class GNOMETerminalData : TerminalData yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)10, ConsoleKey.Enter, false, false, false)); - yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); - yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)10, ConsoleKey.Enter, 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)); @@ -300,7 +460,7 @@ public class XTermData : TerminalData 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)8, ConsoleKey.Backspace, 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, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); @@ -317,7 +477,6 @@ public class XTermData : TerminalData yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)10, ConsoleKey.Enter, 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)); @@ -356,7 +515,7 @@ public class UXTermData : TerminalData 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)8, ConsoleKey.Backspace, 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, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); @@ -373,7 +532,6 @@ public class UXTermData : TerminalData yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)10, ConsoleKey.Enter, 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)); @@ -412,21 +570,17 @@ public class PuTTYData_xterm : TerminalData 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)8, ConsoleKey.Backspace, false, false, false)); - yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, 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[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); yield return (new byte[] { 27, 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); - yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); - yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); + yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); + yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); yield return (new byte[] { 27, 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); - yield return (new byte[] { 10 }, new ConsoleKeyInfo((char)10, ConsoleKey.Enter, false, false, false)); - yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); - yield return (new byte[] { 27, 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, true, false)); - yield return (new byte[] { 27, 10 }, new ConsoleKeyInfo((char)10, ConsoleKey.Enter, false, true, false)); } } } @@ -459,8 +613,8 @@ public class PuTTYData_linux : TerminalData 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)8, ConsoleKey.Backspace, false, false, false)); - yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, 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[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); @@ -468,10 +622,8 @@ public class PuTTYData_linux : TerminalData yield return (new byte[] { 27, 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); - yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); + yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); yield return (new byte[] { 27, 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); - yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); - yield return (new byte[] { 27, 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, 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)); @@ -508,8 +660,8 @@ public class PuTTYData_putty : TerminalData 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)8, ConsoleKey.Backspace, false, false, false)); - yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, 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[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); @@ -517,9 +669,7 @@ public class PuTTYData_putty : TerminalData yield return (new byte[] { 27, 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); - yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); - yield return (new byte[] { 27, 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); - yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); + yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); } } } @@ -551,8 +701,8 @@ public class WindowsTerminalData : TerminalData 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)8, ConsoleKey.Backspace, false, false, false)); - yield return (new byte[] { 27, 127 }, new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, 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[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); @@ -567,7 +717,6 @@ public class WindowsTerminalData : TerminalData yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); - yield return (new byte[] { 13 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, 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)); From 20593ef5ff2303afa3d886c71395eb27edfb5841 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 11 Jul 2022 16:29:15 +0200 Subject: [PATCH 22/40] add tmux test data --- .../tests/KeyMapperTests.Unix.cs | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs index 97f31fa2e4369..0e9d5086ba949 100644 --- a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs +++ b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs @@ -19,7 +19,8 @@ public class KeyMapperTests new PuTTYData_xterm(), new PuTTYData_linux(), new PuTTYData_putty(), - new WindowsTerminalData() + new WindowsTerminalData(), + new TmuxData(), }; public static IEnumerable RecordedScenarios @@ -728,3 +729,34 @@ public class WindowsTerminalData : TerminalData } +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, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); + yield return (new byte[] { 27, 91, 50, 52, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); + yield return (new byte[] { 27, 91, 50, 52, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); + yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); + yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); + yield return (new byte[] { 27, 91, 49, 59, 51, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); + yield return (new byte[] { 27, 91, 49, 59, 55, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); + yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); + yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); + yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); + yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); + yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); + } + } +} From c612f4fb9d5a4d8d4ead42936a51ba3c709dcfcd Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 11 Jul 2022 16:30:26 +0200 Subject: [PATCH 23/40] add rxvt-unicode test data and implementation for rxvt modifiers --- .../System.Console/src/System/IO/KeyMapper.cs | 45 +++++++++---- .../tests/KeyMapperTests.Unix.cs | 66 ++++++++++++++++++- 2 files changed, 96 insertions(+), 15 deletions(-) diff --git a/src/libraries/System.Console/src/System/IO/KeyMapper.cs b/src/libraries/System.Console/src/System/IO/KeyMapper.cs index 0b1709959c1fa..565f9baffd479 100644 --- a/src/libraries/System.Console/src/System/IO/KeyMapper.cs +++ b/src/libraries/System.Console/src/System/IO/KeyMapper.cs @@ -121,7 +121,7 @@ internal static class KeyMapper } // Is it a three character sequence? (examples: '^[[H' (Home), '^[OP' (F1), '^[Ow' (End)) - if (input[1] == 'O' || char.IsAsciiLetterUpper(input[2]) || input[2] == 'w') + if (input[1] == 'O' || char.IsAsciiLetter(input[2])) { if (!TryMapUsingTerminfoDb(buffer.AsMemory(startIndex, MinimalSequenceLength), terminalFormatStrings, ref key, ref isShift, ref isAlt, ref isCtrl)) { @@ -153,7 +153,7 @@ internal static class KeyMapper return false; } - if (input[SequencePrefixLength + digitCount] is VTSeqenceEndTag) // it's a VT Sequence like ^[[11~ + if (input[SequencePrefixLength + digitCount] is VTSeqenceEndTag or '^' or '$' or '@') // it's a VT Sequence like ^[[11~ { int sequenceLength = SequencePrefixLength + digitCount + 1; if (!TryMapUsingTerminfoDb(buffer.AsMemory(startIndex, sequenceLength), terminalFormatStrings, ref key, ref isShift, ref isAlt, ref isCtrl)) @@ -165,6 +165,12 @@ internal static class KeyMapper return false; // it was not a known sequence } } + + if (input[SequencePrefixLength + digitCount] is '^' or '$' or '@') // rxvt modifiers + { + Apply(MapRxvtModifiers(input[SequencePrefixLength + digitCount]), ref isShift, ref isAlt, ref isCtrl); + } + startIndex += sequenceLength; return true; } @@ -188,9 +194,7 @@ internal static class KeyMapper if (key != default) { startIndex += SequencePrefixLength + digitCount + 3; // 3 stands for separator, modifier and end tag or id - isShift = (modifiers & ConsoleModifiers.Shift) != 0; - isAlt = (modifiers & ConsoleModifiers.Alt) != 0; - isCtrl = (modifiers & ConsoleModifiers.Control) != 0; + Apply(modifiers, ref isShift, ref isAlt, ref isCtrl); return true; } @@ -203,22 +207,21 @@ internal static class KeyMapper if (terminalFormatStrings.KeyFormatToConsoleKey.TryGetValue(inputSequence, out ConsoleKeyInfo consoleKeyInfo)) { key = consoleKeyInfo.Key; - isShift = (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0; - isAlt = (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0; - isCtrl = (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0; + Apply(consoleKeyInfo.Modifiers, ref isShift, ref isAlt, ref isCtrl); return true; } return false; } + // lowercase characters are used by rxvt static ConsoleKey MapKeyId(char single) => single switch { - 'A' => ConsoleKey.UpArrow, - 'B' => ConsoleKey.DownArrow, - 'C' => ConsoleKey.RightArrow, - 'D' => ConsoleKey.LeftArrow, - 'F' or 'w' => ConsoleKey.End, // 'w' can be used by rxvt + 'A' or 'a' => ConsoleKey.UpArrow, + 'B' or 'b' => ConsoleKey.DownArrow, + 'C' or 'c' => ConsoleKey.RightArrow, + 'D' or 'd' => ConsoleKey.LeftArrow, + 'F' or 'w' => ConsoleKey.End, 'H' => ConsoleKey.Home, 'M' => ConsoleKey.Enter, 'P' => ConsoleKey.F1, @@ -274,6 +277,22 @@ static ConsoleModifiers MapModifiers(char modifier) '8' => ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control, _ => default }; + + static ConsoleModifiers MapRxvtModifiers(char modifier) + => modifier switch + { + '^' => ConsoleModifiers.Control, + '$' => ConsoleModifiers.Shift, + '@' => ConsoleModifiers.Control | ConsoleModifiers.Shift, + _ => default + }; + + static void Apply(ConsoleModifiers modifiers, ref bool isShift, ref bool isAlt, ref bool isCtrl) + { + isShift = (modifiers & ConsoleModifiers.Shift) != 0; + isAlt = (modifiers & ConsoleModifiers.Alt) != 0; + isCtrl = (modifiers & ConsoleModifiers.Control) != 0; + } } private static void DecodeFromSingleChar(char single, out ConsoleKey key, out char ch, out bool isShift, out bool isCtrl) diff --git a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs index 0e9d5086ba949..fc63e3379729f 100644 --- a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs +++ b/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs @@ -21,6 +21,7 @@ public class KeyMapperTests new PuTTYData_putty(), new WindowsTerminalData(), new TmuxData(), + new RxvtUnicode(), }; public static IEnumerable RecordedScenarios @@ -161,6 +162,32 @@ public void ThreeCharactersKey(TerminalData terminalData, string input, ConsoleK 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 ThreeCharactersKeyRxvt() + { + RxvtUnicode terminalData = new RxvtUnicode(); + + foreach ((string input, ConsoleKey expectedKey) in ThreeCharactersKeysRxvt) + { + ConsoleKeyInfo consoleKeyInfo = Map(input.ToCharArray(), terminalData.TerminalDb, terminalData.Verase, input.Length); + + Assert.Equal(expectedKey, consoleKeyInfo.Key); + Assert.Equal(default, consoleKeyInfo.KeyChar); + Assert.Equal(ConsoleModifiers.Control, consoleKeyInfo.Modifiers); // this particular test excercises the extended strings code path + } + } + private static IEnumerable<(string chars, ConsoleKey key)> VTSequences { get @@ -205,6 +232,11 @@ public static IEnumerable VTSequencesrguments [MemberData(nameof(VTSequencesrguments))] public void VTSequencesAreMapped(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 = Map(input.ToCharArray(), terminalData.TerminalDb, terminalData.Verase, input.Length); Assert.Equal(expectedKey, consoleKeyInfo.Key); @@ -270,9 +302,9 @@ public void VTSequencesAreMapped(TerminalData terminalData, string input, Consol }); } } - foreach (char letter in Enumerable.Range('a', 'z' - 'a')) + foreach (char letter in Enumerable.Range('e', 'z' - 'e')) { - if (letter is not 'w') // only valid letter + if (letter is not 'w') // only valid letter in this range { yield return ($"\u001BO{letter}", new[] { @@ -728,7 +760,37 @@ public class WindowsTerminalData : TerminalData } } +// 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////////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"; // /lib/terminfo/r/rxvt-unicode-256color + internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios + { + get + { + yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); + yield return (new byte[] { 27, 91, 50, 52, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); + yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); + yield return (new byte[] { 27, 91, 50, 52, 36 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); + yield return (new byte[] { 27, 27, 91, 50, 52, 64 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); + yield return (new byte[] { 27, 91, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); + yield return (new byte[] { 27, 91, 55, 94 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); + yield return (new byte[] { 27, 27, 91, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); + yield return (new byte[] { 27, 27, 91, 55, 94 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); + yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); + yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); + yield return (new byte[] { 27, 79, 100 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); + yield return (new byte[] { 27, 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); + } + } +} + +// Ubuntu 18.04 x64 public class TmuxData : TerminalData { protected override string EncodingCharset => "utf-8"; From 3e62636dcd3034bad4b0f95f6d039f8f3c7b11b8 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 12 Jul 2022 09:52:04 +0200 Subject: [PATCH 24/40] polishing --- .../System.Console/src/System.Console.csproj | 3 +- .../System/IO/{KeyMapper.cs => KeyParser.cs} | 198 ++---------------- .../src/System/IO/Net6KeyParser.cs | 166 +++++++++++++++ .../src/System/IO/StdInReader.cs | 2 +- ...yMapperTests.Unix.cs => KeyParserTests.cs} | 10 +- .../tests/System.Console.Tests.csproj | 5 +- 6 files changed, 195 insertions(+), 189 deletions(-) rename src/libraries/System.Console/src/System/IO/{KeyMapper.cs => KeyParser.cs} (67%) create mode 100644 src/libraries/System.Console/src/System/IO/Net6KeyParser.cs rename src/libraries/System.Console/tests/{KeyMapperTests.Unix.cs => KeyParserTests.cs} (99%) diff --git a/src/libraries/System.Console/src/System.Console.csproj b/src/libraries/System.Console/src/System.Console.csproj index 9a6482bce9374..8e7f5a2bb7447 100644 --- a/src/libraries/System.Console/src/System.Console.csproj +++ b/src/libraries/System.Console/src/System.Console.csproj @@ -168,7 +168,8 @@ - + + = 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); - } - - internal static void MapNew(char[] buffer, ConsolePal.TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter, + internal static void Parse(char[] buffer, ConsolePal.TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter, out ConsoleKey key, out char character, out bool isShift, out bool isAlt, out bool isCtrl, ref int startIndex, int endIndex) { int length = endIndex - startIndex; + Debug.Assert(length > 0); - // VERASE overrides anything from Terminfo + // VERASE overrides anything from Terminfo. Both settings can be different for Linux and macOS. if (buffer[startIndex] != posixDisableValue && buffer[startIndex] == veraseCharacter) { isShift = isAlt = isCtrl = false; - character = buffer[startIndex++]; + character = buffer[startIndex++]; // the original char is preserved on purpose (backward compat + consistency) key = ConsoleKey.Backspace; return; } @@ -82,10 +38,7 @@ internal static class KeyMapper isAlt = true; return; } - else - { - startIndex--; - } + startIndex--; } else if (length >= MinimalSequenceLength && TryParseTerminalInputSequence(buffer, terminalFormatStrings, out key, out character, out isShift, out isAlt, out isCtrl, ref startIndex, endIndex)) { @@ -94,13 +47,13 @@ internal static class KeyMapper if (length == 2 && buffer[startIndex] == Escape && buffer[startIndex + 1] != Escape) { - DecodeFromSingleChar(buffer[++startIndex], out key, out character, out isShift, out isCtrl); + ParseFromSingleChar(buffer[++startIndex], out key, out character, out isShift, out isCtrl); startIndex++; isAlt = key != default; // two char sequences starting with Escape are Alt+$Key } else { - DecodeFromSingleChar(buffer[startIndex], out key, out character, out isShift, out isCtrl); + ParseFromSingleChar(buffer[startIndex], out key, out character, out isShift, out isCtrl); startIndex++; isAlt = false; } @@ -153,7 +106,7 @@ internal static class KeyMapper return false; } - if (input[SequencePrefixLength + digitCount] is VTSeqenceEndTag or '^' or '$' or '@') // it's a VT Sequence like ^[[11~ + if (input[SequencePrefixLength + digitCount] is VtSequenceEndTag or '^' or '$' or '@') // it's a VT Sequence like ^[[11~ or rxvt like ^[[11^ { int sequenceLength = SequencePrefixLength + digitCount + 1; if (!TryMapUsingTerminfoDb(buffer.AsMemory(startIndex, sequenceLength), terminalFormatStrings, ref key, ref isShift, ref isAlt, ref isCtrl)) @@ -176,18 +129,18 @@ internal static class KeyMapper } // 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 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 VTSeqenceEndTag)) + || (!char.IsAsciiLetterUpper(input[SequencePrefixLength + digitCount + 2]) && input[SequencePrefixLength + digitCount + 2] is not VtSequenceEndTag)) { return false; } - ConsoleModifiers modifiers = MapModifiers(input[SequencePrefixLength + digitCount + 1]); + ConsoleModifiers modifiers = MapXtermModifiers(input[SequencePrefixLength + digitCount + 1]); - key = input[SequencePrefixLength + digitCount + 2] is VTSeqenceEndTag + key = input[SequencePrefixLength + digitCount + 2] is VtSequenceEndTag ? MapEscapeSequenceNumber(byte.Parse(input.Slice(SequencePrefixLength, digitCount))) : MapKeyId(input[SequencePrefixLength + digitCount + 2]); @@ -213,10 +166,10 @@ internal static class KeyMapper return false; } - // lowercase characters are used by rxvt static ConsoleKey MapKeyId(char single) => single switch { + // lowercase characters are used by rxvt 'A' or 'a' => ConsoleKey.UpArrow, 'B' or 'b' => ConsoleKey.DownArrow, 'C' or 'c' => ConsoleKey.RightArrow, @@ -265,7 +218,7 @@ static ConsoleKey MapEscapeSequenceNumber(byte number) _ => default }; - static ConsoleModifiers MapModifiers(char modifier) + static ConsoleModifiers MapXtermModifiers(char modifier) => modifier switch { '2' => ConsoleModifiers.Shift, @@ -295,7 +248,7 @@ static void Apply(ConsoleModifiers modifiers, ref bool isShift, ref bool isAlt, } } - private static void DecodeFromSingleChar(char single, out ConsoleKey key, out char ch, out bool isShift, out bool isCtrl) + private static void ParseFromSingleChar(char single, out ConsoleKey key, out char ch, out bool isShift, out bool isCtrl) { isShift = isCtrl = false; ch = single; @@ -342,7 +295,7 @@ static ConsoleKey ControlAndLetterPressed(char single, out char ch, out bool isC { // 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. Tab (9) is more likely to be pressed. + // 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 @@ -368,119 +321,4 @@ static ConsoleKey ControlAndDigitPressed(char single, out char ch, out bool isCt }; } } - - private static bool TryGetSpecialConsoleKey(char[] givenChars, int startIndex, int endIndex, - ConsolePal.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/Net6KeyParser.cs b/src/libraries/System.Console/src/System/IO/Net6KeyParser.cs new file mode 100644 index 0000000000000..496d3f1b8f4fb --- /dev/null +++ b/src/libraries/System.Console/src/System/IO/Net6KeyParser.cs @@ -0,0 +1,166 @@ +// 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 bool Parse(char[] buffer, ConsolePal.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 (Parse(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, + ConsolePal.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 0cca6643b007d..8f81b6adf478c 100644 --- a/src/libraries/System.Console/src/System/IO/StdInReader.cs +++ b/src/libraries/System.Console/src/System/IO/StdInReader.cs @@ -360,7 +360,7 @@ private unsafe ConsoleKeyInfo ReadKey() } } - KeyMapper.MapBufferToConsoleKey(_unprocessedBufferToBeRead, ConsolePal.TerminalFormatStringsInstance, ConsolePal.s_posixDisableValue, ConsolePal.s_veraseCharacter, + Net6KeyParser.Parse(_unprocessedBufferToBeRead, ConsolePal.TerminalFormatStringsInstance, ConsolePal.s_posixDisableValue, ConsolePal.s_veraseCharacter, out key, out ch, out isShift, out isAlt, out isCtrl, ref _startIndex, _endIndex); // Replace the '\n' char for Enter by '\r' to match Windows behavior. diff --git a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs b/src/libraries/System.Console/tests/KeyParserTests.cs similarity index 99% rename from src/libraries/System.Console/tests/KeyMapperTests.Unix.cs rename to src/libraries/System.Console/tests/KeyParserTests.cs index fc63e3379729f..a260af73ca70b 100644 --- a/src/libraries/System.Console/tests/KeyMapperTests.Unix.cs +++ b/src/libraries/System.Console/tests/KeyParserTests.cs @@ -9,7 +9,7 @@ namespace System.Tests; -public class KeyMapperTests +public class KeyParserTests { private static readonly TerminalData[] Terminals = { @@ -55,7 +55,7 @@ private static ConsoleKeyInfo Map(char[] chars, ConsolePal.TerminalFormatStrings { int startIndex = 0; - KeyMapper.MapNew(chars, terminalFormatStrings, 0, verase, + KeyParser.Parse(chars, terminalFormatStrings, 0, verase, out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length); //Assert.True(KeyMapper.MapBufferToConsoleKey(chars, terminalFormatStrings, 0, verase, // out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length)); @@ -148,7 +148,7 @@ public void VeraseIsRespected(TerminalData terminalData, char input, ConsoleKey public static IEnumerable ThreeCharactersKeysArguments => Terminals - .Where(t => t is not PuTTYData_putty) // different mappings (handled by KeysAreProperlyMapped test) + .Where(t => t is not PuTTYData_putty) // different mappings (handled by KeysAreProperlyMapped test) .SelectMany(terminal => ThreeCharactersKeys.Select(tuple => new object[] { terminal, tuple.chars, tuple.key })); [Theory] @@ -260,7 +260,7 @@ public void VTSequencesAreMapped(TerminalData terminalData, string input, Consol 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) }); - // Enter using "^[OM" special sequence: they char + // Enter using "^[OM" special sequence: they char yield return ("\u001BOM", new[] { new ConsoleKeyInfo('\r', ConsoleKey.Enter, false, false, false) }); // Escape key pressed multiple times @@ -380,7 +380,7 @@ public void TrickySequencesAreHandledProperly(TerminalData terminalData, string foreach (ConsoleKeyInfo expectedKey in expectedKeys) { - KeyMapper.MapNew(chars, terminalData.TerminalDb, 0, terminalData.Verase, + KeyParser.Parse(chars, terminalData.TerminalDb, 0, terminalData.Verase, out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length); ConsoleKeyInfo parsed = new(ch, consoleKey, isShift, isAlt, isCtrl); diff --git a/src/libraries/System.Console/tests/System.Console.Tests.csproj b/src/libraries/System.Console/tests/System.Console.Tests.csproj index cbd9479baeba2..3770f28cd0661 100644 --- a/src/libraries/System.Console/tests/System.Console.Tests.csproj +++ b/src/libraries/System.Console/tests/System.Console.Tests.csproj @@ -38,9 +38,10 @@ - + + - + Date: Tue, 12 Jul 2022 12:06:57 +0200 Subject: [PATCH 25/40] switch to the new implementation and allow the users to differentiate between Enter (\r) and Ctrl+Enter (\n) like we do on Windows --- src/libraries/System.Console/src/System/IO/StdInReader.cs | 8 +------- src/native/libs/System.Native/pal_console.c | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Console/src/System/IO/StdInReader.cs b/src/libraries/System.Console/src/System/IO/StdInReader.cs index 8f81b6adf478c..0ce4eedbb309d 100644 --- a/src/libraries/System.Console/src/System/IO/StdInReader.cs +++ b/src/libraries/System.Console/src/System/IO/StdInReader.cs @@ -360,15 +360,9 @@ private unsafe ConsoleKeyInfo ReadKey() } } - Net6KeyParser.Parse(_unprocessedBufferToBeRead, ConsolePal.TerminalFormatStringsInstance, ConsolePal.s_posixDisableValue, ConsolePal.s_veraseCharacter, + KeyParser.Parse(_unprocessedBufferToBeRead, ConsolePal.TerminalFormatStringsInstance, ConsolePal.s_posixDisableValue, ConsolePal.s_veraseCharacter, out key, out ch, out isShift, out isAlt, out 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); } finally diff --git a/src/native/libs/System.Native/pal_console.c b/src/native/libs/System.Native/pal_console.c index 1fd55aa3d0430..c53790342ed3b 100644 --- a/src/native/libs/System.Native/pal_console.c +++ b/src/native/libs/System.Native/pal_console.c @@ -168,7 +168,7 @@ static bool ConfigureTerminal(bool signalForBreak, bool forChild, uint8_t minCha if (!forChild) { - termios.c_iflag &= (uint32_t)(~(IXON | IXOFF)); + termios.c_iflag &= (uint32_t)(~(IXON | IXOFF | ICRNL | INLCR | IGNCR)); termios.c_lflag &= (uint32_t)(~(ECHO | ICANON | IEXTEN)); } From 254c7625f543096a55ad6d90433e3d81cfd9d645 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 12 Jul 2022 16:04:03 +0200 Subject: [PATCH 26/40] add way more test cases and fix identified issues remove UXTerm as its the same as Term --- .../ConsolePal.TerminalFormatStrings.cs | 3 + .../System.Console/src/System/IO/KeyParser.cs | 82 +- .../System.Console/tests/KeyParserTests.cs | 1318 +++++++++++++---- 3 files changed, 1082 insertions(+), 321 deletions(-) diff --git a/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs b/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs index 43286323abf6a..936483f2d9419 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs @@ -61,6 +61,8 @@ internal sealed class TerminalFormatStrings 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) { @@ -81,6 +83,7 @@ public TerminalFormatStrings(TermInfo.Database? db) CursorLeft = db.GetString(TermInfo.WellKnownStrings.CursorLeft); ClrEol = db.GetString(TermInfo.WellKnownStrings.ClrEol); + IsRxvtTerm = !string.IsNullOrEmpty(db.Term) && db.Term.IndexOf("rxvt", StringComparison.OrdinalIgnoreCase) >= 0; Title = GetTitle(db); Debug.WriteLineIf(db.GetString(TermInfo.WellKnownStrings.CursorPositionReport) != CursorPositionReport, diff --git a/src/libraries/System.Console/src/System/IO/KeyParser.cs b/src/libraries/System.Console/src/System/IO/KeyParser.cs index 3f834642f44ee..3662e5f04fe60 100644 --- a/src/libraries/System.Console/src/System/IO/KeyParser.cs +++ b/src/libraries/System.Console/src/System/IO/KeyParser.cs @@ -67,18 +67,40 @@ internal static class KeyParser character = default; key = default; - // xterm and VT sequences start with "^[[", some xterm start with "^[O" ("^[" stands for Escape (27)) + // sequences start with either "^[[" or "^[O". "^[" stands for Escape (27). if (input.Length < MinimalSequenceLength || input[0] != Escape || (input[1] != '[' && input[1] != 'O')) { return false; } - // Is it a three character sequence? (examples: '^[[H' (Home), '^[OP' (F1), '^[Ow' (End)) - if (input[1] == 'O' || char.IsAsciiLetter(input[2])) + // Is it a three character sequence? (examples: '^[[H' (Home), '^[OP' (F1)) + if (input[1] == 'O' || char.IsAsciiLetter(input[2]) || input.Length == MinimalSequenceLength) { if (!TryMapUsingTerminfoDb(buffer.AsMemory(startIndex, MinimalSequenceLength), terminalFormatStrings, ref key, ref isShift, ref isAlt, ref isCtrl)) { - key = MapKeyId(input[2]); // fallback to well known mappings + // 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. + if (input[1] == 'O'|| terminalFormatStrings.IsRxvtTerm) + { + key = MapKeyIdOXterm(input[2]); // fallback to well known mappings + + if (key != default) + { + // lowercase characters are used by rxvt to express Shift modifier for the arrow keys + isShift = char.IsBetween(input[2], 'a', 'd'); + } + } + else + { + (key, ConsoleModifiers mod) = MapSCO(input[2]); // fallback to well known mappings + + if (key != default) + { + Apply(mod, ref isShift, ref isAlt, ref isCtrl); + } + } if (key == default) { @@ -90,9 +112,15 @@ internal static class KeyParser return true; } - if (input.Length == MinimalSequenceLength) + // 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')) { - return false; + if (!TryMapUsingTerminfoDb(buffer.AsMemory(startIndex, 4), terminalFormatStrings, ref key, ref isShift, ref isAlt, ref isCtrl)) + { + key = ConsoleKey.F1 + input[3] - 'A'; + } + 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 @@ -142,7 +170,7 @@ internal static class KeyParser key = input[SequencePrefixLength + digitCount + 2] is VtSequenceEndTag ? MapEscapeSequenceNumber(byte.Parse(input.Slice(SequencePrefixLength, digitCount))) - : MapKeyId(input[SequencePrefixLength + digitCount + 2]); + : MapKeyIdOXterm(input[SequencePrefixLength + digitCount + 2]); if (key != default) { @@ -166,24 +194,53 @@ internal static class KeyParser return false; } - static ConsoleKey MapKeyId(char single) - => single switch + // maps "^[O{character}" for all Terminals and "^[[{character}" for rxvt Terminals + static ConsoleKey MapKeyIdOXterm(char character) + => character switch { - // lowercase characters are used by rxvt 'A' or 'a' => ConsoleKey.UpArrow, 'B' or 'b' => ConsoleKey.DownArrow, 'C' or 'c' => ConsoleKey.RightArrow, 'D' or 'd' => ConsoleKey.LeftArrow, 'F' or 'w' => ConsoleKey.End, 'H' => ConsoleKey.Home, - 'M' => ConsoleKey.Enter, 'P' => ConsoleKey.F1, 'Q' => ConsoleKey.F2, 'R' => ConsoleKey.F3, 'S' => ConsoleKey.F4, + 'T' => ConsoleKey.F5, // VT 100+ + 'U' => ConsoleKey.F6, // VT 100+ + 'V' => ConsoleKey.F7, // VT 100+ + 'W' => ConsoleKey.F8, // VT 100+ + 'X' => ConsoleKey.F9, // VT 100+ + 'Y' => ConsoleKey.F10, // VT 100+ + 'Z' => ConsoleKey.F11, // VT 100+ + '[' => ConsoleKey.F12, // 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 + { + 'H' => (ConsoleKey.Home, 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 { @@ -214,10 +271,11 @@ static ConsoleKey MapEscapeSequenceNumber(byte number) 32 => ConsoleKey.F18, 33 => ConsoleKey.F19, 34 => ConsoleKey.F20, - // 9, 16, 22, 27, 30 and 35 have no mapping (https://en.wikipedia.org/wiki/ANSI_escape_code#Fe_Escape_sequences) + // 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 { diff --git a/src/libraries/System.Console/tests/KeyParserTests.cs b/src/libraries/System.Console/tests/KeyParserTests.cs index a260af73ca70b..57e604dde42ec 100644 --- a/src/libraries/System.Console/tests/KeyParserTests.cs +++ b/src/libraries/System.Console/tests/KeyParserTests.cs @@ -15,7 +15,7 @@ public class KeyParserTests { new XTermData(), new GNOMETerminalData(), - new UXTermData(), + new LinuxConsole(), new PuTTYData_xterm(), new PuTTYData_linux(), new PuTTYData_putty(), @@ -35,6 +35,477 @@ public static IEnumerable 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 PuTTyStandardHomeAndEndKeys) + { + yield return new object[] { putty, bytes, cki }; + } + // 2b: rxvt + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTyRxvtHomeAndEndKeys) + { + yield return new object[] { putty, bytes, cki }; + } + + // 3. Terminal => Keyboard => The function keys and keypad + // 3a: ESC[n~ + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTyESCnFunctionKeys) + { + yield return new object[] { putty, bytes, cki }; + } + // 3b: Linux + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTyLinuxFunctionKeys) + { + yield return new object[] { putty, bytes, cki }; + } + // 3c: Xterm R6 + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTyXtermR6FunctionKeys) + { + yield return new object[] { putty, bytes, cki }; + } + // 3d: VT 400 + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTyVT400FunctionKeys) + { + yield return new object[] { putty, bytes, cki }; + } + // 3e: VT 100+ + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTyVT100FunctionKeys) + { + yield return new object[] { putty, bytes, cki }; + } + // 3f: SCO + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTySCOFunctionKeys) + { + yield return new object[] { putty, bytes, cki }; + } + // 3g: Xterm 216+ + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTyXterm216FunctionKeys) + { + 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 PuTTyCtrlTogglesAppModeArrows) + { + yield return new object[] { putty, bytes, cki }; + } + // 4b: xterm-style bitmap does not work as expected in application mode, so we don't test it + } + } + } + + // PuTTy: Home and End keys: Standard + internal static IEnumerable<(byte[], ConsoleKeyInfo)> PuTTyStandardHomeAndEndKeys + { + 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)> PuTTyRxvtHomeAndEndKeys + { + 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)> PuTTyESCnFunctionKeys + { + 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)> PuTTyLinuxFunctionKeys + { + 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)> PuTTyXtermR6FunctionKeys + { + 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)> PuTTyVT400FunctionKeys + { + 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)> PuTTyVT100FunctionKeys + { + 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)> PuTTySCOFunctionKeys + { + 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)> PuTTyXterm216FunctionKeys + { + 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)> PuTTyCtrlTogglesAppModeArrows + { + 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. } } @@ -124,44 +595,6 @@ public void VeraseIsRespected(TerminalData terminalData, char input, ConsoleKey Assert.Equal((ConsoleModifiers)0, consoleKeyInfo.Modifiers); } - private static IEnumerable<(string chars, ConsoleKey key)> ThreeCharactersKeys - { - get - { - // "^[[" - yield return ("\u001B[H", ConsoleKey.Home); - yield return ("\u001B[F", ConsoleKey.End); - yield return ("\u001B[A", ConsoleKey.UpArrow); - yield return ("\u001B[B", ConsoleKey.DownArrow); - yield return ("\u001B[C", ConsoleKey.RightArrow); - yield return ("\u001B[D", ConsoleKey.LeftArrow); - // "^[O" - yield return ("\u001BOP", ConsoleKey.F1); - yield return ("\u001BOQ", ConsoleKey.F2); - yield return ("\u001BOR", ConsoleKey.F3); - yield return ("\u001BOS", ConsoleKey.F4); - yield return ("\u001BOw", ConsoleKey.End); // rxvt - - // "\u001BOM" (enter) is tested elsewhere, as it requires character verification - } - } - - public static IEnumerable ThreeCharactersKeysArguments - => Terminals - .Where(t => t is not PuTTYData_putty) // different mappings (handled by KeysAreProperlyMapped test) - .SelectMany(terminal => ThreeCharactersKeys.Select(tuple => new object[] { terminal, tuple.chars, tuple.key })); - - [Theory] - [MemberData(nameof(ThreeCharactersKeysArguments))] - public void ThreeCharactersKey(TerminalData terminalData, string input, ConsoleKey expectedKey) - { - ConsoleKeyInfo consoleKeyInfo = Map(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 @@ -225,11 +658,11 @@ public void ThreeCharactersKeyRxvt() } } - public static IEnumerable VTSequencesrguments + public static IEnumerable VTSequencesArguments => Terminals.SelectMany(terminal => VTSequences.Select(tuple => new object[] { terminal, tuple.chars, tuple.key })); [Theory] - [MemberData(nameof(VTSequencesrguments))] + [MemberData(nameof(VTSequencesArguments))] public void VTSequencesAreMapped(TerminalData terminalData, string input, ConsoleKey expectedKey) { if (terminalData is RxvtUnicode && input == "\u001B[4~" && expectedKey == ConsoleKey.End) @@ -260,8 +693,6 @@ public void VTSequencesAreMapped(TerminalData terminalData, string input, Consol 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) }); - // Enter using "^[OM" special sequence: they char - yield return ("\u001BOM", new[] { new ConsoleKeyInfo('\r', ConsoleKey.Enter, false, false, false) }); // Escape key pressed multiple times for (int i = 1; i <= 5; i++) @@ -283,44 +714,6 @@ public void VTSequencesAreMapped(TerminalData terminalData, string input, Consol new ConsoleKeyInfo('~', default, false, false, false), }); - // invalid short ID - foreach (char letter in Enumerable.Range('E', 'Z' - 'E')) - { - if (!(letter is 'F' or 'H' or 'M' or 'P' or 'Q' or 'R' or 'S')) // valid letters - { - yield return ($"\u001BO{letter}", new[] - { - new ConsoleKeyInfo('\u001B', ConsoleKey.Escape, false, false, false), - new ConsoleKeyInfo('O', ConsoleKey.O, true, false, false), - new ConsoleKeyInfo(letter, ConsoleKey.A + letter - 'A', true, false, false), - }); - yield return ($"\u001B[{letter}", new[] - { - new ConsoleKeyInfo('\u001B', ConsoleKey.Escape, false, false, false), - new ConsoleKeyInfo('[', default, false, false, false), - new ConsoleKeyInfo(letter, ConsoleKey.A + letter - 'A', true, false, false), - }); - } - } - foreach (char letter in Enumerable.Range('e', 'z' - 'e')) - { - if (letter is not 'w') // only valid letter in this range - { - yield return ($"\u001BO{letter}", new[] - { - new ConsoleKeyInfo('\u001B', ConsoleKey.Escape, false, false, false), - new ConsoleKeyInfo('O', ConsoleKey.O, true, false, false), - new ConsoleKeyInfo(letter, ConsoleKey.A + letter - 'a', false, false, false), - }); - yield return ($"\u001B[{letter}", new[] - { - new ConsoleKeyInfo('\u001B', ConsoleKey.Escape, false, false, false), - new ConsoleKeyInfo('[', default, false, false, false), - new ConsoleKeyInfo(letter, ConsoleKey.A + letter - 'a', false, false, false), - }); - } - } - // Invalid escape sequences: // Invalid modifiers (valid values are <2, 8>) foreach (int invalidModifier in new[] { 0, 1, 9 }) @@ -392,6 +785,30 @@ public void TrickySequencesAreHandledProperly(TerminalData terminalData, string Assert.Equal(chars.Length, startIndex); } + + [Fact] + public void NewLineEscapeSequenceProducesCharacter() + { + XTermData xTerm = new(); + + ConsoleKeyInfo consoleKeyInfo = Map("\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 = Map("\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); + } } public abstract class TerminalData @@ -441,26 +858,103 @@ public class GNOMETerminalData : TerminalData 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[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); - yield return (new byte[] { 27, 91, 50, 52, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); - yield return (new byte[] { 27, 91, 50, 52, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); - yield return (new byte[] { 27, 79, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); - yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); - yield return (new byte[] { 27, 91, 49, 59, 51, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); - yield return (new byte[] { 27, 91, 49, 59, 55, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); - yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); - yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); - yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); - yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, 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, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); - yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, 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 } } } @@ -496,86 +990,151 @@ public class XTermData : TerminalData 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, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); - yield return (new byte[] { 27, 91, 50, 52, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); - yield return (new byte[] { 27, 91, 50, 52, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); yield return (new byte[] { 27, 79, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); - yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); - yield return (new byte[] { 27, 91, 49, 59, 51, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); - yield return (new byte[] { 27, 91, 49, 59, 55, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); - yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); - yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); - yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); - yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, 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, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); - yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, 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 } } } -// Ubuntu 18.04 x64 -public class UXTermData : TerminalData +// 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 => "xterm"; + protected override string Term => "linux"; 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 + 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[] { 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, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); - yield return (new byte[] { 27, 91, 50, 52, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); - yield return (new byte[] { 27, 91, 50, 52, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); - yield return (new byte[] { 27, 79, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); - yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); - yield return (new byte[] { 27, 91, 49, 59, 51, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); - yield return (new byte[] { 27, 91, 49, 59, 55, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); - yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); - yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); - yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); - yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, 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, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); - yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); + 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 } } } -// Windows 11 machine connected via PuTTY to Ubuntu 20.04 arm64 machine using default settings ("xterm" Terminal-type setting) +// 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 => ""; @@ -583,42 +1142,10 @@ public class PuTTYData_xterm : TerminalData 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 - { - 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[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); - yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); - yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); - yield return (new byte[] { 27, 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); - yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); - yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); - yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); - yield return (new byte[] { 27, 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); - } - } + internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios => Array.Empty<(byte[], ConsoleKeyInfo)>(); } -// Windows 11 machine connected via PuTTY to Ubuntu 20.04 arm64 machine using "linux" Terminal-type setting +// Windows 11 machine connected via PuTTY to Ubuntu 20.04 arm64 machine using TERM=linux public class PuTTYData_linux : TerminalData { protected override string EncodingCharset => ""; @@ -626,47 +1153,10 @@ public class PuTTYData_linux : TerminalData 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 - { - 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[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); - yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); - yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); - yield return (new byte[] { 27, 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); - yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); - yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); - yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); - yield return (new byte[] { 27, 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, 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, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); - yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); - } - } + internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios => Array.Empty<(byte[], ConsoleKeyInfo)>(); } -// Windows 11 machine connected via PuTTY to Ubuntu 20.04 arm64 machine using "putty" Terminal-type setting +// Windows 11 machine connected via PuTTY to Ubuntu 20.04 arm64 machine using TERM=putty public class PuTTYData_putty : TerminalData { protected override string EncodingCharset => ""; @@ -674,37 +1164,7 @@ public class PuTTYData_putty : TerminalData 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 - { - 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[] { 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[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); - yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); - yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); - yield return (new byte[] { 27, 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); - yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); - yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); - yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); - } - } + internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios => Array.Empty<(byte[], ConsoleKeyInfo)>(); } // Windows (11) Terminal connected via SSH to Ubuntu 20.04 arm64 @@ -736,26 +1196,108 @@ public class WindowsTerminalData : TerminalData 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[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); - yield return (new byte[] { 27, 91, 50, 52, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); - yield return (new byte[] { 27, 91, 50, 52, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); - yield return (new byte[] { 27, 79, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); - yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); - yield return (new byte[] { 27, 91, 49, 59, 51, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); - yield return (new byte[] { 27, 91, 49, 59, 55, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); - yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); - yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); - yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); - yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, 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, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); - yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, 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 } } } @@ -772,20 +1314,101 @@ public class RxvtUnicode : TerminalData { get { - yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 94 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); - yield return (new byte[] { 27, 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); - yield return (new byte[] { 27, 91, 50, 52, 36 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); - yield return (new byte[] { 27, 27, 91, 50, 52, 64 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); - yield return (new byte[] { 27, 91, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); - yield return (new byte[] { 27, 91, 55, 94 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); - yield return (new byte[] { 27, 27, 91, 55, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); - yield return (new byte[] { 27, 27, 91, 55, 94 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); - yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); - yield return (new byte[] { 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); - yield return (new byte[] { 27, 79, 100 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); - yield return (new byte[] { 27, 27, 91, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); + 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 } } } @@ -802,23 +1425,100 @@ public class TmuxData : TerminalData { get { - yield return (new byte[] { 27, 91, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Delete, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 59, 53, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, false, true)); - yield return (new byte[] { 27, 91, 50, 52, 59, 51, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, false, true, false)); - yield return (new byte[] { 27, 91, 50, 52, 59, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, false, false)); - yield return (new byte[] { 27, 91, 50, 52, 59, 56, 126 }, new ConsoleKeyInfo(default, ConsoleKey.F12, true, true, true)); - yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); - yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); - yield return (new byte[] { 27, 91, 49, 59, 51, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, false)); - yield return (new byte[] { 27, 91, 49, 59, 55, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, true, true)); - yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, false, false, false)); - yield return (new byte[] { 27, 79, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, false)); - yield return (new byte[] { 27, 91, 49, 59, 53, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, false, true)); - yield return (new byte[] { 27, 91, 49, 59, 51, 68 }, new ConsoleKeyInfo(default, ConsoleKey.LeftArrow, false, true, false)); - yield return (new byte[] { 27, 91, 49, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, false)); - yield return (new byte[] { 27, 91, 49, 59, 53, 72 }, new ConsoleKeyInfo(default, ConsoleKey.Home, false, false, true)); - yield return (new byte[] { 27, 91, 50, 126 }, new ConsoleKeyInfo(default, ConsoleKey.Insert, 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, 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 } } } From 4023725427bc4e5b2fd25227e0a7095edf09ac0d Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 13 Jul 2022 17:43:55 +0200 Subject: [PATCH 27/40] polishing: reorder the tests --- .../System.Console/tests/KeyParserTests.cs | 1500 +++++++++-------- 1 file changed, 754 insertions(+), 746 deletions(-) diff --git a/src/libraries/System.Console/tests/KeyParserTests.cs b/src/libraries/System.Console/tests/KeyParserTests.cs index 57e604dde42ec..2b86a15c1c332 100644 --- a/src/libraries/System.Console/tests/KeyParserTests.cs +++ b/src/libraries/System.Console/tests/KeyParserTests.cs @@ -24,6 +24,53 @@ public class KeyParserTests 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 = Map(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 @@ -49,635 +96,161 @@ public static IEnumerable RecordedScenarios { // 2. Terminal => Keyboard => The Home and End keys // 2a: Standard - foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTyStandardHomeAndEndKeys) + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.StandardHomeAndEndKeys) { yield return new object[] { putty, bytes, cki }; } // 2b: rxvt - foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTyRxvtHomeAndEndKeys) + 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 PuTTyESCnFunctionKeys) + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.ESCnFunctionKeys) { yield return new object[] { putty, bytes, cki }; } // 3b: Linux - foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTyLinuxFunctionKeys) + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.LinuxFunctionKeys) { yield return new object[] { putty, bytes, cki }; } // 3c: Xterm R6 - foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTyXtermR6FunctionKeys) + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.XtermR6FunctionKeys) { yield return new object[] { putty, bytes, cki }; } // 3d: VT 400 - foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTyVT400FunctionKeys) + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.VT400FunctionKeys) { yield return new object[] { putty, bytes, cki }; } // 3e: VT 100+ - foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTyVT100FunctionKeys) + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.VT100FunctionKeys) { yield return new object[] { putty, bytes, cki }; } // 3f: SCO - foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTySCOFunctionKeys) + foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTy.SCOFunctionKeys) { yield return new object[] { putty, bytes, cki }; } // 3g: Xterm 216+ - foreach ((byte[] bytes, ConsoleKeyInfo cki) in PuTTyXterm216FunctionKeys) + 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 PuTTyCtrlTogglesAppModeArrows) + 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, so we don't test it + // 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 } } } - // PuTTy: Home and End keys: Standard - internal static IEnumerable<(byte[], ConsoleKeyInfo)> PuTTyStandardHomeAndEndKeys + [Theory] + [MemberData(nameof(RecordedScenarios))] + public void KeysAreProperlyMapped(TerminalData terminalData, byte[] recordedBytes, ConsoleKeyInfo expected) { - 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 - } + char[] encoded = terminalData.ConsoleEncoding.GetString(recordedBytes).ToCharArray(); + + ConsoleKeyInfo actual = Map(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); } - // PuTTy: Home and End keys: rxvt - internal static IEnumerable<(byte[], ConsoleKeyInfo)> PuTTyRxvtHomeAndEndKeys + private static IEnumerable<(string chars, ConsoleKey key)> VTSequences { 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 + 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}~"; } } - // PuTTy: The function keys and keypad: ESC[n~ - internal static IEnumerable<(byte[], ConsoleKeyInfo)> PuTTyESCnFunctionKeys + 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) { - get + if (terminalData is RxvtUnicode && input == "\u001B[4~" && expectedKey == ConsoleKey.End) { - 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 + expectedKey = ConsoleKey.Select; // rxvt binds this key to Select in Terminfo and uses "^[[8~" for End key } + + ConsoleKeyInfo consoleKeyInfo = Map(input.ToCharArray(), terminalData.TerminalDb, terminalData.Verase, input.Length); + + Assert.Equal(expectedKey, consoleKeyInfo.Key); + Assert.Equal(default, consoleKeyInfo.KeyChar); + Assert.Equal(default, consoleKeyInfo.Modifiers); } - // PuTTy: The function keys and keypad: Linux - internal static IEnumerable<(byte[], ConsoleKeyInfo)> PuTTyLinuxFunctionKeys + private static IEnumerable<(string chars, ConsoleKey key)> ThreeCharactersKeysRxvt { 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 + yield return ("\u001BOa", ConsoleKey.UpArrow); + yield return ("\u001BOb", ConsoleKey.DownArrow); + yield return ("\u001BOc", ConsoleKey.RightArrow); + yield return ("\u001BOd", ConsoleKey.LeftArrow); } } - // PuTTy: The function keys and keypad: Xterm R6 - internal static IEnumerable<(byte[], ConsoleKeyInfo)> PuTTyXtermR6FunctionKeys + [Fact] + public void ExtendedStringCodePath() { - get + RxvtUnicode terminalData = new RxvtUnicode(); + + foreach ((string input, ConsoleKey expectedKey) in ThreeCharactersKeysRxvt) { - 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 + ConsoleKeyInfo consoleKeyInfo = Map(input.ToCharArray(), terminalData.TerminalDb, terminalData.Verase, input.Length); + + Assert.Equal(expectedKey, consoleKeyInfo.Key); + Assert.Equal(default, consoleKeyInfo.KeyChar); + Assert.Equal(ConsoleModifiers.Control, consoleKeyInfo.Modifiers); // this particular test excercises the extended strings code path } } - // PuTTy: The function keys and keypad: VT 400 - internal static IEnumerable<(byte[], ConsoleKeyInfo)> PuTTyVT400FunctionKeys - { - 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)> PuTTyVT100FunctionKeys - { - 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)> PuTTySCOFunctionKeys - { - 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)> PuTTyXterm216FunctionKeys - { - 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)> PuTTyCtrlTogglesAppModeArrows - { - 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. - } - } - - [Theory] - [MemberData(nameof(RecordedScenarios))] - public void KeysAreProperlyMapped(TerminalData terminalData, byte[] recordedBytes, ConsoleKeyInfo expected) - { - char[] encoded = terminalData.ConsoleEncoding.GetString(recordedBytes).ToCharArray(); - - ConsoleKeyInfo actual = Map(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 ConsoleKeyInfo Map(char[] chars, ConsolePal.TerminalFormatStrings terminalFormatStrings, byte verase, int expectedStartIndex) - { - int startIndex = 0; - - KeyParser.Parse(chars, terminalFormatStrings, 0, verase, - out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length); - //Assert.True(KeyMapper.MapBufferToConsoleKey(chars, terminalFormatStrings, 0, verase, - // out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length)); - - Assert.Equal(expectedStartIndex, startIndex); - - return new ConsoleKeyInfo(ch, consoleKey, isShift, isAlt, isCtrl); - } - - 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 = Map(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); - } - - [Theory] - [MemberData(nameof(AsciiCharactersArguments))] - public void VeraseIsRespected(TerminalData terminalData, char input, ConsoleKey ifNotVerase) - { - ConsoleKeyInfo consoleKeyInfo = Map(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); - } - - 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 ThreeCharactersKeyRxvt() - { - RxvtUnicode terminalData = new RxvtUnicode(); - - foreach ((string input, ConsoleKey expectedKey) in ThreeCharactersKeysRxvt) - { - ConsoleKeyInfo consoleKeyInfo = Map(input.ToCharArray(), terminalData.TerminalDb, terminalData.Verase, input.Length); - - Assert.Equal(expectedKey, consoleKeyInfo.Key); - Assert.Equal(default, consoleKeyInfo.KeyChar); - Assert.Equal(ConsoleModifiers.Control, consoleKeyInfo.Modifiers); // this particular test excercises the extended strings code path - } - } - - 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 VTSequencesAreMapped(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 = Map(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, ConsoleKeyInfo[] keys)> TrickySequences + private static IEnumerable<(string chars, ConsoleKeyInfo[] keys)> EdgeCaseScenarios { get { @@ -761,12 +334,12 @@ public void VTSequencesAreMapped(TerminalData terminalData, string input, Consol } } - public static IEnumerable TrickySequencesArguments - => Terminals.SelectMany(terminal => TrickySequences.Select(tuple => new object[] { terminal, tuple.chars, tuple.keys })); + public static IEnumerable EdgeCaseScenariosArguments + => Terminals.SelectMany(terminal => EdgeCaseScenarios.Select(tuple => new object[] { terminal, tuple.chars, tuple.keys })); [Theory] - [MemberData(nameof(TrickySequencesArguments))] - public void TrickySequencesAreHandledProperly(TerminalData terminalData, string input, ConsoleKeyInfo[] expectedKeys) + [MemberData(nameof(EdgeCaseScenariosArguments))] + public void EdgeCasesAreProperlyHandled(TerminalData terminalData, string input, ConsoleKeyInfo[] expectedKeys) { int startIndex = 0; char[] chars = input.ToCharArray(); @@ -786,6 +359,18 @@ public void TrickySequencesAreHandledProperly(TerminalData terminalData, string Assert.Equal(chars.Length, startIndex); } + [Theory] + [MemberData(nameof(AsciiCharactersArguments))] + public void VeraseIsRespected(TerminalData terminalData, char input, ConsoleKey ifNotVerase) + { + ConsoleKeyInfo consoleKeyInfo = Map(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() { @@ -801,13 +386,27 @@ public void NewLineEscapeSequenceProducesCharacter() [Fact] public void BackTabEscapeSequence() { - XTermData xTerm = new(); + XTermData xTerm = new(); + + ConsoleKeyInfo consoleKeyInfo = Map("\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 Map(char[] chars, ConsolePal.TerminalFormatStrings terminalFormatStrings, byte verase, int expectedStartIndex) + { + int startIndex = 0; - ConsoleKeyInfo consoleKeyInfo = Map("\u001B[Z".ToCharArray(), xTerm.TerminalDb, xTerm.Verase, 3); + KeyParser.Parse(chars, terminalFormatStrings, 0, verase, + out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length); + //Assert.True(Net6KeyParser.Parse(chars, terminalFormatStrings, 0, verase, + // out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length)); - Assert.Equal(ConsoleKey.Tab, consoleKeyInfo.Key); - Assert.Equal(default, consoleKeyInfo.KeyChar); - Assert.Equal(ConsoleModifiers.Shift, consoleKeyInfo.Modifiers); + Assert.Equal(expectedStartIndex, startIndex); + + return new ConsoleKeyInfo(ch, consoleKey, isShift, isAlt, isCtrl); } } @@ -828,6 +427,9 @@ public abstract class TerminalData 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 { @@ -1170,36 +772,261 @@ public class PuTTYData_putty : TerminalData // 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"; + 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 + } + } +} + +// 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 => "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 + 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[] { 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 @@ -1209,7 +1036,6 @@ public class WindowsTerminalData : TerminalData 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 @@ -1223,15 +1049,8 @@ public class WindowsTerminalData : TerminalData 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 @@ -1243,7 +1062,6 @@ public class WindowsTerminalData : TerminalData 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 @@ -1258,11 +1076,11 @@ public class WindowsTerminalData : TerminalData 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, 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, 79, 70 }, new ConsoleKeyInfo(default, ConsoleKey.End, false, false, false)); // End + 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 @@ -1277,18 +1095,23 @@ public class WindowsTerminalData : TerminalData 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 @@ -1302,15 +1125,42 @@ public class WindowsTerminalData : TerminalData } } -// Ubuntu 18.04 x64 -public class RxvtUnicode : TerminalData +internal static class PuTTy { - 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////////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"; // /lib/terminfo/r/rxvt-unicode-256color + // 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 + } + } - internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios + // 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 { @@ -1326,102 +1176,275 @@ public class RxvtUnicode : TerminalData 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, 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=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 - 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 } } -} -// 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 + // 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 + } + } - internal override IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios + // PuTTy: The function keys and keypad: Xterm 216+ + internal static IEnumerable<(byte[], ConsoleKeyInfo)> Xterm216FunctionKeys { get { @@ -1434,6 +1457,8 @@ public class TmuxData : TerminalData 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 @@ -1447,10 +1472,17 @@ public class TmuxData : TerminalData 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, 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 @@ -1460,65 +1492,41 @@ public class TmuxData : TerminalData 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, 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, 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, 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, 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, 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, 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, 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, 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, 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[] { 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. } } + } From d65c6e71781b301ed49d8b421ea67302c22d534a Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 14 Jul 2022 10:15:33 +0200 Subject: [PATCH 28/40] fix compiler error (it's strange as I was not getting it when I was building Console solution only) --- .../src/System/ConsolePal.TerminalFormatStrings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs b/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs index 936483f2d9419..a84f4517deb67 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs @@ -83,7 +83,7 @@ public TerminalFormatStrings(TermInfo.Database? db) CursorLeft = db.GetString(TermInfo.WellKnownStrings.CursorLeft); ClrEol = db.GetString(TermInfo.WellKnownStrings.ClrEol); - IsRxvtTerm = !string.IsNullOrEmpty(db.Term) && db.Term.IndexOf("rxvt", StringComparison.OrdinalIgnoreCase) >= 0; + IsRxvtTerm = !string.IsNullOrEmpty(db.Term) && db.Term.Contains("rxvt", StringComparison.OrdinalIgnoreCase); Title = GetTitle(db); Debug.WriteLineIf(db.GetString(TermInfo.WellKnownStrings.CursorPositionReport) != CursorPositionReport, From 8cd9e3dadf326a17058ecd3b35040c5405d6eea4 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 14 Jul 2022 11:23:43 +0200 Subject: [PATCH 29/40] add more SCO mappings --- .../System.Console/src/System/IO/KeyParser.cs | 7 ++++ .../System.Console/tests/KeyParserTests.cs | 33 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/libraries/System.Console/src/System/IO/KeyParser.cs b/src/libraries/System.Console/src/System/IO/KeyParser.cs index 3662e5f04fe60..9fe5512ef07b0 100644 --- a/src/libraries/System.Console/src/System/IO/KeyParser.cs +++ b/src/libraries/System.Console/src/System/IO/KeyParser.cs @@ -223,7 +223,14 @@ static ConsoleKey MapKeyIdOXterm(char character) 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), diff --git a/src/libraries/System.Console/tests/KeyParserTests.cs b/src/libraries/System.Console/tests/KeyParserTests.cs index 2b86a15c1c332..e831bd005d67d 100644 --- a/src/libraries/System.Console/tests/KeyParserTests.cs +++ b/src/libraries/System.Console/tests/KeyParserTests.cs @@ -152,6 +152,15 @@ public static IEnumerable RecordedScenarios // 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 }; + } + } } } @@ -1528,5 +1537,29 @@ internal static class PuTTy // 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 + } + } } From 7fea037f25414053a578ea41fc064376ea93da0d Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 14 Jul 2022 13:31:58 +0200 Subject: [PATCH 30/40] Numeric Keypad --- .../System.Console/src/System/IO/KeyParser.cs | 38 +++-- .../System.Console/tests/KeyParserTests.cs | 150 ++++++++++++++++++ 2 files changed, 179 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Console/src/System/IO/KeyParser.cs b/src/libraries/System.Console/src/System/IO/KeyParser.cs index 9fe5512ef07b0..092bc92c5a9e9 100644 --- a/src/libraries/System.Console/src/System/IO/KeyParser.cs +++ b/src/libraries/System.Console/src/System/IO/KeyParser.cs @@ -84,7 +84,7 @@ internal static class KeyParser // Example: "^[[a" is Shift+UpArrow for rxvt and Shift+F3 for SCO. if (input[1] == 'O'|| terminalFormatStrings.IsRxvtTerm) { - key = MapKeyIdOXterm(input[2]); // fallback to well known mappings + key = MapKeyIdOXterm(input[2], terminalFormatStrings.IsRxvtTerm); // fallback to well known mappings if (key != default) { @@ -107,7 +107,15 @@ internal static class KeyParser return false; // it was not a known sequence } } - character = key == ConsoleKey.Enter ? '\r' : default; // "^[OM" should produce new line character (was not previously mapped this way) + character = 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 + }; startIndex += MinimalSequenceLength; return true; } @@ -170,7 +178,7 @@ internal static class KeyParser key = input[SequencePrefixLength + digitCount + 2] is VtSequenceEndTag ? MapEscapeSequenceNumber(byte.Parse(input.Slice(SequencePrefixLength, digitCount))) - : MapKeyIdOXterm(input[SequencePrefixLength + digitCount + 2]); + : MapKeyIdOXterm(input[SequencePrefixLength + digitCount + 2], terminalFormatStrings.IsRxvtTerm); if (key != default) { @@ -195,25 +203,37 @@ internal static class KeyParser } // maps "^[O{character}" for all Terminals and "^[[{character}" for rxvt Terminals - static ConsoleKey MapKeyIdOXterm(char character) + static ConsoleKey MapKeyIdOXterm(char character, bool isRxvt) => character switch { - 'A' or 'a' => ConsoleKey.UpArrow, - 'B' or 'b' => ConsoleKey.DownArrow, - 'C' or 'c' => ConsoleKey.RightArrow, - 'D' or 'd' => ConsoleKey.LeftArrow, - 'F' or 'w' => ConsoleKey.End, + 'A' or 'a' or 'x' => ConsoleKey.UpArrow, // lowercase used by rxvt + 'B' or 'b' or 'r' => ConsoleKey.DownArrow, // lowercase used by rxv + 'C' or 'c' or 'v' => ConsoleKey.RightArrow, // lowercase used by rxv + 'D' or 'd' or 't' => ConsoleKey.LeftArrow, // lowercase used by rxv + 'E' => ConsoleKey.NoName, // ^[OE maps to Begin, but we don't have such Key. To reproduce press Num5. + 'F' or 'q' => ConsoleKey.End, 'H' => ConsoleKey.Home, + 'j' => ConsoleKey.Multiply, // used by both xterm and rxvt + 'k' => ConsoleKey.Add, // used by both xterm and rxvt + 'm' => ConsoleKey.Subtract, // used by both xterm and rxvt + 'n' => ConsoleKey.Delete, // rxvt + 'o' => ConsoleKey.Divide, // used by both xterm and rxvt 'P' => ConsoleKey.F1, + 'p' => ConsoleKey.Insert, // rxvt 'Q' => ConsoleKey.F2, 'R' => ConsoleKey.F3, 'S' => ConsoleKey.F4, + 's' => ConsoleKey.PageDown, // rxvt 'T' => ConsoleKey.F5, // VT 100+ 'U' => ConsoleKey.F6, // VT 100+ + 'u' => ConsoleKey.NoName, // it should be Begin, but we don't have such (press Num5 in rxvt to reproduce) 'V' => ConsoleKey.F7, // VT 100+ 'W' => ConsoleKey.F8, // VT 100+ + 'w' when isRxvt => ConsoleKey.Home, + 'w' when !isRxvt => ConsoleKey.End, 'X' => ConsoleKey.F9, // VT 100+ 'Y' => ConsoleKey.F10, // VT 100+ + 'y' => ConsoleKey.PageUp, // rxvt 'Z' => ConsoleKey.F11, // VT 100+ '[' => ConsoleKey.F12, // VT 100+ _ => default diff --git a/src/libraries/System.Console/tests/KeyParserTests.cs b/src/libraries/System.Console/tests/KeyParserTests.cs index e831bd005d67d..051656786c99b 100644 --- a/src/libraries/System.Console/tests/KeyParserTests.cs +++ b/src/libraries/System.Console/tests/KeyParserTests.cs @@ -566,6 +566,37 @@ public class GNOMETerminalData : TerminalData 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)); + // 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)) } } } @@ -703,6 +734,36 @@ public class XTermData : TerminalData 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[] { 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)) } } } @@ -741,6 +802,35 @@ public class LinuxConsole : TerminalData 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[] { 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)) } } } @@ -1020,6 +1110,36 @@ public class RxvtUnicode : TerminalData 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[] { 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)) } } } @@ -1130,6 +1250,36 @@ public class TmuxData : TerminalData 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[] { 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)) } } } From dd07418ea1b5d5636c564d5cfd596a5bcd0726f0 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 21 Jul 2022 12:37:00 +0200 Subject: [PATCH 31/40] add Tmux 256 color test cases (TERM=screen-256color) --- .../System.Console/tests/KeyParserTests.cs | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/src/libraries/System.Console/tests/KeyParserTests.cs b/src/libraries/System.Console/tests/KeyParserTests.cs index 051656786c99b..62792c984ad65 100644 --- a/src/libraries/System.Console/tests/KeyParserTests.cs +++ b/src/libraries/System.Console/tests/KeyParserTests.cs @@ -21,6 +21,7 @@ public class KeyParserTests new PuTTYData_putty(), new WindowsTerminalData(), new TmuxData(), + new Tmux256ColorData(), new RxvtUnicode(), }; @@ -1284,6 +1285,147 @@ yield return (new byte[] { 27, 79, 107 }, new ConsoleKeyInfo('+', ConsoleKey.Add } } +// 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[] { 10 }, 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 From 632267ce0bce2ead856d97cb98233e6cd1efb991 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 21 Jul 2022 12:57:03 +0200 Subject: [PATCH 32/40] add two Numeric Keypad test cases: period & Enter --- .../System.Console/tests/KeyParserTests.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Console/tests/KeyParserTests.cs b/src/libraries/System.Console/tests/KeyParserTests.cs index 62792c984ad65..e7fda04800bb9 100644 --- a/src/libraries/System.Console/tests/KeyParserTests.cs +++ b/src/libraries/System.Console/tests/KeyParserTests.cs @@ -582,6 +582,8 @@ public class GNOMETerminalData : TerminalData 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 @@ -598,6 +600,7 @@ yield return (new byte[] { 27, 79, 111 }, new ConsoleKeyInfo('/', ConsoleKey.Div 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)) } } } @@ -750,6 +753,8 @@ public class XTermData : TerminalData 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 @@ -765,6 +770,7 @@ yield return (new byte[] { 27, 79, 111 }, new ConsoleKeyInfo('/', ConsoleKey.Div 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)) } } } @@ -818,6 +824,8 @@ public class LinuxConsole : TerminalData 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 @@ -832,6 +840,7 @@ yield return (new byte[] { 47 }, new ConsoleKeyInfo('/', ConsoleKey.Divide, fals 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)) } } } @@ -1126,6 +1135,8 @@ public class RxvtUnicode : TerminalData 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 @@ -1141,6 +1152,7 @@ yield return (new byte[] { 27, 79, 111 }, new ConsoleKeyInfo('/', ConsoleKey.Div 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)) } } } @@ -1266,6 +1278,8 @@ public class TmuxData : TerminalData 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 @@ -1281,6 +1295,7 @@ yield return (new byte[] { 27, 79, 111 }, new ConsoleKeyInfo('/', ConsoleKey.Div 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)) } } } @@ -1404,7 +1419,7 @@ yield return (new byte[] { 47 }, new ConsoleKeyInfo('/', ConsoleKey.Divide, fals 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[] { 10 }, new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false)); // Enter (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 From 69f643449717f52a5dbd11a887697052caf333dc Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 21 Jul 2022 13:22:59 +0200 Subject: [PATCH 33/40] fix issue discovered by adding more tmux test cases: ^[OM should be mapped to Enter --- src/libraries/System.Console/src/System/IO/KeyParser.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Console/src/System/IO/KeyParser.cs b/src/libraries/System.Console/src/System/IO/KeyParser.cs index 092bc92c5a9e9..ab2c95edaf3d9 100644 --- a/src/libraries/System.Console/src/System/IO/KeyParser.cs +++ b/src/libraries/System.Console/src/System/IO/KeyParser.cs @@ -216,6 +216,7 @@ static ConsoleKey MapKeyIdOXterm(char character, bool isRxvt) 'j' => ConsoleKey.Multiply, // used by both xterm and rxvt 'k' => ConsoleKey.Add, // used by both xterm and rxvt 'm' => ConsoleKey.Subtract, // used by both xterm and rxvt + 'M' => ConsoleKey.Enter, // used by xterm, rxvt (they have it Terminfo) and tmux (no record in Terminfo) 'n' => ConsoleKey.Delete, // rxvt 'o' => ConsoleKey.Divide, // used by both xterm and rxvt 'P' => ConsoleKey.F1, From 9c352b4c4add06948cb8000ef4b2ab97e2d36dea Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 21 Jul 2022 13:53:11 +0200 Subject: [PATCH 34/40] address code review feedback: move TerminalFormatStrings classs out if ConsolePal --- .../System.Console/src/System.Console.csproj | 2 +- .../ConsolePal.TerminalFormatStrings.cs | 255 ------------------ .../System.Console/src/System/IO/KeyParser.cs | 6 +- .../src/System/IO/Net6KeyParser.cs | 4 +- .../src/System/TerminalFormatStrings.cs | 252 +++++++++++++++++ .../System.Console/tests/KeyParserTests.cs | 8 +- .../tests/System.Console.Tests.csproj | 2 +- 7 files changed, 263 insertions(+), 266 deletions(-) delete mode 100644 src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs create mode 100644 src/libraries/System.Console/src/System/TerminalFormatStrings.cs diff --git a/src/libraries/System.Console/src/System.Console.csproj b/src/libraries/System.Console/src/System.Console.csproj index 6ac032509dd18..8c8a6553c2a1e 100644 --- a/src/libraries/System.Console/src/System.Console.csproj +++ b/src/libraries/System.Console/src/System.Console.csproj @@ -158,7 +158,7 @@ - + diff --git a/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs b/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs deleted file mode 100644 index a84f4517deb67..0000000000000 --- a/src/libraries/System.Console/src/System/ConsolePal.TerminalFormatStrings.cs +++ /dev/null @@ -1,255 +0,0 @@ -// 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; - -internal static partial class ConsolePal -{ - /// 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"; - } - - 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); - } - } - - 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 index ab2c95edaf3d9..c85c9cb76db69 100644 --- a/src/libraries/System.Console/src/System/IO/KeyParser.cs +++ b/src/libraries/System.Console/src/System/IO/KeyParser.cs @@ -14,7 +14,7 @@ internal static class KeyParser private const int MinimalSequenceLength = 3; private const int SequencePrefixLength = 2; // ^[[ ("^[" stands for Escape) - internal static void Parse(char[] buffer, ConsolePal.TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter, + internal static void Parse(char[] buffer, TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter, out ConsoleKey key, out char character, out bool isShift, out bool isAlt, out bool isCtrl, ref int startIndex, int endIndex) { int length = endIndex - startIndex; @@ -59,7 +59,7 @@ internal static class KeyParser } } - private static bool TryParseTerminalInputSequence(char[] buffer, ConsolePal.TerminalFormatStrings terminalFormatStrings, + private static bool TryParseTerminalInputSequence(char[] buffer, TerminalFormatStrings terminalFormatStrings, out ConsoleKey key, out char character, out bool isShift, out bool isAlt, out bool isCtrl, ref int startIndex, int endIndex) { ReadOnlySpan input = buffer.AsSpan(startIndex, endIndex - startIndex); @@ -189,7 +189,7 @@ internal static class KeyParser return false; - static bool TryMapUsingTerminfoDb(ReadOnlyMemory inputSequence, ConsolePal.TerminalFormatStrings terminalFormatStrings, + static bool TryMapUsingTerminfoDb(ReadOnlyMemory inputSequence, TerminalFormatStrings terminalFormatStrings, ref ConsoleKey key, ref bool isShift, ref bool isAlt, ref bool isCtrl) { // Check if the string prefix matches. diff --git a/src/libraries/System.Console/src/System/IO/Net6KeyParser.cs b/src/libraries/System.Console/src/System/IO/Net6KeyParser.cs index 496d3f1b8f4fb..61afee0cd9803 100644 --- a/src/libraries/System.Console/src/System/IO/Net6KeyParser.cs +++ b/src/libraries/System.Console/src/System/IO/Net6KeyParser.cs @@ -5,7 +5,7 @@ namespace System.IO; internal static class Net6KeyParser { - internal static bool Parse(char[] buffer, ConsolePal.TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter, + internal static bool Parse(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. @@ -51,7 +51,7 @@ internal static class Net6KeyParser } private static bool TryGetSpecialConsoleKey(char[] givenChars, int startIndex, int endIndex, - ConsolePal.TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter, out ConsoleKeyInfo key, out int keyLength) + TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter, out ConsoleKeyInfo key, out int keyLength) { int unprocessedCharCount = endIndex - startIndex; 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..ba8ea8762eaf9 --- /dev/null +++ b/src/libraries/System.Console/src/System/TerminalFormatStrings.cs @@ -0,0 +1,252 @@ +// 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"; + } + + 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); + } + + 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 index e7fda04800bb9..8394618636e81 100644 --- a/src/libraries/System.Console/tests/KeyParserTests.cs +++ b/src/libraries/System.Console/tests/KeyParserTests.cs @@ -405,7 +405,7 @@ public void BackTabEscapeSequence() Assert.Equal(ConsoleModifiers.Shift, consoleKeyInfo.Modifiers); } - private static ConsoleKeyInfo Map(char[] chars, ConsolePal.TerminalFormatStrings terminalFormatStrings, byte verase, int expectedStartIndex) + private static ConsoleKeyInfo Map(char[] chars, TerminalFormatStrings terminalFormatStrings, byte verase, int expectedStartIndex) { int startIndex = 0; @@ -422,7 +422,7 @@ private static ConsoleKeyInfo Map(char[] chars, ConsolePal.TerminalFormatStrings public abstract class TerminalData { - private ConsolePal.TerminalFormatStrings? _terminalDb; + private TerminalFormatStrings? _terminalDb; private Encoding? _consoleEncoding; protected abstract string EncodingCharset { get; } @@ -431,8 +431,8 @@ public abstract class TerminalData internal abstract byte Verase { get; } internal abstract IEnumerable<(byte[], ConsoleKeyInfo)> RecordedScenarios { get; } - internal ConsolePal.TerminalFormatStrings TerminalDb => _terminalDb ??= - new ConsolePal.TerminalFormatStrings(new TermInfo.Database(Term, Convert.FromBase64String(EncodedTerminalDb))); + 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(); } diff --git a/src/libraries/System.Console/tests/System.Console.Tests.csproj b/src/libraries/System.Console/tests/System.Console.Tests.csproj index a4528775f9c99..311abe1aaf873 100644 --- a/src/libraries/System.Console/tests/System.Console.Tests.csproj +++ b/src/libraries/System.Console/tests/System.Console.Tests.csproj @@ -37,7 +37,7 @@ - + From f9749926480e4ac56a2bcc498f7e44eb6ad22977 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 21 Jul 2022 13:59:56 +0200 Subject: [PATCH 35/40] address code review feedback: handle all variants of tmux when getting title --- .../System.Console/src/System/TerminalFormatStrings.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libraries/System.Console/src/System/TerminalFormatStrings.cs b/src/libraries/System.Console/src/System/TerminalFormatStrings.cs index ba8ea8762eaf9..3700e00d595fc 100644 --- a/src/libraries/System.Console/src/System/TerminalFormatStrings.cs +++ b/src/libraries/System.Console/src/System/TerminalFormatStrings.cs @@ -193,6 +193,10 @@ private static string GetTitle(TermInfo.Database db) { term = "xterm"; } + else if (term.StartsWith("screen", StringComparison.Ordinal)) // normalize all tmux configs + { + term = "screen"; + } switch (term) { From b3366a8b28baa6d524d58224ff3fd72d425e79c4 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 21 Jul 2022 14:22:25 +0200 Subject: [PATCH 36/40] address code review feedback: reduce number of out parameters --- .../System.Console/src/System/IO/KeyParser.cs | 239 ++++++++---------- .../src/System/IO/StdInReader.cs | 9 +- .../src/System/TerminalFormatStrings.cs | 2 +- .../System.Console/tests/KeyParserTests.cs | 11 +- 4 files changed, 114 insertions(+), 147 deletions(-) diff --git a/src/libraries/System.Console/src/System/IO/KeyParser.cs b/src/libraries/System.Console/src/System/IO/KeyParser.cs index c85c9cb76db69..4a417b0d1eebc 100644 --- a/src/libraries/System.Console/src/System/IO/KeyParser.cs +++ b/src/libraries/System.Console/src/System/IO/KeyParser.cs @@ -1,6 +1,7 @@ // 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; @@ -14,8 +15,7 @@ internal static class KeyParser private const int MinimalSequenceLength = 3; private const int SequencePrefixLength = 2; // ^[[ ("^[" stands for Escape) - internal static void Parse(char[] buffer, TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter, - out ConsoleKey key, out char character, out bool isShift, out bool isAlt, out bool isCtrl, ref int startIndex, int endIndex) + 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); @@ -23,49 +23,38 @@ internal static class KeyParser // VERASE overrides anything from Terminfo. Both settings can be different for Linux and macOS. if (buffer[startIndex] != posixDisableValue && buffer[startIndex] == veraseCharacter) { - isShift = isAlt = isCtrl = false; - character = buffer[startIndex++]; // the original char is preserved on purpose (backward compat + consistency) - key = ConsoleKey.Backspace; - return; + // 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 use 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 key, out character, out isShift, out _, out isCtrl, ref startIndex, endIndex)) + if (TryParseTerminalInputSequence(buffer, terminalFormatStrings, out ConsoleKeyInfo parsed, ref startIndex, endIndex)) { - isAlt = true; - return; + 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 key, out character, out isShift, out isAlt, out isCtrl, ref startIndex, endIndex)) + else if (length >= MinimalSequenceLength && TryParseTerminalInputSequence(buffer, terminalFormatStrings, out ConsoleKeyInfo parsed, ref startIndex, endIndex)) { - return; + return parsed; } if (length == 2 && buffer[startIndex] == Escape && buffer[startIndex + 1] != Escape) { - ParseFromSingleChar(buffer[++startIndex], out key, out character, out isShift, out isCtrl); - startIndex++; - isAlt = key != default; // two char sequences starting with Escape are Alt+$Key - } - else - { - ParseFromSingleChar(buffer[startIndex], out key, out character, out isShift, out isCtrl); - startIndex++; - isAlt = false; + 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 ConsoleKey key, out char character, out bool isShift, out bool isAlt, out bool isCtrl, ref int startIndex, int endIndex) + private static bool TryParseTerminalInputSequence(char[] buffer, TerminalFormatStrings terminalFormatStrings, out ConsoleKeyInfo parsed, ref int startIndex, int endIndex) { ReadOnlySpan input = buffer.AsSpan(startIndex, endIndex - startIndex); - isShift = isAlt = isCtrl = false; - character = default; - key = default; + parsed = default; // sequences start with either "^[[" or "^[O". "^[" stands for Escape (27). if (input.Length < MinimalSequenceLength || input[0] != Escape || (input[1] != '[' && input[1] != 'O')) @@ -73,49 +62,40 @@ internal static class KeyParser 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 (!TryMapUsingTerminfoDb(buffer.AsMemory(startIndex, MinimalSequenceLength), terminalFormatStrings, ref key, ref isShift, ref isAlt, ref isCtrl)) + 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. - if (input[1] == 'O'|| terminalFormatStrings.IsRxvtTerm) - { - key = MapKeyIdOXterm(input[2], terminalFormatStrings.IsRxvtTerm); // fallback to well known mappings - - if (key != default) - { - // lowercase characters are used by rxvt to express Shift modifier for the arrow keys - isShift = char.IsBetween(input[2], 'a', 'd'); - } - } - else - { - (key, ConsoleModifiers mod) = MapSCO(input[2]); // fallback to well known mappings - - if (key != default) - { - Apply(mod, ref isShift, ref isAlt, ref isCtrl); - } - } + (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); } - character = 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 - }; + startIndex += MinimalSequenceLength; return true; } @@ -123,10 +103,11 @@ internal static class KeyParser // 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 (!TryMapUsingTerminfoDb(buffer.AsMemory(startIndex, 4), terminalFormatStrings, ref key, ref isShift, ref isAlt, ref isCtrl)) + if (!terminfoDb.TryGetValue(buffer.AsMemory(startIndex, 4), out parsed)) { - key = ConsoleKey.F1 + input[3] - 'A'; + parsed = new ConsoleKeyInfo(default, ConsoleKey.F1 + input[3] - 'A', false, false, false); } + startIndex += 4; return true; } @@ -139,25 +120,27 @@ internal static class KeyParser 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 (input[SequencePrefixLength + digitCount] is VtSequenceEndTag or '^' or '$' or '@') // it's a VT Sequence like ^[[11~ or rxvt like ^[[11^ { int sequenceLength = SequencePrefixLength + digitCount + 1; - if (!TryMapUsingTerminfoDb(buffer.AsMemory(startIndex, sequenceLength), terminalFormatStrings, ref key, ref isShift, ref isAlt, ref isCtrl)) + 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 (input[SequencePrefixLength + digitCount] is '^' or '$' or '@') // rxvt modifiers - { - Apply(MapRxvtModifiers(input[SequencePrefixLength + digitCount]), ref isShift, ref isAlt, ref isCtrl); + if (input[SequencePrefixLength + digitCount] is '^' or '$' or '@') // rxvt modifiers + { + modifiers = MapRxvtModifiers(input[SequencePrefixLength + digitCount]); + } + + parsed = Create(default, key, modifiers); } startIndex += sequenceLength; @@ -174,69 +157,60 @@ internal static class KeyParser return false; } - ConsoleModifiers modifiers = MapXtermModifiers(input[SequencePrefixLength + digitCount + 1]); + 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); - - if (key != default) - { - startIndex += SequencePrefixLength + digitCount + 3; // 3 stands for separator, modifier and end tag or id - Apply(modifiers, ref isShift, ref isAlt, ref isCtrl); - return true; - } - - return false; + : MapKeyIdOXterm(input[SequencePrefixLength + digitCount + 2], terminalFormatStrings.IsRxvtTerm).key; - static bool TryMapUsingTerminfoDb(ReadOnlyMemory inputSequence, TerminalFormatStrings terminalFormatStrings, - ref ConsoleKey key, ref bool isShift, ref bool isAlt, ref bool isCtrl) + if (key == default) { - // Check if the string prefix matches. - if (terminalFormatStrings.KeyFormatToConsoleKey.TryGetValue(inputSequence, out ConsoleKeyInfo consoleKeyInfo)) - { - key = consoleKeyInfo.Key; - Apply(consoleKeyInfo.Modifiers, ref isShift, ref isAlt, ref isCtrl); - return true; - } 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 MapKeyIdOXterm(char character, bool isRxvt) + static (ConsoleKey key, ConsoleModifiers modifiers) MapKeyIdOXterm(char character, bool isRxvt) => character switch { - 'A' or 'a' or 'x' => ConsoleKey.UpArrow, // lowercase used by rxvt - 'B' or 'b' or 'r' => ConsoleKey.DownArrow, // lowercase used by rxv - 'C' or 'c' or 'v' => ConsoleKey.RightArrow, // lowercase used by rxv - 'D' or 'd' or 't' => ConsoleKey.LeftArrow, // lowercase used by rxv - 'E' => ConsoleKey.NoName, // ^[OE maps to Begin, but we don't have such Key. To reproduce press Num5. - 'F' or 'q' => ConsoleKey.End, - 'H' => ConsoleKey.Home, - 'j' => ConsoleKey.Multiply, // used by both xterm and rxvt - 'k' => ConsoleKey.Add, // used by both xterm and rxvt - 'm' => ConsoleKey.Subtract, // used by both xterm and rxvt - 'M' => ConsoleKey.Enter, // used by xterm, rxvt (they have it Terminfo) and tmux (no record in Terminfo) - 'n' => ConsoleKey.Delete, // rxvt - 'o' => ConsoleKey.Divide, // used by both xterm and rxvt - 'P' => ConsoleKey.F1, - 'p' => ConsoleKey.Insert, // rxvt - 'Q' => ConsoleKey.F2, - 'R' => ConsoleKey.F3, - 'S' => ConsoleKey.F4, - 's' => ConsoleKey.PageDown, // rxvt - 'T' => ConsoleKey.F5, // VT 100+ - 'U' => ConsoleKey.F6, // VT 100+ - 'u' => ConsoleKey.NoName, // it should be Begin, but we don't have such (press Num5 in rxvt to reproduce) - 'V' => ConsoleKey.F7, // VT 100+ - 'W' => ConsoleKey.F8, // VT 100+ - 'w' when isRxvt => ConsoleKey.Home, - 'w' when !isRxvt => ConsoleKey.End, - 'X' => ConsoleKey.F9, // VT 100+ - 'Y' => ConsoleKey.F10, // VT 100+ - 'y' => ConsoleKey.PageUp, // rxvt - 'Z' => ConsoleKey.F11, // VT 100+ - '[' => ConsoleKey.F12, // VT 100+ + '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 }; @@ -326,20 +300,16 @@ static ConsoleModifiers MapRxvtModifiers(char modifier) _ => default }; - static void Apply(ConsoleModifiers modifiers, ref bool isShift, ref bool isAlt, ref bool isCtrl) - { - isShift = (modifiers & ConsoleModifiers.Shift) != 0; - isAlt = (modifiers & ConsoleModifiers.Alt) != 0; - isCtrl = (modifiers & ConsoleModifiers.Control) != 0; - } + 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 void ParseFromSingleChar(char single, out ConsoleKey key, out char ch, out bool isShift, out bool isCtrl) + private static ConsoleKeyInfo ParseFromSingleChar(char single, bool isAlt) { - isShift = isCtrl = false; - ch = single; + bool isShift = false, isCtrl = false; + char keyChar = single; - key = single switch + ConsoleKey key = single switch { '\b' => ConsoleKey.Backspace, '\t' => ConsoleKey.Tab, @@ -358,9 +328,9 @@ private static void ParseFromSingleChar(char single, out ConsoleKey key, out cha _ 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 ch, out isCtrl), - _ when char.IsBetween(single, (char)28, (char)31) => ControlAndDigitPressed(single, out ch, out isCtrl), - '\u0000' => ControlAndDigitPressed(single, out ch, out isCtrl), + _ 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 }; @@ -369,6 +339,13 @@ private static void ParseFromSingleChar(char single, out ConsoleKey key, out cha 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. @@ -377,7 +354,7 @@ static ConsoleKey UppercaseCharacter(char single, out bool isShift) return ConsoleKey.A + single - 'A'; } - static ConsoleKey ControlAndLetterPressed(char single, out char ch, out bool isCtrl) + 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. @@ -388,18 +365,18 @@ static ConsoleKey ControlAndLetterPressed(char single, out char ch, out bool isC Debug.Assert(single != 'b' && single != '\t' && single != '\n' && single != '\r'); isCtrl = true; - ch = default; // we could use the letter here, but it's impossible to distinguish upper vs lowercase (and Windows doesn't do it as well) + 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 ch, out bool isCtrl) + 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; - ch = default; // consistent with Windows + keyChar = default; // consistent with Windows return single switch { '\u0000' => ConsoleKey.D2, // was not previously mapped this way diff --git a/src/libraries/System.Console/src/System/IO/StdInReader.cs b/src/libraries/System.Console/src/System/IO/StdInReader.cs index ecab465302dc8..d194ddd14bdb7 100644 --- a/src/libraries/System.Console/src/System/IO/StdInReader.cs +++ b/src/libraries/System.Console/src/System/IO/StdInReader.cs @@ -330,10 +330,6 @@ private unsafe ConsoleKeyInfo ReadKey() Interop.Sys.InitializeConsoleBeforeRead(); try { - ConsoleKey key; - char ch; - bool isAlt, isCtrl, isShift; - if (IsUnprocessedBufferEmpty()) { // Read in bytes @@ -357,10 +353,7 @@ private unsafe ConsoleKeyInfo ReadKey() } } - KeyParser.Parse(_unprocessedBufferToBeRead, ConsolePal.TerminalFormatStringsInstance, ConsolePal.s_posixDisableValue, ConsolePal.s_veraseCharacter, - out key, out ch, out isShift, out isAlt, out isCtrl, ref _startIndex, _endIndex); - - return new ConsoleKeyInfo(ch, key, isShift, isAlt, isCtrl); + return KeyParser.Parse(_unprocessedBufferToBeRead, ConsolePal.TerminalFormatStringsInstance, ConsolePal.s_posixDisableValue, ConsolePal.s_veraseCharacter, ref _startIndex, _endIndex); } finally { diff --git a/src/libraries/System.Console/src/System/TerminalFormatStrings.cs b/src/libraries/System.Console/src/System/TerminalFormatStrings.cs index 3700e00d595fc..897f67c733e8b 100644 --- a/src/libraries/System.Console/src/System/TerminalFormatStrings.cs +++ b/src/libraries/System.Console/src/System/TerminalFormatStrings.cs @@ -226,7 +226,7 @@ private void AddKey(TermInfo.Database db, TermInfo.WellKnownStrings keyId, Conso { ReadOnlyMemory keyFormat = db.GetString(keyId).AsMemory(); if (!keyFormat.IsEmpty) - KeyFormatToConsoleKey[keyFormat] = new ConsoleKeyInfo('\0', key, shift, alt, control); + KeyFormatToConsoleKey[keyFormat] = new ConsoleKeyInfo(key == ConsoleKey.Enter ? '\r' : '\0', key, shift, alt, control); } private void AddPrefixKey(TermInfo.Database db, string extendedNamePrefix, ConsoleKey key) diff --git a/src/libraries/System.Console/tests/KeyParserTests.cs b/src/libraries/System.Console/tests/KeyParserTests.cs index 8394618636e81..bb01faaeb74b3 100644 --- a/src/libraries/System.Console/tests/KeyParserTests.cs +++ b/src/libraries/System.Console/tests/KeyParserTests.cs @@ -356,10 +356,7 @@ public void EdgeCasesAreProperlyHandled(TerminalData terminalData, string input, foreach (ConsoleKeyInfo expectedKey in expectedKeys) { - KeyParser.Parse(chars, terminalData.TerminalDb, 0, terminalData.Verase, - out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length); - - ConsoleKeyInfo parsed = new(ch, consoleKey, isShift, isAlt, isCtrl); + 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); @@ -409,14 +406,14 @@ private static ConsoleKeyInfo Map(char[] chars, TerminalFormatStrings terminalFo { int startIndex = 0; - KeyParser.Parse(chars, terminalFormatStrings, 0, verase, - out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length); + ConsoleKeyInfo parsed = KeyParser.Parse(chars, terminalFormatStrings, 0, verase, ref startIndex, chars.Length); + //Assert.True(Net6KeyParser.Parse(chars, terminalFormatStrings, 0, verase, // out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length)); Assert.Equal(expectedStartIndex, startIndex); - return new ConsoleKeyInfo(ch, consoleKey, isShift, isAlt, isCtrl); + return parsed; } } From dc82fb5988ad70aa4dc39a30c98b58ca6acdd8cb Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 21 Jul 2022 15:45:20 +0200 Subject: [PATCH 37/40] fix TermInfo tests --- src/libraries/System.Console/tests/TermInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Console/tests/TermInfo.cs b/src/libraries/System.Console/tests/TermInfo.cs index c3b1201d8b527..14c7d8e79008c 100644 --- a/src/libraries/System.Console/tests/TermInfo.cs +++ b/src/libraries/System.Console/tests/TermInfo.cs @@ -15,7 +15,7 @@ public class TermInfo private const string TerminfoDatabaseFactoryType = TerminfoType + "+DatabaseFactory"; private const string ParameterizedStringsType = TerminfoType + "+ParameterizedStrings"; private const string FormatParamType = ParameterizedStringsType + "+FormatParam"; - private const string TerminalFormatStringsType = "System.ConsolePal+TerminalFormatStrings"; + private const string TerminalFormatStringsType = "System.TerminalFormatStrings"; private const string ReadDatabaseMethod = "ReadDatabase"; private const string EvaluateMethod = "Evaluate"; private const string ForegroundFormatField = "Foreground"; From 214da946ff849e6b0283d10ef8c5d978089ac6f1 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 21 Jul 2022 17:25:51 +0200 Subject: [PATCH 38/40] add .NET 6 compat switch --- .../Interop.ReadStdinUnbuffered.cs | 2 +- .../Interop.SetSignalForBreak.cs | 2 +- .../Unix/System.Native/Interop.StdinReady.cs | 2 +- .../Common/src/System/Console/ConsoleUtils.cs | 28 +++++++++++++++++++ .../src/System/ConsolePal.Unix.cs | 6 ++-- .../src/System/IO/Net6KeyParser.cs | 18 ++++++++++-- .../src/System/IO/StdInReader.cs | 11 +++++--- .../System.Console/tests/KeyParserTests.cs | 3 -- src/native/libs/System.Native/pal_console.c | 26 +++++++++++------ src/native/libs/System.Native/pal_console.h | 6 ++-- 10 files changed, 77 insertions(+), 27 deletions(-) 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/ConsolePal.Unix.cs b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs index 50e6639a11b2c..6c5783bf30c1a 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs @@ -153,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()); } } @@ -479,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. @@ -554,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 { diff --git a/src/libraries/System.Console/src/System/IO/Net6KeyParser.cs b/src/libraries/System.Console/src/System/IO/Net6KeyParser.cs index 61afee0cd9803..7420978e566e4 100644 --- a/src/libraries/System.Console/src/System/IO/Net6KeyParser.cs +++ b/src/libraries/System.Console/src/System/IO/Net6KeyParser.cs @@ -5,7 +5,21 @@ namespace System.IO; internal static class Net6KeyParser { - internal static bool Parse(char[] buffer, TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter, + 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. @@ -26,7 +40,7 @@ internal static class Net6KeyParser endIndex - startIndex >= 2) // We have at least two characters to read { startIndex++; - if (Parse(buffer, terminalFormatStrings, posixDisableValue, veraseCharacter, out key, out ch, out isShift, out _, out isCtrl, ref startIndex, endIndex)) + if (MapBufferToConsoleKey(buffer, terminalFormatStrings, posixDisableValue, veraseCharacter, out key, out ch, out isShift, out _, out isCtrl, ref startIndex, endIndex)) { isAlt = true; return true; diff --git a/src/libraries/System.Console/src/System/IO/StdInReader.cs b/src/libraries/System.Console/src/System/IO/StdInReader.cs index d194ddd14bdb7..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. @@ -327,7 +327,8 @@ private unsafe ConsoleKeyInfo ReadKey() { Debug.Assert(_availableKeys.Count == 0); - Interop.Sys.InitializeConsoleBeforeRead(); + bool useNet6KeyParser = ConsoleUtils.UseNet6KeyParser; + Interop.Sys.InitializeConsoleBeforeRead(distinguishNewLines: !useNet6KeyParser); try { if (IsUnprocessedBufferEmpty()) @@ -353,7 +354,9 @@ private unsafe ConsoleKeyInfo ReadKey() } } - return KeyParser.Parse(_unprocessedBufferToBeRead, ConsolePal.TerminalFormatStringsInstance, ConsolePal.s_posixDisableValue, ConsolePal.s_veraseCharacter, ref _startIndex, _endIndex); + 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 { @@ -362,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/tests/KeyParserTests.cs b/src/libraries/System.Console/tests/KeyParserTests.cs index bb01faaeb74b3..506d2fd0e6985 100644 --- a/src/libraries/System.Console/tests/KeyParserTests.cs +++ b/src/libraries/System.Console/tests/KeyParserTests.cs @@ -408,9 +408,6 @@ private static ConsoleKeyInfo Map(char[] chars, TerminalFormatStrings terminalFo ConsoleKeyInfo parsed = KeyParser.Parse(chars, terminalFormatStrings, 0, verase, ref startIndex, chars.Length); - //Assert.True(Net6KeyParser.Parse(chars, terminalFormatStrings, 0, verase, - // out ConsoleKey consoleKey, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref startIndex, chars.Length)); - Assert.Equal(expectedStartIndex, startIndex); return parsed; diff --git a/src/native/libs/System.Native/pal_console.c b/src/native/libs/System.Native/pal_console.c index c53790342ed3b..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 | ICRNL | INLCR | IGNCR)); + 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 { From 7766ed384b651e7f96f8d289612316fe2500299a Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 1 Aug 2022 10:28:33 +0200 Subject: [PATCH 39/40] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David CantĂș --- src/libraries/System.Console/src/System/IO/KeyParser.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Console/src/System/IO/KeyParser.cs b/src/libraries/System.Console/src/System/IO/KeyParser.cs index 4a417b0d1eebc..64206507b8432 100644 --- a/src/libraries/System.Console/src/System/IO/KeyParser.cs +++ b/src/libraries/System.Console/src/System/IO/KeyParser.cs @@ -27,7 +27,7 @@ internal static ConsoleKeyInfo Parse(char[] buffer, TerminalFormatStrings termin return new ConsoleKeyInfo(buffer[startIndex++], ConsoleKey.Backspace, false, false, false); } - // Escape Sequences start with Escape. But some terminals like PuTTY and rxvt use Escape to express that for given sequence Alt was pressed. + // 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++; @@ -75,7 +75,7 @@ private static bool TryParseTerminalInputSequence(char[] buffer, TerminalFormatS // 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 + (key, modifiers) = input[1] == 'O' || terminalFormatStrings.IsRxvtTerm ? MapKeyIdOXterm(input[2], terminalFormatStrings.IsRxvtTerm) : MapSCO(input[2]); @@ -301,7 +301,7 @@ static ConsoleModifiers MapRxvtModifiers(char modifier) }; static ConsoleKeyInfo Create(char keyChar, ConsoleKey key, ConsoleModifiers modifiers) - => new (keyChar, key, (modifiers & ConsoleModifiers.Shift) != 0, (modifiers & ConsoleModifiers.Alt) != 0, (modifiers & ConsoleModifiers.Control) != 0); + => new(keyChar, key, (modifiers & ConsoleModifiers.Shift) != 0, (modifiers & ConsoleModifiers.Alt) != 0, (modifiers & ConsoleModifiers.Control) != 0); } private static ConsoleKeyInfo ParseFromSingleChar(char single, bool isAlt) From c8010c5bf26a81a716c9a21ed485805fce7b10c3 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 1 Aug 2022 10:35:41 +0200 Subject: [PATCH 40/40] address code review feedback --- .../System.Console/src/System/IO/KeyParser.cs | 9 +++++++-- .../System.Console/tests/KeyParserTests.cs | 18 +++++++++--------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Console/src/System/IO/KeyParser.cs b/src/libraries/System.Console/src/System/IO/KeyParser.cs index 64206507b8432..d54e0800a42b9 100644 --- a/src/libraries/System.Console/src/System/IO/KeyParser.cs +++ b/src/libraries/System.Console/src/System/IO/KeyParser.cs @@ -124,8 +124,9 @@ private static bool TryParseTerminalInputSequence(char[] buffer, TerminalFormatS return false; } - if (input[SequencePrefixLength + digitCount] is VtSequenceEndTag or '^' or '$' or '@') // it's a VT Sequence like ^[[11~ or rxvt like ^[[11^ + 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)) { @@ -135,7 +136,7 @@ private static bool TryParseTerminalInputSequence(char[] buffer, TerminalFormatS return false; // it was not a known sequence } - if (input[SequencePrefixLength + digitCount] is '^' or '$' or '@') // rxvt modifiers + if (IsRxvtModifier(input[SequencePrefixLength + digitCount])) { modifiers = MapRxvtModifiers(input[SequencePrefixLength + digitCount]); } @@ -291,6 +292,10 @@ static ConsoleModifiers MapXtermModifiers(char modifier) _ => 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 { diff --git a/src/libraries/System.Console/tests/KeyParserTests.cs b/src/libraries/System.Console/tests/KeyParserTests.cs index 506d2fd0e6985..f5cebdeeff17e 100644 --- a/src/libraries/System.Console/tests/KeyParserTests.cs +++ b/src/libraries/System.Console/tests/KeyParserTests.cs @@ -65,7 +65,7 @@ public static IEnumerable AsciiCharactersArguments [MemberData(nameof(AsciiCharactersArguments))] public void AsciiCharacters(TerminalData terminalData, char input, ConsoleKey expectedKey) { - ConsoleKeyInfo consoleKeyInfo = Map(new[] { input }, terminalData.TerminalDb, terminalData.Verase, 1); + ConsoleKeyInfo consoleKeyInfo = Parse(new[] { input }, terminalData.TerminalDb, terminalData.Verase, 1); Assert.Equal(input, consoleKeyInfo.KeyChar); Assert.Equal(expectedKey, consoleKeyInfo.Key); @@ -171,7 +171,7 @@ public void KeysAreProperlyMapped(TerminalData terminalData, byte[] recordedByte { char[] encoded = terminalData.ConsoleEncoding.GetString(recordedBytes).ToCharArray(); - ConsoleKeyInfo actual = Map(encoded, terminalData.TerminalDb, terminalData.Verase, encoded.Length); + ConsoleKeyInfo actual = Parse(encoded, terminalData.TerminalDb, terminalData.Verase, encoded.Length); Assert.Equal(expected.Key, actual.Key); Assert.Equal(expected.Modifiers, actual.Modifiers); @@ -227,7 +227,7 @@ public void VTSequencesAreProperlyMapped(TerminalData terminalData, string input expectedKey = ConsoleKey.Select; // rxvt binds this key to Select in Terminfo and uses "^[[8~" for End key } - ConsoleKeyInfo consoleKeyInfo = Map(input.ToCharArray(), terminalData.TerminalDb, terminalData.Verase, input.Length); + ConsoleKeyInfo consoleKeyInfo = Parse(input.ToCharArray(), terminalData.TerminalDb, terminalData.Verase, input.Length); Assert.Equal(expectedKey, consoleKeyInfo.Key); Assert.Equal(default, consoleKeyInfo.KeyChar); @@ -252,11 +252,11 @@ public void ExtendedStringCodePath() foreach ((string input, ConsoleKey expectedKey) in ThreeCharactersKeysRxvt) { - ConsoleKeyInfo consoleKeyInfo = Map(input.ToCharArray(), terminalData.TerminalDb, terminalData.Verase, input.Length); + 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); // this particular test excercises the extended strings code path + Assert.Equal(ConsoleModifiers.Control, consoleKeyInfo.Modifiers); } } @@ -370,7 +370,7 @@ public void EdgeCasesAreProperlyHandled(TerminalData terminalData, string input, [MemberData(nameof(AsciiCharactersArguments))] public void VeraseIsRespected(TerminalData terminalData, char input, ConsoleKey ifNotVerase) { - ConsoleKeyInfo consoleKeyInfo = Map(new[] { input }, terminalData.TerminalDb, (byte)input, 1); + ConsoleKeyInfo consoleKeyInfo = Parse(new[] { input }, terminalData.TerminalDb, (byte)input, 1); Assert.Equal(input, consoleKeyInfo.KeyChar); Assert.Equal(ConsoleKey.Backspace, consoleKeyInfo.Key); @@ -383,7 +383,7 @@ public void NewLineEscapeSequenceProducesCharacter() { XTermData xTerm = new(); - ConsoleKeyInfo consoleKeyInfo = Map("\u001BOM".ToCharArray(), xTerm.TerminalDb, xTerm.Verase, 3); + ConsoleKeyInfo consoleKeyInfo = Parse("\u001BOM".ToCharArray(), xTerm.TerminalDb, xTerm.Verase, 3); Assert.Equal(ConsoleKey.Enter, consoleKeyInfo.Key); Assert.Equal('\r', consoleKeyInfo.KeyChar); @@ -395,14 +395,14 @@ public void BackTabEscapeSequence() { XTermData xTerm = new(); - ConsoleKeyInfo consoleKeyInfo = Map("\u001B[Z".ToCharArray(), xTerm.TerminalDb, xTerm.Verase, 3); + 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 Map(char[] chars, TerminalFormatStrings terminalFormatStrings, byte verase, int expectedStartIndex) + private static ConsoleKeyInfo Parse(char[] chars, TerminalFormatStrings terminalFormatStrings, byte verase, int expectedStartIndex) { int startIndex = 0;