mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-11 13:56:33 -06:00
Fix wide char support for WriteConsoleOutputAttribute (#18796)
When we overwrite the attributes during the fill, we must retain the lead/trail byte attributes. Closes #18746 ## Validation Steps Performed * Added a unit test ✅
This commit is contained in:
parent
6682bed311
commit
90c312f7da
@ -110,15 +110,25 @@ static FillConsoleResult FillConsoleImpl(SCREEN_INFORMATION& screenInfo, FillCon
|
|||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
case FillConsoleMode::WriteAttribute:
|
case FillConsoleMode::WriteAttribute:
|
||||||
|
{
|
||||||
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++columns, ++inputPos)
|
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++columns, ++inputPos)
|
||||||
{
|
{
|
||||||
infoBuffer[columns].Attributes = input[inputPos];
|
// Overwrite all attributes except for the lead/trail byte markers.
|
||||||
|
// Those are used by WriteConsoleOutputWImplHelper to correctly serialize the input.
|
||||||
|
constexpr auto LT = COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE;
|
||||||
|
auto& attributes = infoBuffer[columns].Attributes;
|
||||||
|
attributes = (input[inputPos] & ~LT) | (attributes & LT);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case FillConsoleMode::FillAttribute:
|
case FillConsoleMode::FillAttribute:
|
||||||
for (const auto attr = input[0]; columns < columnsAvailable && inputPos < lengthToWrite; ++columns, ++inputPos)
|
for (const auto attr = input[0]; columns < columnsAvailable && inputPos < lengthToWrite; ++columns, ++inputPos)
|
||||||
{
|
{
|
||||||
infoBuffer[columns].Attributes = attr;
|
// Overwrite all attributes except for the lead/trail byte markers.
|
||||||
|
// Those are used by WriteConsoleOutputWImplHelper to correctly serialize the input.
|
||||||
|
constexpr auto LT = COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE;
|
||||||
|
auto& attributes = infoBuffer[columns].Attributes;
|
||||||
|
attributes = (attr & ~LT) | (attributes & LT);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FillConsoleMode::WriteCharacter:
|
case FillConsoleMode::WriteCharacter:
|
||||||
|
|||||||
@ -52,6 +52,16 @@ static constexpr std::wstring_view s_initialContentVT{
|
|||||||
// clang-format on
|
// clang-format on
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr std::wstring_view s_initialContentVTWide{
|
||||||
|
// clang-format off
|
||||||
|
L""
|
||||||
|
sgr_red("〇") sgr_blu("一") sgr_red("二") sgr_blu("三") "\r\n"
|
||||||
|
sgr_red("四") sgr_blu("五") sgr_red("六") sgr_blu("七") "\r\n"
|
||||||
|
sgr_blu("八") sgr_red("九") sgr_blu("十") sgr_red("百") "\r\n"
|
||||||
|
sgr_blu("千") sgr_red("万") sgr_blu("億") sgr_red("兆")
|
||||||
|
// clang-format on
|
||||||
|
};
|
||||||
|
|
||||||
class ::Microsoft::Console::VirtualTerminal::VtIoTests
|
class ::Microsoft::Console::VirtualTerminal::VtIoTests
|
||||||
{
|
{
|
||||||
BEGIN_TEST_CLASS(VtIoTests)
|
BEGIN_TEST_CLASS(VtIoTests)
|
||||||
@ -71,11 +81,11 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests
|
|||||||
return { &rxBuf[0], read };
|
return { &rxBuf[0], read };
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupInitialContents() const
|
void setupInitialContents(bool wide) const
|
||||||
{
|
{
|
||||||
auto& sm = screenInfo->GetStateMachine();
|
auto& sm = screenInfo->GetStateMachine();
|
||||||
sm.ProcessString(L"\033c");
|
sm.ProcessString(L"\033c");
|
||||||
sm.ProcessString(s_initialContentVT);
|
sm.ProcessString(wide ? s_initialContentVTWide : s_initialContentVT);
|
||||||
sm.ProcessString(L"\x1b[H" sgr_rst());
|
sm.ProcessString(L"\x1b[H" sgr_rst());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,7 +287,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests
|
|||||||
|
|
||||||
TEST_METHOD(WriteConsoleOutputAttribute)
|
TEST_METHOD(WriteConsoleOutputAttribute)
|
||||||
{
|
{
|
||||||
setupInitialContents();
|
setupInitialContents(false);
|
||||||
|
|
||||||
static constexpr std::array payload{ red, blu, red, blu };
|
static constexpr std::array payload{ red, blu, red, blu };
|
||||||
static constexpr til::point target{ 6, 1 };
|
static constexpr til::point target{ 6, 1 };
|
||||||
@ -295,7 +305,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests
|
|||||||
|
|
||||||
TEST_METHOD(WriteConsoleOutputCharacterW)
|
TEST_METHOD(WriteConsoleOutputCharacterW)
|
||||||
{
|
{
|
||||||
setupInitialContents();
|
setupInitialContents(false);
|
||||||
|
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
std::string_view expected;
|
std::string_view expected;
|
||||||
@ -354,7 +364,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests
|
|||||||
actual = readOutput();
|
actual = readOutput();
|
||||||
VERIFY_ARE_EQUAL(expected, actual);
|
VERIFY_ARE_EQUAL(expected, actual);
|
||||||
|
|
||||||
setupInitialContents();
|
setupInitialContents(false);
|
||||||
|
|
||||||
// Writing at the start of a line.
|
// Writing at the start of a line.
|
||||||
THROW_IF_FAILED(routines.FillConsoleOutputAttributeImpl(*screenInfo, red, 3, { 0, 0 }, cellsModified, false));
|
THROW_IF_FAILED(routines.FillConsoleOutputAttributeImpl(*screenInfo, red, 3, { 0, 0 }, cellsModified, false));
|
||||||
@ -388,6 +398,25 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests
|
|||||||
VERIFY_ARE_EQUAL(expected, actual);
|
VERIFY_ARE_EQUAL(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(FillConsoleOutputAttributeWide)
|
||||||
|
{
|
||||||
|
setupInitialContents(true);
|
||||||
|
|
||||||
|
size_t cellsModified = 0;
|
||||||
|
std::string_view expected;
|
||||||
|
std::string_view actual;
|
||||||
|
|
||||||
|
// Writing nothing should produce nothing.
|
||||||
|
THROW_IF_FAILED(routines.FillConsoleOutputAttributeImpl(*screenInfo, red, 4, { 2, 1 }, cellsModified, false));
|
||||||
|
expected =
|
||||||
|
decsc() //
|
||||||
|
cup(2, 3) sgr_red("五六") //
|
||||||
|
decrc();
|
||||||
|
actual = readOutput();
|
||||||
|
VERIFY_ARE_EQUAL(4u, cellsModified);
|
||||||
|
VERIFY_ARE_EQUAL(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_METHOD(FillConsoleOutputCharacterW)
|
TEST_METHOD(FillConsoleOutputCharacterW)
|
||||||
{
|
{
|
||||||
size_t cellsModified = 0;
|
size_t cellsModified = 0;
|
||||||
@ -407,7 +436,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests
|
|||||||
actual = readOutput();
|
actual = readOutput();
|
||||||
VERIFY_ARE_EQUAL(expected, actual);
|
VERIFY_ARE_EQUAL(expected, actual);
|
||||||
|
|
||||||
setupInitialContents();
|
setupInitialContents(false);
|
||||||
|
|
||||||
// Writing at the start of a line.
|
// Writing at the start of a line.
|
||||||
THROW_IF_FAILED(routines.FillConsoleOutputCharacterWImpl(*screenInfo, L'a', 3, { 0, 0 }, cellsModified, false));
|
THROW_IF_FAILED(routines.FillConsoleOutputCharacterWImpl(*screenInfo, L'a', 3, { 0, 0 }, cellsModified, false));
|
||||||
@ -453,7 +482,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests
|
|||||||
std::string_view expected;
|
std::string_view expected;
|
||||||
std::string_view actual;
|
std::string_view actual;
|
||||||
|
|
||||||
setupInitialContents();
|
setupInitialContents(false);
|
||||||
|
|
||||||
// Scrolling from nowhere to somewhere are no-ops and should not emit anything.
|
// Scrolling from nowhere to somewhere are no-ops and should not emit anything.
|
||||||
THROW_IF_FAILED(routines.ScrollConsoleScreenBufferWImpl(*screenInfo, { 0, 0, -1, -1 }, {}, std::nullopt, L' ', 0, false));
|
THROW_IF_FAILED(routines.ScrollConsoleScreenBufferWImpl(*screenInfo, { 0, 0, -1, -1 }, {}, std::nullopt, L' ', 0, false));
|
||||||
@ -487,7 +516,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests
|
|||||||
//
|
//
|
||||||
// m n M N o p O P
|
// m n M N o p O P
|
||||||
//
|
//
|
||||||
setupInitialContents();
|
setupInitialContents(false);
|
||||||
|
|
||||||
// Scrolling from somewhere to somewhere.
|
// Scrolling from somewhere to somewhere.
|
||||||
//
|
//
|
||||||
@ -626,7 +655,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests
|
|||||||
std::string_view expected;
|
std::string_view expected;
|
||||||
std::string_view actual;
|
std::string_view actual;
|
||||||
|
|
||||||
setupInitialContents();
|
setupInitialContents(false);
|
||||||
|
|
||||||
// Scrolling from nowhere to somewhere are no-ops and should not emit anything.
|
// Scrolling from nowhere to somewhere are no-ops and should not emit anything.
|
||||||
THROW_IF_FAILED(routines.ScrollConsoleScreenBufferWImpl(*screenInfo, { 0, 0, -1, -1 }, {}, std::nullopt, L' ', 0, false));
|
THROW_IF_FAILED(routines.ScrollConsoleScreenBufferWImpl(*screenInfo, { 0, 0, -1, -1 }, {}, std::nullopt, L' ', 0, false));
|
||||||
@ -660,7 +689,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests
|
|||||||
//
|
//
|
||||||
// m n M N o p O P
|
// m n M N o p O P
|
||||||
//
|
//
|
||||||
setupInitialContents();
|
setupInitialContents(false);
|
||||||
|
|
||||||
// Scrolling from somewhere to somewhere.
|
// Scrolling from somewhere to somewhere.
|
||||||
//
|
//
|
||||||
@ -797,7 +826,7 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests
|
|||||||
&screenInfoAlt));
|
&screenInfoAlt));
|
||||||
|
|
||||||
routines.SetConsoleActiveScreenBufferImpl(*screenInfoAlt);
|
routines.SetConsoleActiveScreenBufferImpl(*screenInfoAlt);
|
||||||
setupInitialContents();
|
setupInitialContents(false);
|
||||||
THROW_IF_FAILED(routines.SetConsoleOutputModeImpl(*screenInfoAlt, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING));
|
THROW_IF_FAILED(routines.SetConsoleOutputModeImpl(*screenInfoAlt, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING));
|
||||||
readOutput();
|
readOutput();
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user