mirror of
https://github.com/microsoft/terminal.git
synced 2026-02-04 05:35:20 +00:00
Pass through line rendition attributes over conpty (#13933)
This PR introduces a mechanism for passing through line rendition attributes to the conpty client, so we can support double-width and double-height text in Windows Terminal. Line renditions were first implemented in conhost (with the GDI renderer) in PR #8664, and were implemented in the DX renderer in PR #13102. By default this won't add any additional overhead to the conpty output, but as soon as there is any usage of double-size text, we switch to a mode in which every line output will be prefixed with a line rendition sequence. This is to ensure that the line attributes in the client terminal are always in sync with the host. Since this does add some overhead to the conpty output, we'd prefer not to remain in this mode longer than necessary. So whenever there is a full repaint of the entire viewport, we check to see if all of the lines are single-width. If that is the case, we can then safely skip the line rendition sequences in future updates. One other small optimization is when the conpty update is only writing out a single character (this is something we already check for). When that is the case, we can safely skip the line rendition prefix, because a single character update should never include a change of the line rendition. ## Validation Steps Performed I've manually tested that Windows Terminal now passes the double-size tests in _Vttest_, and also confirmed various edge cases are working correctly in my own double-size tests. Closes #11595
This commit is contained in:
@@ -325,7 +325,7 @@ CATCH_RETURN()
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::PrepareLineTransform(const LineRendition lineRendition, const size_t targetRow, const size_t viewportLeft) noexcept
|
||||
[[nodiscard]] HRESULT AtlasEngine::PrepareLineTransform(const LineRendition lineRendition, const til::CoordType targetRow, const til::CoordType viewportLeft) noexcept
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] HRESULT NotifyNewText(const std::wstring_view newText) noexcept override;
|
||||
[[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept override;
|
||||
[[nodiscard]] HRESULT ResetLineTransform() noexcept override;
|
||||
[[nodiscard]] HRESULT PrepareLineTransform(LineRendition lineRendition, size_t targetRow, size_t viewportLeft) noexcept override;
|
||||
[[nodiscard]] HRESULT PrepareLineTransform(LineRendition lineRendition, til::CoordType targetRow, til::CoordType viewportLeft) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBackground() noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferLine(gsl::span<const Cluster> clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF color, size_t cchLine, til::point coordTarget) noexcept override;
|
||||
|
||||
@@ -53,8 +53,8 @@ HRESULT RenderEngineBase::ResetLineTransform() noexcept
|
||||
}
|
||||
|
||||
HRESULT RenderEngineBase::PrepareLineTransform(const LineRendition /*lineRendition*/,
|
||||
const size_t /*targetRow*/,
|
||||
const size_t /*viewportLeft*/) noexcept
|
||||
const til::CoordType /*targetRow*/,
|
||||
const til::CoordType /*viewportLeft*/) noexcept
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
@@ -2343,8 +2343,8 @@ CATCH_RETURN();
|
||||
// Return Value:
|
||||
// - S_OK if successful. S_FALSE if already set. E_FAIL if there was an error.
|
||||
[[nodiscard]] HRESULT DxEngine::PrepareLineTransform(const LineRendition lineRendition,
|
||||
const size_t targetRow,
|
||||
const size_t viewportLeft) noexcept
|
||||
const til::CoordType targetRow,
|
||||
const til::CoordType viewportLeft) noexcept
|
||||
{
|
||||
auto lineTransform = D2D1::Matrix3x2F{ 0, 0, 0, 0, 0, 0 };
|
||||
const auto fontSize = _fontRenderData->GlyphCell();
|
||||
|
||||
@@ -97,8 +97,8 @@ namespace Microsoft::Console::Render
|
||||
|
||||
[[nodiscard]] HRESULT ResetLineTransform() noexcept override;
|
||||
[[nodiscard]] HRESULT PrepareLineTransform(const LineRendition lineRendition,
|
||||
const size_t targetRow,
|
||||
const size_t viewportLeft) noexcept override;
|
||||
const til::CoordType targetRow,
|
||||
const til::CoordType viewportLeft) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT PaintBackground() noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferLine(const gsl::span<const Cluster> clusters,
|
||||
|
||||
@@ -43,8 +43,8 @@ namespace Microsoft::Console::Render
|
||||
|
||||
[[nodiscard]] HRESULT ResetLineTransform() noexcept override;
|
||||
[[nodiscard]] HRESULT PrepareLineTransform(const LineRendition lineRendition,
|
||||
const size_t targetRow,
|
||||
const size_t viewportLeft) noexcept override;
|
||||
const til::CoordType targetRow,
|
||||
const til::CoordType viewportLeft) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT PaintBackground() noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferLine(const gsl::span<const Cluster> clusters,
|
||||
|
||||
@@ -210,8 +210,8 @@ GdiEngine::~GdiEngine()
|
||||
// Return Value:
|
||||
// - S_OK if successful. S_FALSE if already set. E_FAIL if there was an error.
|
||||
[[nodiscard]] HRESULT GdiEngine::PrepareLineTransform(const LineRendition lineRendition,
|
||||
const size_t targetRow,
|
||||
const size_t viewportLeft) noexcept
|
||||
const til::CoordType targetRow,
|
||||
const til::CoordType viewportLeft) noexcept
|
||||
{
|
||||
XFORM lineTransform = {};
|
||||
// The X delta is to account for the horizontal viewport offset.
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] virtual HRESULT NotifyNewText(const std::wstring_view newText) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT ResetLineTransform() noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT PrepareLineTransform(LineRendition lineRendition, size_t targetRow, size_t viewportLeft) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT PrepareLineTransform(LineRendition lineRendition, til::CoordType targetRow, til::CoordType viewportLeft) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT PaintBackground() noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT PaintBufferLine(gsl::span<const Cluster> clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF color, size_t cchLine, til::point coordTarget) noexcept = 0;
|
||||
|
||||
@@ -38,8 +38,8 @@ namespace Microsoft::Console::Render
|
||||
|
||||
[[nodiscard]] HRESULT ResetLineTransform() noexcept override;
|
||||
[[nodiscard]] HRESULT PrepareLineTransform(const LineRendition lineRendition,
|
||||
const size_t targetRow,
|
||||
const size_t viewportLeft) noexcept override;
|
||||
const til::CoordType targetRow,
|
||||
const til::CoordType viewportLeft) noexcept override;
|
||||
|
||||
[[nodiscard]] virtual bool RequiresContinuousRedraw() noexcept override;
|
||||
|
||||
|
||||
@@ -25,6 +25,10 @@ using namespace Microsoft::Console::Types;
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
// If we're using line renditions, and this is a full screen paint, we can
|
||||
// potentially stop using them at the end of this frame.
|
||||
_stopUsingLineRenditions = _usingLineRenditions && _AllIsInvalid();
|
||||
|
||||
// If there's nothing to do, quick return
|
||||
auto somethingToDo = _invalidMap.any() ||
|
||||
_scrollDelta != til::point{ 0, 0 } ||
|
||||
@@ -75,6 +79,14 @@ using namespace Microsoft::Console::Types;
|
||||
}
|
||||
_circled = false;
|
||||
|
||||
// If _stopUsingLineRenditions is still true at the end of the frame, that
|
||||
// means we've refreshed the entire viewport with every line being single
|
||||
// width, so we can safely stop using them from now on.
|
||||
if (_stopUsingLineRenditions)
|
||||
{
|
||||
_usingLineRenditions = false;
|
||||
}
|
||||
|
||||
// If we deferred a cursor movement during the frame, make sure we put the
|
||||
// cursor in the right place before we end the frame.
|
||||
if (_deferredCursorPos != INVALID_COORDS)
|
||||
@@ -100,6 +112,45 @@ using namespace Microsoft::Console::Types;
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT VtEngine::ResetLineTransform() noexcept
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT VtEngine::PrepareLineTransform(const LineRendition lineRendition,
|
||||
const til::CoordType targetRow,
|
||||
const til::CoordType /*viewportLeft*/) noexcept
|
||||
{
|
||||
// We don't want to waste bandwidth writing out line rendition attributes
|
||||
// until we know they're in use. But once they are in use, we have to keep
|
||||
// applying them on every line until we know they definitely aren't being
|
||||
// used anymore (we check that at the end of any fullscreen paint).
|
||||
if (lineRendition != LineRendition::SingleWidth)
|
||||
{
|
||||
_stopUsingLineRenditions = false;
|
||||
_usingLineRenditions = true;
|
||||
}
|
||||
// One simple optimization is that we can skip sending the line attributes
|
||||
// when _quickReturn is true. That indicates that we're writing out a single
|
||||
// character, which should preclude there being a rendition switch.
|
||||
if (_usingLineRenditions && !_quickReturn)
|
||||
{
|
||||
RETURN_IF_FAILED(_MoveCursor({ _lastText.x, targetRow }));
|
||||
switch (lineRendition)
|
||||
{
|
||||
case LineRendition::SingleWidth:
|
||||
return _Write("\x1b#5");
|
||||
case LineRendition::DoubleWidth:
|
||||
return _Write("\x1b#6");
|
||||
case LineRendition::DoubleHeightTop:
|
||||
return _Write("\x1b#3");
|
||||
case LineRendition::DoubleHeightBottom:
|
||||
return _Write("\x1b#4");
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Paints the background of the invalid area of the frame.
|
||||
// Arguments:
|
||||
|
||||
@@ -29,6 +29,8 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe,
|
||||
const Viewport initialViewport) :
|
||||
RenderEngineBase(),
|
||||
_hFile(std::move(pipe)),
|
||||
_usingLineRenditions(false),
|
||||
_stopUsingLineRenditions(false),
|
||||
_lastTextAttributes(INVALID_COLOR, INVALID_COLOR),
|
||||
_lastViewport(initialViewport),
|
||||
_pool(til::pmr::get_default_resource()),
|
||||
|
||||
@@ -58,6 +58,8 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] HRESULT InvalidateSelection(const std::vector<til::rect>& rectangles) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateAll() noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateFlush(_In_ const bool circled, _Out_ bool* const pForcePaint) noexcept override;
|
||||
[[nodiscard]] HRESULT ResetLineTransform() noexcept override;
|
||||
[[nodiscard]] HRESULT PrepareLineTransform(const LineRendition lineRendition, const til::CoordType targetRow, const til::CoordType viewportLeft) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBackground() noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferLine(gsl::span<const Cluster> clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF color, size_t cchLine, til::point coordTarget) noexcept override;
|
||||
@@ -96,6 +98,8 @@ namespace Microsoft::Console::Render
|
||||
std::string _formatBuffer;
|
||||
std::string _conversionBuffer;
|
||||
|
||||
bool _usingLineRenditions;
|
||||
bool _stopUsingLineRenditions;
|
||||
TextAttribute _lastTextAttributes;
|
||||
|
||||
std::function<void(bool)> _pfnSetLookingForDSR;
|
||||
|
||||
Reference in New Issue
Block a user