Remove std::deque from Renderer (#10923)
This commit improves the renderer classes by: * reducing binary size by 4kB * improving performance by 5% * reducing code complexity ## References * #10563 -- vtebench tracking issue ## PR Checklist * [x] I work here * [x] Tests added/passed ## Validation Steps Performed * Ran vtebench/termbench and noted ~5% perf. improvements
This commit is contained in:
parent
2c3368f766
commit
15c02b77a0
|
@ -164,10 +164,7 @@ class AliasTests
|
||||||
wcscpy_s(rgwchTargetBefore.get(), cchTarget, rgwchTarget.get());
|
wcscpy_s(rgwchTargetBefore.get(), cchTarget, rgwchTarget.get());
|
||||||
|
|
||||||
size_t cbTargetUsed = 0;
|
size_t cbTargetUsed = 0;
|
||||||
auto const cbTargetUsedBefore = cbTargetUsed;
|
|
||||||
|
|
||||||
DWORD dwLines = 0;
|
DWORD dwLines = 0;
|
||||||
auto const dwLinesBefore = dwLines;
|
|
||||||
|
|
||||||
// Register the wrong alias name before we try.
|
// Register the wrong alias name before we try.
|
||||||
std::wstring exe(L"exe.exe");
|
std::wstring exe(L"exe.exe");
|
||||||
|
@ -306,7 +303,6 @@ class AliasTests
|
||||||
auto rgwchTargetBefore = std::make_unique<wchar_t[]>(cchTarget);
|
auto rgwchTargetBefore = std::make_unique<wchar_t[]>(cchTarget);
|
||||||
wcscpy_s(rgwchTargetBefore.get(), cchTarget, rgwchTarget.get());
|
wcscpy_s(rgwchTargetBefore.get(), cchTarget, rgwchTarget.get());
|
||||||
|
|
||||||
const size_t cbTarget = cchTarget * sizeof(wchar_t);
|
|
||||||
size_t cbTargetUsed = 0;
|
size_t cbTargetUsed = 0;
|
||||||
auto const cbTargetUsedBefore = cbTargetUsed;
|
auto const cbTargetUsedBefore = cbTargetUsed;
|
||||||
|
|
||||||
|
|
|
@ -899,7 +899,6 @@ void TextBufferTests::TestUnBold()
|
||||||
|
|
||||||
const auto& row = tbi.GetRowByOffset(y);
|
const auto& row = tbi.GetRowByOffset(y);
|
||||||
const auto attrRow = &row.GetAttrRow();
|
const auto attrRow = &row.GetAttrRow();
|
||||||
const auto len = tbi.GetSize().Width();
|
|
||||||
const std::vector<TextAttribute> attrs{ attrRow->begin(), attrRow->end() };
|
const std::vector<TextAttribute> attrs{ attrRow->begin(), attrRow->end() };
|
||||||
const auto attrA = attrs[x - 2];
|
const auto attrA = attrs[x - 2];
|
||||||
const auto attrB = attrs[x - 1];
|
const auto attrB = attrs[x - 1];
|
||||||
|
@ -951,7 +950,6 @@ void TextBufferTests::TestUnBoldRgb()
|
||||||
|
|
||||||
const auto& row = tbi.GetRowByOffset(y);
|
const auto& row = tbi.GetRowByOffset(y);
|
||||||
const auto attrRow = &row.GetAttrRow();
|
const auto attrRow = &row.GetAttrRow();
|
||||||
const auto len = tbi.GetSize().Width();
|
|
||||||
const std::vector<TextAttribute> attrs{ attrRow->begin(), attrRow->end() };
|
const std::vector<TextAttribute> attrs{ attrRow->begin(), attrRow->end() };
|
||||||
const auto attrA = attrs[x - 2];
|
const auto attrA = attrs[x - 2];
|
||||||
const auto attrB = attrs[x - 1];
|
const auto attrB = attrs[x - 1];
|
||||||
|
@ -1011,7 +1009,6 @@ void TextBufferTests::TestComplexUnBold()
|
||||||
|
|
||||||
const auto& row = tbi.GetRowByOffset(y);
|
const auto& row = tbi.GetRowByOffset(y);
|
||||||
const auto attrRow = &row.GetAttrRow();
|
const auto attrRow = &row.GetAttrRow();
|
||||||
const auto len = tbi.GetSize().Width();
|
|
||||||
const std::vector<TextAttribute> attrs{ attrRow->begin(), attrRow->end() };
|
const std::vector<TextAttribute> attrs{ attrRow->begin(), attrRow->end() };
|
||||||
const auto attrA = attrs[x - 6];
|
const auto attrA = attrs[x - 6];
|
||||||
const auto attrB = attrs[x - 5];
|
const auto attrB = attrs[x - 5];
|
||||||
|
@ -1094,7 +1091,6 @@ void TextBufferTests::CopyAttrs()
|
||||||
|
|
||||||
const auto& row = tbi.GetRowByOffset(0);
|
const auto& row = tbi.GetRowByOffset(0);
|
||||||
const auto attrRow = &row.GetAttrRow();
|
const auto attrRow = &row.GetAttrRow();
|
||||||
const auto len = tbi.GetSize().Width();
|
|
||||||
const std::vector<TextAttribute> attrs{ attrRow->begin(), attrRow->end() };
|
const std::vector<TextAttribute> attrs{ attrRow->begin(), attrRow->end() };
|
||||||
const auto attrA = attrs[0];
|
const auto attrA = attrs[0];
|
||||||
const auto attrB = attrs[1];
|
const auto attrB = attrs[1];
|
||||||
|
@ -1146,7 +1142,6 @@ void TextBufferTests::EmptySgrTest()
|
||||||
|
|
||||||
const auto& row = tbi.GetRowByOffset(y);
|
const auto& row = tbi.GetRowByOffset(y);
|
||||||
const auto attrRow = &row.GetAttrRow();
|
const auto attrRow = &row.GetAttrRow();
|
||||||
const auto len = tbi.GetSize().Width();
|
|
||||||
const std::vector<TextAttribute> attrs{ attrRow->begin(), attrRow->end() };
|
const std::vector<TextAttribute> attrs{ attrRow->begin(), attrRow->end() };
|
||||||
const auto attrA = attrs[x - 3];
|
const auto attrA = attrs[x - 3];
|
||||||
const auto attrB = attrs[x - 2];
|
const auto attrB = attrs[x - 2];
|
||||||
|
@ -1208,7 +1203,6 @@ void TextBufferTests::TestReverseReset()
|
||||||
|
|
||||||
const auto& row = tbi.GetRowByOffset(y);
|
const auto& row = tbi.GetRowByOffset(y);
|
||||||
const auto attrRow = &row.GetAttrRow();
|
const auto attrRow = &row.GetAttrRow();
|
||||||
const auto len = tbi.GetSize().Width();
|
|
||||||
const std::vector<TextAttribute> attrs{ attrRow->begin(), attrRow->end() };
|
const std::vector<TextAttribute> attrs{ attrRow->begin(), attrRow->end() };
|
||||||
const auto attrA = attrs[x - 3];
|
const auto attrA = attrs[x - 3];
|
||||||
const auto attrB = attrs[x - 2];
|
const auto attrB = attrs[x - 2];
|
||||||
|
@ -1310,7 +1304,6 @@ void TextBufferTests::CopyLastAttr()
|
||||||
const ROW& row1 = tbi.GetRowByOffset(y + 1);
|
const ROW& row1 = tbi.GetRowByOffset(y + 1);
|
||||||
const ROW& row2 = tbi.GetRowByOffset(y + 2);
|
const ROW& row2 = tbi.GetRowByOffset(y + 2);
|
||||||
const ROW& row3 = tbi.GetRowByOffset(y + 3);
|
const ROW& row3 = tbi.GetRowByOffset(y + 3);
|
||||||
const auto len = tbi.GetSize().Width();
|
|
||||||
|
|
||||||
const std::vector<TextAttribute> attrs1{ row1.GetAttrRow().begin(), row1.GetAttrRow().end() };
|
const std::vector<TextAttribute> attrs1{ row1.GetAttrRow().begin(), row1.GetAttrRow().end() };
|
||||||
const std::vector<TextAttribute> attrs2{ row2.GetAttrRow().begin(), row2.GetAttrRow().end() };
|
const std::vector<TextAttribute> attrs2{ row2.GetAttrRow().begin(), row2.GetAttrRow().end() };
|
||||||
|
|
|
@ -456,6 +456,7 @@ void VtIoTests::RendererDtorAndThreadAndDx()
|
||||||
// which is what CI uses.
|
// which is what CI uses.
|
||||||
/*Sleep(500);*/
|
/*Sleep(500);*/
|
||||||
|
|
||||||
|
(void)dxEngine->Enable();
|
||||||
pThread->EnablePainting();
|
pThread->EnablePainting();
|
||||||
pRenderer->TriggerTeardown();
|
pRenderer->TriggerTeardown();
|
||||||
pRenderer.reset();
|
pRenderer.reset();
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
// Copyright (c) Microsoft Corporation.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
#include "precomp.h"
|
|
||||||
|
|
||||||
#include "../inc/Cluster.hpp"
|
|
||||||
#include "../../inc/unicode.hpp"
|
|
||||||
#include "../types/inc/convert.hpp"
|
|
||||||
|
|
||||||
using namespace Microsoft::Console::Render;
|
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - Instantiates a new cluster structure
|
|
||||||
// Arguments:
|
|
||||||
// - text - This is a view of the text that forms this cluster (one or more wchar_t*s)
|
|
||||||
// - columns - This is the number of columns in the grid that the cluster should consume when drawn
|
|
||||||
Cluster::Cluster(const std::wstring_view text, const size_t columns) :
|
|
||||||
_text(text),
|
|
||||||
_columns(columns)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - Provides the embedded text as a single character
|
|
||||||
// - This might replace the string with the replacement character if it doesn't fit as one wchar_t
|
|
||||||
// Arguments:
|
|
||||||
// - <none>
|
|
||||||
// Return Value:
|
|
||||||
// - The only wchar_t in the string or the Unicode replacement character as appropriate.
|
|
||||||
const wchar_t Cluster::GetTextAsSingle() const noexcept
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Utf16ToUcs2(_text);
|
|
||||||
}
|
|
||||||
CATCH_LOG();
|
|
||||||
return UNICODE_REPLACEMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - Provides the string of wchar_ts for this cluster.
|
|
||||||
// Arguments:
|
|
||||||
// - <none>
|
|
||||||
// Return Value:
|
|
||||||
// - String view of wchar_ts.
|
|
||||||
const std::wstring_view& Cluster::GetText() const noexcept
|
|
||||||
{
|
|
||||||
return _text;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - Gets the number of columns in the grid that this character should consume
|
|
||||||
// visually when rendered onto a line.
|
|
||||||
// Arguments:
|
|
||||||
// - <none>
|
|
||||||
// Return Value:
|
|
||||||
// - Number of columns to use when drawing (not a pixel count).
|
|
||||||
const size_t Cluster::GetColumns() const noexcept
|
|
||||||
{
|
|
||||||
return _columns;
|
|
||||||
}
|
|
|
@ -11,7 +11,6 @@
|
||||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\BlinkingState.cpp" />
|
<ClCompile Include="..\BlinkingState.cpp" />
|
||||||
<ClCompile Include="..\Cluster.cpp" />
|
|
||||||
<ClCompile Include="..\FontInfo.cpp" />
|
<ClCompile Include="..\FontInfo.cpp" />
|
||||||
<ClCompile Include="..\FontInfoBase.cpp" />
|
<ClCompile Include="..\FontInfoBase.cpp" />
|
||||||
<ClCompile Include="..\FontInfoDesired.cpp" />
|
<ClCompile Include="..\FontInfoDesired.cpp" />
|
||||||
|
|
|
@ -45,9 +45,6 @@
|
||||||
<ClCompile Include="..\BlinkingState.cpp">
|
<ClCompile Include="..\BlinkingState.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\Cluster.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\precomp.h">
|
<ClInclude Include="..\precomp.h">
|
||||||
|
|
|
@ -16,6 +16,12 @@ static constexpr auto maxRetriesForRenderEngine = 3;
|
||||||
// The renderer will wait this number of milliseconds * how many tries have elapsed before trying again.
|
// The renderer will wait this number of milliseconds * how many tries have elapsed before trying again.
|
||||||
static constexpr auto renderBackoffBaseTimeMilliseconds{ 150 };
|
static constexpr auto renderBackoffBaseTimeMilliseconds{ 150 };
|
||||||
|
|
||||||
|
#define FOREACH_ENGINE(var) \
|
||||||
|
for (auto var : _engines) \
|
||||||
|
if (!var) \
|
||||||
|
break; \
|
||||||
|
else
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Creates a new renderer controller for a console.
|
// - Creates a new renderer controller for a console.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
|
@ -23,22 +29,17 @@ static constexpr auto renderBackoffBaseTimeMilliseconds{ 150 };
|
||||||
// - pEngine - The output engine for targeting each rendering frame
|
// - pEngine - The output engine for targeting each rendering frame
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - An instance of a Renderer.
|
// - An instance of a Renderer.
|
||||||
// NOTE: CAN THROW IF MEMORY ALLOCATION FAILS.
|
|
||||||
Renderer::Renderer(IRenderData* pData,
|
Renderer::Renderer(IRenderData* pData,
|
||||||
_In_reads_(cEngines) IRenderEngine** const rgpEngines,
|
_In_reads_(cEngines) IRenderEngine** const rgpEngines,
|
||||||
const size_t cEngines,
|
const size_t cEngines,
|
||||||
std::unique_ptr<IRenderThread> thread) :
|
std::unique_ptr<IRenderThread> thread) :
|
||||||
_pData(THROW_HR_IF_NULL(E_INVALIDARG, pData)),
|
_pData(THROW_HR_IF_NULL(E_INVALIDARG, pData)),
|
||||||
_pThread{ std::move(thread) },
|
_pThread{ std::move(thread) },
|
||||||
_destructing{ false },
|
|
||||||
_clusterBuffer{},
|
|
||||||
_viewport{ pData->GetViewport() }
|
_viewport{ pData->GetViewport() }
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < cEngines; i++)
|
for (size_t i = 0; i < cEngines; i++)
|
||||||
{
|
{
|
||||||
IRenderEngine* engine = rgpEngines[i];
|
AddRenderEngine(rgpEngines[i]);
|
||||||
// NOTE: THIS CAN THROW IF MEMORY ALLOCATION FAILS.
|
|
||||||
AddRenderEngine(engine);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +51,7 @@ Renderer::Renderer(IRenderData* pData,
|
||||||
// - <none>
|
// - <none>
|
||||||
Renderer::~Renderer()
|
Renderer::~Renderer()
|
||||||
{
|
{
|
||||||
|
// IRenderThread blocks until it has shut down.
|
||||||
_destructing = true;
|
_destructing = true;
|
||||||
_pThread.reset();
|
_pThread.reset();
|
||||||
}
|
}
|
||||||
|
@ -62,12 +64,7 @@ Renderer::~Renderer()
|
||||||
// - HRESULT S_OK, GDI error, Safe Math error, or state/argument errors.
|
// - HRESULT S_OK, GDI error, Safe Math error, or state/argument errors.
|
||||||
[[nodiscard]] HRESULT Renderer::PaintFrame()
|
[[nodiscard]] HRESULT Renderer::PaintFrame()
|
||||||
{
|
{
|
||||||
if (_destructing)
|
FOREACH_ENGINE(pEngine)
|
||||||
{
|
|
||||||
return S_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (IRenderEngine* const pEngine : _rgpEngines)
|
|
||||||
{
|
{
|
||||||
auto tries = maxRetriesForRenderEngine;
|
auto tries = maxRetriesForRenderEngine;
|
||||||
while (tries > 0)
|
while (tries > 0)
|
||||||
|
@ -93,6 +90,7 @@ Renderer::~Renderer()
|
||||||
// abort applications that host us.
|
// abort applications that host us.
|
||||||
return S_FALSE;
|
return S_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a bit of backoff.
|
// Add a bit of backoff.
|
||||||
// Sleep 150ms, 300ms, 450ms before failing out and disabling the renderer.
|
// Sleep 150ms, 300ms, 450ms before failing out and disabling the renderer.
|
||||||
Sleep(renderBackoffBaseTimeMilliseconds * (maxRetriesForRenderEngine - tries));
|
Sleep(renderBackoffBaseTimeMilliseconds * (maxRetriesForRenderEngine - tries));
|
||||||
|
@ -202,9 +200,10 @@ void Renderer::_NotifyPaintFrame()
|
||||||
// - <none>
|
// - <none>
|
||||||
void Renderer::TriggerSystemRedraw(const RECT* const prcDirtyClient)
|
void Renderer::TriggerSystemRedraw(const RECT* const prcDirtyClient)
|
||||||
{
|
{
|
||||||
std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) {
|
FOREACH_ENGINE(pEngine)
|
||||||
|
{
|
||||||
LOG_IF_FAILED(pEngine->InvalidateSystem(prcDirtyClient));
|
LOG_IF_FAILED(pEngine->InvalidateSystem(prcDirtyClient));
|
||||||
});
|
}
|
||||||
|
|
||||||
_NotifyPaintFrame();
|
_NotifyPaintFrame();
|
||||||
}
|
}
|
||||||
|
@ -235,9 +234,10 @@ void Renderer::TriggerRedraw(const Viewport& region)
|
||||||
if (view.TrimToViewport(&srUpdateRegion))
|
if (view.TrimToViewport(&srUpdateRegion))
|
||||||
{
|
{
|
||||||
view.ConvertToOrigin(&srUpdateRegion);
|
view.ConvertToOrigin(&srUpdateRegion);
|
||||||
std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) {
|
FOREACH_ENGINE(pEngine)
|
||||||
|
{
|
||||||
LOG_IF_FAILED(pEngine->Invalidate(&srUpdateRegion));
|
LOG_IF_FAILED(pEngine->Invalidate(&srUpdateRegion));
|
||||||
});
|
}
|
||||||
|
|
||||||
_NotifyPaintFrame();
|
_NotifyPaintFrame();
|
||||||
}
|
}
|
||||||
|
@ -286,7 +286,7 @@ void Renderer::TriggerRedrawCursor(const COORD* const pcoord)
|
||||||
if (cursorView.IsValid())
|
if (cursorView.IsValid())
|
||||||
{
|
{
|
||||||
const SMALL_RECT updateRect = view.ConvertToOrigin(cursorView).ToExclusive();
|
const SMALL_RECT updateRect = view.ConvertToOrigin(cursorView).ToExclusive();
|
||||||
for (IRenderEngine* pEngine : _rgpEngines)
|
FOREACH_ENGINE(pEngine)
|
||||||
{
|
{
|
||||||
LOG_IF_FAILED(pEngine->InvalidateCursor(&updateRect));
|
LOG_IF_FAILED(pEngine->InvalidateCursor(&updateRect));
|
||||||
}
|
}
|
||||||
|
@ -305,9 +305,10 @@ void Renderer::TriggerRedrawCursor(const COORD* const pcoord)
|
||||||
// - <none>
|
// - <none>
|
||||||
void Renderer::TriggerRedrawAll()
|
void Renderer::TriggerRedrawAll()
|
||||||
{
|
{
|
||||||
std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) {
|
FOREACH_ENGINE(pEngine)
|
||||||
|
{
|
||||||
LOG_IF_FAILED(pEngine->InvalidateAll());
|
LOG_IF_FAILED(pEngine->InvalidateAll());
|
||||||
});
|
}
|
||||||
|
|
||||||
_NotifyPaintFrame();
|
_NotifyPaintFrame();
|
||||||
}
|
}
|
||||||
|
@ -325,7 +326,7 @@ void Renderer::TriggerTeardown() noexcept
|
||||||
_pThread->WaitForPaintCompletionAndDisable(INFINITE);
|
_pThread->WaitForPaintCompletionAndDisable(INFINITE);
|
||||||
|
|
||||||
// Then walk through and do one final paint on the caller's thread.
|
// Then walk through and do one final paint on the caller's thread.
|
||||||
for (IRenderEngine* const pEngine : _rgpEngines)
|
FOREACH_ENGINE(pEngine)
|
||||||
{
|
{
|
||||||
bool fEngineRequestsRepaint = false;
|
bool fEngineRequestsRepaint = false;
|
||||||
HRESULT hr = pEngine->PrepareForTeardown(&fEngineRequestsRepaint);
|
HRESULT hr = pEngine->PrepareForTeardown(&fEngineRequestsRepaint);
|
||||||
|
@ -349,7 +350,7 @@ void Renderer::TriggerSelection()
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Get selection rectangles
|
// Get selection rectangles
|
||||||
const auto rects = _GetSelectionRects();
|
auto rects = _GetSelectionRects();
|
||||||
|
|
||||||
// Restrict all previous selection rectangles to inside the current viewport bounds
|
// Restrict all previous selection rectangles to inside the current viewport bounds
|
||||||
for (auto& sr : _previousSelection)
|
for (auto& sr : _previousSelection)
|
||||||
|
@ -367,12 +368,13 @@ void Renderer::TriggerSelection()
|
||||||
sr = Viewport::FromInclusive(rc).ToExclusive();
|
sr = Viewport::FromInclusive(rc).ToExclusive();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) {
|
FOREACH_ENGINE(pEngine)
|
||||||
|
{
|
||||||
LOG_IF_FAILED(pEngine->InvalidateSelection(_previousSelection));
|
LOG_IF_FAILED(pEngine->InvalidateSelection(_previousSelection));
|
||||||
LOG_IF_FAILED(pEngine->InvalidateSelection(rects));
|
LOG_IF_FAILED(pEngine->InvalidateSelection(rects));
|
||||||
});
|
}
|
||||||
|
|
||||||
_previousSelection = rects;
|
_previousSelection = std::move(rects);
|
||||||
|
|
||||||
_NotifyPaintFrame();
|
_NotifyPaintFrame();
|
||||||
}
|
}
|
||||||
|
@ -390,36 +392,25 @@ bool Renderer::_CheckViewportAndScroll()
|
||||||
SMALL_RECT const srOldViewport = _viewport.ToInclusive();
|
SMALL_RECT const srOldViewport = _viewport.ToInclusive();
|
||||||
SMALL_RECT const srNewViewport = _pData->GetViewport().ToInclusive();
|
SMALL_RECT const srNewViewport = _pData->GetViewport().ToInclusive();
|
||||||
|
|
||||||
COORD coordDelta;
|
if (srOldViewport == srNewViewport)
|
||||||
coordDelta.X = srOldViewport.Left - srNewViewport.Left;
|
|
||||||
coordDelta.Y = srOldViewport.Top - srNewViewport.Top;
|
|
||||||
|
|
||||||
for (auto engine : _rgpEngines)
|
|
||||||
{
|
{
|
||||||
LOG_IF_FAILED(engine->UpdateViewport(srNewViewport));
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_viewport = Viewport::FromInclusive(srNewViewport);
|
_viewport = Viewport::FromInclusive(srNewViewport);
|
||||||
|
|
||||||
// If we're keeping some buffers between calls, let them know about the viewport size
|
COORD coordDelta;
|
||||||
// so they can prepare the buffers for changes to either preallocate memory at once
|
coordDelta.X = srOldViewport.Left - srNewViewport.Left;
|
||||||
// (instead of growing naturally) or shrink down to reduce usage as appropriate.
|
coordDelta.Y = srOldViewport.Top - srNewViewport.Top;
|
||||||
const size_t lineLength = gsl::narrow_cast<size_t>(til::rectangle{ srNewViewport }.width());
|
|
||||||
til::manage_vector(_clusterBuffer, lineLength, _shrinkThreshold);
|
|
||||||
|
|
||||||
if (coordDelta.X != 0 || coordDelta.Y != 0)
|
FOREACH_ENGINE(engine)
|
||||||
{
|
{
|
||||||
for (auto engine : _rgpEngines)
|
LOG_IF_FAILED(engine->UpdateViewport(srNewViewport));
|
||||||
{
|
LOG_IF_FAILED(engine->InvalidateScroll(&coordDelta));
|
||||||
LOG_IF_FAILED(engine->InvalidateScroll(&coordDelta));
|
|
||||||
}
|
|
||||||
|
|
||||||
_ScrollPreviousSelection(coordDelta);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
_ScrollPreviousSelection(coordDelta);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
|
@ -448,9 +439,10 @@ void Renderer::TriggerScroll()
|
||||||
// - <none>
|
// - <none>
|
||||||
void Renderer::TriggerScroll(const COORD* const pcoordDelta)
|
void Renderer::TriggerScroll(const COORD* const pcoordDelta)
|
||||||
{
|
{
|
||||||
std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) {
|
FOREACH_ENGINE(pEngine)
|
||||||
|
{
|
||||||
LOG_IF_FAILED(pEngine->InvalidateScroll(pcoordDelta));
|
LOG_IF_FAILED(pEngine->InvalidateScroll(pcoordDelta));
|
||||||
});
|
}
|
||||||
|
|
||||||
_ScrollPreviousSelection(*pcoordDelta);
|
_ScrollPreviousSelection(*pcoordDelta);
|
||||||
|
|
||||||
|
@ -468,7 +460,7 @@ void Renderer::TriggerCircling()
|
||||||
{
|
{
|
||||||
const auto rects = _GetSelectionRects();
|
const auto rects = _GetSelectionRects();
|
||||||
|
|
||||||
for (IRenderEngine* const pEngine : _rgpEngines)
|
FOREACH_ENGINE(pEngine)
|
||||||
{
|
{
|
||||||
bool fEngineRequestsRepaint = false;
|
bool fEngineRequestsRepaint = false;
|
||||||
HRESULT hr = pEngine->InvalidateCircling(&fEngineRequestsRepaint);
|
HRESULT hr = pEngine->InvalidateCircling(&fEngineRequestsRepaint);
|
||||||
|
@ -493,7 +485,7 @@ void Renderer::TriggerCircling()
|
||||||
void Renderer::TriggerTitleChange()
|
void Renderer::TriggerTitleChange()
|
||||||
{
|
{
|
||||||
const auto newTitle = _pData->GetConsoleTitle();
|
const auto newTitle = _pData->GetConsoleTitle();
|
||||||
for (IRenderEngine* const pEngine : _rgpEngines)
|
FOREACH_ENGINE(pEngine)
|
||||||
{
|
{
|
||||||
LOG_IF_FAILED(pEngine->InvalidateTitle(newTitle));
|
LOG_IF_FAILED(pEngine->InvalidateTitle(newTitle));
|
||||||
}
|
}
|
||||||
|
@ -522,10 +514,11 @@ HRESULT Renderer::_PaintTitle(IRenderEngine* const pEngine)
|
||||||
// - <none>
|
// - <none>
|
||||||
void Renderer::TriggerFontChange(const int iDpi, const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo)
|
void Renderer::TriggerFontChange(const int iDpi, const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo)
|
||||||
{
|
{
|
||||||
std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) {
|
FOREACH_ENGINE(pEngine)
|
||||||
|
{
|
||||||
LOG_IF_FAILED(pEngine->UpdateDpi(iDpi));
|
LOG_IF_FAILED(pEngine->UpdateDpi(iDpi));
|
||||||
LOG_IF_FAILED(pEngine->UpdateFont(FontInfoDesired, FontInfo));
|
LOG_IF_FAILED(pEngine->UpdateFont(FontInfoDesired, FontInfo));
|
||||||
});
|
}
|
||||||
|
|
||||||
_NotifyPaintFrame();
|
_NotifyPaintFrame();
|
||||||
}
|
}
|
||||||
|
@ -547,16 +540,19 @@ void Renderer::UpdateSoftFont(const gsl::span<const uint16_t> bitPattern, const
|
||||||
const auto softFontCharCount = cellSize.cy ? bitPattern.size() / cellSize.cy : 0;
|
const auto softFontCharCount = cellSize.cy ? bitPattern.size() / cellSize.cy : 0;
|
||||||
_lastSoftFontChar = _firstSoftFontChar + softFontCharCount - 1;
|
_lastSoftFontChar = _firstSoftFontChar + softFontCharCount - 1;
|
||||||
|
|
||||||
for (const auto pEngine : _rgpEngines)
|
FOREACH_ENGINE(pEngine)
|
||||||
{
|
{
|
||||||
LOG_IF_FAILED(pEngine->UpdateSoftFont(bitPattern, cellSize, centeringHint));
|
LOG_IF_FAILED(pEngine->UpdateSoftFont(bitPattern, cellSize, centeringHint));
|
||||||
}
|
}
|
||||||
TriggerRedrawAll();
|
TriggerRedrawAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We initially tried to have a "_isSoftFontChar" member function, but MSVC
|
||||||
|
// failed to inline it at _all_ call sites (check invocations inside loops).
|
||||||
|
// This issue strangely doesn't occur with static functions.
|
||||||
bool Renderer::s_IsSoftFontChar(const std::wstring_view& v, const size_t firstSoftFontChar, const size_t lastSoftFontChar)
|
bool Renderer::s_IsSoftFontChar(const std::wstring_view& v, const size_t firstSoftFontChar, const size_t lastSoftFontChar)
|
||||||
{
|
{
|
||||||
return v.size() == 1 && v.front() >= firstSoftFontChar && v.front() <= lastSoftFontChar;
|
return v.size() == 1 && v[0] >= firstSoftFontChar && v[0] <= lastSoftFontChar;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
|
@ -570,21 +566,11 @@ bool Renderer::s_IsSoftFontChar(const std::wstring_view& v, const size_t firstSo
|
||||||
// - S_OK if set successfully or relevant GDI error via HRESULT.
|
// - S_OK if set successfully or relevant GDI error via HRESULT.
|
||||||
[[nodiscard]] HRESULT Renderer::GetProposedFont(const int iDpi, const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo)
|
[[nodiscard]] HRESULT Renderer::GetProposedFont(const int iDpi, const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo)
|
||||||
{
|
{
|
||||||
// If there's no head, return E_FAIL. The caller should decide how to
|
|
||||||
// handle this.
|
|
||||||
// Currently, the only caller is the WindowProc:WM_GETDPISCALEDSIZE handler.
|
|
||||||
// It will assume that the proposed font is 1x1, regardless of DPI.
|
|
||||||
if (_rgpEngines.size() < 1)
|
|
||||||
{
|
|
||||||
return E_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// There will only every really be two engines - the real head and the VT
|
// There will only every really be two engines - the real head and the VT
|
||||||
// renderer. We won't know which is which, so iterate over them.
|
// renderer. We won't know which is which, so iterate over them.
|
||||||
// Only return the result of the successful one if it's not S_FALSE (which is the VT renderer)
|
// Only return the result of the successful one if it's not S_FALSE (which is the VT renderer)
|
||||||
// TODO: 14560740 - The Window might be able to get at this info in a more sane manner
|
// TODO: 14560740 - The Window might be able to get at this info in a more sane manner
|
||||||
FAIL_FAST_IF(!(_rgpEngines.size() <= 2));
|
FOREACH_ENGINE(pEngine)
|
||||||
for (IRenderEngine* const pEngine : _rgpEngines)
|
|
||||||
{
|
{
|
||||||
const HRESULT hr = LOG_IF_FAILED(pEngine->GetProposedFont(FontInfoDesired, FontInfo, iDpi));
|
const HRESULT hr = LOG_IF_FAILED(pEngine->GetProposedFont(FontInfoDesired, FontInfo, iDpi));
|
||||||
// We're looking for specifically S_OK, S_FALSE is not good enough.
|
// We're looking for specifically S_OK, S_FALSE is not good enough.
|
||||||
|
@ -592,7 +578,7 @@ bool Renderer::s_IsSoftFontChar(const std::wstring_view& v, const size_t firstSo
|
||||||
{
|
{
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
@ -615,14 +601,13 @@ bool Renderer::IsGlyphWideByFont(const std::wstring_view glyph)
|
||||||
// renderer. We won't know which is which, so iterate over them.
|
// renderer. We won't know which is which, so iterate over them.
|
||||||
// Only return the result of the successful one if it's not S_FALSE (which is the VT renderer)
|
// Only return the result of the successful one if it's not S_FALSE (which is the VT renderer)
|
||||||
// TODO: 14560740 - The Window might be able to get at this info in a more sane manner
|
// TODO: 14560740 - The Window might be able to get at this info in a more sane manner
|
||||||
FAIL_FAST_IF(!(_rgpEngines.size() <= 2));
|
FOREACH_ENGINE(pEngine)
|
||||||
for (IRenderEngine* const pEngine : _rgpEngines)
|
|
||||||
{
|
{
|
||||||
const HRESULT hr = LOG_IF_FAILED(pEngine->IsGlyphWideByFont(glyph, &fIsFullWidth));
|
const HRESULT hr = LOG_IF_FAILED(pEngine->IsGlyphWideByFont(glyph, &fIsFullWidth));
|
||||||
// We're looking for specifically S_OK, S_FALSE is not good enough.
|
// We're looking for specifically S_OK, S_FALSE is not good enough.
|
||||||
if (hr == S_OK)
|
if (hr == S_OK)
|
||||||
{
|
{
|
||||||
return fIsFullWidth;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -691,6 +676,12 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
|
||||||
|
|
||||||
for (const auto& dirtyRect : dirtyAreas)
|
for (const auto& dirtyRect : dirtyAreas)
|
||||||
{
|
{
|
||||||
|
// Shortcut: don't bother redrawing if the width is 0.
|
||||||
|
if (dirtyRect.left() == dirtyRect.right())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
auto dirty = Viewport::FromInclusive(dirtyRect);
|
auto dirty = Viewport::FromInclusive(dirtyRect);
|
||||||
|
|
||||||
// Shift the origin of the dirty region to match the underlying buffer so we can
|
// Shift the origin of the dirty region to match the underlying buffer so we can
|
||||||
|
@ -702,47 +693,43 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
|
||||||
// we need to walk through line-by-line and repaint onto the screen.
|
// we need to walk through line-by-line and repaint onto the screen.
|
||||||
const auto redraw = Viewport::Intersect(dirty, view);
|
const auto redraw = Viewport::Intersect(dirty, view);
|
||||||
|
|
||||||
// Shortcut: don't bother redrawing if the width is 0.
|
// Retrieve the text buffer so we can read information out of it.
|
||||||
if (redraw.Width() > 0)
|
const auto& buffer = _pData->GetTextBuffer();
|
||||||
|
|
||||||
|
// Now walk through each row of text that we need to redraw.
|
||||||
|
for (auto row = redraw.Top(); row < redraw.BottomExclusive(); row++)
|
||||||
{
|
{
|
||||||
// Retrieve the text buffer so we can read information out of it.
|
// Calculate the boundaries of a single line. This is from the left to right edge of the dirty
|
||||||
const auto& buffer = _pData->GetTextBuffer();
|
// area in width and exactly 1 tall.
|
||||||
|
const auto screenLine = SMALL_RECT{ redraw.Left(), row, redraw.RightInclusive(), row };
|
||||||
|
|
||||||
// Now walk through each row of text that we need to redraw.
|
// Convert the screen coordinates of the line to an equivalent
|
||||||
for (auto row = redraw.Top(); row < redraw.BottomExclusive(); row++)
|
// range of buffer cells, taking line rendition into account.
|
||||||
{
|
const auto lineRendition = buffer.GetLineRendition(row);
|
||||||
// Calculate the boundaries of a single line. This is from the left to right edge of the dirty
|
const auto bufferLine = Viewport::FromInclusive(ScreenToBufferLine(screenLine, lineRendition));
|
||||||
// area in width and exactly 1 tall.
|
|
||||||
const auto screenLine = SMALL_RECT{ redraw.Left(), row, redraw.RightInclusive(), row };
|
|
||||||
|
|
||||||
// Convert the screen coordinates of the line to an equivalent
|
// Find where on the screen we should place this line information. This requires us to re-map
|
||||||
// range of buffer cells, taking line rendition into account.
|
// the buffer-based origin of the line back onto the screen-based origin of the line.
|
||||||
const auto lineRendition = buffer.GetLineRendition(row);
|
// For example, the screen might say we need to paint line 1 because it is dirty but the viewport
|
||||||
const auto bufferLine = Viewport::FromInclusive(ScreenToBufferLine(screenLine, lineRendition));
|
// is actually looking at line 26 relative to the buffer. This means that we need line 27 out
|
||||||
|
// of the backing buffer to fill in line 1 of the screen.
|
||||||
|
const auto screenPosition = bufferLine.Origin() - COORD{ 0, view.Top() };
|
||||||
|
|
||||||
// Find where on the screen we should place this line information. This requires us to re-map
|
// Retrieve the cell information iterator limited to just this line we want to redraw.
|
||||||
// the buffer-based origin of the line back onto the screen-based origin of the line.
|
auto it = buffer.GetCellDataAt(bufferLine.Origin(), bufferLine);
|
||||||
// For example, the screen might say we need to paint line 1 because it is dirty but the viewport
|
|
||||||
// is actually looking at line 26 relative to the buffer. This means that we need line 27 out
|
|
||||||
// of the backing buffer to fill in line 1 of the screen.
|
|
||||||
const auto screenPosition = bufferLine.Origin() - COORD{ 0, view.Top() };
|
|
||||||
|
|
||||||
// Retrieve the cell information iterator limited to just this line we want to redraw.
|
// Calculate if two things are true:
|
||||||
auto it = buffer.GetCellDataAt(bufferLine.Origin(), bufferLine);
|
// 1. this row wrapped
|
||||||
|
// 2. We're painting the last col of the row.
|
||||||
|
// In that case, set lineWrapped=true for the _PaintBufferOutputHelper call.
|
||||||
|
const auto lineWrapped = (buffer.GetRowByOffset(bufferLine.Origin().Y).WasWrapForced()) &&
|
||||||
|
(bufferLine.RightExclusive() == buffer.GetSize().Width());
|
||||||
|
|
||||||
// Calculate if two things are true:
|
// Prepare the appropriate line transform for the current row and viewport offset.
|
||||||
// 1. this row wrapped
|
LOG_IF_FAILED(pEngine->PrepareLineTransform(lineRendition, screenPosition.Y, view.Left()));
|
||||||
// 2. We're painting the last col of the row.
|
|
||||||
// In that case, set lineWrapped=true for the _PaintBufferOutputHelper call.
|
|
||||||
const auto lineWrapped = (buffer.GetRowByOffset(bufferLine.Origin().Y).WasWrapForced()) &&
|
|
||||||
(bufferLine.RightExclusive() == buffer.GetSize().Width());
|
|
||||||
|
|
||||||
// Prepare the appropriate line transform for the current row and viewport offset.
|
// Ask the helper to paint through this specific line.
|
||||||
LOG_IF_FAILED(pEngine->PrepareLineTransform(lineRendition, screenPosition.Y, view.Left()));
|
_PaintBufferOutputHelper(pEngine, it, screenPosition, lineWrapped);
|
||||||
|
|
||||||
// Ask the helper to paint through this specific line.
|
|
||||||
_PaintBufferOutputHelper(pEngine, it, screenPosition, lineWrapped);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -750,7 +737,7 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
|
||||||
static bool _IsAllSpaces(const std::wstring_view v)
|
static bool _IsAllSpaces(const std::wstring_view v)
|
||||||
{
|
{
|
||||||
// first non-space char is not found (is npos)
|
// first non-space char is not found (is npos)
|
||||||
return v.find_first_not_of(L" ") == decltype(v)::npos;
|
return v.find_first_not_of(L' ') == decltype(v)::npos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
|
void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
|
||||||
|
@ -839,7 +826,7 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
|
||||||
|
|
||||||
// Walk through the text data and turn it into rendering clusters.
|
// Walk through the text data and turn it into rendering clusters.
|
||||||
// Keep the columnCount as we go to improve performance over digging it out of the vector at the end.
|
// Keep the columnCount as we go to improve performance over digging it out of the vector at the end.
|
||||||
size_t columnCount = 0;
|
size_t columnCount = it->Columns();
|
||||||
|
|
||||||
// If we're on the first cluster to be added and it's marked as "trailing"
|
// If we're on the first cluster to be added and it's marked as "trailing"
|
||||||
// (a.k.a. the right half of a two column character), then we need some special handling.
|
// (a.k.a. the right half of a two column character), then we need some special handling.
|
||||||
|
@ -850,14 +837,7 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
|
||||||
// And tell the next function to trim off the left half of it.
|
// And tell the next function to trim off the left half of it.
|
||||||
trimLeft = true;
|
trimLeft = true;
|
||||||
// And add one to the number of columns we expect it to take as we insert it.
|
// And add one to the number of columns we expect it to take as we insert it.
|
||||||
columnCount = it->Columns() + 1;
|
++columnCount;
|
||||||
_clusterBuffer.emplace_back(it->Chars(), columnCount);
|
|
||||||
}
|
|
||||||
// Otherwise if it's not a special case, just insert it as is.
|
|
||||||
else
|
|
||||||
{
|
|
||||||
columnCount = it->Columns();
|
|
||||||
_clusterBuffer.emplace_back(it->Chars(), columnCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (columnCount > 1)
|
if (columnCount > 1)
|
||||||
|
@ -866,6 +846,7 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance the cluster and column counts.
|
// Advance the cluster and column counts.
|
||||||
|
_clusterBuffer.emplace_back(it->Chars(), columnCount);
|
||||||
it += std::max<size_t>(it->Columns(), 1); // prevent infinite loop for no visible columns
|
it += std::max<size_t>(it->Columns(), 1); // prevent infinite loop for no visible columns
|
||||||
cols += columnCount;
|
cols += columnCount;
|
||||||
|
|
||||||
|
@ -1263,6 +1244,7 @@ std::vector<SMALL_RECT> Renderer::_GetSelectionRects() const
|
||||||
Viewport view = _pData->GetViewport();
|
Viewport view = _pData->GetViewport();
|
||||||
|
|
||||||
std::vector<SMALL_RECT> result;
|
std::vector<SMALL_RECT> result;
|
||||||
|
result.reserve(rects.size());
|
||||||
|
|
||||||
for (auto rect : rects)
|
for (auto rect : rects)
|
||||||
{
|
{
|
||||||
|
@ -1322,7 +1304,17 @@ void Renderer::_ScrollPreviousSelection(const til::point delta)
|
||||||
void Renderer::AddRenderEngine(_In_ IRenderEngine* const pEngine)
|
void Renderer::AddRenderEngine(_In_ IRenderEngine* const pEngine)
|
||||||
{
|
{
|
||||||
THROW_HR_IF_NULL(E_INVALIDARG, pEngine);
|
THROW_HR_IF_NULL(E_INVALIDARG, pEngine);
|
||||||
_rgpEngines.push_back(pEngine);
|
|
||||||
|
for (auto& p : _engines)
|
||||||
|
{
|
||||||
|
if (!p)
|
||||||
|
{
|
||||||
|
p = pEngine;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
THROW_HR_MSG(E_UNEXPECTED, "engines array is full");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method Description:
|
// Method Description:
|
||||||
|
@ -1354,7 +1346,7 @@ void Renderer::UpdateLastHoveredInterval(const std::optional<PointTree::interval
|
||||||
// - Blocks until the engines are able to render without blocking.
|
// - Blocks until the engines are able to render without blocking.
|
||||||
void Renderer::WaitUntilCanRender()
|
void Renderer::WaitUntilCanRender()
|
||||||
{
|
{
|
||||||
for (const auto pEngine : _rgpEngines)
|
FOREACH_ENGINE(pEngine)
|
||||||
{
|
{
|
||||||
pEngine->WaitUntilCanRender();
|
pEngine->WaitUntilCanRender();
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,73 +87,39 @@ namespace Microsoft::Console::Render
|
||||||
void UpdateLastHoveredInterval(const std::optional<interval_tree::IntervalTree<til::point, size_t>::interval>& newInterval);
|
void UpdateLastHoveredInterval(const std::optional<interval_tree::IntervalTree<til::point, size_t>::interval>& newInterval);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::deque<IRenderEngine*> _rgpEngines;
|
static IRenderEngine::GridLines s_GetGridlines(const TextAttribute& textAttribute) noexcept;
|
||||||
|
static bool s_IsSoftFontChar(const std::wstring_view& v, const size_t firstSoftFontChar, const size_t lastSoftFontChar);
|
||||||
IRenderData* _pData; // Non-ownership pointer
|
|
||||||
|
|
||||||
std::unique_ptr<IRenderThread> _pThread;
|
|
||||||
bool _destructing = false;
|
|
||||||
|
|
||||||
std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> _hoveredInterval;
|
|
||||||
|
|
||||||
void _NotifyPaintFrame();
|
void _NotifyPaintFrame();
|
||||||
|
|
||||||
[[nodiscard]] HRESULT _PaintFrameForEngine(_In_ IRenderEngine* const pEngine) noexcept;
|
[[nodiscard]] HRESULT _PaintFrameForEngine(_In_ IRenderEngine* const pEngine) noexcept;
|
||||||
|
|
||||||
bool _CheckViewportAndScroll();
|
bool _CheckViewportAndScroll();
|
||||||
|
|
||||||
[[nodiscard]] HRESULT _PaintBackground(_In_ IRenderEngine* const pEngine);
|
[[nodiscard]] HRESULT _PaintBackground(_In_ IRenderEngine* const pEngine);
|
||||||
|
|
||||||
void _PaintBufferOutput(_In_ IRenderEngine* const pEngine);
|
void _PaintBufferOutput(_In_ IRenderEngine* const pEngine);
|
||||||
|
void _PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, TextBufferCellIterator it, const COORD target, const bool lineWrapped);
|
||||||
void _PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
|
void _PaintBufferOutputGridLineHelper(_In_ IRenderEngine* const pEngine, const TextAttribute textAttribute, const size_t cchLine, const COORD coordTarget);
|
||||||
TextBufferCellIterator it,
|
|
||||||
const COORD target,
|
|
||||||
const bool lineWrapped);
|
|
||||||
|
|
||||||
static IRenderEngine::GridLines s_GetGridlines(const TextAttribute& textAttribute) noexcept;
|
|
||||||
|
|
||||||
void _PaintBufferOutputGridLineHelper(_In_ IRenderEngine* const pEngine,
|
|
||||||
const TextAttribute textAttribute,
|
|
||||||
const size_t cchLine,
|
|
||||||
const COORD coordTarget);
|
|
||||||
|
|
||||||
void _PaintSelection(_In_ IRenderEngine* const pEngine);
|
void _PaintSelection(_In_ IRenderEngine* const pEngine);
|
||||||
void _PaintCursor(_In_ IRenderEngine* const pEngine);
|
void _PaintCursor(_In_ IRenderEngine* const pEngine);
|
||||||
|
|
||||||
void _PaintOverlays(_In_ IRenderEngine* const pEngine);
|
void _PaintOverlays(_In_ IRenderEngine* const pEngine);
|
||||||
void _PaintOverlay(IRenderEngine& engine, const RenderOverlay& overlay);
|
void _PaintOverlay(IRenderEngine& engine, const RenderOverlay& overlay);
|
||||||
|
[[nodiscard]] HRESULT _UpdateDrawingBrushes(_In_ IRenderEngine* const pEngine, const TextAttribute attr, const bool usingSoftFont, const bool isSettingDefaultBrushes);
|
||||||
[[nodiscard]] HRESULT _UpdateDrawingBrushes(_In_ IRenderEngine* const pEngine,
|
|
||||||
const TextAttribute attr,
|
|
||||||
const bool usingSoftFont,
|
|
||||||
const bool isSettingDefaultBrushes);
|
|
||||||
|
|
||||||
[[nodiscard]] HRESULT _PerformScrolling(_In_ IRenderEngine* const pEngine);
|
[[nodiscard]] HRESULT _PerformScrolling(_In_ IRenderEngine* const pEngine);
|
||||||
|
|
||||||
Microsoft::Console::Types::Viewport _viewport;
|
|
||||||
|
|
||||||
static constexpr float _shrinkThreshold = 0.8f;
|
|
||||||
std::vector<Cluster> _clusterBuffer;
|
|
||||||
|
|
||||||
std::vector<SMALL_RECT> _GetSelectionRects() const;
|
std::vector<SMALL_RECT> _GetSelectionRects() const;
|
||||||
void _ScrollPreviousSelection(const til::point delta);
|
void _ScrollPreviousSelection(const til::point delta);
|
||||||
std::vector<SMALL_RECT> _previousSelection;
|
|
||||||
|
|
||||||
[[nodiscard]] HRESULT _PaintTitle(IRenderEngine* const pEngine);
|
[[nodiscard]] HRESULT _PaintTitle(IRenderEngine* const pEngine);
|
||||||
|
|
||||||
[[nodiscard]] std::optional<CursorOptions> _GetCursorInfo();
|
[[nodiscard]] std::optional<CursorOptions> _GetCursorInfo();
|
||||||
[[nodiscard]] HRESULT _PrepareRenderInfo(_In_ IRenderEngine* const pEngine);
|
[[nodiscard]] HRESULT _PrepareRenderInfo(_In_ IRenderEngine* const pEngine);
|
||||||
|
|
||||||
const size_t _firstSoftFontChar = 0xEF20;
|
std::array<IRenderEngine*, 2> _engines{};
|
||||||
|
IRenderData* _pData = nullptr; // Non-ownership pointer
|
||||||
|
std::unique_ptr<IRenderThread> _pThread;
|
||||||
|
static constexpr size_t _firstSoftFontChar = 0xEF20;
|
||||||
size_t _lastSoftFontChar = 0;
|
size_t _lastSoftFontChar = 0;
|
||||||
static bool s_IsSoftFontChar(const std::wstring_view& v, const size_t firstSoftFontChar, const size_t lastSoftFontChar);
|
std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> _hoveredInterval;
|
||||||
|
Microsoft::Console::Types::Viewport _viewport;
|
||||||
// Helper functions to diagnose issues with painting and layout.
|
std::vector<Cluster> _clusterBuffer;
|
||||||
// These are only actually effective/on in Debug builds when the flag is set using an attached debugger.
|
std::vector<SMALL_RECT> _previousSelection;
|
||||||
bool _fDebug = false;
|
|
||||||
|
|
||||||
std::function<void()> _pfnRendererEnteredErrorState;
|
std::function<void()> _pfnRendererEnteredErrorState;
|
||||||
|
bool _destructing = false;
|
||||||
|
|
||||||
#ifdef UNIT_TESTING
|
#ifdef UNIT_TESTING
|
||||||
friend class ConptyOutputTests;
|
friend class ConptyOutputTests;
|
||||||
|
|
|
@ -24,7 +24,6 @@ PRECOMPILED_CXX = 1
|
||||||
PRECOMPILED_INCLUDE = ..\precomp.h
|
PRECOMPILED_INCLUDE = ..\precomp.h
|
||||||
|
|
||||||
SOURCES = \
|
SOURCES = \
|
||||||
..\Cluster.cpp \
|
|
||||||
..\BlinkingState.cpp \
|
..\BlinkingState.cpp \
|
||||||
..\FontInfo.cpp \
|
..\FontInfo.cpp \
|
||||||
..\FontInfoBase.cpp \
|
..\FontInfoBase.cpp \
|
||||||
|
|
|
@ -16,24 +16,52 @@ Author(s):
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <unicode.hpp>
|
||||||
|
|
||||||
namespace Microsoft::Console::Render
|
namespace Microsoft::Console::Render
|
||||||
{
|
{
|
||||||
class Cluster
|
class Cluster
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Cluster(const std::wstring_view text, const size_t columns);
|
constexpr Cluster() noexcept = default;
|
||||||
|
constexpr Cluster(const std::wstring_view text, const size_t columns) noexcept :
|
||||||
|
_text{ text },
|
||||||
|
_columns{ columns }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
const wchar_t GetTextAsSingle() const noexcept;
|
// Provides the embedded text as a single character
|
||||||
|
// This might replace the string with the replacement character if it doesn't fit as one wchar_t.
|
||||||
|
constexpr wchar_t GetTextAsSingle() const noexcept
|
||||||
|
{
|
||||||
|
if (_text.size() == 1)
|
||||||
|
{
|
||||||
|
return til::at(_text, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return UNICODE_REPLACEMENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const std::wstring_view& GetText() const noexcept;
|
// Provides the string of wchar_ts for this cluster.
|
||||||
|
constexpr std::wstring_view GetText() const noexcept
|
||||||
|
{
|
||||||
|
return _text;
|
||||||
|
}
|
||||||
|
|
||||||
const size_t GetColumns() const noexcept;
|
// Gets the number of columns in the grid that this character should consume
|
||||||
|
// visually when rendered onto a line.
|
||||||
|
constexpr size_t GetColumns() const noexcept
|
||||||
|
{
|
||||||
|
return _columns;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// This is the UTF-16 string of characters that form a particular drawing cluster
|
// This is the UTF-16 string of characters that form a particular drawing cluster
|
||||||
const std::wstring_view _text;
|
std::wstring_view _text;
|
||||||
|
|
||||||
// This is how many columns we're expecting this cluster to take in the display grid
|
// This is how many columns we're expecting this cluster to take in the display grid
|
||||||
const size_t _columns;
|
size_t _columns = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue