コード例 #1
0
ファイル: KeyParser.cs プロジェクト: mikem8361/runtime
    private static bool TryParseTerminalInputSequence(char[] buffer, TerminalFormatStrings terminalFormatStrings, out ConsoleKeyInfo parsed, ref int startIndex, int endIndex)
    {
        ReadOnlySpan <char> input = buffer.AsSpan(startIndex, endIndex - startIndex);

        parsed = default;

        // sequences start with either "^[[" or "^[O". "^[" stands for Escape (27).
        if (input.Length < MinimalSequenceLength || input[0] != Escape || (input[1] != '[' && input[1] != 'O'))
        {
            return(false);
        }

        Dictionary <ReadOnlyMemory <char>, ConsoleKeyInfo> terminfoDb = terminalFormatStrings.KeyFormatToConsoleKey; // the most important source of truth
        ConsoleModifiers modifiers = 0;
        ConsoleKey       key;

        // Is it a three character sequence? (examples: '^[[H' (Home), '^[OP' (F1))
        if (input[1] == 'O' || char.IsAsciiLetter(input[2]) || input.Length == MinimalSequenceLength)
        {
            if (!terminfoDb.TryGetValue(buffer.AsMemory(startIndex, MinimalSequenceLength), out parsed))
            {
                // All terminals which use "^[O{letter}" escape sequences don't define conflicting mappings.
                // Example: ^[OH either means Home or simply is not used by given terminal.
                // But with "^[[{character}" sequences, there are conflicts between rxvt and SCO.
                // Example: "^[[a" is Shift+UpArrow for rxvt and Shift+F3 for SCO.
                (key, modifiers) = input[1] == 'O' || terminalFormatStrings.IsRxvtTerm
                    ? MapKeyIdOXterm(input[2], terminalFormatStrings.IsRxvtTerm)
                    : MapSCO(input[2]);

                if (key == default)
                {
                    return(false); // it was not a known sequence
                }

                char keyChar = key switch
                {
                    ConsoleKey.Enter => '\r', // "^[OM" should produce new line character (was not previously mapped this way)
                    ConsoleKey.Add => '+',
                    ConsoleKey.Subtract => '-',
                    ConsoleKey.Divide => '/',
                    ConsoleKey.Multiply => '*',
                    _ => default
                };
                parsed = Create(keyChar, key, modifiers);
            }

            startIndex += MinimalSequenceLength;
            return(true);
        }

        // Is it a four character sequence used by Linux Console or PuTTy configured to emulate it? (examples: '^[[[A' (F1), '^[[[B' (F2))
        if (input[1] == '[' && input[2] == '[' && char.IsBetween(input[3], 'A', 'E'))
        {
            if (!terminfoDb.TryGetValue(buffer.AsMemory(startIndex, 4), out parsed))
            {
                parsed = new ConsoleKeyInfo(default, ConsoleKey.F1 + input[3] - 'A', false, false, false);
コード例 #2
0
    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));
    }
コード例 #3
0
    private static bool MapBufferToConsoleKey(char[] buffer, TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter,
                                              out ConsoleKey key, out char ch, out bool isShift, out bool isAlt, out bool isCtrl, ref int startIndex, int endIndex)
    {
        // Try to get the special key match from the TermInfo static information.
        if (TryGetSpecialConsoleKey(buffer, startIndex, endIndex, terminalFormatStrings, posixDisableValue, veraseCharacter, out ConsoleKeyInfo keyInfo, out int keyLength))
        {
            key     = keyInfo.Key;
            isShift = (keyInfo.Modifiers & ConsoleModifiers.Shift) != 0;
            isAlt   = (keyInfo.Modifiers & ConsoleModifiers.Alt) != 0;
            isCtrl  = (keyInfo.Modifiers & ConsoleModifiers.Control) != 0;

            ch          = ((keyLength == 1) ? buffer[startIndex] : '\0'); // ignore keyInfo.KeyChar
            startIndex += keyLength;
            return(true);
        }

        // Check if we can match Esc + combination and guess if alt was pressed.
        if (buffer[startIndex] == (char)0x1B && // Alt is send as an escape character
            endIndex - startIndex >= 2)         // We have at least two characters to read
        {
            startIndex++;
            if (MapBufferToConsoleKey(buffer, terminalFormatStrings, posixDisableValue, veraseCharacter, out key, out ch, out isShift, out _, out isCtrl, ref startIndex, endIndex))
            {
                isAlt = true;
                return(true);
            }
            else
            {
                // We could not find a matching key here so, Alt+ combination assumption is in-correct.
                // The current key needs to be marked as Esc key.
                // Also, we do not increment _startIndex as we already did it.
                key   = ConsoleKey.Escape;
                ch    = (char)0x1B;
                isAlt = false;
                return(true);
            }
        }

        // Try reading the first char in the buffer and interpret it as a key.
        ch    = buffer[startIndex++];
        key   = GetKeyFromCharValue(ch, out isShift, out isCtrl);
        isAlt = false;
        return(key != default(ConsoleKey));
    }
コード例 #4
0
    private static bool TryGetSpecialConsoleKey(char[] givenChars, int startIndex, int endIndex,
                                                TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter, out ConsoleKeyInfo key, out int keyLength)
    {
        int unprocessedCharCount = endIndex - startIndex;

        // First process special control character codes.  These override anything from terminfo.
        if (unprocessedCharCount > 0)
        {
            // Is this an erase / backspace?
            char c = givenChars[startIndex];
            if (c != posixDisableValue && c == veraseCharacter)
            {
                key       = new ConsoleKeyInfo(c, ConsoleKey.Backspace, shift: false, alt: false, control: false);
                keyLength = 1;
                return(true);
            }
        }

        // Then process terminfo mappings.
        int minRange = terminalFormatStrings.MinKeyFormatLength;

        if (unprocessedCharCount >= minRange)
        {
            int maxRange = Math.Min(unprocessedCharCount, terminalFormatStrings.MaxKeyFormatLength);

            for (int i = maxRange; i >= minRange; i--)
            {
                var currentString = new ReadOnlyMemory <char>(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);
    }
コード例 #5
0
ファイル: KeyParser.cs プロジェクト: mikem8361/runtime
    private const int SequencePrefixLength  = 2; // ^[[ ("^[" stands for Escape)

    internal static ConsoleKeyInfo Parse(char[] buffer, TerminalFormatStrings terminalFormatStrings, byte posixDisableValue, byte veraseCharacter, ref int startIndex, int endIndex)
    {
        int length = endIndex - startIndex;

        Debug.Assert(length > 0);

        // VERASE overrides anything from Terminfo. Both settings can be different for Linux and macOS.
        if (buffer[startIndex] != posixDisableValue && buffer[startIndex] == veraseCharacter)
        {
            // the original char is preserved on purpose (backward compat + consistency)
            return(new ConsoleKeyInfo(buffer[startIndex++], ConsoleKey.Backspace, false, false, false));
        }

        // Escape Sequences start with Escape. But some terminals like PuTTY and rxvt prepend Escape to express that for given sequence Alt was pressed.
        if (length >= MinimalSequenceLength + 1 && buffer[startIndex] == Escape && buffer[startIndex + 1] == Escape)
        {
            startIndex++;
            if (TryParseTerminalInputSequence(buffer, terminalFormatStrings, out ConsoleKeyInfo parsed, ref startIndex, endIndex))
            {
                return(new ConsoleKeyInfo(parsed.KeyChar, parsed.Key, (parsed.Modifiers & ConsoleModifiers.Shift) != 0, alt: true, (parsed.Modifiers & ConsoleModifiers.Control) != 0));
            }
            startIndex--;
        }
        else if (length >= MinimalSequenceLength && TryParseTerminalInputSequence(buffer, terminalFormatStrings, out ConsoleKeyInfo parsed, ref startIndex, endIndex))
        {
            return(parsed);
        }

        if (length == 2 && buffer[startIndex] == Escape && buffer[startIndex + 1] != Escape)
        {
            startIndex++; // skip the Escape
            return(ParseFromSingleChar(buffer[startIndex++], isAlt: true));
        }

        return(ParseFromSingleChar(buffer[startIndex++], isAlt: false));
    }