@@ -37,6 +37,7 @@ internal static partial class ConsolePal
3737 private static int s_windowWidth ; // Cached WindowWidth, -1 when invalid.
3838 private static int s_windowHeight ; // Cached WindowHeight, invalid when s_windowWidth == -1.
3939 private static int s_invalidateCachedSettings = 1 ; // Tracks whether we should invalidate the cached settings.
40+ private static SafeFileHandle ? s_terminalHandle ; // Tracks the handle used for writing to the terminal.
4041
4142 /// <summary>Gets the lazily-initialized terminal information for the terminal.</summary>
4243 public static TerminalFormatStrings TerminalFormatStringsInstance { get { return s_terminalFormatStringsInstance . Value ; } }
@@ -127,13 +128,7 @@ public static ConsoleKeyInfo ReadKey(bool intercept)
127128 throw new InvalidOperationException ( SR . InvalidOperation_ConsoleReadKeyOnFile ) ;
128129 }
129130
130- bool previouslyProcessed ;
131- ConsoleKeyInfo keyInfo = StdInReader . ReadKey ( out previouslyProcessed ) ;
132-
133- if ( ! intercept && ! previouslyProcessed && keyInfo . KeyChar != '\0 ' )
134- {
135- Console . Write ( keyInfo . KeyChar ) ;
136- }
131+ ConsoleKeyInfo keyInfo = StdInReader . ReadKey ( intercept ) ;
137132 return keyInfo ;
138133 }
139134
@@ -205,7 +200,7 @@ public static string Title
205200 if ( ! string . IsNullOrEmpty ( titleFormat ) )
206201 {
207202 string ansiStr = TermInfo . ParameterizedStrings . Evaluate ( titleFormat , value ) ;
208- WriteStdoutAnsiString ( ansiStr , mayChangeCursorPosition : false ) ;
203+ WriteTerminalAnsiString ( ansiStr , mayChangeCursorPosition : false ) ;
209204 }
210205 }
211206 }
@@ -214,15 +209,15 @@ public static void Beep()
214209 {
215210 if ( ! Console . IsOutputRedirected )
216211 {
217- WriteStdoutAnsiString ( TerminalFormatStringsInstance . Bell , mayChangeCursorPosition : false ) ;
212+ WriteTerminalAnsiString ( TerminalFormatStringsInstance . Bell , mayChangeCursorPosition : false ) ;
218213 }
219214 }
220215
221216 public static void Clear ( )
222217 {
223218 if ( ! Console . IsOutputRedirected )
224219 {
225- WriteStdoutAnsiString ( TerminalFormatStringsInstance . Clear ) ;
220+ WriteTerminalAnsiString ( TerminalFormatStringsInstance . Clear ) ;
226221 }
227222 }
228223
@@ -231,6 +226,11 @@ public static void SetCursorPosition(int left, int top)
231226 if ( Console . IsOutputRedirected )
232227 return ;
233228
229+ SetTerminalCursorPosition ( left , top ) ;
230+ }
231+
232+ public static void SetTerminalCursorPosition ( int left , int top )
233+ {
234234 lock ( Console . Out )
235235 {
236236 if ( TryGetCachedCursorPosition ( out int leftCurrent , out int topCurrent ) &&
@@ -244,7 +244,7 @@ public static void SetCursorPosition(int left, int top)
244244 if ( ! string . IsNullOrEmpty ( cursorAddressFormat ) )
245245 {
246246 string ansiStr = TermInfo . ParameterizedStrings . Evaluate ( cursorAddressFormat , top , left ) ;
247- WriteStdoutAnsiString ( ansiStr ) ;
247+ WriteTerminalAnsiString ( ansiStr ) ;
248248 }
249249
250250 SetCachedCursorPosition ( left , top ) ;
@@ -355,19 +355,18 @@ private static void GetWindowSize(out int width, out int height)
355355 // Invalidate before reading cached values.
356356 CheckTerminalSettingsInvalidated ( ) ;
357357
358- if ( s_windowWidth == - 1 )
358+ Interop . Sys . WinSize winsize ;
359+ if ( s_windowWidth == - 1 &&
360+ s_terminalHandle != null &&
361+ Interop . Sys . GetWindowSize ( s_terminalHandle , out winsize ) == 0 )
359362 {
360- Interop . Sys . WinSize winsize ;
361- if ( Interop . Sys . GetWindowSize ( out winsize ) == 0 )
362- {
363- s_windowWidth = winsize . Col ;
364- s_windowHeight = winsize . Row ;
365- }
366- else
367- {
368- s_windowWidth = TerminalFormatStringsInstance . Columns ;
369- s_windowHeight = TerminalFormatStringsInstance . Lines ;
370- }
363+ s_windowWidth = winsize . Col ;
364+ s_windowHeight = winsize . Row ;
365+ }
366+ else
367+ {
368+ s_windowWidth = TerminalFormatStringsInstance . Columns ;
369+ s_windowHeight = TerminalFormatStringsInstance . Lines ;
371370 }
372371 width = s_windowWidth ;
373372 height = s_windowHeight ;
@@ -403,7 +402,7 @@ public static bool CursorVisible
403402 {
404403 if ( ! Console . IsOutputRedirected )
405404 {
406- WriteStdoutAnsiString ( value ?
405+ WriteTerminalAnsiString ( value ?
407406 TerminalFormatStringsInstance . CursorVisible :
408407 TerminalFormatStringsInstance . CursorInvisible ) ;
409408 }
@@ -412,6 +411,11 @@ public static bool CursorVisible
412411
413412 public static ( int Left , int Top ) GetCursorPosition ( )
414413 {
414+ if ( Console . IsInputRedirected || Console . IsOutputRedirected )
415+ {
416+ return ( 0 , 0 ) ;
417+ }
418+
415419 TryGetCursorPosition ( out int left , out int top ) ;
416420 return ( left , top ) ;
417421 }
@@ -436,14 +440,9 @@ public static (int Left, int Top) GetCursorPosition()
436440 /// <param name="reinitializeForRead">Indicates whether this method is called as part of a on-going Read operation.</param>
437441 internal static bool TryGetCursorPosition ( out int left , out int top , bool reinitializeForRead = false )
438442 {
439- left = top = 0 ;
443+ Debug . Assert ( ! Console . IsInputRedirected ) ;
440444
441- // Getting the cursor position involves both writing out a request string and
442- // parsing a response string from the terminal. So if anything is redirected, bail.
443- if ( Console . IsInputRedirected || Console . IsOutputRedirected )
444- {
445- return false ;
446- }
445+ left = top = 0 ;
447446
448447 int cursorVersion ;
449448 lock ( Console . Out )
@@ -485,7 +484,7 @@ internal static bool TryGetCursorPosition(out int left, out int top, bool reinit
485484 {
486485 // Write out the cursor position report request.
487486 Debug . Assert ( ! string . IsNullOrEmpty ( TerminalFormatStrings . CursorPositionReport ) ) ;
488- WriteStdoutAnsiString ( TerminalFormatStrings . CursorPositionReport , mayChangeCursorPosition : false ) ;
487+ WriteTerminalAnsiString ( TerminalFormatStrings . CursorPositionReport , mayChangeCursorPosition : false ) ;
489488
490489 // Read the cursor position report (CPR), of the form \ESC[row;colR. This is not
491490 // as easy as it sounds. Prior to the CPR having been supplied to stdin, other
@@ -802,7 +801,7 @@ private static void WriteSetColorString(bool foreground, ConsoleColor color)
802801 string evaluatedString = s_fgbgAndColorStrings [ fgbgIndex , ccValue ] ; // benign race
803802 if ( evaluatedString != null )
804803 {
805- WriteStdoutAnsiString ( evaluatedString ) ;
804+ WriteTerminalAnsiString ( evaluatedString ) ;
806805 return ;
807806 }
808807
@@ -842,7 +841,7 @@ private static void WriteSetColorString(bool foreground, ConsoleColor color)
842841 int ansiCode = consoleColorToAnsiCode [ ccValue ] % maxColors ;
843842 evaluatedString = TermInfo . ParameterizedStrings . Evaluate ( formatString , ansiCode ) ;
844843
845- WriteStdoutAnsiString ( evaluatedString ) ;
844+ WriteTerminalAnsiString ( evaluatedString ) ;
846845
847846 s_fgbgAndColorStrings [ fgbgIndex , ccValue ] = evaluatedString ; // benign race
848847 }
@@ -854,7 +853,7 @@ private static void WriteResetColorString()
854853 {
855854 if ( ConsoleUtils . EmitAnsiColorCodes )
856855 {
857- WriteStdoutAnsiString ( TerminalFormatStringsInstance . Reset ) ;
856+ WriteTerminalAnsiString ( TerminalFormatStringsInstance . Reset ) ;
858857 }
859858 }
860859
@@ -897,16 +896,17 @@ private static unsafe void EnsureInitializedCore()
897896 throw new Win32Exception ( ) ;
898897 }
899898
899+ s_terminalHandle = ! Console . IsOutputRedirected ? Interop . Sys . FileDescriptors . STDOUT_FILENO :
900+ ! Console . IsInputRedirected ? Interop . Sys . FileDescriptors . STDIN_FILENO :
901+ null ;
902+
900903 // Provide the native lib with the correct code from the terminfo to transition us into
901904 // "application mode". This will both transition it immediately, as well as allow
902905 // the native lib later to handle signals that require re-entering the mode.
903- if ( ! Console . IsOutputRedirected )
906+ if ( s_terminalHandle != null &&
907+ TerminalFormatStringsInstance . KeypadXmit is string keypadXmit )
904908 {
905- string ? keypadXmit = TerminalFormatStringsInstance . KeypadXmit ;
906- if ( keypadXmit != null )
907- {
908- Interop . Sys . SetKeypadXmit ( keypadXmit ) ;
909- }
909+ Interop . Sys . SetKeypadXmit ( s_terminalHandle , keypadXmit ) ;
910910 }
911911
912912 if ( ! Console . IsInputRedirected )
@@ -952,17 +952,32 @@ private static unsafe int Read(SafeFileHandle fd, Span<byte> buffer)
952952 }
953953 }
954954
955+ internal static void WriteToTerminal ( ReadOnlySpan < byte > buffer , bool mayChangeCursorPosition = true )
956+ {
957+ Debug . Assert ( s_terminalHandle is not null ) ;
958+
959+ lock ( Console . Out ) // synchronize with other writers
960+ {
961+ Write ( s_terminalHandle , buffer , mayChangeCursorPosition ) ;
962+ }
963+ }
964+
965+ internal static unsafe void WriteFromConsoleStream ( SafeFileHandle fd , ReadOnlySpan < byte > buffer )
966+ {
967+ EnsureConsoleInitialized ( ) ;
968+
969+ lock ( Console . Out ) // synchronize with other writers
970+ {
971+ Write ( fd , buffer ) ;
972+ }
973+ }
974+
955975 /// <summary>Writes data from the buffer into the file descriptor.</summary>
956976 /// <param name="fd">The file descriptor.</param>
957977 /// <param name="buffer">The buffer from which to write data.</param>
958978 /// <param name="mayChangeCursorPosition">Writing this buffer may change the cursor position.</param>
959- internal static unsafe void Write ( SafeFileHandle fd , ReadOnlySpan < byte > buffer , bool mayChangeCursorPosition = true )
979+ private static unsafe void Write ( SafeFileHandle fd , ReadOnlySpan < byte > buffer , bool mayChangeCursorPosition = true )
960980 {
961- // Console initialization might emit data to stdout.
962- // In order to avoid splitting user data we need to
963- // complete it before any writes are performed.
964- EnsureConsoleInitialized ( ) ;
965-
966981 fixed ( byte * p = buffer )
967982 {
968983 byte * bufPtr = p ;
@@ -1098,7 +1113,7 @@ private static void InvalidateTerminalSettings()
10981113 /// <summary>Writes a terminfo-based ANSI escape string to stdout.</summary>
10991114 /// <param name="value">The string to write.</param>
11001115 /// <param name="mayChangeCursorPosition">Writing this value may change the cursor position.</param>
1101- internal static void WriteStdoutAnsiString ( string ? value , bool mayChangeCursorPosition = true )
1116+ internal static void WriteTerminalAnsiString ( string ? value , bool mayChangeCursorPosition = true )
11021117 {
11031118 if ( string . IsNullOrEmpty ( value ) )
11041119 return ;
@@ -1115,10 +1130,8 @@ internal static void WriteStdoutAnsiString(string? value, bool mayChangeCursorPo
11151130 data = Encoding . UTF8 . GetBytes ( value ) ;
11161131 }
11171132
1118- lock ( Console . Out ) // synchronize with other writers
1119- {
1120- Write ( Interop . Sys . FileDescriptors . STDOUT_FILENO , data , mayChangeCursorPosition ) ;
1121- }
1133+ EnsureConsoleInitialized ( ) ;
1134+ WriteToTerminal ( data , mayChangeCursorPosition ) ;
11221135 }
11231136 }
11241137}
0 commit comments