mirror of
https://github.com/microsoft/terminal.git
synced 2026-02-14 05:35:26 +00:00
Fix a hang when writing wide glyphs into a 1 column terminal (#15171)
The code changes are mostly self-explanatory: Just skip glyphs that can never be inserted. I implemented it slightly incorrectly (a newline will be inserted every time you write such a wide glyph), but it's a niche issue and I think the simplicity of the fix is more important than its exact correctness. It also contains a fix for some severe log spam due to `_PrepareForDoubleByteSequence` complaining in this situation. The spam is so bad that it freezes the app for a few seconds during text buffer reflow. Closes #7416 ## Validation Steps Performed * Open an extra pane and run `TerminalStress.exe` in there * Resize to 1 column * Doesn't hang ✅
This commit is contained in:
@@ -272,10 +272,6 @@ bool TextBuffer::_AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribut
|
||||
// - false otherwise (out of memory)
|
||||
bool TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute)
|
||||
{
|
||||
// This function corrects most errors. If this is false, we had an uncorrectable one which
|
||||
// older versions of conhost simply let pass by unflinching.
|
||||
LOG_HR_IF(E_NOT_VALID_STATE, !(_AssertValidDoubleByteSequence(dbcsAttribute))); // Shouldn't be uncorrectable sequences unless something is very wrong.
|
||||
|
||||
auto fSuccess = true;
|
||||
// Now compensate if we don't have enough space for the upcoming double byte sequence
|
||||
// We only need to compensate for leading bytes
|
||||
|
||||
@@ -139,18 +139,26 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string)
|
||||
|
||||
if (isWrapping)
|
||||
{
|
||||
// We want to wrap, but we failed to write even a single character into the row.
|
||||
// ROW::Write() returns the lineWidth and leaves stringIterator untouched. To prevent a
|
||||
// deadlock, because stringIterator never advances, we need to throw that glyph away.
|
||||
//
|
||||
// This can happen under two circumstances:
|
||||
// * The glyph is wider than the buffer and can never be inserted in
|
||||
// the first place. There's no good way to detect this, so we check
|
||||
// whether the begin column is the left margin, which is the column
|
||||
// at which any legit insertion should work at a minimum.
|
||||
// * The DECAWM Autowrap mode is disabled ("\x1b[?7l", !wrapAtEOL) and
|
||||
// we tried writing a wide glyph into the last column which can't work.
|
||||
if (textPositionBefore == textPositionAfter && (state.columnBegin == 0 || !wrapAtEOL))
|
||||
{
|
||||
textBuffer.ConsumeGrapheme(state.text);
|
||||
}
|
||||
|
||||
if (wrapAtEOL)
|
||||
{
|
||||
cursor.DelayEOLWrap();
|
||||
}
|
||||
else if (textPositionBefore == textPositionAfter)
|
||||
{
|
||||
// We want to wrap, but we're not allowed to and we failed to write even a single character into the row.
|
||||
// This can only mean one thing! The DECAWM Autowrap mode is disabled ("\x1b[?7l") and we tried writing a
|
||||
// wide glyph into the last column. ROW::Write() returns the lineWidth and leaves stringIterator untouched.
|
||||
// To prevent a deadlock, because stringIterator never advances, we need to throw that glyph away.
|
||||
textBuffer.ConsumeGrapheme(state.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user