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)
{
Interop.Sys.InitializeConsoleBeforeRead(minChars: 0, decisecondsTimeout: 10);
try
{
// Write out the cursor position report request.
WriteStdoutAnsiString(TerminalFormatStrings.CursorPositionReport);
// Read the cursor position report reponse, of the form \ESC[row;colR. There's a race
// condition here if the user is typing, or if other threads are accessing the console;
// to try to avoid losing such data, we push unexpected inputs into the stdin buffer, but
// even with that, there's a a potential that we could misinterpret data from the user as
// being part of the cursor position response. This is inherent to the nature of the protocol.
StdInReader r = StdInReader.Inner;
byte b;
while (true) // \ESC
{
if (r.ReadStdin(&b, 1) != 1) return;
if (b == 0x1B) break;
r.AppendExtraBuffer(&b, 1);
}
while (true) // [
{
if (r.ReadStdin(&b, 1) != 1) return;
if (b == '[') break;
r.AppendExtraBuffer(&b, 1);
}
try
{
int row = 0;
while (true) // row until ';'
{
if (r.ReadStdin(&b, 1) != 1) return;
if (b == ';') break;
if (IsDigit((char)b))
{
row = checked((row * 10) + (b - '0'));
}
else
{
r.AppendExtraBuffer(&b, 1);
}
}
if (row >= 1) top = row - 1;
int col = 0;
while (true) // col until 'R'
{
if (r.ReadStdin(&b, 1) == 0) return;
if (b == 'R') break;
if (IsDigit((char)b))
{
col = checked((col * 10) + (b - '0'));
}
else
{
r.AppendExtraBuffer(&b, 1);
}
}
if (col >= 1) left = col - 1;
}
catch (OverflowException) { return; }
}
finally
{
Interop.Sys.UninitializeConsoleAfterRead();
}
}
}