terminal/src/host/consoleInformation.cpp

409 lines
12 KiB
C++
Raw Normal View History

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include <intsafe.h>
#include "misc.h"
#include "output.h"
#include "srvinit.h"
#include "../interactivity/inc/ServiceLocator.hpp"
#include "../types/inc/convert.hpp"
using Microsoft::Console::Interactivity::ServiceLocator;
Add support for the "blink" graphic rendition attribute (#7490) This PR adds support for the _blink_ graphic rendition attribute. When a character is output with this attribute set, it "blinks" at a regular interval, by cycling its color between the normal rendition and a dimmer shade of that color. The majority of the blinking mechanism is encapsulated in a new `BlinkingState` class, which is shared between the Terminal and Conhost implementations. This class keeps track of the position in the blinking cycle, which determines whether characters are rendered as normal or faint. In Windows Terminal, the state is stored in the `Terminal` class, and in Conhost it's stored in the `CONSOLE_INFORMATION` class. In both cases, the `IsBlinkingFaint` method is used to determine the current blinking rendition, and that is passed on as a parameter to the `TextAttribute::CalculateRgbColors` method when these classes are looking up attribute colors. Prior to calculating the colors, the current attribute is also passed to the `RecordBlinkingUsage` method, which keeps track of whether there are actually any blink attributes in use. This is used to determine whether the screen needs to be refreshed when the blinking cycle toggles between the normal and faint renditions. The refresh itself is handled by the `ToggleBlinkingRendition` method, which is triggered by a timer. In Conhost this is just piggybacking on the existing cursor blink timer, but in Windows Terminal it needs to have its own separate timer, since the cursor timer is reset whenever a key is pressed, which is not something we want for attribute blinking. Although the `ToggleBlinkingRendition` is called at the same rate as the cursor blinking, we actually only want the cells to blink at half that frequency. We thus have a counter that cycles through four phases, and blinking is rendered as faint for two of those four. Then every two cycles - when the state changes - a redraw is triggered, but only if there are actually blinking attributes in use (as previously recorded). As mentioned earlier, the blinking frequency is based on the cursor blink rate, so that means it'll automatically be disabled if a user has set their cursor blink rate to none. It can also be disabled by turning off the _Show animations in Windows_ option. In Conhost these settings take effect immediately, but in Windows Terminal they only apply when a new tab is opened. This PR also adds partial support for the `SGR 6` _rapid blink_ attribute. This is not used by DEC terminals, but was defined in the ECMA/ANSI standards. It's not widely supported, but many terminals just it implement it as an alias for the regular `SGR 5` blink attribute, so that's what I've done here too. ## Validation Steps Performed I've checked the _Graphic rendition test pattern_ in Vttest, and compared our representation of the blink attribute to that of an actual DEC VT220 terminal as seen on [YouTube]. With the right color scheme it's a reasonably close match. [YouTube]: https://www.youtube.com/watch?v=03Pz5AmxbE4&t=1m55s Closes #7388
2020-09-22 01:21:33 +02:00
using Microsoft::Console::Render::BlinkingState;
using Microsoft::Console::VirtualTerminal::VtIo;
CONSOLE_INFORMATION::CONSOLE_INFORMATION() :
// ProcessHandleList initializes itself
pInputBuffer(nullptr),
pCurrentScreenBuffer(nullptr),
ScreenBuffers(nullptr),
OutputQueue(),
// ExeAliasList initialized below
_OriginalTitle(),
_Title(),
_LinkTitle(),
Flags(0),
PopupCount(0),
CP(0),
OutputCP(0),
CtrlFlags(0),
LimitingProcessId(0),
// ColorTable initialized below
// CPInfo initialized below
// OutputCPInfo initialized below
_cookedReadData(nullptr),
ConsoleIme{},
_vtIo(),
_blinker{},
renderData{}
{
ZeroMemory((void*)&CPInfo, sizeof(CPInfo));
ZeroMemory((void*)&OutputCPInfo, sizeof(OutputCPInfo));
InitializeCriticalSection(&_csConsoleLock);
}
CONSOLE_INFORMATION::~CONSOLE_INFORMATION()
{
DeleteCriticalSection(&_csConsoleLock);
}
bool CONSOLE_INFORMATION::IsConsoleLocked() const
{
// The critical section structure's OwningThread field contains the ThreadId despite having the HANDLE type.
// This requires us to hard cast the ID to compare.
return _csConsoleLock.OwningThread == (HANDLE)GetCurrentThreadId();
}
#pragma prefast(suppress : 26135, "Adding lock annotation spills into entire project. Future work.")
void CONSOLE_INFORMATION::LockConsole()
{
EnterCriticalSection(&_csConsoleLock);
}
#pragma prefast(suppress : 26135, "Adding lock annotation spills into entire project. Future work.")
bool CONSOLE_INFORMATION::TryLockConsole()
{
return !!TryEnterCriticalSection(&_csConsoleLock);
}
#pragma prefast(suppress : 26135, "Adding lock annotation spills into entire project. Future work.")
void CONSOLE_INFORMATION::UnlockConsole()
{
LeaveCriticalSection(&_csConsoleLock);
}
ULONG CONSOLE_INFORMATION::GetCSRecursionCount()
{
return _csConsoleLock.RecursionCount;
}
// Routine Description:
// - This routine allocates and initialized a console and its associated
// data - input buffer and screen buffer.
// - NOTE: Will read global ServiceLocator::LocateGlobals().getConsoleInformation expecting Settings to already be filled.
// Arguments:
// - title - Window Title to display
// Return Value:
// - STATUS_SUCCESS if successful.
[[nodiscard]] NTSTATUS CONSOLE_INFORMATION::AllocateConsole(const std::wstring_view title)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
// Synchronize flags
WI_SetFlagIf(gci.Flags, CONSOLE_AUTO_POSITION, !!gci.GetAutoPosition());
WI_SetFlagIf(gci.Flags, CONSOLE_QUICK_EDIT_MODE, !!gci.GetQuickEdit());
WI_SetFlagIf(gci.Flags, CONSOLE_HISTORY_NODUP, !!gci.GetHistoryNoDup());
Selection* const pSelection = &Selection::Instance();
pSelection->SetLineSelection(!!gci.GetLineSelection());
SetConsoleCPInfo(TRUE);
SetConsoleCPInfo(FALSE);
// Initialize input buffer.
try
{
gci.pInputBuffer = new InputBuffer();
}
catch (...)
{
return NTSTATUS_FROM_HRESULT(wil::ResultFromCaughtException());
}
try
{
gci.SetTitle(title);
gci.SetOriginalTitle(std::wstring(TranslateConsoleTitle(gci.GetTitle().c_str(), TRUE, FALSE)));
}
catch (...)
{
return NTSTATUS_FROM_HRESULT(wil::ResultFromCaughtException());
}
NTSTATUS Status = DoCreateScreenBuffer();
if (!NT_SUCCESS(Status))
{
goto ErrorExit2;
}
gci.pCurrentScreenBuffer = gci.ScreenBuffers;
gci.GetActiveOutputBuffer().ScrollScale = gci.GetScrollScale();
gci.ConsoleIme.RefreshAreaAttributes();
if (NT_SUCCESS(Status))
{
return STATUS_SUCCESS;
}
RIPMSG1(RIP_WARNING, "Console init failed with status 0x%x", Status);
delete gci.ScreenBuffers;
gci.ScreenBuffers = nullptr;
ErrorExit2:
delete gci.pInputBuffer;
return Status;
}
VtIo* CONSOLE_INFORMATION::GetVtIo()
{
return &_vtIo;
}
bool CONSOLE_INFORMATION::IsInVtIoMode() const
{
return _vtIo.IsUsingVt();
}
bool CONSOLE_INFORMATION::HasPendingCookedRead() const noexcept
{
return _cookedReadData != nullptr;
}
const COOKED_READ_DATA& CONSOLE_INFORMATION::CookedReadData() const noexcept
{
return *_cookedReadData;
}
COOKED_READ_DATA& CONSOLE_INFORMATION::CookedReadData() noexcept
{
return *_cookedReadData;
}
void CONSOLE_INFORMATION::SetCookedReadData(COOKED_READ_DATA* readData) noexcept
{
_cookedReadData = readData;
}
// Method Description:
// - Return the active screen buffer of the console.
// Arguments:
// - <none>
// Return Value:
// - the active screen buffer of the console.
SCREEN_INFORMATION& CONSOLE_INFORMATION::GetActiveOutputBuffer()
{
return *pCurrentScreenBuffer;
}
const SCREEN_INFORMATION& CONSOLE_INFORMATION::GetActiveOutputBuffer() const
{
return *pCurrentScreenBuffer;
}
bool CONSOLE_INFORMATION::HasActiveOutputBuffer() const
{
return (pCurrentScreenBuffer != nullptr);
}
// Method Description:
// - Return the active input buffer of the console.
// Arguments:
// - <none>
// Return Value:
// - the active input buffer of the console.
InputBuffer* const CONSOLE_INFORMATION::GetActiveInputBuffer() const
{
return pInputBuffer;
}
// Method Description:
// - Return the default foreground color of the console. If the settings are
// configured to have a default foreground color (separate from the color
// table), this will return that value. Otherwise it will return the value
// from the colortable corresponding to our default attributes.
// Arguments:
// - <none>
// Return Value:
// - the default foreground color of the console.
COLORREF CONSOLE_INFORMATION::GetDefaultForeground() const noexcept
{
Add support for the "blink" graphic rendition attribute (#7490) This PR adds support for the _blink_ graphic rendition attribute. When a character is output with this attribute set, it "blinks" at a regular interval, by cycling its color between the normal rendition and a dimmer shade of that color. The majority of the blinking mechanism is encapsulated in a new `BlinkingState` class, which is shared between the Terminal and Conhost implementations. This class keeps track of the position in the blinking cycle, which determines whether characters are rendered as normal or faint. In Windows Terminal, the state is stored in the `Terminal` class, and in Conhost it's stored in the `CONSOLE_INFORMATION` class. In both cases, the `IsBlinkingFaint` method is used to determine the current blinking rendition, and that is passed on as a parameter to the `TextAttribute::CalculateRgbColors` method when these classes are looking up attribute colors. Prior to calculating the colors, the current attribute is also passed to the `RecordBlinkingUsage` method, which keeps track of whether there are actually any blink attributes in use. This is used to determine whether the screen needs to be refreshed when the blinking cycle toggles between the normal and faint renditions. The refresh itself is handled by the `ToggleBlinkingRendition` method, which is triggered by a timer. In Conhost this is just piggybacking on the existing cursor blink timer, but in Windows Terminal it needs to have its own separate timer, since the cursor timer is reset whenever a key is pressed, which is not something we want for attribute blinking. Although the `ToggleBlinkingRendition` is called at the same rate as the cursor blinking, we actually only want the cells to blink at half that frequency. We thus have a counter that cycles through four phases, and blinking is rendered as faint for two of those four. Then every two cycles - when the state changes - a redraw is triggered, but only if there are actually blinking attributes in use (as previously recorded). As mentioned earlier, the blinking frequency is based on the cursor blink rate, so that means it'll automatically be disabled if a user has set their cursor blink rate to none. It can also be disabled by turning off the _Show animations in Windows_ option. In Conhost these settings take effect immediately, but in Windows Terminal they only apply when a new tab is opened. This PR also adds partial support for the `SGR 6` _rapid blink_ attribute. This is not used by DEC terminals, but was defined in the ECMA/ANSI standards. It's not widely supported, but many terminals just it implement it as an alias for the regular `SGR 5` blink attribute, so that's what I've done here too. ## Validation Steps Performed I've checked the _Graphic rendition test pattern_ in Vttest, and compared our representation of the blink attribute to that of an actual DEC VT220 terminal as seen on [YouTube]. With the right color scheme it's a reasonably close match. [YouTube]: https://www.youtube.com/watch?v=03Pz5AmxbE4&t=1m55s Closes #7388
2020-09-22 01:21:33 +02:00
const auto fg = GetDefaultForegroundColor();
return fg != INVALID_COLOR ? fg : GetColorTableEntry(LOBYTE(GetFillAttribute()) & FG_ATTRS);
}
// Method Description:
// - Return the default background color of the console. If the settings are
// configured to have a default background color (separate from the color
// table), this will return that value. Otherwise it will return the value
// from the colortable corresponding to our default attributes.
// Arguments:
// - <none>
// Return Value:
// - the default background color of the console.
COLORREF CONSOLE_INFORMATION::GetDefaultBackground() const noexcept
{
Add support for the "blink" graphic rendition attribute (#7490) This PR adds support for the _blink_ graphic rendition attribute. When a character is output with this attribute set, it "blinks" at a regular interval, by cycling its color between the normal rendition and a dimmer shade of that color. The majority of the blinking mechanism is encapsulated in a new `BlinkingState` class, which is shared between the Terminal and Conhost implementations. This class keeps track of the position in the blinking cycle, which determines whether characters are rendered as normal or faint. In Windows Terminal, the state is stored in the `Terminal` class, and in Conhost it's stored in the `CONSOLE_INFORMATION` class. In both cases, the `IsBlinkingFaint` method is used to determine the current blinking rendition, and that is passed on as a parameter to the `TextAttribute::CalculateRgbColors` method when these classes are looking up attribute colors. Prior to calculating the colors, the current attribute is also passed to the `RecordBlinkingUsage` method, which keeps track of whether there are actually any blink attributes in use. This is used to determine whether the screen needs to be refreshed when the blinking cycle toggles between the normal and faint renditions. The refresh itself is handled by the `ToggleBlinkingRendition` method, which is triggered by a timer. In Conhost this is just piggybacking on the existing cursor blink timer, but in Windows Terminal it needs to have its own separate timer, since the cursor timer is reset whenever a key is pressed, which is not something we want for attribute blinking. Although the `ToggleBlinkingRendition` is called at the same rate as the cursor blinking, we actually only want the cells to blink at half that frequency. We thus have a counter that cycles through four phases, and blinking is rendered as faint for two of those four. Then every two cycles - when the state changes - a redraw is triggered, but only if there are actually blinking attributes in use (as previously recorded). As mentioned earlier, the blinking frequency is based on the cursor blink rate, so that means it'll automatically be disabled if a user has set their cursor blink rate to none. It can also be disabled by turning off the _Show animations in Windows_ option. In Conhost these settings take effect immediately, but in Windows Terminal they only apply when a new tab is opened. This PR also adds partial support for the `SGR 6` _rapid blink_ attribute. This is not used by DEC terminals, but was defined in the ECMA/ANSI standards. It's not widely supported, but many terminals just it implement it as an alias for the regular `SGR 5` blink attribute, so that's what I've done here too. ## Validation Steps Performed I've checked the _Graphic rendition test pattern_ in Vttest, and compared our representation of the blink attribute to that of an actual DEC VT220 terminal as seen on [YouTube]. With the right color scheme it's a reasonably close match. [YouTube]: https://www.youtube.com/watch?v=03Pz5AmxbE4&t=1m55s Closes #7388
2020-09-22 01:21:33 +02:00
const auto bg = GetDefaultBackgroundColor();
return bg != INVALID_COLOR ? bg : GetColorTableEntry((LOBYTE(GetFillAttribute()) & BG_ATTRS) >> 4);
}
// Method Description:
// - Get the colors of a particular text attribute, using our color table,
// and our configured default attributes.
// Arguments:
// - attr: the TextAttribute to retrieve the foreground color of.
// Return Value:
// - The color values of the attribute's foreground and background.
std::pair<COLORREF, COLORREF> CONSOLE_INFORMATION::LookupAttributeColors(const TextAttribute& attr) const noexcept
{
_blinkingState.RecordBlinkingUsage(attr);
return attr.CalculateRgbColors(Get256ColorTable(),
GetDefaultForeground(),
GetDefaultBackground(),
IsScreenReversed(),
_blinkingState.IsBlinkingFaint());
}
// Method Description:
// - Set the console's title, and trigger a renderer update of the title.
// This does not include the title prefix, such as "Mark", "Select", or "Scroll"
// Arguments:
// - newTitle: The new value to use for the title
// Return Value:
// - <none>
void CONSOLE_INFORMATION::SetTitle(const std::wstring_view newTitle)
{
_Title = std::wstring{ newTitle.begin(), newTitle.end() };
auto* const pRender = ServiceLocator::LocateGlobals().pRender;
if (pRender)
{
pRender->TriggerTitleChange();
}
}
// Method Description:
// - Set the console title's prefix, and trigger a renderer update of the title.
// This is the part of the title such as "Mark", "Select", or "Scroll"
// Arguments:
// - newTitlePrefix: The new value to use for the title prefix
// Return Value:
// - <none>
void CONSOLE_INFORMATION::SetTitlePrefix(const std::wstring& newTitlePrefix)
{
_TitlePrefix = newTitlePrefix;
auto* const pRender = ServiceLocator::LocateGlobals().pRender;
if (pRender)
{
pRender->TriggerTitleChange();
}
}
// Method Description:
// - Set the value of the console's original title. This is the title the
// console launched with.
// Arguments:
// - originalTitle: The new value to use for the console's original title
// Return Value:
// - <none>
void CONSOLE_INFORMATION::SetOriginalTitle(const std::wstring& originalTitle)
{
_OriginalTitle = originalTitle;
}
// Method Description:
// - Set the value of the console's link title. If the console was launched
/// from a shortcut, this value will not be the empty string.
// Arguments:
// - linkTitle: The new value to use for the console's link title
// Return Value:
// - <none>
void CONSOLE_INFORMATION::SetLinkTitle(const std::wstring& linkTitle)
{
_LinkTitle = linkTitle;
}
// Method Description:
// - return a reference to the console's title.
// Arguments:
// - <none>
// Return Value:
// - a reference to the console's title.
const std::wstring& CONSOLE_INFORMATION::GetTitle() const noexcept
{
return _Title;
}
// Method Description:
// - Return a new wstring representing the actual display value of the title.
// This is the Prefix+Title.
// Arguments:
// - <none>
// Return Value:
// - a new wstring containing the combined prefix and title.
const std::wstring CONSOLE_INFORMATION::GetTitleAndPrefix() const
{
return _TitlePrefix + _Title;
}
// Method Description:
// - return a reference to the console's original title.
// Arguments:
// - <none>
// Return Value:
// - a reference to the console's original title.
const std::wstring& CONSOLE_INFORMATION::GetOriginalTitle() const noexcept
{
return _OriginalTitle;
}
// Method Description:
// - return a reference to the console's link title.
// Arguments:
// - <none>
// Return Value:
// - a reference to the console's link title.
const std::wstring& CONSOLE_INFORMATION::GetLinkTitle() const noexcept
{
return _LinkTitle;
}
// Method Description:
// - return a reference to the console's cursor blinker.
// Arguments:
// - <none>
// Return Value:
// - a reference to the console's cursor blinker.
Microsoft::Console::CursorBlinker& CONSOLE_INFORMATION::GetCursorBlinker() noexcept
{
return _blinker;
}
Add support for the "blink" graphic rendition attribute (#7490) This PR adds support for the _blink_ graphic rendition attribute. When a character is output with this attribute set, it "blinks" at a regular interval, by cycling its color between the normal rendition and a dimmer shade of that color. The majority of the blinking mechanism is encapsulated in a new `BlinkingState` class, which is shared between the Terminal and Conhost implementations. This class keeps track of the position in the blinking cycle, which determines whether characters are rendered as normal or faint. In Windows Terminal, the state is stored in the `Terminal` class, and in Conhost it's stored in the `CONSOLE_INFORMATION` class. In both cases, the `IsBlinkingFaint` method is used to determine the current blinking rendition, and that is passed on as a parameter to the `TextAttribute::CalculateRgbColors` method when these classes are looking up attribute colors. Prior to calculating the colors, the current attribute is also passed to the `RecordBlinkingUsage` method, which keeps track of whether there are actually any blink attributes in use. This is used to determine whether the screen needs to be refreshed when the blinking cycle toggles between the normal and faint renditions. The refresh itself is handled by the `ToggleBlinkingRendition` method, which is triggered by a timer. In Conhost this is just piggybacking on the existing cursor blink timer, but in Windows Terminal it needs to have its own separate timer, since the cursor timer is reset whenever a key is pressed, which is not something we want for attribute blinking. Although the `ToggleBlinkingRendition` is called at the same rate as the cursor blinking, we actually only want the cells to blink at half that frequency. We thus have a counter that cycles through four phases, and blinking is rendered as faint for two of those four. Then every two cycles - when the state changes - a redraw is triggered, but only if there are actually blinking attributes in use (as previously recorded). As mentioned earlier, the blinking frequency is based on the cursor blink rate, so that means it'll automatically be disabled if a user has set their cursor blink rate to none. It can also be disabled by turning off the _Show animations in Windows_ option. In Conhost these settings take effect immediately, but in Windows Terminal they only apply when a new tab is opened. This PR also adds partial support for the `SGR 6` _rapid blink_ attribute. This is not used by DEC terminals, but was defined in the ECMA/ANSI standards. It's not widely supported, but many terminals just it implement it as an alias for the regular `SGR 5` blink attribute, so that's what I've done here too. ## Validation Steps Performed I've checked the _Graphic rendition test pattern_ in Vttest, and compared our representation of the blink attribute to that of an actual DEC VT220 terminal as seen on [YouTube]. With the right color scheme it's a reasonably close match. [YouTube]: https://www.youtube.com/watch?v=03Pz5AmxbE4&t=1m55s Closes #7388
2020-09-22 01:21:33 +02:00
// Method Description:
// - return a reference to the console's blinking state.
// Arguments:
// - <none>
// Return Value:
// - a reference to the console's blinking state.
BlinkingState& CONSOLE_INFORMATION::GetBlinkingState() const noexcept
{
return _blinkingState;
}
// Method Description:
Improve conpty rendering of default colors in legacy apps (#6698) Essentially what this does is map the default legacy foreground and background attributes (typically white on black) to the `IsDefault` color type in the `TextColor` class. As a result, we can now initialize the buffer for "legacy" shells (like PowerShell and cmd.exe) with default colors, instead of white on black. This fixes the startup rendering in conpty clients, which expect an initial default background color. It also makes these colors update appropriately when the default palette values change. One complication in getting this to work, is that the console permits users to change which color indices are designated as defaults, so we can't assume they'll always be white on black. This means that the legacy-to-`TextAttribute` conversion will need access to those default values. Unfortunately the defaults are stored in the conhost `Settings` class (the `_wFillAttribute` field), which isn't easily accessible to all the code that needs to construct a `TextAttribute` from a legacy value. The `OutputCellIterator` is particularly problematic, because some iterator types need to generate a new `TextAttribute` on every iteration. So after trying a couple of different approaches, I decided that the least worst option would be to add a pair of static properties for the legacy defaults in the `TextAttribute` class itself, then refresh those values from the `Settings` class whenever the defaults changed (this only happens on startup, or when the conhost _Properties_ dialog is edited). And once the `TextAttribute` class had access to those defaults, it was fairly easy to adapt the constructor to handle the conversion of default values to the `IsDefault` color type. I could also then simplify the `TextAttribute::GetLegacyAttributes` method which does the reverse mapping, and which previously required the default values to be passed in as a parameter VALIDATION I had to make one small change to the `TestRoundtripExhaustive` unit test which assumed that all legacy attributes would convert to legacy color types, which is no longer the case, but otherwise all the existing tests passed as is. I added a new unit test verifying that the default legacy attributes correctly mapped to default color types, and the default color types were mapped back to the correct legacy attributes. I've manually confirmed that this fixed the issue raised in #5952, namely that the conhost screen is cleared with the correct default colors, and also that it is correctly refreshed when changing the palette from the properties dialog. And I've combined this PR with #6506, and confirmed that the PowerShell and the cmd shell renderings in Windows Terminal are at least improved, if not always perfect. This is a prerequisite for PR #6506 Closes #5952
2020-07-01 20:08:30 +02:00
// - Generates a CHAR_INFO for this output cell, using the TextAttribute
// GetLegacyAttributes method to generate the legacy style attributes.
// Arguments:
// - cell: The cell to get the CHAR_INFO from
// Return Value:
// - a CHAR_INFO containing legacy information about the cell
CHAR_INFO CONSOLE_INFORMATION::AsCharInfo(const OutputCellView& cell) const noexcept
{
CHAR_INFO ci{ 0 };
ci.Char.UnicodeChar = Utf16ToUcs2(cell.Chars());
// If the current text attributes aren't legacy attributes, then
// use gci to look up the correct legacy attributes to use
// (for mapping RGB values to the nearest table value)
const auto& attr = cell.TextAttr();
Improve conpty rendering of default colors in legacy apps (#6698) Essentially what this does is map the default legacy foreground and background attributes (typically white on black) to the `IsDefault` color type in the `TextColor` class. As a result, we can now initialize the buffer for "legacy" shells (like PowerShell and cmd.exe) with default colors, instead of white on black. This fixes the startup rendering in conpty clients, which expect an initial default background color. It also makes these colors update appropriately when the default palette values change. One complication in getting this to work, is that the console permits users to change which color indices are designated as defaults, so we can't assume they'll always be white on black. This means that the legacy-to-`TextAttribute` conversion will need access to those default values. Unfortunately the defaults are stored in the conhost `Settings` class (the `_wFillAttribute` field), which isn't easily accessible to all the code that needs to construct a `TextAttribute` from a legacy value. The `OutputCellIterator` is particularly problematic, because some iterator types need to generate a new `TextAttribute` on every iteration. So after trying a couple of different approaches, I decided that the least worst option would be to add a pair of static properties for the legacy defaults in the `TextAttribute` class itself, then refresh those values from the `Settings` class whenever the defaults changed (this only happens on startup, or when the conhost _Properties_ dialog is edited). And once the `TextAttribute` class had access to those defaults, it was fairly easy to adapt the constructor to handle the conversion of default values to the `IsDefault` color type. I could also then simplify the `TextAttribute::GetLegacyAttributes` method which does the reverse mapping, and which previously required the default values to be passed in as a parameter VALIDATION I had to make one small change to the `TestRoundtripExhaustive` unit test which assumed that all legacy attributes would convert to legacy color types, which is no longer the case, but otherwise all the existing tests passed as is. I added a new unit test verifying that the default legacy attributes correctly mapped to default color types, and the default color types were mapped back to the correct legacy attributes. I've manually confirmed that this fixed the issue raised in #5952, namely that the conhost screen is cleared with the correct default colors, and also that it is correctly refreshed when changing the palette from the properties dialog. And I've combined this PR with #6506, and confirmed that the PowerShell and the cmd shell renderings in Windows Terminal are at least improved, if not always perfect. This is a prerequisite for PR #6506 Closes #5952
2020-07-01 20:08:30 +02:00
ci.Attributes = attr.GetLegacyAttributes();
ci.Attributes |= cell.DbcsAttr().GeneratePublicApiAttributeFormat();
return ci;
}