diff --git a/src/System.Console/src/System/ConsolePal.Unix.cs b/src/System.Console/src/System/ConsolePal.Unix.cs index 326a14e2b098..855aa55519ac 100644 --- a/src/System.Console/src/System/ConsolePal.Unix.cs +++ b/src/System.Console/src/System/ConsolePal.Unix.cs @@ -332,6 +332,13 @@ public static int CursorTop } } + /// + /// Tracks whether we've ever successfully received a response to a cursor position request (CPR). + /// If we have, then we can be more aggressive about expecting a response to subsequent requests, + /// e.g. using a longer timeout. + /// + private static bool s_everReceivedCursorPositionResponse; + /// Gets the current cursor position. This involves both writing to stdout and reading stdin. private static unsafe void GetCursorPosition(out int left, out int top) { @@ -358,7 +365,15 @@ private static unsafe void GetCursorPosition(out int left, out int top) // one thread's get_CursorLeft/Top from providing input to the other's Console.Read*. lock (StdInReader) { - Interop.Sys.InitializeConsoleBeforeRead(minChars: 0, decisecondsTimeout: 10); + // Because the CPR request/response protocol involves blocking until we get a certain + // response from the terminal, we want to avoid doing so if we don't know the terminal + // will definitely response. As such, we start with minChars == 0, which causes the + // terminal's read timer to start immediately. Once we've received a response for + // a request such that we know the terminal supports the protocol, we then specify + // minChars == 1. With that, the timer won't start until the first character is + // received. This makes the mechanism more reliable when there are high latencies + // involved in reading/writing, such as when accessing a remote system. + Interop.Sys.InitializeConsoleBeforeRead(minChars: (byte)(s_everReceivedCursorPositionResponse ? 1 : 0), decisecondsTimeout: 10); try { // Write out the cursor position report request. @@ -425,6 +440,9 @@ private static unsafe void GetCursorPosition(out int left, out int top) // else back into the StdInReader. ReadRowOrCol(bracketPos, semiPos, r, readBytes, ref top); ReadRowOrCol(semiPos, rPos, r, readBytes, ref left); + + // Mark that we've successfully received a CPR response at least once. + s_everReceivedCursorPositionResponse = true; } finally {