525be22bd8
## References * See also #8617 ## PR Checklist * [x] Supports #3075 * [x] I work here. * [x] Manual test. ## Detailed Description of the Pull Request / Additional comments ### Window Title Generation Every time the renderer checks the title, it's doing two bad things that I've fixed: 1. It's assembling the prefix to the full title doing a concatenation. No one ever gets just the prefix ever after it is set besides the concat. So instead of storing prefix and the title, I store the assembled prefix + title and the bare title. 2. A copy must be made because it was returning `std::wstring` instead of `std::wstring&`. Now it returns the ref. ### Dirty Area Return Every time the renderer checks the dirty area, which is sometimes multiple times per pass (regular text printing, again for selection, etc.), a vector is created off the heap to return the rectangles. The consumers only ever iterate this data. Now we return a span over a rectangle or rectangles that the engine must store itself. 1. For some renderers, it's always a constant 1 element. They update that 1 element when dirty is queried and return it in the span with a span size of 1. 2. For other renderers with more complex behavior, they're already holding a cached vector of rectangles. Now it's effectively giving out the ref to those in the span for iteration. ### Bitmap Runs The `til::bitmap` used a `std::optional<std::vector<til::rectangle>>` inside itself to cache its runs and would clear the optional when the runs became invalidated. Unfortunately doing `.reset()` to clear the optional will destroy the underlying vector and have it release its memory. We know it's about to get reallocated again, so we're just going to make it a `std::pmr::vector` and give it a memory pool. The alternative solution here was to use a `bool` and `std::vector<til::rectangle>` and just flag when the vector was invalid, but that was honestly more code changes and I love excuses to try out PMR now. Also, instead of returning the ref to the vector... I'm just returning a span now. Everyone just iterates it anyway, may as well not share the implementation detail. ### UTF-8 conversions When testing with Terminal and looking at the `conhost.exe`'s PTY renderer, it spends a TON of allocation time on converting all the UTF-16 stuff inside to UTF-8 before it sends it out the PTY. This was because `ConvertToA` was allocating a string inside itself and returning it just to have it freed after printing and looping back around again... as a PTY does. The change here is to use `til::u16u8` that accepts a buffer out parameter so the caller can just hold onto it. ## Validation Steps Performed - [x] `big.txt` in conhost.exe (GDI renderer) - [x] `big.txt` in Terminal (DX, PTY renderer) - [x] Ensure WDDM and BGFX build under Razzle with this change.
418 lines
12 KiB
C++
418 lines
12 KiB
C++
// 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;
|
|
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(),
|
|
_Prefix(),
|
|
_TitleAndPrefix(),
|
|
_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);
|
|
|
|
// TranslateConsoleTitle must have a null terminated string.
|
|
// This should only happen once on startup so the copy shouldn't be costly
|
|
// but could be eliminated by rewriting TranslateConsoleTitle.
|
|
const std::wstring nullTerminatedTitle{ gci.GetTitle() };
|
|
gci.SetOriginalTitle(std::wstring(TranslateConsoleTitle(nullTerminatedTitle.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
|
|
{
|
|
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
|
|
{
|
|
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() };
|
|
_TitleAndPrefix = _Prefix + _Title;
|
|
|
|
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_view newTitlePrefix)
|
|
{
|
|
_Prefix = newTitlePrefix;
|
|
_TitleAndPrefix = _Prefix + _Title;
|
|
|
|
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_view 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_view linkTitle)
|
|
{
|
|
_LinkTitle = linkTitle;
|
|
}
|
|
|
|
// Method Description:
|
|
// - return a reference to the console's title.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - the console's title.
|
|
const std::wstring_view 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:
|
|
// - the combined prefix and title.
|
|
const std::wstring_view CONSOLE_INFORMATION::GetTitleAndPrefix() const
|
|
{
|
|
return _TitleAndPrefix;
|
|
}
|
|
|
|
// Method Description:
|
|
// - return a reference to the console's original title.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - the console's original title.
|
|
const std::wstring_view CONSOLE_INFORMATION::GetOriginalTitle() const noexcept
|
|
{
|
|
return _OriginalTitle;
|
|
}
|
|
|
|
// Method Description:
|
|
// - return a reference to the console's link title.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - the console's link title.
|
|
const std::wstring_view 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;
|
|
}
|
|
|
|
// 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:
|
|
// - 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();
|
|
ci.Attributes = attr.GetLegacyAttributes();
|
|
ci.Attributes |= cell.DbcsAttr().GeneratePublicApiAttributeFormat();
|
|
return ci;
|
|
}
|