mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-10 00:48:23 -06:00
Remove VT color quirk for PowerShell (#17666)
Roughly 4 years ago we gave Windows Terminal the ability to differentiate between black/white and the default colors. One of the victims was PowerShell and most importantly PSReadLine, which emit SRG 37 & 40 when what they really want is 38 & 48. We fixed this on our side by adding a shim. Since the addition of VT passthrough in #17510 we now intentionally lost the ability to translate VT sequences from one thing to another. This meant we also lost the ability to do this shim and as such this PR removes it. Luckily Windows 11 now ships PSReadLine 2.0.0, which contains a proper fix for this. Unfortunately, this is not the case for Windows 10, which ships PSReadLine 2.0.0-beta2. Users affected by this will have to install a newer version of PSReadLine or use the default black/white theme. See 1bf4c082b4586dd056a9e67e363b5ab22197b09f Closes #13037
This commit is contained in:
parent
5174c96d6d
commit
dfb52331f8
@ -65,47 +65,6 @@ void TextAttribute::SetLegacyDefaultAttributes(const WORD defaultAttributes) noe
|
||||
gsl::at(s_legacyBackgroundColorMap, s_legacyDefaultBackground) = TextColor{};
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// Pursuant to GH#6807
|
||||
// This routine replaces VT colors from the 16-color set with the "default"
|
||||
// flag. It is intended to be used as part of the "VT Quirk" in
|
||||
// WriteConsole[AW].
|
||||
//
|
||||
// There is going to be a very long tail of applications that will
|
||||
// explicitly request VT SGR 40/37 when what they really want is to
|
||||
// SetConsoleTextAttribute() with a black background/white foreground.
|
||||
// Instead of making those applications look bad (and therefore making us
|
||||
// look bad, because we're releasing this as an update to something that
|
||||
// "looks good" already), we're introducing this compatibility hack. Before
|
||||
// the color reckoning in GH#6698 + GH#6506, *every* color was subject to
|
||||
// being spontaneously and erroneously turned into the default color. Now,
|
||||
// only the 16-color palette value that matches the active console
|
||||
// background color will be destroyed when the quirk is enabled.
|
||||
//
|
||||
// This is not intended to be a long-term solution. This comment will be
|
||||
// discovered in forty years(*) time and people will laugh at our hubris.
|
||||
//
|
||||
// *it doesn't matter when you're reading this, it will always be 40 years
|
||||
// from now.
|
||||
TextAttribute TextAttribute::StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) noexcept
|
||||
{
|
||||
const auto fg{ attribute.GetForeground() };
|
||||
const auto bg{ attribute.GetBackground() };
|
||||
auto copy{ attribute };
|
||||
if (fg.IsIndex16() &&
|
||||
attribute.IsIntense() == WI_IsFlagSet(s_ansiDefaultForeground, FOREGROUND_INTENSITY) &&
|
||||
fg.GetIndex() == (s_ansiDefaultForeground & ~FOREGROUND_INTENSITY))
|
||||
{
|
||||
// We don't want to turn 1;37m into 39m (or even 1;39m), as this was meant to mimic a legacy color.
|
||||
copy.SetDefaultForeground();
|
||||
}
|
||||
if (bg.IsIndex16() && bg.GetIndex() == s_ansiDefaultBackground)
|
||||
{
|
||||
copy.SetDefaultBackground();
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Returns a WORD with legacy-style attributes for this textattribute.
|
||||
// Parameters:
|
||||
|
||||
@ -93,7 +93,6 @@ public:
|
||||
}
|
||||
|
||||
static void SetLegacyDefaultAttributes(const WORD defaultAttributes) noexcept;
|
||||
static TextAttribute StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) noexcept;
|
||||
WORD GetLegacyAttributes() const noexcept;
|
||||
|
||||
bool IsTopHorizontalDisplayed() const noexcept;
|
||||
|
||||
@ -72,13 +72,11 @@ public:
|
||||
[[nodiscard]] HRESULT WriteConsoleAImpl(IConsoleOutputObject& context,
|
||||
const std::string_view buffer,
|
||||
size_t& read,
|
||||
bool requiresVtQuirk,
|
||||
std::unique_ptr<IWaitRoutine>& waiter) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT WriteConsoleWImpl(IConsoleOutputObject& context,
|
||||
const std::wstring_view buffer,
|
||||
size_t& read,
|
||||
bool requiresVtQuirk,
|
||||
std::unique_ptr<IWaitRoutine>& waiter) noexcept override;
|
||||
|
||||
#pragma region ThreadCreationInfo
|
||||
|
||||
@ -406,7 +406,6 @@ void WriteClearScreen(SCREEN_INFORMATION& screenInfo)
|
||||
[[nodiscard]] NTSTATUS DoWriteConsole(_In_reads_bytes_(*pcbBuffer) PCWCHAR pwchBuffer,
|
||||
_Inout_ size_t* const pcbBuffer,
|
||||
SCREEN_INFORMATION& screenInfo,
|
||||
bool requiresVtQuirk,
|
||||
std::unique_ptr<WriteData>& waiter)
|
||||
try
|
||||
{
|
||||
@ -416,22 +415,10 @@ try
|
||||
waiter = std::make_unique<WriteData>(screenInfo,
|
||||
pwchBuffer,
|
||||
*pcbBuffer,
|
||||
gci.OutputCP,
|
||||
requiresVtQuirk);
|
||||
gci.OutputCP);
|
||||
return CONSOLE_STATUS_WAIT;
|
||||
}
|
||||
|
||||
const auto restoreVtQuirk = wil::scope_exit([&]() {
|
||||
if (requiresVtQuirk)
|
||||
{
|
||||
screenInfo.ResetIgnoreLegacyEquivalentVTAttributes();
|
||||
}
|
||||
});
|
||||
if (requiresVtQuirk)
|
||||
{
|
||||
screenInfo.SetIgnoreLegacyEquivalentVTAttributes();
|
||||
}
|
||||
|
||||
const std::wstring_view str{ pwchBuffer, *pcbBuffer / sizeof(WCHAR) };
|
||||
|
||||
if (WI_IsAnyFlagClear(screenInfo.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT))
|
||||
@ -464,7 +451,6 @@ NT_CATCH_RETURN()
|
||||
[[nodiscard]] HRESULT WriteConsoleWImplHelper(IConsoleOutputObject& context,
|
||||
const std::wstring_view buffer,
|
||||
size_t& read,
|
||||
bool requiresVtQuirk,
|
||||
std::unique_ptr<WriteData>& waiter) noexcept
|
||||
{
|
||||
try
|
||||
@ -477,7 +463,7 @@ NT_CATCH_RETURN()
|
||||
size_t cbTextBufferLength;
|
||||
RETURN_IF_FAILED(SizeTMult(buffer.size(), sizeof(wchar_t), &cbTextBufferLength));
|
||||
|
||||
auto Status = DoWriteConsole(const_cast<wchar_t*>(buffer.data()), &cbTextBufferLength, context, requiresVtQuirk, waiter);
|
||||
auto Status = DoWriteConsole(const_cast<wchar_t*>(buffer.data()), &cbTextBufferLength, context, waiter);
|
||||
|
||||
// Convert back from bytes to characters for the resulting string length written.
|
||||
read = cbTextBufferLength / sizeof(wchar_t);
|
||||
@ -510,7 +496,6 @@ NT_CATCH_RETURN()
|
||||
[[nodiscard]] HRESULT ApiRoutines::WriteConsoleAImpl(IConsoleOutputObject& context,
|
||||
const std::string_view buffer,
|
||||
size_t& read,
|
||||
bool requiresVtQuirk,
|
||||
std::unique_ptr<IWaitRoutine>& waiter) noexcept
|
||||
{
|
||||
try
|
||||
@ -622,7 +607,7 @@ NT_CATCH_RETURN()
|
||||
|
||||
// Make the W version of the call
|
||||
size_t wcBufferWritten{};
|
||||
const auto hr{ WriteConsoleWImplHelper(screenInfo, wstr, wcBufferWritten, requiresVtQuirk, writeDataWaiter) };
|
||||
const auto hr{ WriteConsoleWImplHelper(screenInfo, wstr, wcBufferWritten, writeDataWaiter) };
|
||||
|
||||
// If there is no waiter, process the byte count now.
|
||||
if (nullptr == writeDataWaiter.get())
|
||||
@ -700,7 +685,6 @@ NT_CATCH_RETURN()
|
||||
[[nodiscard]] HRESULT ApiRoutines::WriteConsoleWImpl(IConsoleOutputObject& context,
|
||||
const std::wstring_view buffer,
|
||||
size_t& read,
|
||||
bool requiresVtQuirk,
|
||||
std::unique_ptr<IWaitRoutine>& waiter) noexcept
|
||||
{
|
||||
try
|
||||
@ -709,7 +693,7 @@ NT_CATCH_RETURN()
|
||||
auto unlock = wil::scope_exit([&] { UnlockConsole(); });
|
||||
|
||||
std::unique_ptr<WriteData> writeDataWaiter;
|
||||
RETURN_IF_FAILED(WriteConsoleWImplHelper(context.GetActiveBuffer(), buffer, read, requiresVtQuirk, writeDataWaiter));
|
||||
RETURN_IF_FAILED(WriteConsoleWImplHelper(context.GetActiveBuffer(), buffer, read, writeDataWaiter));
|
||||
|
||||
// Transfer specific waiter pointer into the generic interface wrapper.
|
||||
waiter.reset(writeDataWaiter.release());
|
||||
|
||||
@ -28,5 +28,4 @@ void WriteClearScreen(SCREEN_INFORMATION& screenInfo);
|
||||
[[nodiscard]] NTSTATUS DoWriteConsole(_In_reads_bytes_(pcbBuffer) const wchar_t* pwchBuffer,
|
||||
_Inout_ size_t* const pcbBuffer,
|
||||
SCREEN_INFORMATION& screenInfo,
|
||||
bool requiresVtQuirk,
|
||||
std::unique_ptr<WriteData>& waiter);
|
||||
|
||||
@ -41,8 +41,7 @@ SCREEN_INFORMATION::SCREEN_INFORMATION(
|
||||
_PopupAttributes{ popupAttributes },
|
||||
_virtualBottom{ 0 },
|
||||
_currentFont{ fontInfo },
|
||||
_desiredFont{ fontInfo },
|
||||
_ignoreLegacyEquivalentVTAttributes{ false }
|
||||
_desiredFont{ fontInfo }
|
||||
{
|
||||
// Check if VT mode should be enabled by default. This can be true if
|
||||
// VirtualTerminalLevel is set to !=0 in the registry, or when conhost
|
||||
@ -2014,13 +2013,6 @@ const TextAttribute& SCREEN_INFORMATION::GetPopupAttributes() const noexcept
|
||||
// <none>
|
||||
void SCREEN_INFORMATION::SetAttributes(const TextAttribute& attributes)
|
||||
{
|
||||
if (_ignoreLegacyEquivalentVTAttributes)
|
||||
{
|
||||
// See the comment on StripErroneousVT16VersionsOfLegacyDefaults for more info.
|
||||
_textBuffer->SetCurrentAttributes(TextAttribute::StripErroneousVT16VersionsOfLegacyDefaults(attributes));
|
||||
return;
|
||||
}
|
||||
|
||||
_textBuffer->SetCurrentAttributes(attributes);
|
||||
|
||||
// If we're an alt buffer, DON'T propagate this setting up to the main buffer.
|
||||
@ -2466,17 +2458,3 @@ const FontInfoDesired& SCREEN_INFORMATION::GetDesiredFont() const noexcept
|
||||
{
|
||||
return _desiredFont;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Engages the legacy VT handling quirk; see TextAttribute::StripErroneousVT16VersionsOfLegacyDefaults
|
||||
void SCREEN_INFORMATION::SetIgnoreLegacyEquivalentVTAttributes() noexcept
|
||||
{
|
||||
_ignoreLegacyEquivalentVTAttributes = true;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Disengages the legacy VT handling quirk; see TextAttribute::StripErroneousVT16VersionsOfLegacyDefaults
|
||||
void SCREEN_INFORMATION::ResetIgnoreLegacyEquivalentVTAttributes() noexcept
|
||||
{
|
||||
_ignoreLegacyEquivalentVTAttributes = false;
|
||||
}
|
||||
|
||||
@ -213,9 +213,6 @@ public:
|
||||
FontInfoDesired& GetDesiredFont() noexcept;
|
||||
const FontInfoDesired& GetDesiredFont() const noexcept;
|
||||
|
||||
void SetIgnoreLegacyEquivalentVTAttributes() noexcept;
|
||||
void ResetIgnoreLegacyEquivalentVTAttributes() noexcept;
|
||||
|
||||
[[nodiscard]] NTSTATUS ResizeWithReflow(const til::size coordnewScreenSize);
|
||||
[[nodiscard]] NTSTATUS ResizeTraditional(const til::size coordNewScreenSize);
|
||||
|
||||
@ -277,8 +274,6 @@ private:
|
||||
// the viewport to move (SetBufferInfo, WriteConsole, etc)
|
||||
til::CoordType _virtualBottom;
|
||||
|
||||
bool _ignoreLegacyEquivalentVTAttributes;
|
||||
|
||||
std::optional<til::size> _deferredPtyResize{ std::nullopt };
|
||||
|
||||
static void _handleDeferredResize(SCREEN_INFORMATION& siMain);
|
||||
|
||||
@ -385,7 +385,7 @@ class ApiRoutinesTests
|
||||
const auto cchWriteLength = std::min(cchIncrement, cchTestText - i);
|
||||
|
||||
// Run the test method
|
||||
const auto hr = _pApiRoutines->WriteConsoleAImpl(si, { pszTestText + i, cchWriteLength }, cchRead, false, waiter);
|
||||
const auto hr = _pApiRoutines->WriteConsoleAImpl(si, { pszTestText + i, cchWriteLength }, cchRead, waiter);
|
||||
|
||||
VERIFY_ARE_EQUAL(S_OK, hr, L"Successful result code from writing.");
|
||||
if (!fInduceWait)
|
||||
@ -441,7 +441,7 @@ class ApiRoutinesTests
|
||||
|
||||
size_t cchRead = 0;
|
||||
std::unique_ptr<IWaitRoutine> waiter;
|
||||
const auto hr = _pApiRoutines->WriteConsoleWImpl(si, testText, cchRead, false, waiter);
|
||||
const auto hr = _pApiRoutines->WriteConsoleWImpl(si, testText, cchRead, waiter);
|
||||
|
||||
VERIFY_ARE_EQUAL(S_OK, hr, L"Successful result code from writing.");
|
||||
if (!fInduceWait)
|
||||
|
||||
@ -248,8 +248,6 @@ class ScreenBufferTests
|
||||
TEST_METHOD(DontChangeVirtualBottomWithMakeCursorVisible);
|
||||
TEST_METHOD(RetainHorizontalOffsetWhenMovingToBottom);
|
||||
|
||||
TEST_METHOD(TestWriteConsoleVTQuirkMode);
|
||||
|
||||
TEST_METHOD(TestReflowEndOfLineColor);
|
||||
TEST_METHOD(TestReflowSmallerLongLineWithColor);
|
||||
TEST_METHOD(TestReflowBiggerLongLineWithColor);
|
||||
@ -2548,7 +2546,7 @@ void ScreenBufferTests::TestAltBufferVtDispatching()
|
||||
std::unique_ptr<WriteData> waiter;
|
||||
std::wstring seq = L"\x1b[5;6H";
|
||||
auto seqCb = 2 * seq.size();
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, false, waiter));
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, waiter));
|
||||
|
||||
VERIFY_ARE_EQUAL(til::point(0, 0), mainCursor.GetPosition());
|
||||
// recall: vt coordinates are (row, column), 1-indexed
|
||||
@ -2563,14 +2561,14 @@ void ScreenBufferTests::TestAltBufferVtDispatching()
|
||||
|
||||
seq = L"\x1b[48;2;255;0;255m";
|
||||
seqCb = 2 * seq.size();
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, false, waiter));
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, waiter));
|
||||
|
||||
VERIFY_ARE_EQUAL(expectedDefaults, mainBuffer.GetAttributes());
|
||||
VERIFY_ARE_EQUAL(expectedRgb, alternate.GetAttributes());
|
||||
|
||||
seq = L"X";
|
||||
seqCb = 2 * seq.size();
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, false, waiter));
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, waiter));
|
||||
|
||||
VERIFY_ARE_EQUAL(til::point(0, 0), mainCursor.GetPosition());
|
||||
VERIFY_ARE_EQUAL(til::point(6, 4), altCursor.GetPosition());
|
||||
@ -7565,127 +7563,6 @@ void ScreenBufferTests::RetainHorizontalOffsetWhenMovingToBottom()
|
||||
VERIFY_ARE_EQUAL(initialOrigin.x, si.GetViewport().Left());
|
||||
}
|
||||
|
||||
void ScreenBufferTests::TestWriteConsoleVTQuirkMode()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Data:useQuirk", L"{false, true}")
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
bool useQuirk;
|
||||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"useQuirk", useQuirk), L"whether to enable the quirk");
|
||||
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
gci.LockConsole(); // Lock must be taken to manipulate buffer.
|
||||
auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); });
|
||||
|
||||
auto& mainBuffer = gci.GetActiveOutputBuffer();
|
||||
auto& cursor = mainBuffer.GetTextBuffer().GetCursor();
|
||||
// Make sure we're in VT mode
|
||||
WI_SetFlag(mainBuffer.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
|
||||
const TextAttribute defaultAttribute{};
|
||||
// Make sure we're using the default attributes at the start of the test,
|
||||
// Otherwise they could be polluted from a previous test.
|
||||
mainBuffer.SetAttributes(defaultAttribute);
|
||||
|
||||
const auto verifyLastAttribute = [&](const TextAttribute& expected) {
|
||||
const auto& row = mainBuffer.GetTextBuffer().GetRowByOffset(cursor.GetPosition().y);
|
||||
auto iter{ row.AttrBegin() };
|
||||
iter += cursor.GetPosition().x - 1;
|
||||
VERIFY_ARE_EQUAL(expected, *iter);
|
||||
};
|
||||
|
||||
std::unique_ptr<WriteData> waiter;
|
||||
|
||||
std::wstring seq{};
|
||||
size_t seqCb{ 0 };
|
||||
|
||||
/* Write red on blue, verify that it comes through */
|
||||
{
|
||||
TextAttribute vtRedOnBlueAttribute{};
|
||||
vtRedOnBlueAttribute.SetForeground(TextColor{ TextColor::DARK_RED, false });
|
||||
vtRedOnBlueAttribute.SetBackground(TextColor{ TextColor::DARK_BLUE, false });
|
||||
|
||||
seq = L"\x1b[31;44m";
|
||||
seqCb = 2 * seq.size();
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, useQuirk, waiter));
|
||||
|
||||
VERIFY_ARE_EQUAL(vtRedOnBlueAttribute, mainBuffer.GetAttributes());
|
||||
|
||||
seq = L"X";
|
||||
seqCb = 2 * seq.size();
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, useQuirk, waiter));
|
||||
|
||||
verifyLastAttribute(vtRedOnBlueAttribute);
|
||||
}
|
||||
|
||||
/* Write white on black, verify that it acts as expected for the quirk mode */
|
||||
{
|
||||
TextAttribute vtWhiteOnBlackAttribute{};
|
||||
vtWhiteOnBlackAttribute.SetForeground(TextColor{ TextColor::DARK_WHITE, false });
|
||||
vtWhiteOnBlackAttribute.SetBackground(TextColor{ TextColor::DARK_BLACK, false });
|
||||
|
||||
const auto quirkExpectedAttribute{ useQuirk ? defaultAttribute : vtWhiteOnBlackAttribute };
|
||||
|
||||
seq = L"\x1b[37;40m"; // the quirk should suppress this, turning it into "defaults"
|
||||
seqCb = 2 * seq.size();
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, useQuirk, waiter));
|
||||
|
||||
VERIFY_ARE_EQUAL(quirkExpectedAttribute, mainBuffer.GetAttributes());
|
||||
|
||||
seq = L"X";
|
||||
seqCb = 2 * seq.size();
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, useQuirk, waiter));
|
||||
|
||||
verifyLastAttribute(quirkExpectedAttribute);
|
||||
}
|
||||
|
||||
/* Write bright white on black, verify that it acts as expected for the quirk mode */
|
||||
{
|
||||
TextAttribute vtBrightWhiteOnBlackAttribute{};
|
||||
vtBrightWhiteOnBlackAttribute.SetForeground(TextColor{ TextColor::DARK_WHITE, false });
|
||||
vtBrightWhiteOnBlackAttribute.SetBackground(TextColor{ TextColor::DARK_BLACK, false });
|
||||
vtBrightWhiteOnBlackAttribute.SetIntense(true);
|
||||
|
||||
auto vtBrightWhiteOnDefaultAttribute{ vtBrightWhiteOnBlackAttribute }; // copy the above attribute
|
||||
vtBrightWhiteOnDefaultAttribute.SetDefaultBackground();
|
||||
|
||||
const auto quirkExpectedAttribute{ useQuirk ? vtBrightWhiteOnDefaultAttribute : vtBrightWhiteOnBlackAttribute };
|
||||
|
||||
seq = L"\x1b[1;37;40m"; // the quirk should suppress black only, turning it into "default background"
|
||||
seqCb = 2 * seq.size();
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, useQuirk, waiter));
|
||||
|
||||
VERIFY_ARE_EQUAL(quirkExpectedAttribute, mainBuffer.GetAttributes());
|
||||
|
||||
seq = L"X";
|
||||
seqCb = 2 * seq.size();
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, useQuirk, waiter));
|
||||
|
||||
verifyLastAttribute(quirkExpectedAttribute);
|
||||
}
|
||||
|
||||
/* Write a 256-color white on a 256-color black, make sure the quirk does not suppress it */
|
||||
{
|
||||
TextAttribute vtWhiteOnBlack256Attribute{};
|
||||
vtWhiteOnBlack256Attribute.SetForeground(TextColor{ TextColor::DARK_WHITE, true });
|
||||
vtWhiteOnBlack256Attribute.SetBackground(TextColor{ TextColor::DARK_BLACK, true });
|
||||
|
||||
// reset (disable intense from the last test) before setting both colors
|
||||
seq = L"\x1b[m\x1b[38;5;7;48;5;0m"; // the quirk should *not* suppress this (!)
|
||||
seqCb = 2 * seq.size();
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, useQuirk, waiter));
|
||||
|
||||
VERIFY_ARE_EQUAL(vtWhiteOnBlack256Attribute, mainBuffer.GetAttributes());
|
||||
|
||||
seq = L"X";
|
||||
seqCb = 2 * seq.size();
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(&seq[0], &seqCb, mainBuffer, useQuirk, waiter));
|
||||
|
||||
verifyLastAttribute(vtWhiteOnBlack256Attribute);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenBufferTests::TestReflowEndOfLineColor()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
|
||||
@ -1396,16 +1396,16 @@ void TextBufferTests::TestBackspaceStringsAPI()
|
||||
L"Using WriteCharsLegacy, write \\b \\b as a single string."));
|
||||
size_t aCb = 2;
|
||||
size_t seqCb = 6;
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(L"a", &aCb, si, false, waiter));
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(L"\b \b", &seqCb, si, false, waiter));
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(L"a", &aCb, si, waiter));
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(L"\b \b", &seqCb, si, waiter));
|
||||
VERIFY_ARE_EQUAL(cursor.GetPosition().x, x0);
|
||||
VERIFY_ARE_EQUAL(cursor.GetPosition().y, y0);
|
||||
|
||||
seqCb = 2;
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(L"a", &seqCb, si, false, waiter));
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(L"\b", &seqCb, si, false, waiter));
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(L" ", &seqCb, si, false, waiter));
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(L"\b", &seqCb, si, false, waiter));
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(L"a", &seqCb, si, waiter));
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(L"\b", &seqCb, si, waiter));
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(L" ", &seqCb, si, waiter));
|
||||
VERIFY_SUCCEEDED(DoWriteConsole(L"\b", &seqCb, si, waiter));
|
||||
VERIFY_ARE_EQUAL(cursor.GetPosition().x, x0);
|
||||
VERIFY_ARE_EQUAL(cursor.GetPosition().y, y0);
|
||||
}
|
||||
|
||||
@ -241,19 +241,19 @@ class ::Microsoft::Console::VirtualTerminal::VtIoTests
|
||||
std::string_view expected;
|
||||
std::string_view actual;
|
||||
|
||||
THROW_IF_FAILED(routines.WriteConsoleWImpl(*screenInfo, L"", written, false, waiter));
|
||||
THROW_IF_FAILED(routines.WriteConsoleWImpl(*screenInfo, L"", written, waiter));
|
||||
expected = "";
|
||||
actual = readOutput();
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
|
||||
// Force-wrap because we write up to the last column.
|
||||
THROW_IF_FAILED(routines.WriteConsoleWImpl(*screenInfo, L"aaaaaaaa", written, false, waiter));
|
||||
THROW_IF_FAILED(routines.WriteConsoleWImpl(*screenInfo, L"aaaaaaaa", written, waiter));
|
||||
expected = "aaaaaaaa\r\n";
|
||||
actual = readOutput();
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
|
||||
// Force-wrap because we write up to the last column, but this time with a tab.
|
||||
THROW_IF_FAILED(routines.WriteConsoleWImpl(*screenInfo, L"a\t\r\nb", written, false, waiter));
|
||||
THROW_IF_FAILED(routines.WriteConsoleWImpl(*screenInfo, L"a\t\r\nb", written, waiter));
|
||||
expected = "a\t\r\n\r\nb";
|
||||
actual = readOutput();
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
|
||||
@ -24,14 +24,12 @@
|
||||
WriteData::WriteData(SCREEN_INFORMATION& siContext,
|
||||
_In_reads_bytes_(cbContext) PCWCHAR pwchContext,
|
||||
const size_t cbContext,
|
||||
const UINT uiOutputCodepage,
|
||||
const bool requiresVtQuirk) :
|
||||
const UINT uiOutputCodepage) :
|
||||
IWaitRoutine(ReplyDataType::Write),
|
||||
_siContext(siContext),
|
||||
_pwchContext(THROW_IF_NULL_ALLOC(reinterpret_cast<wchar_t*>(new byte[cbContext]))),
|
||||
_cbContext(cbContext),
|
||||
_uiOutputCodepage(uiOutputCodepage),
|
||||
_requiresVtQuirk(requiresVtQuirk),
|
||||
_fLeadByteCaptured(false),
|
||||
_fLeadByteConsumed(false),
|
||||
_cchUtf8Consumed(0)
|
||||
@ -126,7 +124,6 @@ bool WriteData::Notify(const WaitTerminationReason TerminationReason,
|
||||
auto Status = DoWriteConsole(_pwchContext,
|
||||
&cbContext,
|
||||
_siContext,
|
||||
_requiresVtQuirk,
|
||||
waiter);
|
||||
|
||||
if (Status == CONSOLE_STATUS_WAIT)
|
||||
|
||||
@ -27,8 +27,7 @@ public:
|
||||
WriteData(SCREEN_INFORMATION& siContext,
|
||||
_In_reads_bytes_(cbContext) PCWCHAR pwchContext,
|
||||
const size_t cbContext,
|
||||
const UINT uiOutputCodepage,
|
||||
const bool requiresVtQuirk);
|
||||
const UINT uiOutputCodepage);
|
||||
~WriteData();
|
||||
|
||||
void SetLeadByteAdjustmentStatus(const bool fLeadByteCaptured,
|
||||
@ -49,7 +48,6 @@ private:
|
||||
wchar_t* const _pwchContext;
|
||||
const size_t _cbContext;
|
||||
UINT const _uiOutputCodepage;
|
||||
bool _requiresVtQuirk;
|
||||
bool _fLeadByteCaptured;
|
||||
bool _fLeadByteConsumed;
|
||||
size_t _cchUtf8Consumed;
|
||||
|
||||
@ -374,8 +374,6 @@ constexpr T saturate(auto val)
|
||||
std::unique_ptr<IWaitRoutine> waiter;
|
||||
size_t cbRead;
|
||||
|
||||
const auto requiresVtQuirk{ m->GetProcessHandle()->GetShimPolicy().IsVtColorQuirkRequired() };
|
||||
|
||||
// We have to hold onto the HR from the call and return it.
|
||||
// We can't return some other error after the actual API call.
|
||||
// This is because the write console function is allowed to write part of the string and then return an error.
|
||||
@ -391,7 +389,7 @@ constexpr T saturate(auto val)
|
||||
TraceLoggingUInt32(a->NumBytes, "NumBytes"),
|
||||
TraceLoggingCountedWideString(buffer.data(), static_cast<ULONG>(buffer.size()), "Buffer"));
|
||||
|
||||
hr = m->_pApiRoutines->WriteConsoleWImpl(*pScreenInfo, buffer, cchInputRead, requiresVtQuirk, waiter);
|
||||
hr = m->_pApiRoutines->WriteConsoleWImpl(*pScreenInfo, buffer, cchInputRead, waiter);
|
||||
|
||||
// We must set the reply length in bytes. Convert back from characters.
|
||||
LOG_IF_FAILED(SizeTMult(cchInputRead, sizeof(wchar_t), &cbRead));
|
||||
@ -406,7 +404,7 @@ constexpr T saturate(auto val)
|
||||
TraceLoggingUInt32(a->NumBytes, "NumBytes"),
|
||||
TraceLoggingCountedString(buffer.data(), static_cast<ULONG>(buffer.size()), "Buffer"));
|
||||
|
||||
hr = m->_pApiRoutines->WriteConsoleAImpl(*pScreenInfo, buffer, cchInputRead, requiresVtQuirk, waiter);
|
||||
hr = m->_pApiRoutines->WriteConsoleAImpl(*pScreenInfo, buffer, cchInputRead, waiter);
|
||||
|
||||
// Reply length is already in bytes (chars), don't need to convert.
|
||||
cbRead = cchInputRead;
|
||||
|
||||
@ -27,24 +27,6 @@ ConsoleShimPolicy::ConsoleShimPolicy(const HANDLE hProcess)
|
||||
const auto isInboxPowershell = til::equals_insensitive_ascii(clientName, L"powershell.exe");
|
||||
const auto isPwsh = til::equals_insensitive_ascii(clientName, L"pwsh.exe");
|
||||
_isPowershell = isInboxPowershell || isPwsh;
|
||||
|
||||
// Inside Windows, we are guaranteed that we're building alongside a new (good) inbox Powershell.
|
||||
// Therefore, we can default _requiresVtColorQuirk to false.
|
||||
#ifndef __INSIDE_WINDOWS
|
||||
// Outside of Windows, we need to check the OS version: Powershell was fixed in early Iron builds.
|
||||
static auto doesInboxPowershellVersionRequireQuirk = [] {
|
||||
OSVERSIONINFOEXW osver{};
|
||||
osver.dwOSVersionInfoSize = sizeof(osver);
|
||||
osver.dwBuildNumber = 20348; // Windows Server 2022 RTM
|
||||
|
||||
DWORDLONG dwlConditionMask = 0;
|
||||
VER_SET_CONDITION(dwlConditionMask, VER_BUILDNUMBER, VER_LESS);
|
||||
|
||||
return VerifyVersionInfoW(&osver, VER_BUILDNUMBER, dwlConditionMask) != FALSE;
|
||||
}();
|
||||
_requiresVtColorQuirk = isInboxPowershell && doesInboxPowershellVersionRequireQuirk;
|
||||
// All modern versions of pwsh.exe have been fixed, and we can direct users to update.
|
||||
#endif
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
@ -73,15 +55,3 @@ bool ConsoleShimPolicy::IsPowershellExe() const noexcept
|
||||
{
|
||||
return _isPowershell;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns true if the connected client application is known to
|
||||
// attempt VT color promotion of legacy colors. See GH#6807 for more.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - True as laid out above.
|
||||
bool ConsoleShimPolicy::IsVtColorQuirkRequired() const noexcept
|
||||
{
|
||||
return _requiresVtColorQuirk;
|
||||
}
|
||||
|
||||
@ -24,10 +24,8 @@ public:
|
||||
ConsoleShimPolicy(const HANDLE hProcess);
|
||||
bool IsCmdExe() const noexcept;
|
||||
bool IsPowershellExe() const noexcept;
|
||||
bool IsVtColorQuirkRequired() const noexcept;
|
||||
|
||||
private:
|
||||
bool _isCmd{ false };
|
||||
bool _isPowershell{ false };
|
||||
bool _requiresVtColorQuirk{ false };
|
||||
};
|
||||
|
||||
@ -80,13 +80,11 @@ public:
|
||||
[[nodiscard]] virtual HRESULT WriteConsoleAImpl(IConsoleOutputObject& context,
|
||||
const std::string_view buffer,
|
||||
size_t& read,
|
||||
bool requiresVtQuirk,
|
||||
std::unique_ptr<IWaitRoutine>& waiter) noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual HRESULT WriteConsoleWImpl(IConsoleOutputObject& context,
|
||||
const std::wstring_view buffer,
|
||||
size_t& read,
|
||||
bool requiresVtQuirk,
|
||||
std::unique_ptr<IWaitRoutine>& waiter) noexcept = 0;
|
||||
|
||||
#pragma region Thread Creation Info
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user