Fix conpty rendering of control characters in the buffer (#16825)

When using the legacy console APIs, it's possible to write arbitrary
codepoints into the buffer. If any of those codepoints are in the C0 or
C1 range, and the buffer contents are forwarded over conpty, they can
end up mistakenly interpreted as controls by the connected terminal.

This PR fixes that issue by converting any C0 and C1 codepoints in the
buffer into printable glyphs before forwarding them over conpty. I've
used the C0 glyphs from the DOS 437 codepage and just a `?` for the C1
codepoints, since that's what you would typically have seen in the v1
console with a raster font.

Although this doesn't address the main problem in #16410, it should at
least fix the rendering issues they're seeing when running their app in
Windows Terminal.

I've confirmed that the test case in #4363 now looks the same in Windows
Terminal as it does in conhost, and I've tested the Windows version of
the terminal game [Gorched], and confirmed that it now works correctly
in Window Terminal.

[Gorched]: https://github.com/zladovan/gorched

Closes #4363
Closes #6265
This commit is contained in:
James Holderness 2024-03-06 19:16:03 +00:00 committed by GitHub
parent 338c5047d7
commit 563b7312b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 18 additions and 0 deletions

View File

@ -472,7 +472,25 @@ using namespace Microsoft::Console::Types;
_bufferLine.append(cluster.GetText());
totalWidth += cluster.GetColumns();
}
// If any of the values in the buffer are C0 or C1 controls, we need to
// convert them to printable codepoints, otherwise they'll end up being
// evaluated as control characters by the receiving terminal. We use the
// DOS 437 code page for the C0 controls and DEL, and just a `?` for the
// C1 controls, since that's what you would most likely have seen in the
// legacy v1 console with raster fonts.
const auto cchLine = _bufferLine.size();
std::for_each_n(_bufferLine.begin(), cchLine, [](auto& ch) {
static constexpr std::wstring_view C0Glyphs = L" ☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼";
if (ch < C0Glyphs.size())
{
ch = til::at(C0Glyphs, ch);
}
else if (ch >= L'\u007F' && ch < L'\u00A0')
{
ch = (ch == L'\u007F' ? L'' : L'?');
}
});
const auto spaceIndex = _bufferLine.find_last_not_of(L' ');
const auto foundNonspace = spaceIndex != decltype(_bufferLine)::npos;