예제 #1
0
        /// <summary>Reads from the stdin reader, unbuffered, until the specified condition is met.</summary>
        private static unsafe void ReadStdinUnbufferedUntil(
            StdInStreamReader reader,
            byte *buffer, int bufferSize,
            ref int bytesRead, ref int pos,
            Func <byte, bool> condition)
        {
            while (true)
            {
                for (; pos < bytesRead && !condition(buffer[pos]); pos++)
                {
                    ;
                }
                if (pos < bytesRead)
                {
                    return;
                }

                bytesRead = reader.ReadStdinUnbuffered(buffer, bufferSize);
                pos       = 0;
            }
        }
예제 #2
0
        /// <summary>Gets the current cursor position.  This involves both writing to stdout and reading stdin.</summary>
        private static unsafe void GetCursorPosition(out int left, out int top)
        {
            left = top = 0;

            // Getting the cursor position involves both writing out a request string and
            // parsing a response string from the terminal.  So if anything is redirected, bail.
            if (Console.IsInputRedirected || Console.IsOutputRedirected)
            {
                return;
            }

            // Get the cursor position request format string.
            Debug.Assert(!string.IsNullOrEmpty(TerminalFormatStrings.CursorPositionReport));

            // Synchronize with all other stdin readers.  We need to do this in case multiple threads are
            // trying to read/write concurrently, and to minimize the chances of resulting conflicts.
            // This does mean that Console.get_CursorLeft/Top can't be used concurrently Console.Read*, etc.;
            // attempting to do so will block one of them until the other completes, but in doing so we prevent
            // one thread's get_CursorLeft/Top from providing input to the other's Console.Read*.
            lock (StdInReader)
            {
                // Write out the cursor position report request.
                WriteStdoutAnsiString(TerminalFormatStrings.CursorPositionReport);

                // Read the response.  There's a race condition here if the user is typing,
                // or if other threads are accessing the console; there's relatively little
                // we can do about that, but we try not to lose any data.
                StdInStreamReader r          = StdInReader.Inner;
                const int         BufferSize = 1024;
                byte *            bytes      = stackalloc byte[BufferSize];

                int bytesRead = 0, i = 0;

                // Response expected in the form "\ESC[row;colR".  However, user typing concurrently
                // with the request/response sequence can result in other characters, and potentially
                // other escape sequences (e.g. for an arrow key) being entered concurrently with
                // the response.  To avoid garbage showing up in the user's input, we are very liberal
                // with regards to eating all input from this point until all aspects of the sequence
                // have been consumed.

                // Find the ESC as the start of the sequence.
                ReadStdinUnbufferedUntil(r, bytes, BufferSize, ref bytesRead, ref i, b => b == 0x1B);
                i++; // move past the ESC

                // Find the '['
                ReadStdinUnbufferedUntil(r, bytes, BufferSize, ref bytesRead, ref i, b => b == '[');

                // Find the first Int32 and parse it.
                ReadStdinUnbufferedUntil(r, bytes, BufferSize, ref bytesRead, ref i, b => IsDigit((char)b));
                int row = ParseInt32(bytes, bytesRead, ref i);
                if (row >= 1)
                {
                    top = row - 1;
                }

                // Find the second Int32 and parse it.
                ReadStdinUnbufferedUntil(r, bytes, BufferSize, ref bytesRead, ref i, b => IsDigit((char)b));
                int col = ParseInt32(bytes, bytesRead, ref i);
                if (col >= 1)
                {
                    left = col - 1;
                }

                // Find the ending 'R'
                ReadStdinUnbufferedUntil(r, bytes, BufferSize, ref bytesRead, ref i, b => b == 'R');
            }
        }