@@ -20,6 +20,29 @@ sealed class WindowsStandardInputReader : IStandardInputReader
2020 private readonly WindowsConsoleModeConfig ? _stdInConfig ;
2121 private bool _reading ;
2222 private bool _convertLineEndings ;
23+ private bool _readConsole ;
24+ private Action < int , int > ? _windowSizeChanged ;
25+
26+ public event Action < int , int > ? WindowSizeChanged
27+ {
28+ add
29+ {
30+ _windowSizeChanged += value ;
31+ if ( _readConsole )
32+ {
33+ value ? . Invoke ( Console . WindowWidth , Console . WindowHeight ) ;
34+ }
35+ }
36+ remove
37+ {
38+ _windowSizeChanged -= value ;
39+ }
40+ }
41+
42+ private void EmitWindowSizeChanged ( int width , int height )
43+ {
44+ _windowSizeChanged ? . Invoke ( width , height ) ;
45+ }
2346
2447 public WindowsStandardInputReader ( bool forTerminal )
2548 {
@@ -28,6 +51,7 @@ public WindowsStandardInputReader(bool forTerminal)
2851 _stdInConfig = forTerminal ? WindowsConsoleModeConfig . Configure ( STD_INPUT_HANDLE , TerminalEnableStdInFlags , TerminalDisableStdInFlags )
2952 : WindowsConsoleModeConfig . Configure ( STD_INPUT_HANDLE , NoTerminalEnableStdInFlags , NoTerminalDisableStdInFlags ) ;
3053 _convertLineEndings = ! forTerminal ;
54+ _readConsole = forTerminal ;
3155 }
3256
3357 _handle = GetStdHandle ( STD_INPUT_HANDLE ) ;
@@ -48,45 +72,90 @@ private unsafe int Read(Memory<char> buffer, CancellationToken cancellationToken
4872 {
4973 cancellationToken . ThrowIfCancellationRequested ( ) ;
5074
51- bool readSuccess ;
52-
5375 _reading = true ;
5476 // Note: this poor man's cancellation may cause characters to get lost.
5577 // we don't care about that because the application exists.
5678 using var ctr = cancellationToken . UnsafeRegister ( o => ( ( WindowsStandardInputReader ) o ! ) . CancelIO ( ) , this ) ;
79+ int charsWritten = _readConsole ? ReadConsole ( _handle , buffer . Span ) : Readfile ( _handle , buffer ) ;
80+ _reading = false ;
81+
82+ if ( charsWritten == - 1 )
83+ {
84+ cancellationToken . ThrowIfCancellationRequested ( ) ;
85+
86+ int errorCode = Marshal . GetLastPInvokeError ( ) ;
87+ throw new Win32Exception ( errorCode ) ;
88+ }
89+
90+ return charsWritten ;
91+ }
92+
93+ private unsafe int Readfile ( IntPtr hConsoleInput , Memory < char > buffer )
94+ {
5795 byte [ ] bytes = new byte [ _encoding . GetMaxByteCount ( buffer . Length ) ] ;
5896 int bytesRead ;
5997 fixed ( byte * ptr = bytes )
6098 {
61- readSuccess = ( 0 != ReadFile ( _handle , ptr , buffer . Length , out bytesRead , IntPtr . Zero ) ) ;
99+ bool readSuccess = 0 != ReadFile ( _handle , ptr , buffer . Length , out bytesRead , IntPtr . Zero ) ;
100+ if ( ! readSuccess )
101+ {
102+ return - 1 ;
103+ }
62104 }
63- _reading = false ;
105+ Span < byte > bytesReadSpan = bytes . AsSpan ( 0 , bytesRead ) ;
106+ if ( _convertLineEndings )
107+ {
108+ if ( bytesReadSpan . EndsWith ( "\r \n "u8 ) )
109+ {
110+ bytesReadSpan = bytesReadSpan [ ..^ 1 ] ;
111+ bytesReadSpan [ ^ 1 ] = ( byte ) '\n ' ;
112+ }
113+ else if ( bytesReadSpan . EndsWith ( "\r "u8 ) )
114+ {
115+ bytesReadSpan [ ^ 1 ] = ( byte ) '\n ' ;
116+ }
117+ }
118+ _decoder . Convert ( bytesReadSpan , buffer . Span , flush : false , out int bytesUsed , out int charsRead , out bool completed ) ;
119+ Debug . Assert ( bytesReadSpan . Length == bytesUsed ) ;
120+ return charsRead ;
121+ }
64122
65- if ( readSuccess )
123+ private unsafe int ReadConsole ( IntPtr hConsoleInput , Span < char > buffer )
124+ {
125+ Span < INPUT_RECORD > inputRecords = stackalloc INPUT_RECORD [ Math . Min ( buffer . Length , 1024 ) ] ;
126+ while ( true )
66127 {
67- Span < byte > bytesReadSpan = bytes . AsSpan ( 0 , bytesRead ) ;
68- if ( _convertLineEndings )
128+ int numEventsRead ;
129+ fixed ( INPUT_RECORD * ptr = inputRecords )
69130 {
70- if ( bytesReadSpan . EndsWith ( "\r \n "u8 ) )
131+ bool readSuccess = ReadConsoleInput ( _handle , ptr , inputRecords . Length , out numEventsRead ) ;
132+ if ( ! readSuccess )
71133 {
72- bytesReadSpan = bytesReadSpan [ ..^ 1 ] ;
73- bytesReadSpan [ ^ 1 ] = ( byte ) '\n ' ;
134+ return - 1 ;
74135 }
75- else if ( bytesReadSpan . EndsWith ( "\r "u8 ) )
136+ }
137+
138+ int charsWritten = 0 ;
139+ foreach ( INPUT_RECORD record in inputRecords . Slice ( 0 , numEventsRead ) )
140+ {
141+ switch ( record . EventType )
76142 {
77- bytesReadSpan [ ^ 1 ] = ( byte ) '\n ' ;
143+ case KEY_EVENT :
144+ if ( record . KeyEvent . KeyDown != 0 && record . KeyEvent . uChar != 0 )
145+ {
146+ buffer [ charsWritten ++ ] = record . KeyEvent . uChar ;
147+ }
148+ break ;
149+ case WINDOW_BUFFER_SIZE_EVENT :
150+ EmitWindowSizeChanged ( record . WindowBufferSizeEvent . Size . X , record . WindowBufferSizeEvent . Size . Y ) ;
151+ break ;
78152 }
79153 }
80- _decoder . Convert ( bytesReadSpan , buffer . Span , flush : false , out int bytesUsed , out int charsUsed , out bool completed ) ;
81- Debug . Assert ( bytesReadSpan . Length == bytesUsed ) ;
82- return charsUsed ;
83- }
84- else
85- {
86- cancellationToken . ThrowIfCancellationRequested ( ) ;
87154
88- int errorCode = Marshal . GetLastPInvokeError ( ) ;
89- throw new Win32Exception ( errorCode ) ;
155+ if ( charsWritten > 0 )
156+ {
157+ return charsWritten ;
158+ }
90159 }
91160 }
92161
0 commit comments