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:
Leonard Hecker 2024-08-06 14:23:03 +02:00 committed by GitHub
parent 5174c96d6d
commit dfb52331f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 23 additions and 275 deletions

View File

@ -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:

View File

@ -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;

View File

@ -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

View File

@ -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());

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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)

View File

@ -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()

View File

@ -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);
}

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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 };
};

View File

@ -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