@@ -61,9 +61,6 @@ COOKED_READ_DATA::COOKED_READ_DATA(_In_ InputBuffer* const pInputBuffer,
61
61
THROW_IF_FAILED (_screenInfo.GetMainBuffer ().AllocateIoHandle (ConsoleHandleData::HandleType::Output, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, _tempHandle));
62
62
#endif
63
63
64
- const auto cursorPos = _getViewportCursorPosition ();
65
- _originInViewport = cursorPos;
66
-
67
64
if (!initialData.empty ())
68
65
{
69
66
// The console API around `nInitialChars` in `CONSOLE_READCONSOLE_CONTROL` is pretty weird.
@@ -94,6 +91,7 @@ COOKED_READ_DATA::COOKED_READ_DATA(_In_ InputBuffer* const pInputBuffer,
94
91
// It replicates part of the _redisplay() logic to layout the text at various
95
92
// starting positions until it finds one that matches the current cursor position.
96
93
94
+ const auto cursorPos = _getViewportCursorPosition ();
97
95
const auto size = _screenInfo.GetVtPageArea ().Dimensions ();
98
96
99
97
// Guess the initial cursor position based on the string length, assuming that 1 char = 1 column.
@@ -285,22 +283,29 @@ bool COOKED_READ_DATA::Read(const bool isUnicode, size_t& numBytes, ULONG& contr
285
283
286
284
// Printing wide glyphs at the end of a row results in a forced line wrap and a padding whitespace to be inserted.
287
285
// When the text buffer resizes these padding spaces may vanish and the _distanceCursor and _distanceEnd measurements become inaccurate.
288
- // To fix this, this function is called before a resize and will clear the input line. Afterwards , RedrawAfterResize() will restore it.
286
+ // To fix this, this function is called before a resize and will clear the input line. Afterward , RedrawAfterResize() will restore it.
289
287
void COOKED_READ_DATA::EraseBeforeResize ()
290
288
{
289
+ // If we've already erased the buffer, we don't need to do it again.
291
290
if (_redrawPending)
292
291
{
293
292
return ;
294
293
}
295
294
295
+ // If we don't have an origin, we've never had user input, and consequently there's nothing to erase.
296
+ if (!_originInViewport)
297
+ {
298
+ return ;
299
+ }
300
+
296
301
_redrawPending = true ;
297
302
298
303
// Position the cursor the start of the prompt before reflow.
299
304
// Then, after reflow, we'll be able to ask the buffer where it went (the new origin).
300
305
// This uses the buffer APIs directly, so that we don't emit unnecessary VT into ConPTY's output.
301
306
auto & textBuffer = _screenInfo.GetTextBuffer ();
302
307
auto & cursor = textBuffer.GetCursor ();
303
- auto cursorPos = _originInViewport;
308
+ auto cursorPos = * _originInViewport;
304
309
_screenInfo.GetVtPageArea ().ConvertFromOrigin (&cursorPos);
305
310
cursor.SetPosition (cursorPos);
306
311
}
@@ -316,7 +321,10 @@ void COOKED_READ_DATA::RedrawAfterResize()
316
321
_redrawPending = false ;
317
322
318
323
// Get the new cursor position after the reflow, since it may have changed.
319
- _originInViewport = _getViewportCursorPosition ();
324
+ if (_originInViewport)
325
+ {
326
+ _originInViewport = _getViewportCursorPosition ();
327
+ }
320
328
321
329
// Ensure that we don't use any scroll sequences or try to clear previous pager contents.
322
330
// They have all been erased with the CSI J above.
@@ -348,7 +356,7 @@ bool COOKED_READ_DATA::PresentingPopup() const noexcept
348
356
return !_popups.empty ();
349
357
}
350
358
351
- til::point_span COOKED_READ_DATA::GetBoundaries () const noexcept
359
+ til::point_span COOKED_READ_DATA::GetBoundaries () noexcept
352
360
{
353
361
const auto viewport = _screenInfo.GetViewport ();
354
362
const auto virtualViewport = _screenInfo.GetVtPageArea ();
@@ -357,7 +365,7 @@ til::point_span COOKED_READ_DATA::GetBoundaries() const noexcept
357
365
const til::point max{ viewport.RightInclusive (), viewport.BottomInclusive () };
358
366
359
367
// Convert from VT-viewport-relative coordinate space back to the console one.
360
- auto beg = _originInViewport ;
368
+ auto beg = _getOriginInViewport () ;
361
369
virtualViewport.ConvertFromOrigin (&beg);
362
370
363
371
// Since the pager may be longer than the viewport is tall, we need to clamp the coordinates to still remain within
@@ -831,6 +839,20 @@ til::point COOKED_READ_DATA::_getViewportCursorPosition() const noexcept
831
839
return cursorPos;
832
840
}
833
841
842
+ // Some applications initiate a read on stdin and _then_ print the prompt prefix to stdout.
843
+ // While that's not correct (because it's a race condition), we can make it significantly
844
+ // less bad by delaying the calculation of the origin until we actually need it.
845
+ // This turns it from a race between application and terminal into a race between
846
+ // application and user, which is much less likely to hit.
847
+ til::point COOKED_READ_DATA::_getOriginInViewport () noexcept
848
+ {
849
+ if (!_originInViewport)
850
+ {
851
+ _originInViewport.emplace (_getViewportCursorPosition ());
852
+ }
853
+ return *_originInViewport;
854
+ }
855
+
834
856
void COOKED_READ_DATA::_replace (size_t offset, size_t remove, const wchar_t * input, size_t count)
835
857
{
836
858
const auto size = _buffer.size ();
@@ -885,7 +907,8 @@ void COOKED_READ_DATA::_redisplay()
885
907
}
886
908
887
909
const auto size = _screenInfo.GetVtPageArea ().Dimensions ();
888
- auto originInViewportFinal = _originInViewport;
910
+ auto originInViewport = _getOriginInViewport ();
911
+ auto originInViewportFinal = originInViewport;
889
912
til::point cursorPositionFinal;
890
913
til::point pagerPromptEnd;
891
914
std::vector<Line> lines;
@@ -894,7 +917,7 @@ void COOKED_READ_DATA::_redisplay()
894
917
// and if MSVC says that then that must be true.
895
918
for (;;)
896
919
{
897
- cursorPositionFinal = { _originInViewport .x , 0 };
920
+ cursorPositionFinal = { originInViewport .x , 0 };
898
921
899
922
// Construct the first line manually so that it starts at the correct horizontal position.
900
923
LayoutResult res{ .column = cursorPositionFinal.x };
@@ -1057,8 +1080,8 @@ void COOKED_READ_DATA::_redisplay()
1057
1080
if (gsl::narrow_cast<til::CoordType>(lines.size ()) > size.height && originInViewportFinal.x != 0 )
1058
1081
{
1059
1082
lines.clear ();
1060
- _originInViewport.x = 0 ;
1061
1083
_bufferDirtyBeg = 0 ;
1084
+ originInViewport.x = 0 ;
1062
1085
originInViewportFinal = {};
1063
1086
continue ;
1064
1087
}
@@ -1092,7 +1115,7 @@ void COOKED_READ_DATA::_redisplay()
1092
1115
if (_clearPending)
1093
1116
{
1094
1117
_clearPending = false ;
1095
- _appendCUP (output, _originInViewport );
1118
+ _appendCUP (output, originInViewport );
1096
1119
output.append (L" \x1b [J" );
1097
1120
}
1098
1121
@@ -1111,7 +1134,7 @@ void COOKED_READ_DATA::_redisplay()
1111
1134
// The check for origin == {0,0} is important because it ensures that we "own" the entire viewport and
1112
1135
// that scrolling our contents doesn't scroll away the user's output that may still be in the viewport.
1113
1136
// (Anything below the origin is assumed to belong to us.)
1114
- if (const auto delta = pagerContentTop - _pagerContentTop; delta != 0 && _originInViewport == til::point{})
1137
+ if (const auto delta = pagerContentTop - _pagerContentTop; delta != 0 && originInViewport == til::point{})
1115
1138
{
1116
1139
const auto deltaAbs = abs (delta);
1117
1140
til::CoordType beg = 0 ;
@@ -1179,7 +1202,7 @@ void COOKED_READ_DATA::_redisplay()
1179
1202
1180
1203
for (til::CoordType i = 0 ; i < pagerHeight; i++)
1181
1204
{
1182
- const auto row = std::min (_originInViewport .y + i, size.height - 1 );
1205
+ const auto row = std::min (originInViewport .y + i, size.height - 1 );
1183
1206
1184
1207
// If the last write left the cursor at the end of a line, the next write will start at the beginning of the next line.
1185
1208
// This avoids needless calls to _appendCUP. The reason it's here and not at the end of the loop is similar to how
@@ -1223,7 +1246,7 @@ void COOKED_READ_DATA::_redisplay()
1223
1246
1224
1247
if (pagerHeight < pagerHeightPrevious)
1225
1248
{
1226
- const auto row = std::min (_originInViewport .y + pagerHeight, size.height - 1 );
1249
+ const auto row = std::min (originInViewport .y + pagerHeight, size.height - 1 );
1227
1250
_appendCUP (output, { 0 , row });
1228
1251
output.append (L" \x1b [K" );
1229
1252
0 commit comments