From 5a83be53d7625c95949a6365c1aa6d7bbb6971d5 Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Sun, 11 Jul 2021 17:48:44 +0800 Subject: [PATCH 1/9] Initial refactor of IRenderEngine interface --- src/cascadia/TerminalControl/ControlCore.cpp | 2 +- src/interactivity/onecore/BgfxEngine.cpp | 30 - src/interactivity/onecore/BgfxEngine.hpp | 3 - src/renderer/base/RenderEngineBase.cpp | 350 ++++++++++- src/renderer/base/renderer.cpp | 627 +------------------ src/renderer/base/renderer.hpp | 34 - src/renderer/dx/DxRenderer.cpp | 231 ++++++- src/renderer/dx/DxRenderer.hpp | 20 +- src/renderer/gdi/gdirenderer.hpp | 23 +- src/renderer/gdi/paint.cpp | 39 ++ src/renderer/gdi/state.cpp | 177 +++++- src/renderer/inc/IRenderEngine.hpp | 28 +- src/renderer/inc/RenderEngineBase.hpp | 54 +- src/renderer/uia/UiaRenderer.cpp | 117 +--- src/renderer/uia/UiaRenderer.hpp | 19 +- src/renderer/vt/XtermEngine.hpp | 2 +- src/renderer/vt/paint.cpp | 183 ++++-- src/renderer/vt/vtrenderer.hpp | 16 +- src/renderer/wddmcon/WddmConRenderer.cpp | 35 -- src/renderer/wddmcon/WddmConRenderer.hpp | 7 - 20 files changed, 951 insertions(+), 1046 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 932042a8b1f..e22fb024755 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -462,7 +462,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _lastHoveredId = newId; _lastHoveredInterval = newInterval; _renderEngine->UpdateHyperlinkHoveredId(newId); - _renderer->UpdateLastHoveredInterval(newInterval); + _renderEngine->UpdateLastHoveredInterval(newInterval); _renderer->TriggerRedrawAll(); } diff --git a/src/interactivity/onecore/BgfxEngine.cpp b/src/interactivity/onecore/BgfxEngine.cpp index cf690ecd398..a79e9e63c32 100644 --- a/src/interactivity/onecore/BgfxEngine.cpp +++ b/src/interactivity/onecore/BgfxEngine.cpp @@ -114,11 +114,6 @@ BgfxEngine::BgfxEngine(PVOID SharedViewBase, LONG DisplayHeight, LONG DisplayWid return S_FALSE; } -[[nodiscard]] HRESULT BgfxEngine::ScrollFrame() noexcept -{ - return S_OK; -} - [[nodiscard]] HRESULT BgfxEngine::PaintBackground() noexcept { PVOID OldRunBase; @@ -166,19 +161,6 @@ BgfxEngine::BgfxEngine(PVOID SharedViewBase, LONG DisplayHeight, LONG DisplayWid CATCH_RETURN(); } -[[nodiscard]] HRESULT BgfxEngine::PaintBufferGridLines(GridLines const /*lines*/, - COLORREF const /*color*/, - size_t const /*cchLine*/, - COORD const /*coordTarget*/) noexcept -{ - return S_OK; -} - -[[nodiscard]] HRESULT BgfxEngine::PaintSelection(const SMALL_RECT /*rect*/) noexcept -{ - return S_OK; -} - [[nodiscard]] HRESULT BgfxEngine::PaintCursor(const CursorOptions& options) noexcept { // TODO: MSFT: 11448021 - Modify BGFX to support rendering full-width @@ -257,15 +239,3 @@ BgfxEngine::BgfxEngine(PVOID SharedViewBase, LONG DisplayHeight, LONG DisplayWid *pResult = false; return S_OK; } - -// Method Description: -// - Updates the window's title string. -// Does nothing for BGFX. -// Arguments: -// - newTitle: the new string to use for the title of the window -// Return Value: -// - S_OK -[[nodiscard]] HRESULT BgfxEngine::_DoUpdateTitle(_In_ const std::wstring_view /*newTitle*/) noexcept -{ - return S_OK; -} diff --git a/src/interactivity/onecore/BgfxEngine.hpp b/src/interactivity/onecore/BgfxEngine.hpp index 4fddcfb0b39..713255128bc 100644 --- a/src/interactivity/onecore/BgfxEngine.hpp +++ b/src/interactivity/onecore/BgfxEngine.hpp @@ -71,9 +71,6 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override; [[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override; - protected: - [[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring_view newTitle) noexcept override; - private: ULONG_PTR _sharedViewBase; SIZE_T _runLength; diff --git a/src/renderer/base/RenderEngineBase.cpp b/src/renderer/base/RenderEngineBase.cpp index f31fff625d7..6dbcbf4238a 100644 --- a/src/renderer/base/RenderEngineBase.cpp +++ b/src/renderer/base/RenderEngineBase.cpp @@ -3,9 +3,12 @@ #include "precomp.h" #include "../inc/RenderEngineBase.hpp" +#include "../../buffer/out/textBuffer.hpp" + #pragma hdrstop using namespace Microsoft::Console; using namespace Microsoft::Console::Render; +using namespace Microsoft::Console::Types; RenderEngineBase::RenderEngineBase() : _titleChanged(false), @@ -23,49 +26,340 @@ HRESULT RenderEngineBase::InvalidateTitle(const std::wstring_view proposedTitle) return S_OK; } -HRESULT RenderEngineBase::UpdateTitle(const std::wstring_view newTitle) noexcept +// Method Description: +// - By default, no one should need continuous redraw. It ruins performance +// in terms of CPU, memory, and battery life to just paint forever. +// That's why we sleep when there's nothing to draw. +// But if you REALLY WANT to do special effects... you need to keep painting. +[[nodiscard]] bool RenderEngineBase::RequiresContinuousRedraw() noexcept { - HRESULT hr = S_FALSE; - if (newTitle != _lastFrameTitle) + return false; +} + +// Method Description: +// - Blocks until the engine is able to render without blocking. +void RenderEngineBase::WaitUntilCanRender() noexcept +{ + // do nothing by default +} + +[[nodiscard]] std::optional RenderEngineBase::_GetCursorInfo(IRenderData* pData) +{ + if (pData->IsCursorVisible()) { - RETURN_IF_FAILED(_DoUpdateTitle(newTitle)); - _lastFrameTitle = newTitle; - _titleChanged = false; - hr = S_OK; + // Get cursor position in buffer + COORD coordCursor = pData->GetCursorPosition(); + + // GH#3166: Only draw the cursor if it's actually in the viewport. It + // might be on the line that's in that partially visible row at the + // bottom of the viewport, the space that's not quite a full line in + // height. Since we don't draw that text, we shouldn't draw the cursor + // there either. + + // The cursor is never rendered as double height, so we don't care about + // the exact line rendition - only whether it's double width or not. + const auto doubleWidth = pData->GetTextBuffer().IsDoubleWidthLine(coordCursor.Y); + const auto lineRendition = doubleWidth ? LineRendition::DoubleWidth : LineRendition::SingleWidth; + + // We need to convert the screen coordinates of the viewport to an + // equivalent range of buffer cells, taking line rendition into account. + const auto view = ScreenToBufferLine(pData->GetViewport().ToInclusive(), lineRendition); + + // Note that we allow the X coordinate to be outside the left border by 1 position, + // because the cursor could still be visible if the focused character is double width. + const auto xInRange = coordCursor.X >= view.Left - 1 && coordCursor.X <= view.Right; + const auto yInRange = coordCursor.Y >= view.Top && coordCursor.Y <= view.Bottom; + if (xInRange && yInRange) + { + // Adjust cursor Y offset to viewport. + // The viewport X offset is saved in the options and handled with a transform. + coordCursor.Y -= view.Top; + + COLORREF cursorColor = pData->GetCursorColor(); + bool useColor = cursorColor != INVALID_COLOR; + + // Build up the cursor parameters including position, color, and drawing options + CursorOptions options; + options.coordCursor = coordCursor; + options.viewportLeft = pData->GetViewport().Left(); + options.lineRendition = lineRendition; + options.ulCursorHeightPercent = pData->GetCursorHeight(); + options.cursorPixelWidth = pData->GetCursorPixelWidth(); + options.fIsDoubleWidth = pData->IsCursorDoubleWidth(); + options.cursorType = pData->GetCursorStyle(); + options.fUseColor = useColor; + options.cursorColor = cursorColor; + options.isOn = pData->IsCursorOn(); + + return { options }; + } } - return hr; + return std::nullopt; } -HRESULT RenderEngineBase::PrepareRenderInfo(const RenderFrameInfo& /*info*/) noexcept +void RenderEngineBase::_LoopDirtyLines(IRenderData* pData, std::function action) { - return S_FALSE; + // This is the subsection of the entire screen buffer that is currently being presented. + // It can move left/right or top/bottom depending on how the viewport is scrolled + // relative to the entire buffer. + const auto view = pData->GetViewport(); + + // This is effectively the number of cells on the visible screen that need to be redrawn. + // The origin is always 0, 0 because it represents the screen itself, not the underlying buffer. + gsl::span dirtyAreas; + LOG_IF_FAILED(GetDirtyArea(dirtyAreas)); + + // Calling pData virtual functions is expensive. This won't change during painting. + auto globalInvert = pData->IsScreenReversed(); + auto gridLineDrawingAllowed = pData->IsGridLineDrawingAllowed(); + + for (const auto& dirtyRect : dirtyAreas) + { + auto dirty = Viewport::FromInclusive(dirtyRect); + + // Shift the origin of the dirty region to match the underlying buffer so we can + // compare the two regions directly for intersection. + dirty = Viewport::Offset(dirty, view.Origin()); + + // The intersection between what is dirty on the screen (in need of repaint) + // and what is supposed to be visible on the screen (the viewport) is what + // we need to walk through line-by-line and repaint onto the screen. + const auto redraw = Viewport::Intersect(dirty, view); + + // Shortcut: don't bother redrawing if the width is 0. + if (redraw.Width() > 0) + { + // Retrieve the text buffer so we can read information out of it. + 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++) + { + auto renderData = _CalculateRenderDataForDirtyLine(buffer, view, redraw, row); + renderData.pData = pData; + renderData.globalInvert = globalInvert; + renderData.gridLineDrawingAllowed = gridLineDrawingAllowed; + action(renderData); + } + } + } } -HRESULT RenderEngineBase::ResetLineTransform() noexcept +void RenderEngineBase::_LoopOverlay(IRenderData* pData, std::function action) { - return S_FALSE; + // First get the screen buffer's viewport. + Viewport view = pData->GetViewport(); + + // Now get the overlay's viewport and adjust it to where it is supposed to be relative to the window. + const auto overlays = pData->GetOverlays(); + for (const auto& overlay : overlays) + { + SMALL_RECT srCaView = overlay.region.ToInclusive(); + srCaView.Top += overlay.origin.Y; + srCaView.Bottom += overlay.origin.Y; + srCaView.Left += overlay.origin.X; + srCaView.Right += overlay.origin.X; + + // Set it up in a Viewport helper structure and trim it the IME viewport to be within the full console viewport. + Viewport viewConv = Viewport::FromInclusive(srCaView); + + gsl::span dirtyAreas; + LOG_IF_FAILED(GetDirtyArea(dirtyAreas)); + + for (SMALL_RECT srDirty : dirtyAreas) + { + // Dirty is an inclusive rectangle, but oddly enough the IME was an exclusive one, so correct it. + srDirty.Bottom++; + srDirty.Right++; + + if (viewConv.TrimToViewport(&srDirty)) + { + Viewport viewDirty = Viewport::FromInclusive(srDirty); + + for (SHORT iRow = viewDirty.Top(); iRow < viewDirty.BottomInclusive(); iRow++) + { + const COORD target{ viewDirty.Left(), iRow }; + const auto source = target - overlay.origin; + + SMALL_RECT limit; + limit.Top = source.Y; + limit.Bottom = source.Y; + limit.Left = source.X; + limit.Right = overlay.buffer.GetSize().RightInclusive(); + + Viewport bufferLine = Viewport::FromInclusive(limit); + + BufferLineRenderData renderData{ + pData, overlay.buffer, bufferLine, viewConv, target, LineRendition::SingleWidth, false, false, false + }; + action(renderData); + } + } + } + } } -HRESULT RenderEngineBase::PrepareLineTransform(const LineRendition /*lineRendition*/, - const size_t /*targetRow*/, - const size_t /*viewportLeft*/) noexcept +void RenderEngineBase::_LoopSelection(IRenderData* pData, std::function action) { - return S_FALSE; + gsl::span dirtyAreas; + LOG_IF_FAILED(GetDirtyArea(dirtyAreas)); + + // Get selection rectangles + const auto rectangles = _GetSelectionRects(pData); + for (auto rect : rectangles) + { + for (auto& dirtyRect : dirtyAreas) + { + // Make a copy as `TrimToViewport` will manipulate it and + // can destroy it for the next dirtyRect to test against. + auto rectCopy = rect; + Viewport dirtyView = Viewport::FromInclusive(dirtyRect); + if (dirtyView.TrimToViewport(&rectCopy)) + { + action(rectCopy); + } + } + } } -// Method Description: -// - By default, no one should need continuous redraw. It ruins performance -// in terms of CPU, memory, and battery life to just paint forever. -// That's why we sleep when there's nothing to draw. -// But if you REALLY WANT to do special effects... you need to keep painting. -[[nodiscard]] bool RenderEngineBase::RequiresContinuousRedraw() noexcept +BufferLineRenderData RenderEngineBase::_CalculateRenderDataForDirtyLine(const TextBuffer& buffer, + Viewport visible, + Viewport redraw, + SHORT row) const { - return false; + // Calculate the boundaries of a single line. This is from the left to right edge of the dirty + // 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 + // range of buffer cells, taking line rendition into account. + const auto lineRendition = buffer.GetLineRendition(row); + const auto bufferLine = Viewport::FromInclusive(ScreenToBufferLine(screenLine, lineRendition)); + + // Find where on the screen we should place this line information. This requires us to re-map + // the buffer-based origin of the line back onto the screen-based origin of the line. + // 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, visible.Top() }; + + // Calculate if two things are true: + // 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()); + + return BufferLineRenderData{ nullptr, buffer, bufferLine, visible, screenPosition, lineRendition, lineWrapped, false, false }; } +IRenderEngine::GridLines RenderEngineBase::_CalculateGridLines(IRenderData* pData, + const TextAttribute textAttribute, + const COORD coordTarget) { + // Convert console grid line representations into rendering engine enum representations. + IRenderEngine::GridLines lines = s_GetGridlines(textAttribute); + + // For now, we dash underline patterns and switch to regular underline on hover + // Since we're only rendering pattern links on *hover*, there's no point in checking + // the pattern range if we aren't currently hovering. + if (_hoveredInterval.has_value()) + { + const til::point coordTargetTil{ coordTarget }; + if (_hoveredInterval->start <= coordTargetTil && + coordTargetTil <= _hoveredInterval->stop) + { + if (pData->GetPatternId(coordTarget).size() > 0) + { + lines |= IRenderEngine::GridLines::Underline; + } + } + } + + return lines; + } + // Method Description: -// - Blocks until the engine is able to render without blocking. -void RenderEngineBase::WaitUntilCanRender() noexcept -{ - // do nothing by default -} + // - Generates a IRenderEngine::GridLines structure from the values in the + // provided textAttribute + // Arguments: + // - textAttribute: the TextAttribute to generate GridLines from. + // Return Value: + // - a GridLines containing all the gridline info from the TextAttribute + IRenderEngine::GridLines RenderEngineBase::s_GetGridlines(const TextAttribute& textAttribute) noexcept + { + // Convert console grid line representations into rendering engine enum representations. + IRenderEngine::GridLines lines = IRenderEngine::GridLines::None; + + if (textAttribute.IsTopHorizontalDisplayed()) + { + lines |= IRenderEngine::GridLines::Top; + } + + if (textAttribute.IsBottomHorizontalDisplayed()) + { + lines |= IRenderEngine::GridLines::Bottom; + } + + if (textAttribute.IsLeftVerticalDisplayed()) + { + lines |= IRenderEngine::GridLines::Left; + } + + if (textAttribute.IsRightVerticalDisplayed()) + { + lines |= IRenderEngine::GridLines::Right; + } + + if (textAttribute.IsCrossedOut()) + { + lines |= IRenderEngine::GridLines::Strikethrough; + } + + if (textAttribute.IsUnderlined()) + { + lines |= IRenderEngine::GridLines::Underline; + } + + if (textAttribute.IsDoublyUnderlined()) + { + lines |= IRenderEngine::GridLines::DoubleUnderline; + } + + if (textAttribute.IsHyperlink()) + { + lines |= IRenderEngine::GridLines::HyperlinkUnderline; + } + return lines; + } + + // Routine Description: + // - Helper to determine the selected region of the buffer. + // Return Value: + // - A vector of rectangles representing the regions to select, line by line. + std::vector RenderEngineBase::_GetSelectionRects(IRenderData* pData) const + { + const auto& buffer = pData->GetTextBuffer(); + auto rects = pData->GetSelectionRects(); + // Adjust rectangles to viewport + Viewport view = pData->GetViewport(); + + std::vector result; + + for (auto rect : rects) + { + // Convert buffer offsets to the equivalent range of screen cells + // expected by callers, taking line rendition into account. + const auto lineRendition = buffer.GetLineRendition(rect.Top()); + rect = Viewport::FromInclusive(BufferToScreenLine(rect.ToInclusive(), lineRendition)); + + auto sr = view.ConvertToOrigin(rect).ToInclusive(); + + // hopefully temporary, we should be receiving the right selection sizes without correction. + sr.Right++; + sr.Bottom++; + + result.emplace_back(sr); + } + + return result; + } diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index b53bc6eadf6..8224f8d549f 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -143,32 +143,7 @@ try } }); - // A. Prep Colors - RETURN_IF_FAILED(_UpdateDrawingBrushes(pEngine, _pData->GetDefaultBrushColors(), true)); - - // B. Perform Scroll Operations - RETURN_IF_FAILED(_PerformScrolling(pEngine)); - - // C. Prepare the engine with additional information before we start drawing. - RETURN_IF_FAILED(_PrepareRenderInfo(pEngine)); - - // 1. Paint Background - RETURN_IF_FAILED(_PaintBackground(pEngine)); - - // 2. Paint Rows of Text - _PaintBufferOutput(pEngine); - - // 3. Paint overlays that reside above the text buffer - _PaintOverlays(pEngine); - - // 4. Paint Selection - _PaintSelection(pEngine); - - // 5. Paint Cursor - _PaintCursor(pEngine); - - // 6. Paint window title - RETURN_IF_FAILED(_PaintTitle(pEngine)); + RETURN_IF_FAILED(pEngine->PaintFrame(_pData)); // Force scope exit end paint to finish up collecting information and possibly painting endPaint.reset(); @@ -348,32 +323,6 @@ void Renderer::TriggerSelection() { try { - // Get selection rectangles - const auto rects = _GetSelectionRects(); - - // Restrict all previous selection rectangles to inside the current viewport bounds - for (auto& sr : _previousSelection) - { - // Make the exclusive SMALL_RECT into a til::rectangle. - til::rectangle rc{ Viewport::FromExclusive(sr).ToInclusive() }; - - // Make a viewport representing the coordinates that are currently presentable. - const til::rectangle viewport{ til::size{ _pData->GetViewport().Dimensions() } }; - - // Intersect them so we only invalidate things that are still visible. - rc &= viewport; - - // Convert back into the exclusive SMALL_RECT and store in the vector. - sr = Viewport::FromInclusive(rc).ToExclusive(); - } - - std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) { - LOG_IF_FAILED(pEngine->InvalidateSelection(_previousSelection)); - LOG_IF_FAILED(pEngine->InvalidateSelection(rects)); - }); - - _previousSelection = rects; - _NotifyPaintFrame(); } CATCH_LOG(); @@ -496,18 +445,6 @@ void Renderer::TriggerTitleChange() _NotifyPaintFrame(); } -// Routine Description: -// - Update the title for a particular engine. -// Arguments: -// - pEngine: the engine to update the title for. -// Return Value: -// - the HRESULT of the underlying engine's UpdateTitle call. -HRESULT Renderer::_PaintTitle(IRenderEngine* const pEngine) -{ - const auto newTitle = _pData->GetConsoleTitle(); - return pEngine->UpdateTitle(newTitle); -} - // Routine Description: // - Called when a change in font or DPI has been detected. // Arguments: @@ -620,357 +557,6 @@ void Renderer::WaitForPaintCompletionAndDisable(const DWORD dwTimeoutMs) _pThread->WaitForPaintCompletionAndDisable(dwTimeoutMs); } -// Routine Description: -// - Paint helper to fill in the background color of the invalid area within the frame. -// Arguments: -// - -// Return Value: -// - -[[nodiscard]] HRESULT Renderer::_PaintBackground(_In_ IRenderEngine* const pEngine) -{ - return pEngine->PaintBackground(); -} - -// Routine Description: -// - Paint helper to copy the primary console buffer text onto the screen. -// - This portion primarily handles figuring the current viewport, comparing it/trimming it versus the invalid portion of the frame, and queuing up, row by row, which pieces of text need to be further processed. -// - See also: Helper functions that separate out each complexity of text rendering. -// Arguments: -// - -// Return Value: -// - -void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine) -{ - // This is the subsection of the entire screen buffer that is currently being presented. - // It can move left/right or top/bottom depending on how the viewport is scrolled - // relative to the entire buffer. - const auto view = _pData->GetViewport(); - - // This is effectively the number of cells on the visible screen that need to be redrawn. - // The origin is always 0, 0 because it represents the screen itself, not the underlying buffer. - gsl::span dirtyAreas; - LOG_IF_FAILED(pEngine->GetDirtyArea(dirtyAreas)); - - // This is to make sure any transforms are reset when this paint is finished. - auto resetLineTransform = wil::scope_exit([&]() { - LOG_IF_FAILED(pEngine->ResetLineTransform()); - }); - - for (const auto& dirtyRect : dirtyAreas) - { - auto dirty = Viewport::FromInclusive(dirtyRect); - - // Shift the origin of the dirty region to match the underlying buffer so we can - // compare the two regions directly for intersection. - dirty = Viewport::Offset(dirty, view.Origin()); - - // The intersection between what is dirty on the screen (in need of repaint) - // and what is supposed to be visible on the screen (the viewport) is what - // we need to walk through line-by-line and repaint onto the screen. - const auto redraw = Viewport::Intersect(dirty, view); - - // Shortcut: don't bother redrawing if the width is 0. - if (redraw.Width() > 0) - { - // Retrieve the text buffer so we can read information out of it. - 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++) - { - // Calculate the boundaries of a single line. This is from the left to right edge of the dirty - // 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 - // range of buffer cells, taking line rendition into account. - const auto lineRendition = buffer.GetLineRendition(row); - const auto bufferLine = Viewport::FromInclusive(ScreenToBufferLine(screenLine, lineRendition)); - - // Find where on the screen we should place this line information. This requires us to re-map - // the buffer-based origin of the line back onto the screen-based origin of the line. - // 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. - auto it = buffer.GetCellDataAt(bufferLine.Origin(), bufferLine); - - // Calculate if two things are true: - // 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()); - - // Prepare the appropriate line transform for the current row and viewport offset. - LOG_IF_FAILED(pEngine->PrepareLineTransform(lineRendition, screenPosition.Y, view.Left())); - - // Ask the helper to paint through this specific line. - _PaintBufferOutputHelper(pEngine, it, screenPosition, lineWrapped); - } - } - } -} - -static bool _IsAllSpaces(const std::wstring_view v) -{ - // first non-space char is not found (is npos) - return v.find_first_not_of(L" ") == decltype(v)::npos; -} - -void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, - TextBufferCellIterator it, - const COORD target, - const bool lineWrapped) -{ - auto globalInvert{ _pData->IsScreenReversed() }; - - // If we have valid data, let's figure out how to draw it. - if (it) - { - // TODO: MSFT: 20961091 - This is a perf issue. Instead of rebuilding this and allocing memory to hold the reinterpretation, - // we should have an iterator/view adapter for the rendering. - // That would probably also eliminate the RenderData needing to give us the entire TextBuffer as well... - // Retrieve the iterator for one line of information. - size_t cols = 0; - - // Retrieve the first color. - auto color = it->TextAttr(); - // Retrieve the first pattern id - auto patternIds = _pData->GetPatternId(target); - - // And hold the point where we should start drawing. - auto screenPoint = target; - - // This outer loop will continue until we reach the end of the text we are trying to draw. - while (it) - { - // Hold onto the current run color right here for the length of the outer loop. - // We'll be changing the persistent one as we run through the inner loops to detect - // when a run changes, but we will still need to know this color at the bottom - // when we go to draw gridlines for the length of the run. - const auto currentRunColor = color; - - // Hold onto the current pattern id as well - const auto currentPatternId = patternIds; - - // Update the drawing brushes with our color. - THROW_IF_FAILED(_UpdateDrawingBrushes(pEngine, currentRunColor, false)); - - // Advance the point by however many columns we've just outputted and reset the accumulator. - screenPoint.X += gsl::narrow(cols); - cols = 0; - - // Hold onto the start of this run iterator and the target location where we started - // in case we need to do some special work to paint the line drawing characters. - const auto currentRunItStart = it; - const auto currentRunTargetStart = screenPoint; - - // Ensure that our cluster vector is clear. - _clusterBuffer.clear(); - - // Reset our flag to know when we're in the special circumstance - // of attempting to draw only the right-half of a two-column character - // as the first item in our run. - bool trimLeft = false; - - // Run contains wide character (>1 columns) - bool containsWideCharacter = false; - - // This inner loop will accumulate clusters until the color changes. - // When the color changes, it will save the new color off and break. - // We also accumulate clusters according to regex patterns - do - { - COORD thisPoint{ screenPoint.X + gsl::narrow(cols), screenPoint.Y }; - const auto thisPointPatterns = _pData->GetPatternId(thisPoint); - if (color != it->TextAttr() || patternIds != thisPointPatterns) - { - auto newAttr{ it->TextAttr() }; - // foreground doesn't matter for runs of spaces (!) - // if we trick it . . . we call Paint far fewer times for cmatrix - if (!_IsAllSpaces(it->Chars()) || !newAttr.HasIdenticalVisualRepresentationForBlankSpace(color, globalInvert) || patternIds != thisPointPatterns) - { - color = newAttr; - patternIds = thisPointPatterns; - break; // vend this run - } - } - - // 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. - size_t columnCount = 0; - - // 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. - if (_clusterBuffer.empty() && it->DbcsAttr().IsTrailing()) - { - // Move left to the one so the whole character can be struck correctly. - --screenPoint.X; - // And tell the next function to trim off the left half of it. - trimLeft = true; - // And add one to the number of columns we expect it to take as we insert it. - columnCount = it->Columns() + 1; - _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) - { - containsWideCharacter = true; - } - - // Advance the cluster and column counts. - it += std::max(it->Columns(), 1); // prevent infinite loop for no visible columns - cols += columnCount; - - } while (it); - - // Do the painting. - THROW_IF_FAILED(pEngine->PaintBufferLine({ _clusterBuffer.data(), _clusterBuffer.size() }, screenPoint, trimLeft, lineWrapped)); - - // If we're allowed to do grid drawing, draw that now too (since it will be coupled with the color data) - // We're only allowed to draw the grid lines under certain circumstances. - if (_pData->IsGridLineDrawingAllowed()) - { - // See GH: 803 - // If we found a wide character while we looped above, it's possible we skipped over the right half - // attribute that could have contained different line information than the left half. - if (containsWideCharacter) - { - // Start from the original position in this run. - auto lineIt = currentRunItStart; - // Start from the original target in this run. - auto lineTarget = currentRunTargetStart; - - // We need to go through the iterators again to ensure we get the lines associated with each - // exact column. The code above will condense two-column characters into one, but it is possible - // (like with the IME) that the line drawing characters will vary from the left to right half - // of a wider character. - // We could theoretically pre-pass for this in the loop above to be more efficient about walking - // the iterator, but I fear it would make the code even more confusing than it already is. - // Do that in the future if some WPR trace points you to this spot as super bad. - for (auto colsPainted = 0u; colsPainted < cols; ++colsPainted, ++lineIt, ++lineTarget.X) - { - auto lines = lineIt->TextAttr(); - _PaintBufferOutputGridLineHelper(pEngine, lines, 1, lineTarget); - } - } - else - { - // If nothing exciting is going on, draw the lines in bulk. - _PaintBufferOutputGridLineHelper(pEngine, currentRunColor, cols, screenPoint); - } - } - } - } -} - -// Method Description: -// - Generates a IRenderEngine::GridLines structure from the values in the -// provided textAttribute -// Arguments: -// - textAttribute: the TextAttribute to generate GridLines from. -// Return Value: -// - a GridLines containing all the gridline info from the TextAttribute -IRenderEngine::GridLines Renderer::s_GetGridlines(const TextAttribute& textAttribute) noexcept -{ - // Convert console grid line representations into rendering engine enum representations. - IRenderEngine::GridLines lines = IRenderEngine::GridLines::None; - - if (textAttribute.IsTopHorizontalDisplayed()) - { - lines |= IRenderEngine::GridLines::Top; - } - - if (textAttribute.IsBottomHorizontalDisplayed()) - { - lines |= IRenderEngine::GridLines::Bottom; - } - - if (textAttribute.IsLeftVerticalDisplayed()) - { - lines |= IRenderEngine::GridLines::Left; - } - - if (textAttribute.IsRightVerticalDisplayed()) - { - lines |= IRenderEngine::GridLines::Right; - } - - if (textAttribute.IsCrossedOut()) - { - lines |= IRenderEngine::GridLines::Strikethrough; - } - - if (textAttribute.IsUnderlined()) - { - lines |= IRenderEngine::GridLines::Underline; - } - - if (textAttribute.IsDoublyUnderlined()) - { - lines |= IRenderEngine::GridLines::DoubleUnderline; - } - - if (textAttribute.IsHyperlink()) - { - lines |= IRenderEngine::GridLines::HyperlinkUnderline; - } - return lines; -} - -// Routine Description: -// - Paint helper for primary buffer output function. -// - This particular helper sets up the various box drawing lines that can be inscribed around any character in the buffer (left, right, top, underline). -// - See also: All related helpers and buffer output functions. -// Arguments: -// - textAttribute - The line/box drawing attributes to use for this particular run. -// - cchLine - The length of both pwsLine and pbKAttrsLine. -// - coordTarget - The X/Y coordinate position in the buffer which we're attempting to start rendering from. -// Return Value: -// - -void Renderer::_PaintBufferOutputGridLineHelper(_In_ IRenderEngine* const pEngine, - const TextAttribute textAttribute, - const size_t cchLine, - const COORD coordTarget) -{ - // Convert console grid line representations into rendering engine enum representations. - IRenderEngine::GridLines lines = Renderer::s_GetGridlines(textAttribute); - - // For now, we dash underline patterns and switch to regular underline on hover - // Since we're only rendering pattern links on *hover*, there's no point in checking - // the pattern range if we aren't currently hovering. - if (_hoveredInterval.has_value()) - { - const til::point coordTargetTil{ coordTarget }; - if (_hoveredInterval->start <= coordTargetTil && - coordTargetTil <= _hoveredInterval->stop) - { - if (_pData->GetPatternId(coordTarget).size() > 0) - { - lines |= IRenderEngine::GridLines::Underline; - } - } - } - - // Return early if there are no lines to paint. - if (lines != IRenderEngine::GridLines::None) - { - // Get the current foreground color to render the lines. - const COLORREF rgb = _pData->GetAttributeColors(textAttribute).first; - // Draw the lines - LOG_IF_FAILED(pEngine->PaintBufferGridLines(lines, rgb, cchLine, coordTarget)); - } -} - // Routine Description: // - Retrieve information about the cursor, and pack it into a CursorOptions // which the render engine can use for painting the cursor. @@ -1035,212 +621,6 @@ void Renderer::_PaintBufferOutputGridLineHelper(_In_ IRenderEngine* const pEngin return std::nullopt; } -// Routine Description: -// - Paint helper to draw the cursor within the buffer. -// Arguments: -// - engine - The render engine that we're targeting. -// Return Value: -// - -void Renderer::_PaintCursor(_In_ IRenderEngine* const pEngine) -{ - const auto cursorInfo = _GetCursorInfo(); - if (cursorInfo.has_value()) - { - LOG_IF_FAILED(pEngine->PaintCursor(cursorInfo.value())); - } -} - -// Routine Description: -// - Retrieves info from the render data to prepare the engine with, before the -// frame is drawn. Some renderers might want to use this information to affect -// later drawing decisions. -// * Namely, the DX renderer uses this to know the cursor position and state -// before PaintCursor is called, so it can draw the cursor underneath the -// text. -// Arguments: -// - engine - The render engine that we're targeting. -// Return Value: -// - S_OK if the engine prepared successfully, or a relevant error via HRESULT. -[[nodiscard]] HRESULT Renderer::_PrepareRenderInfo(_In_ IRenderEngine* const pEngine) -{ - RenderFrameInfo info; - info.cursorInfo = _GetCursorInfo(); - return pEngine->PrepareRenderInfo(info); -} - -// Routine Description: -// - Paint helper to draw text that overlays the main buffer to provide user interactivity regions -// - This supports IME composition. -// Arguments: -// - engine - The render engine that we're targeting. -// - overlay - The overlay to draw. -// Return Value: -// - -void Renderer::_PaintOverlay(IRenderEngine& engine, - const RenderOverlay& overlay) -{ - try - { - // First get the screen buffer's viewport. - Viewport view = _pData->GetViewport(); - - // Now get the overlay's viewport and adjust it to where it is supposed to be relative to the window. - - SMALL_RECT srCaView = overlay.region.ToInclusive(); - srCaView.Top += overlay.origin.Y; - srCaView.Bottom += overlay.origin.Y; - srCaView.Left += overlay.origin.X; - srCaView.Right += overlay.origin.X; - - // Set it up in a Viewport helper structure and trim it the IME viewport to be within the full console viewport. - Viewport viewConv = Viewport::FromInclusive(srCaView); - - gsl::span dirtyAreas; - LOG_IF_FAILED(engine.GetDirtyArea(dirtyAreas)); - - for (SMALL_RECT srDirty : dirtyAreas) - { - // Dirty is an inclusive rectangle, but oddly enough the IME was an exclusive one, so correct it. - srDirty.Bottom++; - srDirty.Right++; - - if (viewConv.TrimToViewport(&srDirty)) - { - Viewport viewDirty = Viewport::FromInclusive(srDirty); - - for (SHORT iRow = viewDirty.Top(); iRow < viewDirty.BottomInclusive(); iRow++) - { - const COORD target{ viewDirty.Left(), iRow }; - const auto source = target - overlay.origin; - - auto it = overlay.buffer.GetCellLineDataAt(source); - - _PaintBufferOutputHelper(&engine, it, target, false); - } - } - } - } - CATCH_LOG(); -} - -// Routine Description: -// - Paint helper to draw the composition string portion of the IME. -// - This specifically is the string that appears at the cursor on the input line showing what the user is currently typing. -// - See also: Generic Paint IME helper method. -// Arguments: -// - -// Return Value: -// - -void Renderer::_PaintOverlays(_In_ IRenderEngine* const pEngine) -{ - try - { - const auto overlays = _pData->GetOverlays(); - - for (const auto& overlay : overlays) - { - _PaintOverlay(*pEngine, overlay); - } - } - CATCH_LOG(); -} - -// Routine Description: -// - Paint helper to draw the selected area of the window. -// Arguments: -// - -// Return Value: -// - -void Renderer::_PaintSelection(_In_ IRenderEngine* const pEngine) -{ - try - { - gsl::span dirtyAreas; - LOG_IF_FAILED(pEngine->GetDirtyArea(dirtyAreas)); - - // Get selection rectangles - const auto rectangles = _GetSelectionRects(); - for (auto rect : rectangles) - { - for (auto& dirtyRect : dirtyAreas) - { - // Make a copy as `TrimToViewport` will manipulate it and - // can destroy it for the next dirtyRect to test against. - auto rectCopy = rect; - Viewport dirtyView = Viewport::FromInclusive(dirtyRect); - if (dirtyView.TrimToViewport(&rectCopy)) - { - LOG_IF_FAILED(pEngine->PaintSelection(rectCopy)); - } - } - } - } - CATCH_LOG(); -} - -// Routine Description: -// - Helper to convert the text attributes to actual RGB colors and update the rendering pen/brush within the rendering engine before the next draw operation. -// Arguments: -// - pEngine - Which engine is being updated -// - textAttributes - The 16 color foreground/background combination to set -// - isSettingDefaultBrushes - Alerts that the default brushes are being set which will -// impact whether or not to include the hung window/erase window brushes in this operation -// and can affect other draw state that wants to know the default color scheme. -// (Usually only happens when the default is changed, not when each individual color is swapped in a multi-color run.) -// Return Value: -// - -[[nodiscard]] HRESULT Renderer::_UpdateDrawingBrushes(_In_ IRenderEngine* const pEngine, const TextAttribute textAttributes, const bool isSettingDefaultBrushes) -{ - // The last color needs to be each engine's responsibility. If it's local to this function, - // then on the next engine we might not update the color. - return pEngine->UpdateDrawingBrushes(textAttributes, _pData, isSettingDefaultBrushes); -} - -// Routine Description: -// - Helper called before a majority of paint operations to scroll most of the previous frame into the appropriate -// position before we paint the remaining invalid area. -// - Used to save drawing time/improve performance -// Arguments: -// - -// Return Value: -// - -[[nodiscard]] HRESULT Renderer::_PerformScrolling(_In_ IRenderEngine* const pEngine) -{ - return pEngine->ScrollFrame(); -} - -// Routine Description: -// - Helper to determine the selected region of the buffer. -// Return Value: -// - A vector of rectangles representing the regions to select, line by line. -std::vector Renderer::_GetSelectionRects() const -{ - const auto& buffer = _pData->GetTextBuffer(); - auto rects = _pData->GetSelectionRects(); - // Adjust rectangles to viewport - Viewport view = _pData->GetViewport(); - - std::vector result; - - for (auto rect : rects) - { - // Convert buffer offsets to the equivalent range of screen cells - // expected by callers, taking line rendition into account. - const auto lineRendition = buffer.GetLineRendition(rect.Top()); - rect = Viewport::FromInclusive(BufferToScreenLine(rect.ToInclusive(), lineRendition)); - - auto sr = view.ConvertToOrigin(rect).ToInclusive(); - - // hopefully temporary, we should be receiving the right selection sizes without correction. - sr.Right++; - sr.Bottom++; - - result.emplace_back(sr); - } - - return result; -} - // Method Description: // - Offsets all of the selection rectangles we might be holding onto // as the previously selected area. If the whole viewport scrolls, @@ -1303,11 +683,6 @@ void Renderer::ResetErrorStateAndResume() EnablePainting(); } -void Renderer::UpdateLastHoveredInterval(const std::optional& newInterval) -{ - _hoveredInterval = newInterval; -} - // Method Description: // - Blocks until the engines are able to render without blocking. void Renderer::WaitUntilCanRender() diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index 6f8f6d7a8c4..9de23f71d8b 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -80,8 +80,6 @@ namespace Microsoft::Console::Render void SetRendererEnteredErrorStateCallback(std::function pfn); void ResetErrorStateAndResume(); - void UpdateLastHoveredInterval(const std::optional::interval>& newInterval); - private: std::deque _rgpEngines; @@ -90,53 +88,21 @@ namespace Microsoft::Console::Render std::unique_ptr _pThread; bool _destructing = false; - std::optional::interval> _hoveredInterval; - void _NotifyPaintFrame(); [[nodiscard]] HRESULT _PaintFrameForEngine(_In_ IRenderEngine* const pEngine) noexcept; bool _CheckViewportAndScroll(); - [[nodiscard]] HRESULT _PaintBackground(_In_ IRenderEngine* const pEngine); - - void _PaintBufferOutput(_In_ IRenderEngine* const pEngine); - - void _PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, - 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 _PaintCursor(_In_ IRenderEngine* const pEngine); - - void _PaintOverlays(_In_ IRenderEngine* const pEngine); - void _PaintOverlay(IRenderEngine& engine, const RenderOverlay& overlay); - - [[nodiscard]] HRESULT _UpdateDrawingBrushes(_In_ IRenderEngine* const pEngine, const TextAttribute attr, const bool isSettingDefaultBrushes); - - [[nodiscard]] HRESULT _PerformScrolling(_In_ IRenderEngine* const pEngine); - Microsoft::Console::Types::Viewport _viewport; static constexpr float _shrinkThreshold = 0.8f; std::vector _clusterBuffer; - std::vector _GetSelectionRects() const; void _ScrollPreviousSelection(const til::point delta); std::vector _previousSelection; - [[nodiscard]] HRESULT _PaintTitle(IRenderEngine* const pEngine); - [[nodiscard]] std::optional _GetCursorInfo(); - [[nodiscard]] HRESULT _PrepareRenderInfo(_In_ IRenderEngine* const pEngine); // Helper functions to diagnose issues with painting and layout. // These are only actually effective/on in Debug builds when the flag is set using an attached debugger. diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 03904814e2b..0db2d74c2ec 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -8,6 +8,7 @@ #include "../../interactivity/win32/CustomWindowMessages.h" #include "../../types/inc/Viewport.hpp" +#include "../../buffer/out/textBuffer.hpp" #include "../../inc/unicode.hpp" #include "../../inc/DefaultSettings.h" #include @@ -1352,7 +1353,40 @@ try } CATCH_RETURN() -// Routine Description: +// Routine description: +// - Actual painting procedure +// Arguments: +// - +// Return Value: +// - Any DirectX error, a memory error, etc. +[[nodiscard]] HRESULT DxEngine::PaintFrame(IRenderData *pData) noexcept +{ + // Prep Colors + RETURN_IF_FAILED(UpdateDrawingBrushes(pData->GetDefaultBrushColors(), pData, true)); + + // Prepare the engine with additional information before we start drawing. + RenderFrameInfo info; + info.cursorInfo = _GetCursorInfo(pData); + RETURN_IF_FAILED(PrepareRenderInfo(info)); + + // Paint Background + RETURN_IF_FAILED(PaintBackground()); + + // Paint Rows of Text + _LoopDirtyLines(pData, std::bind(&DxEngine::_PaintBufferLineHelper, this, std::placeholders::_1)); + + // Paint overlays that reside above the text buffer + _LoopOverlay(pData, std::bind(&DxEngine::_PaintBufferLineHelper, this, std::placeholders::_1)); + + // Paint Selection + _LoopSelection(pData, std::bind(&DxEngine::PaintSelection, this, std::placeholders::_1)); + + // Paint window title + RETURN_IF_FAILED(_DoUpdateTitle()); + + return S_OK; +} + // Routine Description: // - Ends batch drawing and captures any state necessary for presentation // Arguments: // - @@ -1601,17 +1635,6 @@ void DxEngine::WaitUntilCanRender() noexcept return S_OK; } -// Routine Description: -// - This is currently unused. -// Arguments: -// - -// Return Value: -// - S_OK -[[nodiscard]] HRESULT DxEngine::ScrollFrame() noexcept -{ - return S_OK; -} - // Routine Description: // - This paints in the back most layer of the frame with the background color. // Arguments: @@ -1833,18 +1856,6 @@ try } CATCH_RETURN() -// Routine Description: -// - Does nothing. Our cursor is drawn in CustomTextRenderer::DrawGlyphRun, -// either above or below the text. -// Arguments: -// - options - unused -// Return Value: -// - S_OK -[[nodiscard]] HRESULT DxEngine::PaintCursor(const CursorOptions& /*options*/) noexcept -{ - return S_OK; -} - // Routine Description: // - Paint terminal effects. // Arguments: @@ -2132,7 +2143,7 @@ CATCH_RETURN(); // - newTitle: the new string to use for the title of the window // Return Value: // - S_OK -[[nodiscard]] HRESULT DxEngine::_DoUpdateTitle(_In_ const std::wstring_view /*newTitle*/) noexcept +[[nodiscard]] HRESULT DxEngine::_DoUpdateTitle() noexcept { if (_hwndTarget != INVALID_HANDLE_VALUE) { @@ -2141,6 +2152,176 @@ CATCH_RETURN(); return S_FALSE; } +void DxEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) +{ + // Retrieve the cell information iterator limited to just this line we want to redraw. + auto it = renderData.buffer.GetCellDataAt(renderData.bufferLine.Origin(), renderData.bufferLine); + if (!it) + { + return; + } + auto pData = renderData.pData; + + // If we have valid data, let's figure out how to draw it. + + // TODO: MSFT: 20961091 - This is a perf issue. Instead of rebuilding this and allocing memory to hold the reinterpretation, + // we should have an iterator/view adapter for the rendering. + // That would probably also eliminate the RenderData needing to give us the entire TextBuffer as well... + // Retrieve the iterator for one line of information. + size_t cols = 0; + + // Retrieve the first color. + auto color = it->TextAttr(); + // Retrieve the first pattern id + auto patternIds = pData->GetPatternId(renderData.screenPosition); + + // And hold the point where we should start drawing. + auto screenPoint = renderData.screenPosition; + + // This outer loop will continue until we reach the end of the text we are trying to draw. + while (it) + { + // Hold onto the current run color right here for the length of the outer loop. + // We'll be changing the persistent one as we run through the inner loops to detect + // when a run changes, but we will still need to know this color at the bottom + // when we go to draw gridlines for the length of the run. + const auto currentRunColor = color; + + // Hold onto the current pattern id as well + const auto currentPatternId = patternIds; + + // Update the drawing brushes with our color. + THROW_IF_FAILED(UpdateDrawingBrushes(currentRunColor, pData, false)); + + // Advance the point by however many columns we've just outputted and reset the accumulator. + screenPoint.X += gsl::narrow(cols); + cols = 0; + + // Hold onto the start of this run iterator and the target location where we started + // in case we need to do some special work to paint the line drawing characters. + const auto currentRunItStart = it; + const auto currentRunTargetStart = screenPoint; + + // Ensure that our cluster vector is clear. + _clusterBuffer.clear(); + + // Reset our flag to know when we're in the special circumstance + // of attempting to draw only the right-half of a two-column character + // as the first item in our run. + bool trimLeft = false; + + // Run contains wide character (>1 columns) + bool containsWideCharacter = false; + + // This inner loop will accumulate clusters until the color changes. + // When the color changes, it will save the new color off and break. + // We also accumulate clusters according to regex patterns + do + { + COORD thisPoint{ screenPoint.X + gsl::narrow(cols), screenPoint.Y }; + const auto thisPointPatterns = pData->GetPatternId(thisPoint); + if (color != it->TextAttr() || patternIds != thisPointPatterns) + { + auto newAttr{ it->TextAttr() }; + // foreground doesn't matter for runs of spaces (!) + // if we trick it . . . we call Paint far fewer times for cmatrix + if (!s_IsAllSpaces(it->Chars()) || !newAttr.HasIdenticalVisualRepresentationForBlankSpace(color, renderData.globalInvert) || patternIds != thisPointPatterns) + { + color = newAttr; + patternIds = thisPointPatterns; + break; // vend this run + } + } + + // 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. + size_t columnCount = 0; + + // 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. + if (_clusterBuffer.empty() && it->DbcsAttr().IsTrailing()) + { + // Move left to the one so the whole character can be struck correctly. + --screenPoint.X; + // And tell the next function to trim off the left half of it. + trimLeft = true; + // And add one to the number of columns we expect it to take as we insert it. + columnCount = it->Columns() + 1; + _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) + { + containsWideCharacter = true; + } + + // Advance the cluster and column counts. + it += std::max(it->Columns(), 1); // prevent infinite loop for no visible columns + cols += columnCount; + + } while (it); + + // Do the painting. + THROW_IF_FAILED(PaintBufferLine({ _clusterBuffer.data(), _clusterBuffer.size() }, screenPoint, trimLeft, renderData.lineWrapped)); + + // If we're allowed to do grid drawing, draw that now too (since it will be coupled with the color data) + // We're only allowed to draw the grid lines under certain circumstances. + if (renderData.gridLineDrawingAllowed) + { + // See GH: 803 + // If we found a wide character while we looped above, it's possible we skipped over the right half + // attribute that could have contained different line information than the left half. + if (containsWideCharacter) + { + // Start from the original position in this run. + auto lineIt = currentRunItStart; + // Start from the original target in this run. + auto lineTarget = currentRunTargetStart; + + // We need to go through the iterators again to ensure we get the lines associated with each + // exact column. The code above will condense two-column characters into one, but it is possible + // (like with the IME) that the line drawing characters will vary from the left to right half + // of a wider character. + // We could theoretically pre-pass for this in the loop above to be more efficient about walking + // the iterator, but I fear it would make the code even more confusing than it already is. + // Do that in the future if some WPR trace points you to this spot as super bad. + for (auto colsPainted = 0u; colsPainted < cols; ++colsPainted, ++lineIt, ++lineTarget.X) + { + auto lines = lineIt->TextAttr(); + auto gridLines = _CalculateGridLines(pData, lines, lineTarget); + // Return early if there are no lines to paint. + if (gridLines != IRenderEngine::GridLines::None) + { + // Get the current foreground color to render the lines. + const COLORREF rgb = pData->GetAttributeColors(lines).first; + // Draw the lines + LOG_IF_FAILED(PaintBufferGridLines(gridLines, rgb, 1, lineTarget)); + } + } + } + else + { + // If nothing exciting is going on, draw the lines in bulk. + auto gridLines = _CalculateGridLines(pData, currentRunColor, screenPoint); + // Return early if there are no lines to paint. + if (gridLines != IRenderEngine::GridLines::None) + { + // Get the current foreground color to render the lines. + const COLORREF rgb = pData->GetAttributeColors(currentRunColor).first; + // Draw the lines + LOG_IF_FAILED(PaintBufferGridLines(gridLines, rgb, cols, screenPoint)); + } + } + } + } +} + // Routine Description: // - Helps convert a GDI COLORREF into a Direct2D ColorF // Arguments: diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index cbd34ea3b78..251edfb2d14 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -83,6 +83,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept override; [[nodiscard]] HRESULT StartPaint() noexcept override; + [[nodiscard]] HRESULT PaintFrame(IRenderData* pdata) noexcept override; [[nodiscard]] HRESULT EndPaint() noexcept override; [[nodiscard]] bool RequiresContinuousRedraw() noexcept override; @@ -90,24 +91,20 @@ namespace Microsoft::Console::Render void WaitUntilCanRender() noexcept override; [[nodiscard]] HRESULT Present() noexcept override; - [[nodiscard]] HRESULT ScrollFrame() noexcept override; + [[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept; - [[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept override; - - [[nodiscard]] HRESULT PaintBackground() noexcept override; + [[nodiscard]] HRESULT PaintBackground() noexcept; [[nodiscard]] HRESULT PaintBufferLine(gsl::span const clusters, COORD const coord, bool const fTrimLeft, - const bool lineWrapped) noexcept override; - - [[nodiscard]] HRESULT PaintBufferGridLines(GridLines const lines, COLORREF const color, size_t const cchLine, COORD const coordTarget) noexcept override; - [[nodiscard]] HRESULT PaintSelection(const SMALL_RECT rect) noexcept override; + const bool lineWrapped) noexcept; - [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; + [[nodiscard]] HRESULT PaintBufferGridLines(GridLines const lines, COLORREF const color, size_t const cchLine, COORD const coordTarget) noexcept; + [[nodiscard]] HRESULT PaintSelection(const SMALL_RECT rect) noexcept; [[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const gsl::not_null pData, - const bool isSettingDefaultBrushes) noexcept override; + const bool isSettingDefaultBrushes) noexcept; [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo) noexcept override; [[nodiscard]] HRESULT UpdateDpi(int const iDpi) noexcept override; [[nodiscard]] HRESULT UpdateViewport(const SMALL_RECT srNewViewport) noexcept override; @@ -131,7 +128,8 @@ namespace Microsoft::Console::Render void UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept; protected: - [[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring_view newTitle) noexcept override; + [[nodiscard]] HRESULT _DoUpdateTitle() noexcept; + void _PaintBufferLineHelper(const BufferLineRenderData& renderData); [[nodiscard]] HRESULT _PaintTerminalEffects() noexcept; [[nodiscard]] bool _FullRepaintNeeded() const noexcept; diff --git a/src/renderer/gdi/gdirenderer.hpp b/src/renderer/gdi/gdirenderer.hpp index 230b09c4f6c..3368c07514c 100644 --- a/src/renderer/gdi/gdirenderer.hpp +++ b/src/renderer/gdi/gdirenderer.hpp @@ -36,32 +36,33 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept override; [[nodiscard]] HRESULT StartPaint() noexcept override; + [[nodiscard]] HRESULT PaintFrame(IRenderData* pData) noexcept override; [[nodiscard]] HRESULT EndPaint() noexcept override; [[nodiscard]] HRESULT Present() noexcept override; - [[nodiscard]] HRESULT ScrollFrame() noexcept override; + [[nodiscard]] HRESULT ScrollFrame() noexcept; - [[nodiscard]] HRESULT ResetLineTransform() noexcept override; + [[nodiscard]] HRESULT ResetLineTransform() noexcept; [[nodiscard]] HRESULT PrepareLineTransform(const LineRendition lineRendition, const size_t targetRow, - const size_t viewportLeft) noexcept override; + const size_t viewportLeft) noexcept; - [[nodiscard]] HRESULT PaintBackground() noexcept override; + [[nodiscard]] HRESULT PaintBackground() noexcept; [[nodiscard]] HRESULT PaintBufferLine(gsl::span const clusters, const COORD coord, const bool trimLeft, - const bool lineWrapped) noexcept override; + const bool lineWrapped) noexcept; [[nodiscard]] HRESULT PaintBufferGridLines(const GridLines lines, const COLORREF color, const size_t cchLine, - const COORD coordTarget) noexcept override; - [[nodiscard]] HRESULT PaintSelection(const SMALL_RECT rect) noexcept override; + const COORD coordTarget) noexcept; + [[nodiscard]] HRESULT PaintSelection(const SMALL_RECT rect) noexcept; - [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; + [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept; [[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const gsl::not_null pData, - const bool isSettingDefaultBrushes) noexcept override; + const bool isSettingDefaultBrushes) noexcept; [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override; [[nodiscard]] HRESULT UpdateDpi(const int iDpi) noexcept override; @@ -76,7 +77,9 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override; protected: - [[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring_view newTitle) noexcept override; + void GdiEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData); + + [[nodiscard]] HRESULT _DoUpdateTitle() noexcept; private: HWND _hwndTargetWindow; diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index 089e9777e69..9178cab90b4 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -57,6 +57,44 @@ using namespace Microsoft::Console::Render; return S_OK; } +[[nodiscard]] HRESULT GdiEngine::PaintFrame(IRenderData* pData) noexcept +{ + // Prep Colors + RETURN_IF_FAILED(UpdateDrawingBrushes(pData->GetDefaultBrushColors(), pData, true)); + + // Perform Scroll Operations + RETURN_IF_FAILED(ScrollFrame()); + + // Paint Background + RETURN_IF_FAILED(PaintBackground()); + + // This is to make sure any transforms are reset when this paint is finished. + auto resetLineTransform = wil::scope_exit([&]() { + LOG_IF_FAILED(ResetLineTransform()); + }); + + // Paint Rows of Text + _LoopDirtyLines(pData, std::bind(&GdiEngine::_PaintBufferLineHelper, this, std::placeholders::_1)); + + // Paint overlays that reside above the text buffer + _LoopOverlay(pData, std::bind(&GdiEngine::_PaintBufferLineHelper, this, std::placeholders::_1)); + + // Paint Selection + _LoopSelection(pData, std::bind(&GdiEngine::PaintSelection, this, std::placeholders::_1)); + + // Paint Cursor + const auto cursorInfo = _GetCursorInfo(pData); + if (cursorInfo.has_value()) + { + LOG_IF_FAILED(PaintCursor(cursorInfo.value())); + } + + // Paint window title + RETURN_IF_FAILED(_DoUpdateTitle()); + + return S_OK; +} + // Routine Description: // - Scrolls the existing data on the in-memory frame by the scroll region // deltas we have collectively received through the Invalidate methods @@ -664,6 +702,7 @@ using namespace Microsoft::Console::Render; // Prepare the appropriate line transform for the current row. LOG_IF_FAILED(PrepareLineTransform(options.lineRendition, 0, options.viewportLeft)); + auto resetLineTransform = wil::scope_exit([&]() { LOG_IF_FAILED(ResetLineTransform()); }); diff --git a/src/renderer/gdi/state.cpp b/src/renderer/gdi/state.cpp index 3311e75858e..2049311c597 100644 --- a/src/renderer/gdi/state.cpp +++ b/src/renderer/gdi/state.cpp @@ -5,6 +5,7 @@ #include "gdirenderer.hpp" #include "../../inc/conattrs.hpp" +#include "../../buffer/out/textBuffer.hpp" #include // for GWL_CONSOLE_BKCOLOR #include "../../interactivity/win32/CustomWindowMessages.h" #pragma hdrstop @@ -465,14 +466,184 @@ GdiEngine::~GdiEngine() return _GetProposedFont(FontDesired, Font, iDpi, hFont, hFontItalic); } +void GdiEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) +{ + LOG_IF_FAILED(PrepareLineTransform(renderData.lineRendention, renderData.screenPosition.Y, renderData.visible.Left())); + + // Retrieve the cell information iterator limited to just this line we want to redraw. + auto it = renderData.buffer.GetCellDataAt(renderData.bufferLine.Origin(), renderData.bufferLine); + if (!it) + { + return; + } + auto pData = renderData.pData; + + // If we have valid data, let's figure out how to draw it. + + // TODO: MSFT: 20961091 - This is a perf issue. Instead of rebuilding this and allocing memory to hold the reinterpretation, + // we should have an iterator/view adapter for the rendering. + // That would probably also eliminate the RenderData needing to give us the entire TextBuffer as well... + // Retrieve the iterator for one line of information. + size_t cols = 0; + + // Retrieve the first color. + auto color = it->TextAttr(); + // Retrieve the first pattern id + auto patternIds = pData->GetPatternId(renderData.screenPosition); + + // And hold the point where we should start drawing. + auto screenPoint = renderData.screenPosition; + + // This outer loop will continue until we reach the end of the text we are trying to draw. + while (it) + { + // Hold onto the current run color right here for the length of the outer loop. + // We'll be changing the persistent one as we run through the inner loops to detect + // when a run changes, but we will still need to know this color at the bottom + // when we go to draw gridlines for the length of the run. + const auto currentRunColor = color; + + // Hold onto the current pattern id as well + const auto currentPatternId = patternIds; + + // Update the drawing brushes with our color. + THROW_IF_FAILED(UpdateDrawingBrushes(currentRunColor, pData, false)); + + // Advance the point by however many columns we've just outputted and reset the accumulator. + screenPoint.X += gsl::narrow(cols); + cols = 0; + + // Hold onto the start of this run iterator and the target location where we started + // in case we need to do some special work to paint the line drawing characters. + const auto currentRunItStart = it; + const auto currentRunTargetStart = screenPoint; + + // Ensure that our cluster vector is clear. + _clusterBuffer.clear(); + + // Reset our flag to know when we're in the special circumstance + // of attempting to draw only the right-half of a two-column character + // as the first item in our run. + bool trimLeft = false; + + // Run contains wide character (>1 columns) + bool containsWideCharacter = false; + + // This inner loop will accumulate clusters until the color changes. + // When the color changes, it will save the new color off and break. + // We also accumulate clusters according to regex patterns + do + { + COORD thisPoint{ screenPoint.X + gsl::narrow(cols), screenPoint.Y }; + const auto thisPointPatterns = pData->GetPatternId(thisPoint); + if (color != it->TextAttr() || patternIds != thisPointPatterns) + { + auto newAttr{ it->TextAttr() }; + // foreground doesn't matter for runs of spaces (!) + // if we trick it . . . we call Paint far fewer times for cmatrix + if (!s_IsAllSpaces(it->Chars()) || !newAttr.HasIdenticalVisualRepresentationForBlankSpace(color, renderData.globalInvert) || patternIds != thisPointPatterns) + { + color = newAttr; + patternIds = thisPointPatterns; + break; // vend this run + } + } + + // 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. + size_t columnCount = 0; + + // 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. + if (_clusterBuffer.empty() && it->DbcsAttr().IsTrailing()) + { + // Move left to the one so the whole character can be struck correctly. + --screenPoint.X; + // And tell the next function to trim off the left half of it. + trimLeft = true; + // And add one to the number of columns we expect it to take as we insert it. + columnCount = it->Columns() + 1; + _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) + { + containsWideCharacter = true; + } + + // Advance the cluster and column counts. + it += std::max(it->Columns(), 1); // prevent infinite loop for no visible columns + cols += columnCount; + + } while (it); + + // Do the painting. + THROW_IF_FAILED(PaintBufferLine({ _clusterBuffer.data(), _clusterBuffer.size() }, screenPoint, trimLeft, renderData.lineWrapped)); + + // If we're allowed to do grid drawing, draw that now too (since it will be coupled with the color data) + // We're only allowed to draw the grid lines under certain circumstances. + if (renderData.gridLineDrawingAllowed) + { + // See GH: 803 + // If we found a wide character while we looped above, it's possible we skipped over the right half + // attribute that could have contained different line information than the left half. + if (containsWideCharacter) + { + // Start from the original position in this run. + auto lineIt = currentRunItStart; + // Start from the original target in this run. + auto lineTarget = currentRunTargetStart; + + // We need to go through the iterators again to ensure we get the lines associated with each + // exact column. The code above will condense two-column characters into one, but it is possible + // (like with the IME) that the line drawing characters will vary from the left to right half + // of a wider character. + // We could theoretically pre-pass for this in the loop above to be more efficient about walking + // the iterator, but I fear it would make the code even more confusing than it already is. + // Do that in the future if some WPR trace points you to this spot as super bad. + for (auto colsPainted = 0u; colsPainted < cols; ++colsPainted, ++lineIt, ++lineTarget.X) + { + auto lines = lineIt->TextAttr(); + auto gridLines = _CalculateGridLines(pData, lines, lineTarget); + // Return early if there are no lines to paint. + if (gridLines != IRenderEngine::GridLines::None) + { + // Get the current foreground color to render the lines. + const COLORREF rgb = pData->GetAttributeColors(lines).first; + // Draw the lines + LOG_IF_FAILED(PaintBufferGridLines(gridLines, rgb, 1, lineTarget)); + } + } + } + else + { + // If nothing exciting is going on, draw the lines in bulk. + auto gridLines = _CalculateGridLines(pData, currentRunColor, screenPoint); + // Return early if there are no lines to paint. + if (gridLines != IRenderEngine::GridLines::None) + { + // Get the current foreground color to render the lines. + const COLORREF rgb = pData->GetAttributeColors(currentRunColor).first; + // Draw the lines + LOG_IF_FAILED(PaintBufferGridLines(gridLines, rgb, cols, screenPoint)); + } + } + } + } +} + // Method Description: // - Updates the window's title string. For GDI, this does nothing, because the // title must be updated on the main window's windowproc thread. -// Arguments: -// - newTitle: the new string to use for the title of the window // Return Value: // - S_OK if PostMessageW succeeded, otherwise E_FAIL -[[nodiscard]] HRESULT GdiEngine::_DoUpdateTitle(_In_ const std::wstring_view /*newTitle*/) noexcept +[[nodiscard]] HRESULT GdiEngine::_DoUpdateTitle() noexcept { // the CM_UPDATE_TITLE handler in windowproc will query the updated title. return PostMessageW(_hwndTargetWindow, CM_UPDATE_TITLE, 0, (LPARAM) nullptr) ? S_OK : E_FAIL; diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index 3d6631e410e..2d5e3ab9f8b 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -54,6 +54,8 @@ namespace Microsoft::Console::Render public: [[nodiscard]] virtual HRESULT StartPaint() noexcept = 0; + [[nodiscard]] virtual HRESULT PaintFrame(IRenderData *pData) noexcept = 0; + [[nodiscard]] virtual HRESULT EndPaint() noexcept = 0; [[nodiscard]] virtual bool RequiresContinuousRedraw() noexcept = 0; @@ -62,8 +64,6 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept = 0; - [[nodiscard]] virtual HRESULT ScrollFrame() noexcept = 0; - [[nodiscard]] virtual HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept = 0; @@ -74,29 +74,6 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT InvalidateTitle(const std::wstring_view proposedTitle) noexcept = 0; - [[nodiscard]] virtual HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept = 0; - - [[nodiscard]] virtual HRESULT ResetLineTransform() noexcept = 0; - [[nodiscard]] virtual HRESULT PrepareLineTransform(const LineRendition lineRendition, - const size_t targetRow, - const size_t viewportLeft) noexcept = 0; - - [[nodiscard]] virtual HRESULT PaintBackground() noexcept = 0; - [[nodiscard]] virtual HRESULT PaintBufferLine(gsl::span const clusters, - const COORD coord, - const bool fTrimLeft, - const bool lineWrapped) noexcept = 0; - [[nodiscard]] virtual HRESULT PaintBufferGridLines(const GridLines lines, - const COLORREF color, - const size_t cchLine, - const COORD coordTarget) noexcept = 0; - [[nodiscard]] virtual HRESULT PaintSelection(const SMALL_RECT rect) noexcept = 0; - - [[nodiscard]] virtual HRESULT PaintCursor(const CursorOptions& options) noexcept = 0; - - [[nodiscard]] virtual HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, - const gsl::not_null pData, - const bool isSettingDefaultBrushes) noexcept = 0; [[nodiscard]] virtual HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept = 0; [[nodiscard]] virtual HRESULT UpdateDpi(const int iDpi) noexcept = 0; @@ -109,7 +86,6 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT GetDirtyArea(gsl::span& area) noexcept = 0; [[nodiscard]] virtual HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept = 0; [[nodiscard]] virtual HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept = 0; - [[nodiscard]] virtual HRESULT UpdateTitle(const std::wstring_view newTitle) noexcept = 0; }; inline Microsoft::Console::Render::IRenderEngine::~IRenderEngine() {} diff --git a/src/renderer/inc/RenderEngineBase.hpp b/src/renderer/inc/RenderEngineBase.hpp index 725347d8215..f95fd3114c0 100644 --- a/src/renderer/inc/RenderEngineBase.hpp +++ b/src/renderer/inc/RenderEngineBase.hpp @@ -21,6 +21,19 @@ Author(s): #pragma once namespace Microsoft::Console::Render { + struct BufferLineRenderData + { + IRenderData* pData; + const TextBuffer& buffer; + Microsoft::Console::Types::Viewport bufferLine; + Microsoft::Console::Types::Viewport visible; + COORD screenPosition; + LineRendition lineRendention; + bool lineWrapped; + bool globalInvert; + bool gridLineDrawingAllowed; + }; + class RenderEngineBase : public IRenderEngine { public: @@ -33,27 +46,48 @@ namespace Microsoft::Console::Render RenderEngineBase& operator=(const RenderEngineBase&) = default; RenderEngineBase& operator=(RenderEngineBase&&) = default; - public: - [[nodiscard]] HRESULT InvalidateTitle(const std::wstring_view proposedTitle) noexcept override; + [[nodiscard]] std::optional _GetCursorInfo(IRenderData* pData); + void _LoopDirtyLines(IRenderData* pData, std::function action); + void _LoopOverlay(IRenderData* pData, std::function action); + void _LoopSelection(IRenderData* pData, std::function action); + + BufferLineRenderData _CalculateRenderDataForDirtyLine(const TextBuffer& buffer, + Microsoft::Console::Types::Viewport visible, + Microsoft::Console::Types::Viewport redraw, + SHORT row) const; + + GridLines _CalculateGridLines(IRenderData* pData, + const TextAttribute textAttribute, + const COORD coordTarget); - [[nodiscard]] HRESULT UpdateTitle(const std::wstring_view newTitle) noexcept override; + static GridLines s_GetGridlines(const TextAttribute& textAttribute) noexcept; - [[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept override; + std::vector _GetSelectionRects(IRenderData* pData) const; - [[nodiscard]] HRESULT ResetLineTransform() noexcept override; - [[nodiscard]] HRESULT PrepareLineTransform(const LineRendition lineRendition, - const size_t targetRow, - const size_t viewportLeft) noexcept override; + static bool s_IsAllSpaces(const std::wstring_view v) + { + // first non-space char is not found (is npos) + return v.find_first_not_of(L" ") == decltype(v)::npos; + } + + public: + [[nodiscard]] HRESULT InvalidateTitle(const std::wstring_view proposedTitle) noexcept override; [[nodiscard]] virtual bool RequiresContinuousRedraw() noexcept override; void WaitUntilCanRender() noexcept override; - protected: - [[nodiscard]] virtual HRESULT _DoUpdateTitle(const std::wstring_view newTitle) noexcept = 0; + void UpdateLastHoveredInterval(const std::optional::interval>& newInterval) + { + _hoveredInterval = newInterval; + } + protected: bool _titleChanged; std::wstring _lastFrameTitle; + + std::vector _clusterBuffer; + std::optional::interval> _hoveredInterval; }; inline Microsoft::Console::Render::RenderEngineBase::~RenderEngineBase() {} diff --git a/src/renderer/uia/UiaRenderer.cpp b/src/renderer/uia/UiaRenderer.cpp index ba7ca8e5f64..20ef3bb401c 100644 --- a/src/renderer/uia/UiaRenderer.cpp +++ b/src/renderer/uia/UiaRenderer.cpp @@ -216,6 +216,11 @@ CATCH_RETURN(); return S_OK; } +[[nodiscard]] HRESULT UiaEngine::PaintFrame(IRenderData* /*pData*/) noexcept +{ + return S_OK; +} + // Routine Description: // - Ends batch drawing and notifies automation clients of updated regions // Arguments: @@ -274,106 +279,6 @@ CATCH_RETURN(); return S_FALSE; } -// Routine Description: -// - This is currently unused. -// Arguments: -// - -// Return Value: -// - S_FALSE -[[nodiscard]] HRESULT UiaEngine::ScrollFrame() noexcept -{ - return S_FALSE; -} - -// Routine Description: -// - Paints the background of the invalid area of the frame. -// For UIA, this doesn't mean anything. So do nothing. -// Arguments: -// - -// Return Value: -// - S_FALSE since we do nothing -[[nodiscard]] HRESULT UiaEngine::PaintBackground() noexcept -{ - return S_FALSE; -} - -// Routine Description: -// - Places one line of text onto the screen at the given position -// For UIA, this doesn't mean anything. So do nothing. -// Arguments: -// - clusters - Iterable collection of cluster information (text and columns it should consume) -// - coord - Character coordinate position in the cell grid -// - fTrimLeft - Whether or not to trim off the left half of a double wide character -// Return Value: -// - S_FALSE -[[nodiscard]] HRESULT UiaEngine::PaintBufferLine(gsl::span const /*clusters*/, - COORD const /*coord*/, - const bool /*trimLeft*/, - const bool /*lineWrapped*/) noexcept -{ - return S_FALSE; -} - -// Routine Description: -// - Paints lines around cells (draws in pieces of the grid) -// For UIA, this doesn't mean anything. So do nothing. -// Arguments: -// - lines - -// - color - -// - cchLine - -// - coordTarget - -// Return Value: -// - S_FALSE -[[nodiscard]] HRESULT UiaEngine::PaintBufferGridLines(GridLines const /*lines*/, - COLORREF const /*color*/, - size_t const /*cchLine*/, - COORD const /*coordTarget*/) noexcept -{ - return S_FALSE; -} - -// Routine Description: -// - Reads the selected area, selection mode, and active screen buffer -// from the global properties and dispatches a GDI invert on the selected text area. -// Because the selection is the responsibility of the terminal, and not the -// host, render nothing. -// Arguments: -// - rect - Rectangle to invert or highlight to make the selection area -// Return Value: -// - S_FALSE -[[nodiscard]] HRESULT UiaEngine::PaintSelection(const SMALL_RECT /*rect*/) noexcept -{ - return S_FALSE; -} - -// Routine Description: -// - Draws the cursor on the screen -// For UIA, this doesn't mean anything. So do nothing. -// Arguments: -// - options - Packed options relevant to how to draw the cursor -// Return Value: -// - S_FALSE -[[nodiscard]] HRESULT UiaEngine::PaintCursor(const CursorOptions& /*options*/) noexcept -{ - return S_FALSE; -} - -// Routine Description: -// - Updates the default brush colors used for drawing -// For UIA, this doesn't mean anything. So do nothing. -// Arguments: -// - textAttributes - -// - pData - -// - isSettingDefaultBrushes - -// Return Value: -// - S_FALSE since we do nothing -[[nodiscard]] HRESULT UiaEngine::UpdateDrawingBrushes(const TextAttribute& /*textAttributes*/, - const gsl::not_null /*pData*/, - const bool /*isSettingDefaultBrushes*/) noexcept -{ - return S_FALSE; -} - // Routine Description: // - Updates the font used for drawing // Arguments: @@ -462,15 +367,3 @@ CATCH_RETURN(); { return S_FALSE; } - -// Method Description: -// - Updates the window's title string. -// - Currently unused by this renderer. -// Arguments: -// - newTitle: the new string to use for the title of the window -// Return Value: -// - S_FALSE -[[nodiscard]] HRESULT UiaEngine::_DoUpdateTitle(_In_ const std::wstring_view /*newTitle*/) noexcept -{ - return S_FALSE; -} diff --git a/src/renderer/uia/UiaRenderer.hpp b/src/renderer/uia/UiaRenderer.hpp index 3ead4673b70..86436baf8c0 100644 --- a/src/renderer/uia/UiaRenderer.hpp +++ b/src/renderer/uia/UiaRenderer.hpp @@ -35,13 +35,12 @@ namespace Microsoft::Console::Render // IRenderEngine Members [[nodiscard]] HRESULT StartPaint() noexcept override; + [[nodiscard]] HRESULT PaintFrame(IRenderData* pData) noexcept override; [[nodiscard]] HRESULT EndPaint() noexcept override; [[nodiscard]] HRESULT Present() noexcept override; [[nodiscard]] HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept override; - [[nodiscard]] HRESULT ScrollFrame() noexcept override; - [[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept override; @@ -50,19 +49,6 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT InvalidateAll() noexcept override; [[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept override; - [[nodiscard]] HRESULT PaintBackground() noexcept override; - [[nodiscard]] HRESULT PaintBufferLine(gsl::span const clusters, - COORD const coord, - bool const fTrimLeft, - const bool lineWrapped) noexcept override; - [[nodiscard]] HRESULT PaintBufferGridLines(GridLines const lines, COLORREF const color, size_t const cchLine, COORD const coordTarget) noexcept override; - [[nodiscard]] HRESULT PaintSelection(const SMALL_RECT rect) noexcept override; - - [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; - - [[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, - const gsl::not_null pData, - const bool isSettingDefaultBrushes) noexcept override; [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo) noexcept override; [[nodiscard]] HRESULT UpdateDpi(int const iDpi) noexcept override; [[nodiscard]] HRESULT UpdateViewport(const SMALL_RECT srNewViewport) noexcept override; @@ -73,9 +59,6 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override; [[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override; - protected: - [[nodiscard]] HRESULT _DoUpdateTitle(const std::wstring_view newTitle) noexcept override; - private: bool _isEnabled; bool _isPainting; diff --git a/src/renderer/vt/XtermEngine.hpp b/src/renderer/vt/XtermEngine.hpp index 1dcf2d17349..a491547a702 100644 --- a/src/renderer/vt/XtermEngine.hpp +++ b/src/renderer/vt/XtermEngine.hpp @@ -59,7 +59,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT _MoveCursor(const COORD coord) noexcept override; - [[nodiscard]] HRESULT _DoUpdateTitle(const std::wstring_view newTitle) noexcept override; + [[nodiscard]] HRESULT _DoUpdateTitle(const std::wstring_view newTitle) noexcept; #ifdef UNIT_TESTING friend class VtRendererTest; diff --git a/src/renderer/vt/paint.cpp b/src/renderer/vt/paint.cpp index f8b8a126654..9e4a504d17f 100644 --- a/src/renderer/vt/paint.cpp +++ b/src/renderer/vt/paint.cpp @@ -6,6 +6,7 @@ #include "vtrenderer.hpp" #include "../../inc/conattrs.hpp" #include "../../types/inc/convert.hpp" +#include "../../buffer/out/textBuffer.hpp" #pragma hdrstop using namespace Microsoft::Console::Render; @@ -42,6 +43,27 @@ using namespace Microsoft::Console::Types; return _quickReturn ? S_FALSE : S_OK; } +[[nodiscard]] HRESULT VtEngine::PaintFrame(IRenderData* pData) noexcept +{ + // Prep Colors + RETURN_IF_FAILED(UpdateDrawingBrushes(pData->GetDefaultBrushColors(), pData, true)); + + // Perform Scroll Operations + RETURN_IF_FAILED(ScrollFrame()); + + // Paint Rows of Text + _LoopDirtyLines(pData, std::bind(&VtEngine::_PaintBufferLineHelper, this, std::placeholders::_1)); + + // Paint Cursor + const auto cursorInfo = _GetCursorInfo(pData); + if (cursorInfo.has_value()) + { + LOG_IF_FAILED(PaintCursor(cursorInfo.value())); + } + + return S_OK; +} + // Routine Description: // - EndPaint helper to perform the final cleanup after painting. If we // returned S_FALSE from StartPaint, there's no guarantee this was called. @@ -100,17 +122,6 @@ using namespace Microsoft::Console::Types; return S_FALSE; } -// Routine Description: -// - Paints the background of the invalid area of the frame. -// Arguments: -// - -// Return Value: -// - S_OK -[[nodiscard]] HRESULT VtEngine::PaintBackground() noexcept -{ - return S_OK; -} - // Routine Description: // - Draws one line of the buffer to the screen. Writes the characters to the // pipe. If the characters are outside the ASCII range (0-0x7f), then @@ -133,23 +144,6 @@ using namespace Microsoft::Console::Types; return VtEngine::_PaintAsciiBufferLine(clusters, coord); } -// Method Description: -// - Draws up to one line worth of grid lines on top of characters. -// Arguments: -// - lines - Enum defining which edges of the rectangle to draw -// - color - The color to use for drawing the edges. -// - cchLine - How many characters we should draw the grid lines along (left to right in a row) -// - coordTarget - The starting X/Y position of the first character to draw on. -// Return Value: -// - S_OK -[[nodiscard]] HRESULT VtEngine::PaintBufferGridLines(const GridLines /*lines*/, - const COLORREF /*color*/, - const size_t /*cchLine*/, - const COORD /*coordTarget*/) noexcept -{ - return S_OK; -} - // Routine Description: // - Draws the cursor on the screen // Arguments: @@ -166,22 +160,113 @@ using namespace Microsoft::Console::Types; return S_OK; } -// Routine Description: -// - Inverts the selected region on the current screen buffer. -// - Reads the selected area, selection mode, and active screen buffer -// from the global properties and dispatches a GDI invert on the selected text area. -// Because the selection is the responsibility of the terminal, and not the -// host, render nothing. -// Arguments: -// - rect - Rectangle to invert or highlight to make the selection area -// Return Value: -// - S_OK -[[nodiscard]] HRESULT VtEngine::PaintSelection(const SMALL_RECT /*rect*/) noexcept +void VtEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) { - return S_OK; -} + // Retrieve the cell information iterator limited to just this line we want to redraw. + auto it = renderData.buffer.GetCellDataAt(renderData.bufferLine.Origin(), renderData.bufferLine); + if (!it) + { + return; + } -// Routine Description: + auto pData = renderData.pData; + size_t cols = 0; + + // Retrieve the first color. + auto color = it->TextAttr(); + + // And hold the point where we should start drawing. + auto screenPoint = renderData.screenPosition; + + // This outer loop will continue until we reach the end of the text we are trying to draw. + while (it) + { + // Hold onto the current run color right here for the length of the outer loop. + // We'll be changing the persistent one as we run through the inner loops to detect + // when a run changes, but we will still need to know this color at the bottom + // when we go to draw gridlines for the length of the run. + const auto currentRunColor = color; + + // Update the drawing brushes with our color. + THROW_IF_FAILED(UpdateDrawingBrushes(currentRunColor, pData, false)); + + // Advance the point by however many columns we've just outputted and reset the accumulator. + screenPoint.X += gsl::narrow(cols); + cols = 0; + + // Hold onto the start of this run iterator and the target location where we started + // in case we need to do some special work to paint the line drawing characters. + const auto currentRunItStart = it; + const auto currentRunTargetStart = screenPoint; + + // Ensure that our cluster vector is clear. + _clusterBuffer.clear(); + + // Reset our flag to know when we're in the special circumstance + // of attempting to draw only the right-half of a two-column character + // as the first item in our run. + bool trimLeft = false; + + // Run contains wide character (>1 columns) + bool containsWideCharacter = false; + + // This inner loop will accumulate clusters until the color changes. + // When the color changes, it will save the new color off and break. + // We also accumulate clusters according to regex patterns + do + { + COORD thisPoint{ screenPoint.X + gsl::narrow(cols), screenPoint.Y }; + if (color != it->TextAttr()) + { + auto newAttr{ it->TextAttr() }; + // foreground doesn't matter for runs of spaces (!) + // if we trick it . . . we call Paint far fewer times for cmatrix + if (!s_IsAllSpaces(it->Chars()) || !newAttr.HasIdenticalVisualRepresentationForBlankSpace(color, renderData.globalInvert)) + { + color = newAttr; + break; // vend this run + } + } + + // 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. + size_t columnCount = 0; + + // 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. + if (_clusterBuffer.empty() && it->DbcsAttr().IsTrailing()) + { + // Move left to the one so the whole character can be struck correctly. + --screenPoint.X; + // And tell the next function to trim off the left half of it. + trimLeft = true; + // And add one to the number of columns we expect it to take as we insert it. + columnCount = it->Columns() + 1; + _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) + { + containsWideCharacter = true; + } + + // Advance the cluster and column counts. + it += std::max(it->Columns(), 1); // prevent infinite loop for no visible columns + cols += columnCount; + + } while (it); + + // Do the painting. + THROW_IF_FAILED(PaintBufferLine({ _clusterBuffer.data(), _clusterBuffer.size() }, screenPoint, trimLeft, renderData.lineWrapped)); + } +} + // Routine Description: // - Write a VT sequence to change the current colors of text. Writes true RGB // color sequences. // Arguments: @@ -588,17 +673,3 @@ using namespace Microsoft::Console::Types; return S_OK; } - -// Method Description: -// - Updates the window's title string. Emits the VT sequence to SetWindowTitle. -// Because wintelnet does not understand these sequences by default, we -// don't do anything by default. Other modes can implement if they support -// the sequence. -// Arguments: -// - newTitle: the new string to use for the title of the window -// Return Value: -// - S_OK -[[nodiscard]] HRESULT VtEngine::_DoUpdateTitle(const std::wstring_view /*newTitle*/) noexcept -{ - return S_OK; -} diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index f7b0a788b66..ce9b439f3b4 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -55,23 +55,19 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept override; [[nodiscard]] virtual HRESULT StartPaint() noexcept override; + [[nodiscard]] virtual HRESULT PaintFrame(IRenderData* pdata) noexcept override; + [[nodiscard]] virtual HRESULT EndPaint() noexcept override; [[nodiscard]] virtual HRESULT Present() noexcept override; [[nodiscard]] virtual HRESULT ScrollFrame() noexcept = 0; - [[nodiscard]] HRESULT PaintBackground() noexcept override; [[nodiscard]] virtual HRESULT PaintBufferLine(gsl::span const clusters, const COORD coord, const bool trimLeft, - const bool lineWrapped) noexcept override; - [[nodiscard]] HRESULT PaintBufferGridLines(const GridLines lines, - const COLORREF color, - const size_t cchLine, - const COORD coordTarget) noexcept override; - [[nodiscard]] HRESULT PaintSelection(const SMALL_RECT rect) noexcept override; + const bool lineWrapped) noexcept; - [[nodiscard]] virtual HRESULT PaintCursor(const CursorOptions& options) noexcept override; + [[nodiscard]] virtual HRESULT PaintCursor(const CursorOptions& options) noexcept; [[nodiscard]] virtual HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const gsl::not_null pData, @@ -153,6 +149,8 @@ namespace Microsoft::Console::Render bool _resizeQuirk{ false }; std::optional _newBottomLineBG{ std::nullopt }; + void _PaintBufferLineHelper(const BufferLineRenderData& renderData); + [[nodiscard]] HRESULT _Write(std::string_view const str) noexcept; [[nodiscard]] HRESULT _Flush() noexcept; @@ -233,8 +231,6 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT _WriteTerminalUtf8(const std::wstring_view str) noexcept; [[nodiscard]] HRESULT _WriteTerminalAscii(const std::wstring_view str) noexcept; - [[nodiscard]] virtual HRESULT _DoUpdateTitle(const std::wstring_view newTitle) noexcept override; - /////////////////////////// Unit Testing Helpers /////////////////////////// #ifdef UNIT_TESTING std::function _pfnTestCallback; diff --git a/src/renderer/wddmcon/WddmConRenderer.cpp b/src/renderer/wddmcon/WddmConRenderer.cpp index ed2c027ee4a..ae2d97c8ec1 100644 --- a/src/renderer/wddmcon/WddmConRenderer.cpp +++ b/src/renderer/wddmcon/WddmConRenderer.cpp @@ -228,11 +228,6 @@ bool WddmConEngine::IsInitialized() return S_FALSE; } -[[nodiscard]] HRESULT WddmConEngine::ScrollFrame() noexcept -{ - return S_OK; -} - [[nodiscard]] HRESULT WddmConEngine::PaintBackground() noexcept { RETURN_IF_HANDLE_INVALID(_hWddmConCtx); @@ -287,24 +282,6 @@ bool WddmConEngine::IsInitialized() CATCH_RETURN(); } -[[nodiscard]] HRESULT WddmConEngine::PaintBufferGridLines(GridLines const /*lines*/, - COLORREF const /*color*/, - size_t const /*cchLine*/, - COORD const /*coordTarget*/) noexcept -{ - return S_OK; -} - -[[nodiscard]] HRESULT WddmConEngine::PaintSelection(const SMALL_RECT /*rect*/) noexcept -{ - return S_OK; -} - -[[nodiscard]] HRESULT WddmConEngine::PaintCursor(const CursorOptions& /*options*/) noexcept -{ - return S_OK; -} - [[nodiscard]] HRESULT WddmConEngine::UpdateDrawingBrushes(const TextAttribute& textAttributes, const gsl::not_null /*pData*/, bool const /*isSettingDefaultBrushes*/) noexcept @@ -406,15 +383,3 @@ RECT WddmConEngine::GetDisplaySize() *pResult = false; return S_OK; } - -// Method Description: -// - Updates the window's title string. -// Does nothing for WddmCon. -// Arguments: -// - newTitle: the new string to use for the title of the window -// Return Value: -// - S_OK -[[nodiscard]] HRESULT WddmConEngine::_DoUpdateTitle(_In_ const std::wstring_view /*newTitle*/) noexcept -{ - return S_OK; -} diff --git a/src/renderer/wddmcon/WddmConRenderer.hpp b/src/renderer/wddmcon/WddmConRenderer.hpp index 6898f1ecef8..ec24f63a586 100644 --- a/src/renderer/wddmcon/WddmConRenderer.hpp +++ b/src/renderer/wddmcon/WddmConRenderer.hpp @@ -45,10 +45,6 @@ namespace Microsoft::Console::Render const COORD coord, const bool trimLeft, const bool lineWrapped) noexcept override; - [[nodiscard]] HRESULT PaintBufferGridLines(GridLines const lines, COLORREF const color, size_t const cchLine, COORD const coordTarget) noexcept override; - [[nodiscard]] HRESULT PaintSelection(const SMALL_RECT rect) noexcept override; - - [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; [[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const gsl::not_null pData, @@ -63,9 +59,6 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override; [[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override; - protected: - [[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring_view newTitle) noexcept override; - private: HANDLE _hWddmConCtx; From e4b6addcc8c5aa25eba3b9157d9ec48b10f4dca6 Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Sun, 11 Jul 2021 19:32:17 +0800 Subject: [PATCH 2/9] No PatternId code in conhost --- src/renderer/gdi/state.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/renderer/gdi/state.cpp b/src/renderer/gdi/state.cpp index 2049311c597..63d8b79cb43 100644 --- a/src/renderer/gdi/state.cpp +++ b/src/renderer/gdi/state.cpp @@ -488,8 +488,6 @@ void GdiEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) // Retrieve the first color. auto color = it->TextAttr(); - // Retrieve the first pattern id - auto patternIds = pData->GetPatternId(renderData.screenPosition); // And hold the point where we should start drawing. auto screenPoint = renderData.screenPosition; @@ -503,9 +501,6 @@ void GdiEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) // when we go to draw gridlines for the length of the run. const auto currentRunColor = color; - // Hold onto the current pattern id as well - const auto currentPatternId = patternIds; - // Update the drawing brushes with our color. THROW_IF_FAILED(UpdateDrawingBrushes(currentRunColor, pData, false)); @@ -535,16 +530,14 @@ void GdiEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) do { COORD thisPoint{ screenPoint.X + gsl::narrow(cols), screenPoint.Y }; - const auto thisPointPatterns = pData->GetPatternId(thisPoint); - if (color != it->TextAttr() || patternIds != thisPointPatterns) + if (color != it->TextAttr()) { auto newAttr{ it->TextAttr() }; // foreground doesn't matter for runs of spaces (!) // if we trick it . . . we call Paint far fewer times for cmatrix - if (!s_IsAllSpaces(it->Chars()) || !newAttr.HasIdenticalVisualRepresentationForBlankSpace(color, renderData.globalInvert) || patternIds != thisPointPatterns) + if (!s_IsAllSpaces(it->Chars()) || !newAttr.HasIdenticalVisualRepresentationForBlankSpace(color, renderData.globalInvert)) { color = newAttr; - patternIds = thisPointPatterns; break; // vend this run } } From 24ea604e852041210b2529e47b265f3d41e1641c Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Sun, 11 Jul 2021 20:08:43 +0800 Subject: [PATCH 3/9] Fix conhost overlay --- src/renderer/base/RenderEngineBase.cpp | 28 +++++++++++++++++++++----- src/renderer/dx/DxRenderer.cpp | 2 +- src/renderer/gdi/state.cpp | 2 +- src/renderer/inc/RenderEngineBase.hpp | 3 ++- src/renderer/vt/paint.cpp | 2 +- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/renderer/base/RenderEngineBase.cpp b/src/renderer/base/RenderEngineBase.cpp index 6dbcbf4238a..748f142c418 100644 --- a/src/renderer/base/RenderEngineBase.cpp +++ b/src/renderer/base/RenderEngineBase.cpp @@ -184,13 +184,20 @@ void RenderEngineBase::_LoopOverlay(IRenderData* pData, std::function Date: Sun, 11 Jul 2021 20:20:02 +0800 Subject: [PATCH 4/9] Rm _CalculateRenderDataForDirtyLine --- src/renderer/base/RenderEngineBase.cpp | 91 ++++++++++++-------------- src/renderer/inc/RenderEngineBase.hpp | 5 -- 2 files changed, 41 insertions(+), 55 deletions(-) diff --git a/src/renderer/base/RenderEngineBase.cpp b/src/renderer/base/RenderEngineBase.cpp index 748f142c418..badcccacf13 100644 --- a/src/renderer/base/RenderEngineBase.cpp +++ b/src/renderer/base/RenderEngineBase.cpp @@ -110,8 +110,8 @@ void RenderEngineBase::_LoopDirtyLines(IRenderData* pData, std::functionIsScreenReversed(); - auto gridLineDrawingAllowed = pData->IsGridLineDrawingAllowed(); + const auto globalInvert = pData->IsScreenReversed(); + const auto gridLineDrawingAllowed = pData->IsGridLineDrawingAllowed(); for (const auto& dirtyRect : dirtyAreas) { @@ -135,10 +135,41 @@ void RenderEngineBase::_LoopDirtyLines(IRenderData* pData, std::functionGetViewport(); + const auto globalInvert = pData->IsScreenReversed(); + const auto gridLineDrawingAllowed = pData->IsGridLineDrawingAllowed(); // Now get the overlay's viewport and adjust it to where it is supposed to be relative to the window. const auto overlays = pData->GetOverlays(); @@ -196,8 +229,8 @@ void RenderEngineBase::_LoopOverlay(IRenderData* pData, std::function action); void _LoopSelection(IRenderData* pData, std::function action); - BufferLineRenderData _CalculateRenderDataForDirtyLine(const TextBuffer& buffer, - Microsoft::Console::Types::Viewport visible, - Microsoft::Console::Types::Viewport redraw, - SHORT row) const; - GridLines _CalculateGridLines(IRenderData* pData, const TextAttribute textAttribute, const COORD coordTarget); From 25a3d3c1af86c6d6f0a812e5f9ca5c50f43fe1eb Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Sun, 11 Jul 2021 20:51:36 +0800 Subject: [PATCH 5/9] Fix IME overlay in conhost --- src/renderer/base/RenderEngineBase.cpp | 6 ++++-- src/renderer/gdi/state.cpp | 5 ++++- src/renderer/inc/RenderEngineBase.hpp | 9 ++++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/renderer/base/RenderEngineBase.cpp b/src/renderer/base/RenderEngineBase.cpp index badcccacf13..26bc2a6576c 100644 --- a/src/renderer/base/RenderEngineBase.cpp +++ b/src/renderer/base/RenderEngineBase.cpp @@ -163,9 +163,10 @@ void RenderEngineBase::_LoopDirtyLines(IRenderData* pData, std::function Date: Mon, 12 Jul 2021 18:09:58 +0800 Subject: [PATCH 6/9] Implement selection --- src/cascadia/TerminalControl/ControlCore.cpp | 10 ++-- src/host/ScreenBufferRenderTarget.cpp | 20 ++++---- src/host/ScreenBufferRenderTarget.hpp | 3 +- src/host/selection.cpp | 2 +- src/renderer/base/RenderEngineBase.cpp | 53 +++++++++++++++++++- src/renderer/base/renderer.cpp | 36 ++----------- src/renderer/base/renderer.hpp | 5 +- src/renderer/dx/DxRenderer.cpp | 17 ++++++- src/renderer/dx/DxRenderer.hpp | 4 +- src/renderer/gdi/gdirenderer.hpp | 4 +- src/renderer/gdi/invalidate.cpp | 15 ++++++ src/renderer/inc/IRenderEngine.hpp | 8 ++- src/renderer/inc/IRenderTarget.hpp | 3 +- src/renderer/inc/IRenderer.hpp | 3 +- src/renderer/inc/RenderEngineBase.hpp | 7 ++- src/renderer/uia/UiaRenderer.cpp | 13 +++++ src/renderer/uia/UiaRenderer.hpp | 4 +- src/renderer/vt/XtermEngine.cpp | 2 + src/renderer/vt/invalidate.cpp | 21 +++----- src/renderer/vt/vtrenderer.hpp | 3 +- 20 files changed, 156 insertions(+), 77 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index e22fb024755..067adc2d038 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -307,7 +307,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (!modifiers.IsWinPressed()) { _terminal->ClearSelection(); - _renderer->TriggerSelection(); + _renderer->TriggerIntereaction(::Microsoft::Console::Render::IntereactionType::Selection); } if (vkey == VK_ESCAPE) @@ -808,7 +808,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // save location (for rendering) + render _terminal->SetSelectionEnd(terminalPosition); - _renderer->TriggerSelection(); + _renderer->TriggerIntereaction(::Microsoft::Console::Render::IntereactionType::Selection); } // Called when the Terminal wants to set something to the clipboard, i.e. @@ -868,7 +868,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (!_settings.CopyOnSelect()) { _terminal->ClearSelection(); - _renderer->TriggerSelection(); + _renderer->TriggerIntereaction(::Microsoft::Console::Render::IntereactionType::Selection); } // send data up for clipboard @@ -1120,7 +1120,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { _terminal->SetBlockSelection(false); search.Select(); - _renderer->TriggerSelection(); + _renderer->TriggerIntereaction(::Microsoft::Console::Render::IntereactionType::Selection); } } @@ -1310,7 +1310,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation selectionNeedsToBeCopied = true; } - _renderer->TriggerSelection(); + _renderer->TriggerIntereaction(::Microsoft::Console::Render::IntereactionType::Selection); } void ControlCore::AttachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine) diff --git a/src/host/ScreenBufferRenderTarget.cpp b/src/host/ScreenBufferRenderTarget.cpp index 87f98ce29aa..f24652f4000 100644 --- a/src/host/ScreenBufferRenderTarget.cpp +++ b/src/host/ScreenBufferRenderTarget.cpp @@ -11,6 +11,16 @@ ScreenBufferRenderTarget::ScreenBufferRenderTarget(SCREEN_INFORMATION& owner) : { } +void ScreenBufferRenderTarget::TriggerIntereaction(Microsoft::Console::Render::IntereactionType type) +{ + auto* pRenderer = ServiceLocator::LocateGlobals().pRender; + const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); + if (pRenderer != nullptr && pActive == &_owner) + { + pRenderer->TriggerIntereaction(type); + } +} + void ScreenBufferRenderTarget::TriggerRedraw(const Microsoft::Console::Types::Viewport& region) { auto* pRenderer = ServiceLocator::LocateGlobals().pRender; @@ -61,16 +71,6 @@ void ScreenBufferRenderTarget::TriggerTeardown() noexcept } } -void ScreenBufferRenderTarget::TriggerSelection() -{ - auto* pRenderer = ServiceLocator::LocateGlobals().pRender; - const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); - if (pRenderer != nullptr && pActive == &_owner) - { - pRenderer->TriggerSelection(); - } -} - void ScreenBufferRenderTarget::TriggerScroll() { auto* pRenderer = ServiceLocator::LocateGlobals().pRender; diff --git a/src/host/ScreenBufferRenderTarget.hpp b/src/host/ScreenBufferRenderTarget.hpp index 09a189691c7..6544f05b34d 100644 --- a/src/host/ScreenBufferRenderTarget.hpp +++ b/src/host/ScreenBufferRenderTarget.hpp @@ -20,6 +20,7 @@ Author(s): #pragma once #include "../renderer/inc/IRenderTarget.hpp" +#include "../renderer/inc/IRenderEngine.hpp" // fwdecl class SCREEN_INFORMATION; @@ -28,13 +29,13 @@ class ScreenBufferRenderTarget final : public Microsoft::Console::Render::IRende { public: ScreenBufferRenderTarget(SCREEN_INFORMATION& owner); + void TriggerIntereaction(Microsoft::Console::Render::IntereactionType type) override; void TriggerRedraw(const Microsoft::Console::Types::Viewport& region) override; void TriggerRedraw(const COORD* const pcoord) override; void TriggerRedrawCursor(const COORD* const pcoord) override; void TriggerRedrawAll() override; void TriggerTeardown() noexcept override; - void TriggerSelection() override; void TriggerScroll() override; void TriggerScroll(const COORD* const pcoordDelta) override; void TriggerCircling() override; diff --git a/src/host/selection.cpp b/src/host/selection.cpp index 7814bfae9c9..6da19706cde 100644 --- a/src/host/selection.cpp +++ b/src/host/selection.cpp @@ -120,7 +120,7 @@ void Selection::_PaintSelection() const { if (ServiceLocator::LocateGlobals().pRender != nullptr) { - ServiceLocator::LocateGlobals().pRender->TriggerSelection(); + ServiceLocator::LocateGlobals().pRender->TriggerIntereaction(Microsoft::Console::Render::IntereactionType::Selection); } } diff --git a/src/renderer/base/RenderEngineBase.cpp b/src/renderer/base/RenderEngineBase.cpp index 26bc2a6576c..c94f2c98e39 100644 --- a/src/renderer/base/RenderEngineBase.cpp +++ b/src/renderer/base/RenderEngineBase.cpp @@ -347,7 +347,7 @@ IRenderEngine::GridLines RenderEngineBase::_CalculateGridLines(IRenderData* pDat // - Helper to determine the selected region of the buffer. // Return Value: // - A vector of rectangles representing the regions to select, line by line. - std::vector RenderEngineBase::_GetSelectionRects(IRenderData* pData) const + std::vector RenderEngineBase::_GetSelectionRects(IRenderData* pData) noexcept { const auto& buffer = pData->GetTextBuffer(); auto rects = pData->GetSelectionRects(); @@ -374,3 +374,54 @@ IRenderEngine::GridLines RenderEngineBase::_CalculateGridLines(IRenderData* pDat return result; } + +std::vector RenderEngineBase::_CalculateCurrentSelection(IRenderData* pData) noexcept + { + // Get selection rectangles + const auto rects = _GetSelectionRects(pData); + + // Restrict all previous selection rectangles to inside the current viewport bounds + for (auto& sr : _previousSelection) + { + // Make the exclusive SMALL_RECT into a til::rectangle. + til::rectangle rc{ Viewport::FromExclusive(sr).ToInclusive() }; + + // Make a viewport representing the coordinates that are currently presentable. + const til::rectangle viewport{ til::size{ pData->GetViewport().Dimensions() } }; + + // Intersect them so we only invalidate things that are still visible. + rc &= viewport; + + // Convert back into the exclusive SMALL_RECT and store in the vector. + sr = Viewport::FromInclusive(rc).ToExclusive(); + } + + return rects; + } + +// Method Description: + // - Offsets all of the selection rectangles we might be holding onto + // as the previously selected area. If the whole viewport scrolls, + // we need to scroll these areas also to ensure they're invalidated + // properly when the selection further changes. + // Arguments: + // - delta - The scroll delta + // Return Value: + // - - Updates internal state instead. + void RenderEngineBase::_ScrollPreviousSelection(const til::point delta) + { + if (delta != til::point{ 0, 0 }) + { + for (auto& sr : _previousSelection) + { + // Get a rectangle representing this piece of the selection. + til::rectangle rc = Viewport::FromExclusive(sr).ToInclusive(); + + // Offset the entire existing rectangle by the delta. + rc += delta; + + // Store it back into the vector. + sr = Viewport::FromInclusive(rc).ToExclusive(); + } + } + } diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 8224f8d549f..449f4ba60aa 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -319,10 +319,14 @@ void Renderer::TriggerTeardown() noexcept // - // Return Value: // - -void Renderer::TriggerSelection() +void Renderer::TriggerIntereaction(IntereactionType type) { try { + std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) { + LOG_IF_FAILED(pEngine->InvalidateIntereaction(_pData, type)); + }); + _NotifyPaintFrame(); } CATCH_LOG(); @@ -363,8 +367,6 @@ bool Renderer::_CheckViewportAndScroll() LOG_IF_FAILED(engine->InvalidateScroll(&coordDelta)); } - _ScrollPreviousSelection(coordDelta); - return true; } @@ -401,8 +403,6 @@ void Renderer::TriggerScroll(const COORD* const pcoordDelta) LOG_IF_FAILED(pEngine->InvalidateScroll(pcoordDelta)); }); - _ScrollPreviousSelection(*pcoordDelta); - _NotifyPaintFrame(); } @@ -621,32 +621,6 @@ void Renderer::WaitForPaintCompletionAndDisable(const DWORD dwTimeoutMs) return std::nullopt; } -// Method Description: -// - Offsets all of the selection rectangles we might be holding onto -// as the previously selected area. If the whole viewport scrolls, -// we need to scroll these areas also to ensure they're invalidated -// properly when the selection further changes. -// Arguments: -// - delta - The scroll delta -// Return Value: -// - - Updates internal state instead. -void Renderer::_ScrollPreviousSelection(const til::point delta) -{ - if (delta != til::point{ 0, 0 }) - { - for (auto& sr : _previousSelection) - { - // Get a rectangle representing this piece of the selection. - til::rectangle rc = Viewport::FromExclusive(sr).ToInclusive(); - - // Offset the entire existing rectangle by the delta. - rc += delta; - - // Store it back into the vector. - sr = Viewport::FromInclusive(rc).ToExclusive(); - } - } -} // Method Description: // - Adds another Render engine to this renderer. Future rendering calls will diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index 9de23f71d8b..f50bfe7cd1e 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -46,6 +46,7 @@ namespace Microsoft::Console::Render virtual ~Renderer() override; [[nodiscard]] HRESULT PaintFrame(); + void TriggerIntereaction(IntereactionType type) override; void TriggerSystemRedraw(const RECT* const prcDirtyClient) override; void TriggerRedraw(const Microsoft::Console::Types::Viewport& region) override; @@ -54,7 +55,6 @@ namespace Microsoft::Console::Render void TriggerRedrawAll() override; void TriggerTeardown() noexcept override; - void TriggerSelection() override; void TriggerScroll() override; void TriggerScroll(const COORD* const pcoordDelta) override; @@ -99,9 +99,6 @@ namespace Microsoft::Console::Render static constexpr float _shrinkThreshold = 0.8f; std::vector _clusterBuffer; - void _ScrollPreviousSelection(const til::point delta); - std::vector _previousSelection; - [[nodiscard]] std::optional _GetCursorInfo(); // Helper functions to diagnose issues with painting and layout. diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 3230a6655cc..72c1295c906 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -1063,7 +1063,20 @@ bool DxEngine::_IsAllInvalid() const noexcept return std::llabs(_invalidScroll.y()) >= _invalidMap.size().height(); } -// Routine Description: +[[nodiscard]] HRESULT DxEngine::InvalidateIntereaction(IRenderData* pData, IntereactionType type) noexcept +{ + if (type == IntereactionType::Selection) + { + const auto rects = _CalculateCurrentSelection(pData); + LOG_IF_FAILED(InvalidateSelection(_previousSelection)); + LOG_IF_FAILED(InvalidateSelection(rects)); + _previousSelection = rects; + } + + return S_OK; +} + + // Routine Description: // - Invalidates a rectangle described in characters // Arguments: // - psrRegion - Character rectangle @@ -1160,6 +1173,8 @@ try } } + _ScrollPreviousSelection(*pcoordDelta); + return S_OK; } CATCH_RETURN(); diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index 251edfb2d14..03a67e230de 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -73,10 +73,12 @@ namespace Microsoft::Console::Render HANDLE GetSwapChainHandle(); // IRenderEngine Members + [[nodiscard]] HRESULT InvalidateIntereaction(IRenderData* pData, IntereactionType type) noexcept override; + [[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept override; - [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept override; + [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept; [[nodiscard]] HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept override; [[nodiscard]] HRESULT InvalidateAll() noexcept override; [[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept override; diff --git a/src/renderer/gdi/gdirenderer.hpp b/src/renderer/gdi/gdirenderer.hpp index 3368c07514c..2098ece5bdc 100644 --- a/src/renderer/gdi/gdirenderer.hpp +++ b/src/renderer/gdi/gdirenderer.hpp @@ -26,7 +26,9 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT SetHwnd(const HWND hwnd) noexcept; - [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept override; + [[nodiscard]] HRESULT InvalidateIntereaction(IRenderData* pData, IntereactionType type) noexcept override; + + [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept; [[nodiscard]] HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept override; [[nodiscard]] HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept override; [[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override; diff --git a/src/renderer/gdi/invalidate.cpp b/src/renderer/gdi/invalidate.cpp index ca77b03cd12..37ec65b4e95 100644 --- a/src/renderer/gdi/invalidate.cpp +++ b/src/renderer/gdi/invalidate.cpp @@ -45,6 +45,21 @@ HRESULT GdiEngine::InvalidateScroll(const COORD* const pcoordDelta) noexcept _szInvalidScroll = szInvalidScrollNew; } + _ScrollPreviousSelection(*pcoordDelta); + + return S_OK; +} + +[[nodiscard]] HRESULT GdiEngine::InvalidateIntereaction(IRenderData* pData, IntereactionType type) noexcept +{ + if (type == IntereactionType::Selection) + { + const auto rects = _CalculateCurrentSelection(pData); + LOG_IF_FAILED(InvalidateSelection(_previousSelection)); + LOG_IF_FAILED(InvalidateSelection(rects)); + _previousSelection = rects; + } + return S_OK; } diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index 2d5e3ab9f8b..c0cf0cc2c76 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -27,6 +27,11 @@ namespace Microsoft::Console::Render std::optional cursorInfo; }; + enum class IntereactionType + { + Selection + }; + class IRenderEngine { public: @@ -64,10 +69,11 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept = 0; + [[nodiscard]] virtual HRESULT InvalidateIntereaction(IRenderData* pData, IntereactionType type) noexcept = 0; + [[nodiscard]] virtual HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept = 0; - [[nodiscard]] virtual HRESULT InvalidateSelection(const std::vector& rectangles) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateAll() noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept = 0; diff --git a/src/renderer/inc/IRenderTarget.hpp b/src/renderer/inc/IRenderTarget.hpp index f12f243e1a3..1322d5497db 100644 --- a/src/renderer/inc/IRenderTarget.hpp +++ b/src/renderer/inc/IRenderTarget.hpp @@ -33,6 +33,8 @@ namespace Microsoft::Console::Render IRenderTarget& operator=(IRenderTarget&&) = default; public: + virtual void TriggerIntereaction(IntereactionType type) = 0; + virtual void TriggerRedraw(const Microsoft::Console::Types::Viewport& region) = 0; virtual void TriggerRedraw(const COORD* const pcoord) = 0; virtual void TriggerRedrawCursor(const COORD* const pcoord) = 0; @@ -40,7 +42,6 @@ namespace Microsoft::Console::Render virtual void TriggerRedrawAll() = 0; virtual void TriggerTeardown() noexcept = 0; - virtual void TriggerSelection() = 0; virtual void TriggerScroll() = 0; virtual void TriggerScroll(const COORD* const pcoordDelta) = 0; virtual void TriggerCircling() = 0; diff --git a/src/renderer/inc/IRenderer.hpp b/src/renderer/inc/IRenderer.hpp index 79058c5ae5a..e9e2531ad55 100644 --- a/src/renderer/inc/IRenderer.hpp +++ b/src/renderer/inc/IRenderer.hpp @@ -32,6 +32,8 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT PaintFrame() = 0; + virtual void TriggerIntereaction(IntereactionType type) = 0; + virtual void TriggerSystemRedraw(const RECT* const prcDirtyClient) = 0; virtual void TriggerRedraw(const Microsoft::Console::Types::Viewport& region) = 0; @@ -41,7 +43,6 @@ namespace Microsoft::Console::Render virtual void TriggerRedrawAll() = 0; virtual void TriggerTeardown() noexcept = 0; - virtual void TriggerSelection() = 0; virtual void TriggerScroll() = 0; virtual void TriggerScroll(const COORD* const pcoordDelta) = 0; virtual void TriggerCircling() = 0; diff --git a/src/renderer/inc/RenderEngineBase.hpp b/src/renderer/inc/RenderEngineBase.hpp index e76c6a16e89..f5442539e17 100644 --- a/src/renderer/inc/RenderEngineBase.hpp +++ b/src/renderer/inc/RenderEngineBase.hpp @@ -65,7 +65,10 @@ namespace Microsoft::Console::Render static GridLines s_GetGridlines(const TextAttribute& textAttribute) noexcept; - std::vector _GetSelectionRects(IRenderData* pData) const; + std::vector _GetSelectionRects(IRenderData* pData) noexcept; + std::vector _CalculateCurrentSelection(IRenderData* pData) noexcept; + + void _ScrollPreviousSelection(const til::point delta); static bool s_IsAllSpaces(const std::wstring_view v) { @@ -91,6 +94,8 @@ namespace Microsoft::Console::Render std::vector _clusterBuffer; std::optional::interval> _hoveredInterval; + + std::vector _previousSelection; }; inline Microsoft::Console::Render::RenderEngineBase::~RenderEngineBase() {} diff --git a/src/renderer/uia/UiaRenderer.cpp b/src/renderer/uia/UiaRenderer.cpp index 20ef3bb401c..6a19a1876e7 100644 --- a/src/renderer/uia/UiaRenderer.cpp +++ b/src/renderer/uia/UiaRenderer.cpp @@ -50,6 +50,19 @@ UiaEngine::UiaEngine(IUiaEventDispatcher* dispatcher) : return S_OK; } +[[nodiscard]] HRESULT UiaEngine::InvalidateIntereaction(IRenderData* pData, IntereactionType type) noexcept +{ + if (type == IntereactionType::Selection) + { + const auto rects = _CalculateCurrentSelection(pData); + LOG_IF_FAILED(InvalidateSelection(_previousSelection)); + LOG_IF_FAILED(InvalidateSelection(rects)); + _previousSelection = rects; + } + + return S_OK; +} + // Routine Description: // - Notifies us that the console has changed the character region specified. // - NOTE: This typically triggers on cursor or text buffer changes diff --git a/src/renderer/uia/UiaRenderer.hpp b/src/renderer/uia/UiaRenderer.hpp index 86436baf8c0..9a8a49617d6 100644 --- a/src/renderer/uia/UiaRenderer.hpp +++ b/src/renderer/uia/UiaRenderer.hpp @@ -41,10 +41,12 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept override; + [[nodiscard]] HRESULT InvalidateIntereaction(IRenderData* pData, IntereactionType type) noexcept override; + [[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept override; - [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept override; + [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept; [[nodiscard]] HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept override; [[nodiscard]] HRESULT InvalidateAll() noexcept override; [[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept override; diff --git a/src/renderer/vt/XtermEngine.cpp b/src/renderer/vt/XtermEngine.cpp index a948acd827e..5bf32e1c206 100644 --- a/src/renderer/vt/XtermEngine.cpp +++ b/src/renderer/vt/XtermEngine.cpp @@ -480,6 +480,8 @@ try _scrollDelta += delta; } + _ScrollPreviousSelection(*pcoordDelta); + return S_OK; } CATCH_RETURN(); diff --git a/src/renderer/vt/invalidate.cpp b/src/renderer/vt/invalidate.cpp index 1856ac4cdd4..2e56d668113 100644 --- a/src/renderer/vt/invalidate.cpp +++ b/src/renderer/vt/invalidate.cpp @@ -10,6 +10,12 @@ using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Render; + +[[nodiscard]] HRESULT VtEngine::InvalidateIntereaction(IRenderData* /*pData*/, IntereactionType /*type*/) noexcept +{ + return S_OK; +} + // Routine Description: // - Notifies us that the system has requested a particular pixel area of the // client rectangle should be redrawn. (On WM_PAINT) @@ -24,21 +30,6 @@ using namespace Microsoft::Console::Render; return S_OK; } -// Routine Description: -// - Notifies us that the console has changed the selection region and would -// like it updated -// Arguments: -// - rectangles - Vector of rectangles to draw, line by line -// Return Value: -// - S_OK -[[nodiscard]] HRESULT VtEngine::InvalidateSelection(const std::vector& /*rectangles*/) noexcept -{ - // Selection shouldn't be handled bt the VT Renderer Host, it should be - // handled by the client. - - return S_OK; -} - // Routine Description: // - Notifies us that the console has changed the character region specified. // - NOTE: This typically triggers on cursor or text buffer changes diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index ce9b439f3b4..cee4261a303 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -45,7 +45,8 @@ namespace Microsoft::Console::Render virtual ~VtEngine() override = default; - [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept override; + [[nodiscard]] HRESULT InvalidateIntereaction(IRenderData* pData, IntereactionType type) noexcept override; + [[nodiscard]] virtual HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept = 0; [[nodiscard]] HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept override; [[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override; From 643d7883430aff6bbdef2135bd69a1162f525230 Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Tue, 13 Jul 2021 19:43:58 +0800 Subject: [PATCH 7/9] Revert "Implement selection" This reverts commit 84f0128e6d1392fba3aa254216cfccb7638155a0. --- src/cascadia/TerminalControl/ControlCore.cpp | 10 ++-- src/host/ScreenBufferRenderTarget.cpp | 20 ++++---- src/host/ScreenBufferRenderTarget.hpp | 3 +- src/host/selection.cpp | 2 +- src/renderer/base/RenderEngineBase.cpp | 53 +------------------- src/renderer/base/renderer.cpp | 36 +++++++++++-- src/renderer/base/renderer.hpp | 5 +- src/renderer/dx/DxRenderer.cpp | 17 +------ src/renderer/dx/DxRenderer.hpp | 4 +- src/renderer/gdi/gdirenderer.hpp | 4 +- src/renderer/gdi/invalidate.cpp | 15 ------ src/renderer/inc/IRenderEngine.hpp | 8 +-- src/renderer/inc/IRenderTarget.hpp | 3 +- src/renderer/inc/IRenderer.hpp | 3 +- src/renderer/inc/RenderEngineBase.hpp | 7 +-- src/renderer/uia/UiaRenderer.cpp | 13 ----- src/renderer/uia/UiaRenderer.hpp | 4 +- src/renderer/vt/XtermEngine.cpp | 2 - src/renderer/vt/invalidate.cpp | 21 +++++--- src/renderer/vt/vtrenderer.hpp | 3 +- 20 files changed, 77 insertions(+), 156 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 067adc2d038..e22fb024755 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -307,7 +307,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (!modifiers.IsWinPressed()) { _terminal->ClearSelection(); - _renderer->TriggerIntereaction(::Microsoft::Console::Render::IntereactionType::Selection); + _renderer->TriggerSelection(); } if (vkey == VK_ESCAPE) @@ -808,7 +808,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // save location (for rendering) + render _terminal->SetSelectionEnd(terminalPosition); - _renderer->TriggerIntereaction(::Microsoft::Console::Render::IntereactionType::Selection); + _renderer->TriggerSelection(); } // Called when the Terminal wants to set something to the clipboard, i.e. @@ -868,7 +868,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (!_settings.CopyOnSelect()) { _terminal->ClearSelection(); - _renderer->TriggerIntereaction(::Microsoft::Console::Render::IntereactionType::Selection); + _renderer->TriggerSelection(); } // send data up for clipboard @@ -1120,7 +1120,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { _terminal->SetBlockSelection(false); search.Select(); - _renderer->TriggerIntereaction(::Microsoft::Console::Render::IntereactionType::Selection); + _renderer->TriggerSelection(); } } @@ -1310,7 +1310,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation selectionNeedsToBeCopied = true; } - _renderer->TriggerIntereaction(::Microsoft::Console::Render::IntereactionType::Selection); + _renderer->TriggerSelection(); } void ControlCore::AttachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine) diff --git a/src/host/ScreenBufferRenderTarget.cpp b/src/host/ScreenBufferRenderTarget.cpp index f24652f4000..87f98ce29aa 100644 --- a/src/host/ScreenBufferRenderTarget.cpp +++ b/src/host/ScreenBufferRenderTarget.cpp @@ -11,16 +11,6 @@ ScreenBufferRenderTarget::ScreenBufferRenderTarget(SCREEN_INFORMATION& owner) : { } -void ScreenBufferRenderTarget::TriggerIntereaction(Microsoft::Console::Render::IntereactionType type) -{ - auto* pRenderer = ServiceLocator::LocateGlobals().pRender; - const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); - if (pRenderer != nullptr && pActive == &_owner) - { - pRenderer->TriggerIntereaction(type); - } -} - void ScreenBufferRenderTarget::TriggerRedraw(const Microsoft::Console::Types::Viewport& region) { auto* pRenderer = ServiceLocator::LocateGlobals().pRender; @@ -71,6 +61,16 @@ void ScreenBufferRenderTarget::TriggerTeardown() noexcept } } +void ScreenBufferRenderTarget::TriggerSelection() +{ + auto* pRenderer = ServiceLocator::LocateGlobals().pRender; + const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); + if (pRenderer != nullptr && pActive == &_owner) + { + pRenderer->TriggerSelection(); + } +} + void ScreenBufferRenderTarget::TriggerScroll() { auto* pRenderer = ServiceLocator::LocateGlobals().pRender; diff --git a/src/host/ScreenBufferRenderTarget.hpp b/src/host/ScreenBufferRenderTarget.hpp index 6544f05b34d..09a189691c7 100644 --- a/src/host/ScreenBufferRenderTarget.hpp +++ b/src/host/ScreenBufferRenderTarget.hpp @@ -20,7 +20,6 @@ Author(s): #pragma once #include "../renderer/inc/IRenderTarget.hpp" -#include "../renderer/inc/IRenderEngine.hpp" // fwdecl class SCREEN_INFORMATION; @@ -29,13 +28,13 @@ class ScreenBufferRenderTarget final : public Microsoft::Console::Render::IRende { public: ScreenBufferRenderTarget(SCREEN_INFORMATION& owner); - void TriggerIntereaction(Microsoft::Console::Render::IntereactionType type) override; void TriggerRedraw(const Microsoft::Console::Types::Viewport& region) override; void TriggerRedraw(const COORD* const pcoord) override; void TriggerRedrawCursor(const COORD* const pcoord) override; void TriggerRedrawAll() override; void TriggerTeardown() noexcept override; + void TriggerSelection() override; void TriggerScroll() override; void TriggerScroll(const COORD* const pcoordDelta) override; void TriggerCircling() override; diff --git a/src/host/selection.cpp b/src/host/selection.cpp index 6da19706cde..7814bfae9c9 100644 --- a/src/host/selection.cpp +++ b/src/host/selection.cpp @@ -120,7 +120,7 @@ void Selection::_PaintSelection() const { if (ServiceLocator::LocateGlobals().pRender != nullptr) { - ServiceLocator::LocateGlobals().pRender->TriggerIntereaction(Microsoft::Console::Render::IntereactionType::Selection); + ServiceLocator::LocateGlobals().pRender->TriggerSelection(); } } diff --git a/src/renderer/base/RenderEngineBase.cpp b/src/renderer/base/RenderEngineBase.cpp index c94f2c98e39..26bc2a6576c 100644 --- a/src/renderer/base/RenderEngineBase.cpp +++ b/src/renderer/base/RenderEngineBase.cpp @@ -347,7 +347,7 @@ IRenderEngine::GridLines RenderEngineBase::_CalculateGridLines(IRenderData* pDat // - Helper to determine the selected region of the buffer. // Return Value: // - A vector of rectangles representing the regions to select, line by line. - std::vector RenderEngineBase::_GetSelectionRects(IRenderData* pData) noexcept + std::vector RenderEngineBase::_GetSelectionRects(IRenderData* pData) const { const auto& buffer = pData->GetTextBuffer(); auto rects = pData->GetSelectionRects(); @@ -374,54 +374,3 @@ IRenderEngine::GridLines RenderEngineBase::_CalculateGridLines(IRenderData* pDat return result; } - -std::vector RenderEngineBase::_CalculateCurrentSelection(IRenderData* pData) noexcept - { - // Get selection rectangles - const auto rects = _GetSelectionRects(pData); - - // Restrict all previous selection rectangles to inside the current viewport bounds - for (auto& sr : _previousSelection) - { - // Make the exclusive SMALL_RECT into a til::rectangle. - til::rectangle rc{ Viewport::FromExclusive(sr).ToInclusive() }; - - // Make a viewport representing the coordinates that are currently presentable. - const til::rectangle viewport{ til::size{ pData->GetViewport().Dimensions() } }; - - // Intersect them so we only invalidate things that are still visible. - rc &= viewport; - - // Convert back into the exclusive SMALL_RECT and store in the vector. - sr = Viewport::FromInclusive(rc).ToExclusive(); - } - - return rects; - } - -// Method Description: - // - Offsets all of the selection rectangles we might be holding onto - // as the previously selected area. If the whole viewport scrolls, - // we need to scroll these areas also to ensure they're invalidated - // properly when the selection further changes. - // Arguments: - // - delta - The scroll delta - // Return Value: - // - - Updates internal state instead. - void RenderEngineBase::_ScrollPreviousSelection(const til::point delta) - { - if (delta != til::point{ 0, 0 }) - { - for (auto& sr : _previousSelection) - { - // Get a rectangle representing this piece of the selection. - til::rectangle rc = Viewport::FromExclusive(sr).ToInclusive(); - - // Offset the entire existing rectangle by the delta. - rc += delta; - - // Store it back into the vector. - sr = Viewport::FromInclusive(rc).ToExclusive(); - } - } - } diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 449f4ba60aa..8224f8d549f 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -319,14 +319,10 @@ void Renderer::TriggerTeardown() noexcept // - // Return Value: // - -void Renderer::TriggerIntereaction(IntereactionType type) +void Renderer::TriggerSelection() { try { - std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) { - LOG_IF_FAILED(pEngine->InvalidateIntereaction(_pData, type)); - }); - _NotifyPaintFrame(); } CATCH_LOG(); @@ -367,6 +363,8 @@ bool Renderer::_CheckViewportAndScroll() LOG_IF_FAILED(engine->InvalidateScroll(&coordDelta)); } + _ScrollPreviousSelection(coordDelta); + return true; } @@ -403,6 +401,8 @@ void Renderer::TriggerScroll(const COORD* const pcoordDelta) LOG_IF_FAILED(pEngine->InvalidateScroll(pcoordDelta)); }); + _ScrollPreviousSelection(*pcoordDelta); + _NotifyPaintFrame(); } @@ -621,6 +621,32 @@ void Renderer::WaitForPaintCompletionAndDisable(const DWORD dwTimeoutMs) return std::nullopt; } +// Method Description: +// - Offsets all of the selection rectangles we might be holding onto +// as the previously selected area. If the whole viewport scrolls, +// we need to scroll these areas also to ensure they're invalidated +// properly when the selection further changes. +// Arguments: +// - delta - The scroll delta +// Return Value: +// - - Updates internal state instead. +void Renderer::_ScrollPreviousSelection(const til::point delta) +{ + if (delta != til::point{ 0, 0 }) + { + for (auto& sr : _previousSelection) + { + // Get a rectangle representing this piece of the selection. + til::rectangle rc = Viewport::FromExclusive(sr).ToInclusive(); + + // Offset the entire existing rectangle by the delta. + rc += delta; + + // Store it back into the vector. + sr = Viewport::FromInclusive(rc).ToExclusive(); + } + } +} // Method Description: // - Adds another Render engine to this renderer. Future rendering calls will diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index f50bfe7cd1e..9de23f71d8b 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -46,7 +46,6 @@ namespace Microsoft::Console::Render virtual ~Renderer() override; [[nodiscard]] HRESULT PaintFrame(); - void TriggerIntereaction(IntereactionType type) override; void TriggerSystemRedraw(const RECT* const prcDirtyClient) override; void TriggerRedraw(const Microsoft::Console::Types::Viewport& region) override; @@ -55,6 +54,7 @@ namespace Microsoft::Console::Render void TriggerRedrawAll() override; void TriggerTeardown() noexcept override; + void TriggerSelection() override; void TriggerScroll() override; void TriggerScroll(const COORD* const pcoordDelta) override; @@ -99,6 +99,9 @@ namespace Microsoft::Console::Render static constexpr float _shrinkThreshold = 0.8f; std::vector _clusterBuffer; + void _ScrollPreviousSelection(const til::point delta); + std::vector _previousSelection; + [[nodiscard]] std::optional _GetCursorInfo(); // Helper functions to diagnose issues with painting and layout. diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 72c1295c906..3230a6655cc 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -1063,20 +1063,7 @@ bool DxEngine::_IsAllInvalid() const noexcept return std::llabs(_invalidScroll.y()) >= _invalidMap.size().height(); } -[[nodiscard]] HRESULT DxEngine::InvalidateIntereaction(IRenderData* pData, IntereactionType type) noexcept -{ - if (type == IntereactionType::Selection) - { - const auto rects = _CalculateCurrentSelection(pData); - LOG_IF_FAILED(InvalidateSelection(_previousSelection)); - LOG_IF_FAILED(InvalidateSelection(rects)); - _previousSelection = rects; - } - - return S_OK; -} - - // Routine Description: +// Routine Description: // - Invalidates a rectangle described in characters // Arguments: // - psrRegion - Character rectangle @@ -1173,8 +1160,6 @@ try } } - _ScrollPreviousSelection(*pcoordDelta); - return S_OK; } CATCH_RETURN(); diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index 03a67e230de..251edfb2d14 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -73,12 +73,10 @@ namespace Microsoft::Console::Render HANDLE GetSwapChainHandle(); // IRenderEngine Members - [[nodiscard]] HRESULT InvalidateIntereaction(IRenderData* pData, IntereactionType type) noexcept override; - [[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept override; - [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept; + [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept override; [[nodiscard]] HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept override; [[nodiscard]] HRESULT InvalidateAll() noexcept override; [[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept override; diff --git a/src/renderer/gdi/gdirenderer.hpp b/src/renderer/gdi/gdirenderer.hpp index 2098ece5bdc..3368c07514c 100644 --- a/src/renderer/gdi/gdirenderer.hpp +++ b/src/renderer/gdi/gdirenderer.hpp @@ -26,9 +26,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT SetHwnd(const HWND hwnd) noexcept; - [[nodiscard]] HRESULT InvalidateIntereaction(IRenderData* pData, IntereactionType type) noexcept override; - - [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept; + [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept override; [[nodiscard]] HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept override; [[nodiscard]] HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept override; [[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override; diff --git a/src/renderer/gdi/invalidate.cpp b/src/renderer/gdi/invalidate.cpp index 37ec65b4e95..ca77b03cd12 100644 --- a/src/renderer/gdi/invalidate.cpp +++ b/src/renderer/gdi/invalidate.cpp @@ -45,21 +45,6 @@ HRESULT GdiEngine::InvalidateScroll(const COORD* const pcoordDelta) noexcept _szInvalidScroll = szInvalidScrollNew; } - _ScrollPreviousSelection(*pcoordDelta); - - return S_OK; -} - -[[nodiscard]] HRESULT GdiEngine::InvalidateIntereaction(IRenderData* pData, IntereactionType type) noexcept -{ - if (type == IntereactionType::Selection) - { - const auto rects = _CalculateCurrentSelection(pData); - LOG_IF_FAILED(InvalidateSelection(_previousSelection)); - LOG_IF_FAILED(InvalidateSelection(rects)); - _previousSelection = rects; - } - return S_OK; } diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index c0cf0cc2c76..2d5e3ab9f8b 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -27,11 +27,6 @@ namespace Microsoft::Console::Render std::optional cursorInfo; }; - enum class IntereactionType - { - Selection - }; - class IRenderEngine { public: @@ -69,11 +64,10 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept = 0; - [[nodiscard]] virtual HRESULT InvalidateIntereaction(IRenderData* pData, IntereactionType type) noexcept = 0; - [[nodiscard]] virtual HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept = 0; + [[nodiscard]] virtual HRESULT InvalidateSelection(const std::vector& rectangles) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateAll() noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept = 0; diff --git a/src/renderer/inc/IRenderTarget.hpp b/src/renderer/inc/IRenderTarget.hpp index 1322d5497db..f12f243e1a3 100644 --- a/src/renderer/inc/IRenderTarget.hpp +++ b/src/renderer/inc/IRenderTarget.hpp @@ -33,8 +33,6 @@ namespace Microsoft::Console::Render IRenderTarget& operator=(IRenderTarget&&) = default; public: - virtual void TriggerIntereaction(IntereactionType type) = 0; - virtual void TriggerRedraw(const Microsoft::Console::Types::Viewport& region) = 0; virtual void TriggerRedraw(const COORD* const pcoord) = 0; virtual void TriggerRedrawCursor(const COORD* const pcoord) = 0; @@ -42,6 +40,7 @@ namespace Microsoft::Console::Render virtual void TriggerRedrawAll() = 0; virtual void TriggerTeardown() noexcept = 0; + virtual void TriggerSelection() = 0; virtual void TriggerScroll() = 0; virtual void TriggerScroll(const COORD* const pcoordDelta) = 0; virtual void TriggerCircling() = 0; diff --git a/src/renderer/inc/IRenderer.hpp b/src/renderer/inc/IRenderer.hpp index e9e2531ad55..79058c5ae5a 100644 --- a/src/renderer/inc/IRenderer.hpp +++ b/src/renderer/inc/IRenderer.hpp @@ -32,8 +32,6 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT PaintFrame() = 0; - virtual void TriggerIntereaction(IntereactionType type) = 0; - virtual void TriggerSystemRedraw(const RECT* const prcDirtyClient) = 0; virtual void TriggerRedraw(const Microsoft::Console::Types::Viewport& region) = 0; @@ -43,6 +41,7 @@ namespace Microsoft::Console::Render virtual void TriggerRedrawAll() = 0; virtual void TriggerTeardown() noexcept = 0; + virtual void TriggerSelection() = 0; virtual void TriggerScroll() = 0; virtual void TriggerScroll(const COORD* const pcoordDelta) = 0; virtual void TriggerCircling() = 0; diff --git a/src/renderer/inc/RenderEngineBase.hpp b/src/renderer/inc/RenderEngineBase.hpp index f5442539e17..e76c6a16e89 100644 --- a/src/renderer/inc/RenderEngineBase.hpp +++ b/src/renderer/inc/RenderEngineBase.hpp @@ -65,10 +65,7 @@ namespace Microsoft::Console::Render static GridLines s_GetGridlines(const TextAttribute& textAttribute) noexcept; - std::vector _GetSelectionRects(IRenderData* pData) noexcept; - std::vector _CalculateCurrentSelection(IRenderData* pData) noexcept; - - void _ScrollPreviousSelection(const til::point delta); + std::vector _GetSelectionRects(IRenderData* pData) const; static bool s_IsAllSpaces(const std::wstring_view v) { @@ -94,8 +91,6 @@ namespace Microsoft::Console::Render std::vector _clusterBuffer; std::optional::interval> _hoveredInterval; - - std::vector _previousSelection; }; inline Microsoft::Console::Render::RenderEngineBase::~RenderEngineBase() {} diff --git a/src/renderer/uia/UiaRenderer.cpp b/src/renderer/uia/UiaRenderer.cpp index 6a19a1876e7..20ef3bb401c 100644 --- a/src/renderer/uia/UiaRenderer.cpp +++ b/src/renderer/uia/UiaRenderer.cpp @@ -50,19 +50,6 @@ UiaEngine::UiaEngine(IUiaEventDispatcher* dispatcher) : return S_OK; } -[[nodiscard]] HRESULT UiaEngine::InvalidateIntereaction(IRenderData* pData, IntereactionType type) noexcept -{ - if (type == IntereactionType::Selection) - { - const auto rects = _CalculateCurrentSelection(pData); - LOG_IF_FAILED(InvalidateSelection(_previousSelection)); - LOG_IF_FAILED(InvalidateSelection(rects)); - _previousSelection = rects; - } - - return S_OK; -} - // Routine Description: // - Notifies us that the console has changed the character region specified. // - NOTE: This typically triggers on cursor or text buffer changes diff --git a/src/renderer/uia/UiaRenderer.hpp b/src/renderer/uia/UiaRenderer.hpp index 9a8a49617d6..86436baf8c0 100644 --- a/src/renderer/uia/UiaRenderer.hpp +++ b/src/renderer/uia/UiaRenderer.hpp @@ -41,12 +41,10 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept override; - [[nodiscard]] HRESULT InvalidateIntereaction(IRenderData* pData, IntereactionType type) noexcept override; - [[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept override; - [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept; + [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept override; [[nodiscard]] HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept override; [[nodiscard]] HRESULT InvalidateAll() noexcept override; [[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept override; diff --git a/src/renderer/vt/XtermEngine.cpp b/src/renderer/vt/XtermEngine.cpp index 5bf32e1c206..a948acd827e 100644 --- a/src/renderer/vt/XtermEngine.cpp +++ b/src/renderer/vt/XtermEngine.cpp @@ -480,8 +480,6 @@ try _scrollDelta += delta; } - _ScrollPreviousSelection(*pcoordDelta); - return S_OK; } CATCH_RETURN(); diff --git a/src/renderer/vt/invalidate.cpp b/src/renderer/vt/invalidate.cpp index 2e56d668113..1856ac4cdd4 100644 --- a/src/renderer/vt/invalidate.cpp +++ b/src/renderer/vt/invalidate.cpp @@ -10,12 +10,6 @@ using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Render; - -[[nodiscard]] HRESULT VtEngine::InvalidateIntereaction(IRenderData* /*pData*/, IntereactionType /*type*/) noexcept -{ - return S_OK; -} - // Routine Description: // - Notifies us that the system has requested a particular pixel area of the // client rectangle should be redrawn. (On WM_PAINT) @@ -30,6 +24,21 @@ using namespace Microsoft::Console::Render; return S_OK; } +// Routine Description: +// - Notifies us that the console has changed the selection region and would +// like it updated +// Arguments: +// - rectangles - Vector of rectangles to draw, line by line +// Return Value: +// - S_OK +[[nodiscard]] HRESULT VtEngine::InvalidateSelection(const std::vector& /*rectangles*/) noexcept +{ + // Selection shouldn't be handled bt the VT Renderer Host, it should be + // handled by the client. + + return S_OK; +} + // Routine Description: // - Notifies us that the console has changed the character region specified. // - NOTE: This typically triggers on cursor or text buffer changes diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index cee4261a303..ce9b439f3b4 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -45,8 +45,7 @@ namespace Microsoft::Console::Render virtual ~VtEngine() override = default; - [[nodiscard]] HRESULT InvalidateIntereaction(IRenderData* pData, IntereactionType type) noexcept override; - + [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept override; [[nodiscard]] virtual HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept = 0; [[nodiscard]] HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept override; [[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override; From f71d2851e782b18de6663c602633fbf9977be553 Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Wed, 14 Jul 2021 19:29:47 +0800 Subject: [PATCH 8/9] Another try --- src/host/ut_host/VtRendererTests.cpp | 36 +++--- src/renderer/base/RenderEngineBase.cpp | 118 +++++++++++++++++- src/renderer/base/renderer.cpp | 159 +++---------------------- src/renderer/base/renderer.hpp | 6 - src/renderer/dx/DxRenderer.cpp | 67 +++++++---- src/renderer/dx/DxRenderer.hpp | 7 +- src/renderer/gdi/gdirenderer.hpp | 7 +- src/renderer/gdi/invalidate.cpp | 15 ++- src/renderer/gdi/paint.cpp | 2 +- src/renderer/gdi/state.cpp | 26 ++-- src/renderer/inc/IRenderEngine.hpp | 9 +- src/renderer/inc/RenderEngineBase.hpp | 21 +++- src/renderer/uia/UiaRenderer.cpp | 37 ++---- src/renderer/uia/UiaRenderer.hpp | 5 +- src/renderer/vt/XtermEngine.cpp | 39 +++--- src/renderer/vt/XtermEngine.hpp | 4 +- src/renderer/vt/invalidate.cpp | 17 +-- src/renderer/vt/paint.cpp | 10 +- src/renderer/vt/state.cpp | 26 ++-- src/renderer/vt/vtrenderer.hpp | 7 +- 20 files changed, 307 insertions(+), 311 deletions(-) diff --git a/src/host/ut_host/VtRendererTests.cpp b/src/host/ut_host/VtRendererTests.cpp index b1785380587..d99997b224d 100644 --- a/src/host/ut_host/VtRendererTests.cpp +++ b/src/host/ut_host/VtRendererTests.cpp @@ -240,7 +240,7 @@ void VtRendererTest::Xterm256TestInvalidate() Log::Comment(NoThrowString().Format( L"Make sure that scrolling only invalidates part of the viewport, and sends the right sequences")); COORD scrollDelta = { 0, 1 }; - VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta)); + VERIFY_SUCCEEDED(engine->TriggerScroll(&scrollDelta)); TestPaint(*engine, [&]() { Log::Comment(NoThrowString().Format( L"---- Scrolled one down, only top line is invalid. ----")); @@ -257,7 +257,7 @@ void VtRendererTest::Xterm256TestInvalidate() }); scrollDelta = { 0, 3 }; - VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta)); + VERIFY_SUCCEEDED(engine->TriggerScroll(&scrollDelta)); TestPaint(*engine, [&]() { Log::Comment(NoThrowString().Format( @@ -282,7 +282,7 @@ void VtRendererTest::Xterm256TestInvalidate() }); scrollDelta = { 0, -1 }; - VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta)); + VERIFY_SUCCEEDED(engine->TriggerScroll(&scrollDelta)); TestPaint(*engine, [&]() { Log::Comment(NoThrowString().Format( L"---- Scrolled one up, only bottom line is invalid. ----")); @@ -299,7 +299,7 @@ void VtRendererTest::Xterm256TestInvalidate() }); scrollDelta = { 0, -3 }; - VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta)); + VERIFY_SUCCEEDED(engine->TriggerScroll(&scrollDelta)); TestPaint(*engine, [&]() { Log::Comment(NoThrowString().Format( L"---- Scrolled three up, only bottom 3 lines are invalid. ----")); @@ -327,9 +327,9 @@ void VtRendererTest::Xterm256TestInvalidate() L"Multiple scrolls are coalesced")); scrollDelta = { 0, 1 }; - VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta)); + VERIFY_SUCCEEDED(engine->TriggerScroll(&scrollDelta)); scrollDelta = { 0, 2 }; - VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta)); + VERIFY_SUCCEEDED(engine->TriggerScroll(&scrollDelta)); TestPaint(*engine, [&]() { Log::Comment(NoThrowString().Format( L"---- Scrolled three down, only top 3 lines are invalid. ----")); @@ -354,11 +354,11 @@ void VtRendererTest::Xterm256TestInvalidate() }); scrollDelta = { 0, 1 }; - VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta)); + VERIFY_SUCCEEDED(engine->TriggerScroll(&scrollDelta)); Log::Comment(engine->_invalidMap.to_string().c_str()); scrollDelta = { 0, -1 }; - VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta)); + VERIFY_SUCCEEDED(engine->TriggerScroll(&scrollDelta)); Log::Comment(engine->_invalidMap.to_string().c_str()); qExpectedInput.push_back("\x1b[2J"); @@ -903,7 +903,7 @@ void VtRendererTest::XtermTestInvalidate() Log::Comment(NoThrowString().Format( L"Make sure that scrolling only invalidates part of the viewport, and sends the right sequences")); COORD scrollDelta = { 0, 1 }; - VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta)); + VERIFY_SUCCEEDED(engine->TriggerScroll(&scrollDelta)); TestPaint(*engine, [&]() { Log::Comment(NoThrowString().Format( L"---- Scrolled one down, only top line is invalid. ----")); @@ -920,7 +920,7 @@ void VtRendererTest::XtermTestInvalidate() }); scrollDelta = { 0, 3 }; - VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta)); + VERIFY_SUCCEEDED(engine->TriggerScroll(&scrollDelta)); TestPaint(*engine, [&]() { Log::Comment(NoThrowString().Format( L"---- Scrolled three down, only top 3 lines are invalid. ----")); @@ -944,7 +944,7 @@ void VtRendererTest::XtermTestInvalidate() }); scrollDelta = { 0, -1 }; - VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta)); + VERIFY_SUCCEEDED(engine->TriggerScroll(&scrollDelta)); TestPaint(*engine, [&]() { Log::Comment(NoThrowString().Format( L"---- Scrolled one up, only bottom line is invalid. ----")); @@ -961,7 +961,7 @@ void VtRendererTest::XtermTestInvalidate() }); scrollDelta = { 0, -3 }; - VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta)); + VERIFY_SUCCEEDED(engine->TriggerScroll(&scrollDelta)); TestPaint(*engine, [&]() { Log::Comment(NoThrowString().Format( L"---- Scrolled three up, only bottom 3 lines are invalid. ----")); @@ -989,9 +989,9 @@ void VtRendererTest::XtermTestInvalidate() L"Multiple scrolls are coalesced")); scrollDelta = { 0, 1 }; - VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta)); + VERIFY_SUCCEEDED(engine->TriggerScroll(&scrollDelta)); scrollDelta = { 0, 2 }; - VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta)); + VERIFY_SUCCEEDED(engine->TriggerScroll(&scrollDelta)); TestPaint(*engine, [&]() { Log::Comment(NoThrowString().Format( L"---- Scrolled three down, only top 3 lines are invalid. ----")); @@ -1016,11 +1016,11 @@ void VtRendererTest::XtermTestInvalidate() }); scrollDelta = { 0, 1 }; - VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta)); + VERIFY_SUCCEEDED(engine->TriggerScroll(&scrollDelta)); Log::Comment(engine->_invalidMap.to_string().c_str()); scrollDelta = { 0, -1 }; - VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta)); + VERIFY_SUCCEEDED(engine->TriggerScroll(&scrollDelta)); Log::Comment(engine->_invalidMap.to_string().c_str()); qExpectedInput.push_back("\x1b[2J"); @@ -1417,7 +1417,7 @@ void VtRendererTest::TestResize() // The renderer (in Renderer@_PaintFrameForEngine..._CheckViewportAndScroll) // will manually call UpdateViewport once before actually painting the // first frame. Replicate that behavior here - VERIFY_SUCCEEDED(engine->UpdateViewport(view.ToInclusive())); + engine->UpdateViewport(view.ToInclusive()); TestPaint(*engine, [&]() { VERIFY_IS_FALSE(engine->_firstPaint); @@ -1429,7 +1429,7 @@ void VtRendererTest::TestResize() const auto newView = Viewport::FromDimensions({ 0, 0 }, { 120, 30 }); qExpectedInput.push_back("\x1b[8;30;120t"); - VERIFY_SUCCEEDED(engine->UpdateViewport(newView.ToInclusive())); + engine->UpdateViewport(newView.ToInclusive()); TestPaint(*engine, [&]() { VERIFY_IS_TRUE(engine->_invalidMap.all()); diff --git a/src/renderer/base/RenderEngineBase.cpp b/src/renderer/base/RenderEngineBase.cpp index 26bc2a6576c..67a12ae925e 100644 --- a/src/renderer/base/RenderEngineBase.cpp +++ b/src/renderer/base/RenderEngineBase.cpp @@ -16,6 +16,13 @@ RenderEngineBase::RenderEngineBase() : { } +RenderEngineBase::RenderEngineBase(const Viewport initialViewport) : + _viewport(initialViewport), + _titleChanged(false), + _lastFrameTitle(L"") +{ +} + HRESULT RenderEngineBase::InvalidateTitle(const std::wstring_view proposedTitle) noexcept { if (proposedTitle != _lastFrameTitle) @@ -26,6 +33,53 @@ HRESULT RenderEngineBase::InvalidateTitle(const std::wstring_view proposedTitle) return S_OK; } +COORD RenderEngineBase::UpdateViewport(IRenderData* pData) noexcept +{ + SMALL_RECT const srOldViewport = _viewport.ToInclusive(); + SMALL_RECT const srNewViewport = pData->GetViewport().ToInclusive(); + + COORD coordDelta; + coordDelta.X = srOldViewport.Left - srNewViewport.Left; + coordDelta.Y = srOldViewport.Top - srNewViewport.Top; + + _viewport = Viewport::FromInclusive(srNewViewport); + + // If we're keeping some buffers between calls, let them know about the viewport size + // so they can prepare the buffers for changes to either preallocate memory at once + // (instead of growing naturally) or shrink down to reduce usage as appropriate. + const size_t lineLength = gsl::narrow_cast(til::rectangle{ srNewViewport }.width()); + til::manage_vector(_clusterBuffer, lineLength, _shrinkThreshold); + + return coordDelta; +} + +bool RenderEngineBase::TriggerRedraw(IRenderData* pData, const Viewport& region) noexcept +{ + Viewport view = _viewport; + SMALL_RECT srUpdateRegion = region.ToExclusive(); + + // If the dirty region has double width lines, we need to double the size of + // the right margin to make sure all the affected cells are invalidated. + const auto& buffer = pData->GetTextBuffer(); + for (auto row = srUpdateRegion.Top; row < srUpdateRegion.Bottom; row++) + { + if (buffer.IsDoubleWidthLine(row)) + { + srUpdateRegion.Right *= 2; + break; + } + } + + if (view.TrimToViewport(&srUpdateRegion)) + { + view.ConvertToOrigin(&srUpdateRegion); + LOG_IF_FAILED(Invalidate(&srUpdateRegion)); + return true; + } + + return false; +} + // Method Description: // - By default, no one should need continuous redraw. It ruins performance // in terms of CPU, memory, and battery life to just paint forever. @@ -43,6 +97,16 @@ void RenderEngineBase::WaitUntilCanRender() noexcept // do nothing by default } +// Routine Description: +// - Retrieve information about the cursor, and pack it into a CursorOptions +// which the render engine can use for painting the cursor. +// - If the cursor is "off", or the cursor is out of bounds of the viewport, +// this will return nullopt (indicating the cursor shouldn't be painted this +// frame) +// Arguments: +// - +// Return Value: +// - nullopt if the cursor is off or out-of-frame, otherwise a CursorOptions [[nodiscard]] std::optional RenderEngineBase::_GetCursorInfo(IRenderData* pData) { if (pData->IsCursorVisible()) @@ -347,7 +411,7 @@ IRenderEngine::GridLines RenderEngineBase::_CalculateGridLines(IRenderData* pDat // - Helper to determine the selected region of the buffer. // Return Value: // - A vector of rectangles representing the regions to select, line by line. - std::vector RenderEngineBase::_GetSelectionRects(IRenderData* pData) const + std::vector RenderEngineBase::_GetSelectionRects(IRenderData* pData) noexcept { const auto& buffer = pData->GetTextBuffer(); auto rects = pData->GetSelectionRects(); @@ -374,3 +438,55 @@ IRenderEngine::GridLines RenderEngineBase::_CalculateGridLines(IRenderData* pDat return result; } + + +std::vector RenderEngineBase::_CalculateCurrentSelection(IRenderData* pData) noexcept + { + // Get selection rectangles + const auto rects = _GetSelectionRects(pData); + + // Restrict all previous selection rectangles to inside the current viewport bounds + for (auto& sr : _previousSelection) + { + // Make the exclusive SMALL_RECT into a til::rectangle. + til::rectangle rc{ Viewport::FromExclusive(sr).ToInclusive() }; + + // Make a viewport representing the coordinates that are currently presentable. + const til::rectangle viewport{ til::size{ pData->GetViewport().Dimensions() } }; + + // Intersect them so we only invalidate things that are still visible. + rc &= viewport; + + // Convert back into the exclusive SMALL_RECT and store in the vector. + sr = Viewport::FromInclusive(rc).ToExclusive(); + } + + return rects; + } + + // Method Description: + // - Offsets all of the selection rectangles we might be holding onto + // as the previously selected area. If the whole viewport scrolls, + // we need to scroll these areas also to ensure they're invalidated + // properly when the selection further changes. + // Arguments: + // - delta - The scroll delta + // Return Value: + // - - Updates internal state instead. + void RenderEngineBase::_ScrollPreviousSelection(const til::point delta) + { + if (delta != til::point{ 0, 0 }) + { + for (auto& sr : _previousSelection) + { + // Get a rectangle representing this piece of the selection. + til::rectangle rc = Viewport::FromExclusive(sr).ToInclusive(); + + // Offset the entire existing rectangle by the delta. + rc += delta; + + // Store it back into the vector. + sr = Viewport::FromInclusive(rc).ToExclusive(); + } + } + } diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 8224f8d549f..cee3c89bb4e 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -31,8 +31,7 @@ Renderer::Renderer(IRenderData* pData, _pData(THROW_HR_IF_NULL(E_INVALIDARG, pData)), _pThread{ std::move(thread) }, _destructing{ false }, - _clusterBuffer{}, - _viewport{ pData->GetViewport() } + _clusterBuffer{} { for (size_t i = 0; i < cEngines; i++) { @@ -192,30 +191,13 @@ void Renderer::TriggerSystemRedraw(const RECT* const prcDirtyClient) // - void Renderer::TriggerRedraw(const Viewport& region) { - Viewport view = _viewport; - SMALL_RECT srUpdateRegion = region.ToExclusive(); - - // If the dirty region has double width lines, we need to double the size of - // the right margin to make sure all the affected cells are invalidated. - const auto& buffer = _pData->GetTextBuffer(); - for (auto row = srUpdateRegion.Top; row < srUpdateRegion.Bottom; row++) - { - if (buffer.IsDoubleWidthLine(row)) + std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) { + const bool needRedraw = pEngine->TriggerRedraw(_pData, region); + if (needRedraw) { - srUpdateRegion.Right *= 2; - break; + _NotifyPaintFrame(); } - } - - if (view.TrimToViewport(&srUpdateRegion)) - { - view.ConvertToOrigin(&srUpdateRegion); - std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) { - LOG_IF_FAILED(pEngine->Invalidate(&srUpdateRegion)); - }); - - _NotifyPaintFrame(); - } + }); } // Routine Description: @@ -323,6 +305,10 @@ void Renderer::TriggerSelection() { try { + std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) { + LOG_IF_FAILED(pEngine->TriggerSelection(_pData)); + }); + _NotifyPaintFrame(); } CATCH_LOG(); @@ -336,36 +322,13 @@ void Renderer::TriggerSelection() // - True if something changed and we scrolled. False otherwise. bool Renderer::_CheckViewportAndScroll() { - SMALL_RECT const srOldViewport = _viewport.ToInclusive(); - SMALL_RECT const srNewViewport = _pData->GetViewport().ToInclusive(); - - COORD coordDelta; - coordDelta.X = srOldViewport.Left - srNewViewport.Left; - coordDelta.Y = srOldViewport.Top - srNewViewport.Top; - for (auto engine : _rgpEngines) { - LOG_IF_FAILED(engine->UpdateViewport(srNewViewport)); - } - - _viewport = Viewport::FromInclusive(srNewViewport); - - // If we're keeping some buffers between calls, let them know about the viewport size - // so they can prepare the buffers for changes to either preallocate memory at once - // (instead of growing naturally) or shrink down to reduce usage as appropriate. - const size_t lineLength = gsl::narrow_cast(til::rectangle{ srNewViewport }.width()); - til::manage_vector(_clusterBuffer, lineLength, _shrinkThreshold); - - if (coordDelta.X != 0 || coordDelta.Y != 0) - { - for (auto engine : _rgpEngines) + COORD coordDelta = engine->UpdateViewport(_pData); + if (coordDelta.X != 0 || coordDelta.Y != 0) { - LOG_IF_FAILED(engine->InvalidateScroll(&coordDelta)); + LOG_IF_FAILED(engine->TriggerScroll(&coordDelta)); } - - _ScrollPreviousSelection(coordDelta); - - return true; } return false; @@ -398,11 +361,9 @@ void Renderer::TriggerScroll() void Renderer::TriggerScroll(const COORD* const pcoordDelta) { std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) { - LOG_IF_FAILED(pEngine->InvalidateScroll(pcoordDelta)); + LOG_IF_FAILED(pEngine->TriggerScroll(pcoordDelta)); }); - _ScrollPreviousSelection(*pcoordDelta); - _NotifyPaintFrame(); } @@ -557,97 +518,6 @@ void Renderer::WaitForPaintCompletionAndDisable(const DWORD dwTimeoutMs) _pThread->WaitForPaintCompletionAndDisable(dwTimeoutMs); } -// Routine Description: -// - Retrieve information about the cursor, and pack it into a CursorOptions -// which the render engine can use for painting the cursor. -// - If the cursor is "off", or the cursor is out of bounds of the viewport, -// this will return nullopt (indicating the cursor shouldn't be painted this -// frame) -// Arguments: -// - -// Return Value: -// - nullopt if the cursor is off or out-of-frame, otherwise a CursorOptions -[[nodiscard]] std::optional Renderer::_GetCursorInfo() -{ - if (_pData->IsCursorVisible()) - { - // Get cursor position in buffer - COORD coordCursor = _pData->GetCursorPosition(); - - // GH#3166: Only draw the cursor if it's actually in the viewport. It - // might be on the line that's in that partially visible row at the - // bottom of the viewport, the space that's not quite a full line in - // height. Since we don't draw that text, we shouldn't draw the cursor - // there either. - - // The cursor is never rendered as double height, so we don't care about - // the exact line rendition - only whether it's double width or not. - const auto doubleWidth = _pData->GetTextBuffer().IsDoubleWidthLine(coordCursor.Y); - const auto lineRendition = doubleWidth ? LineRendition::DoubleWidth : LineRendition::SingleWidth; - - // We need to convert the screen coordinates of the viewport to an - // equivalent range of buffer cells, taking line rendition into account. - const auto view = ScreenToBufferLine(_pData->GetViewport().ToInclusive(), lineRendition); - - // Note that we allow the X coordinate to be outside the left border by 1 position, - // because the cursor could still be visible if the focused character is double width. - const auto xInRange = coordCursor.X >= view.Left - 1 && coordCursor.X <= view.Right; - const auto yInRange = coordCursor.Y >= view.Top && coordCursor.Y <= view.Bottom; - if (xInRange && yInRange) - { - // Adjust cursor Y offset to viewport. - // The viewport X offset is saved in the options and handled with a transform. - coordCursor.Y -= view.Top; - - COLORREF cursorColor = _pData->GetCursorColor(); - bool useColor = cursorColor != INVALID_COLOR; - - // Build up the cursor parameters including position, color, and drawing options - CursorOptions options; - options.coordCursor = coordCursor; - options.viewportLeft = _pData->GetViewport().Left(); - options.lineRendition = lineRendition; - options.ulCursorHeightPercent = _pData->GetCursorHeight(); - options.cursorPixelWidth = _pData->GetCursorPixelWidth(); - options.fIsDoubleWidth = _pData->IsCursorDoubleWidth(); - options.cursorType = _pData->GetCursorStyle(); - options.fUseColor = useColor; - options.cursorColor = cursorColor; - options.isOn = _pData->IsCursorOn(); - - return { options }; - } - } - return std::nullopt; -} - -// Method Description: -// - Offsets all of the selection rectangles we might be holding onto -// as the previously selected area. If the whole viewport scrolls, -// we need to scroll these areas also to ensure they're invalidated -// properly when the selection further changes. -// Arguments: -// - delta - The scroll delta -// Return Value: -// - - Updates internal state instead. -void Renderer::_ScrollPreviousSelection(const til::point delta) -{ - if (delta != til::point{ 0, 0 }) - { - for (auto& sr : _previousSelection) - { - // Get a rectangle representing this piece of the selection. - til::rectangle rc = Viewport::FromExclusive(sr).ToInclusive(); - - // Offset the entire existing rectangle by the delta. - rc += delta; - - // Store it back into the vector. - sr = Viewport::FromInclusive(rc).ToExclusive(); - } - } -} - // Method Description: // - Adds another Render engine to this renderer. Future rendering calls will // also be sent to the new renderer. @@ -660,6 +530,7 @@ void Renderer::_ScrollPreviousSelection(const til::point delta) void Renderer::AddRenderEngine(_In_ IRenderEngine* const pEngine) { THROW_HR_IF_NULL(E_INVALIDARG, pEngine); + pEngine->UpdateViewport(_pData); _rgpEngines.push_back(pEngine); } diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index 9de23f71d8b..6f6cdae664f 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -94,16 +94,10 @@ namespace Microsoft::Console::Render bool _CheckViewportAndScroll(); - Microsoft::Console::Types::Viewport _viewport; - - static constexpr float _shrinkThreshold = 0.8f; std::vector _clusterBuffer; - void _ScrollPreviousSelection(const til::point delta); std::vector _previousSelection; - [[nodiscard]] std::optional _GetCursorInfo(); - // Helper functions to diagnose issues with painting and layout. // These are only actually effective/on in Debug builds when the flag is set using an attached debugger. bool _fDebug = false; diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 3230a6655cc..a52402c70f9 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -1122,17 +1122,28 @@ CATCH_RETURN(); // - rectangles - One or more rectangles describing character positions on the grid // Return Value: // - S_OK -[[nodiscard]] HRESULT DxEngine::InvalidateSelection(const std::vector& rectangles) noexcept +[[nodiscard]] HRESULT DxEngine::TriggerSelection(IRenderData* pData) noexcept +try { + const auto rects = _CalculateCurrentSelection(pData); if (!_allInvalid) { - for (const auto& rect : rectangles) + for (const auto& rect : _previousSelection) + { + RETURN_IF_FAILED(Invalidate(&rect)); + } + + for (const auto& rect : rects) { RETURN_IF_FAILED(Invalidate(&rect)); } } + + _previousSelection = rects; + return S_OK; } +CATCH_RETURN(); // Routine Description: // - Scrolls the existing dirty region (if it exists) and @@ -1142,7 +1153,7 @@ CATCH_RETURN(); // - -Y is up, Y is down, -X is left, X is right. // Return Value: // - S_OK -[[nodiscard]] HRESULT DxEngine::InvalidateScroll(const COORD* const pcoordDelta) noexcept +[[nodiscard]] HRESULT DxEngine::TriggerScroll(const COORD* const pcoordDelta) noexcept try { RETURN_HR_IF(E_INVALIDARG, !pcoordDelta); @@ -1160,6 +1171,8 @@ try } } + _ScrollPreviousSelection(*pcoordDelta); + return S_OK; } CATCH_RETURN(); @@ -1360,7 +1373,13 @@ CATCH_RETURN() // Return Value: // - Any DirectX error, a memory error, etc. [[nodiscard]] HRESULT DxEngine::PaintFrame(IRenderData *pData) noexcept +try { + if (pData == nullptr) + { + return S_OK; + } + // Prep Colors RETURN_IF_FAILED(UpdateDrawingBrushes(pData->GetDefaultBrushColors(), pData, true)); @@ -1382,10 +1401,12 @@ CATCH_RETURN() _LoopSelection(pData, std::bind(&DxEngine::PaintSelection, this, std::placeholders::_1)); // Paint window title - RETURN_IF_FAILED(_DoUpdateTitle()); + RETURN_IF_FAILED(_UpdateTitle(pData->GetConsoleTitle())); return S_OK; } +CATCH_RETURN(); + // Routine Description: // - Ends batch drawing and captures any state necessary for presentation // Arguments: @@ -2055,18 +2076,6 @@ float DxEngine::GetScaling() const noexcept return _scale; } -// Method Description: -// - This method will update our internal reference for how big the viewport is. -// Does nothing for DX. -// Arguments: -// - srNewViewport - The bounds of the new viewport. -// Return Value: -// - HRESULT S_OK -[[nodiscard]] HRESULT DxEngine::UpdateViewport(const SMALL_RECT /*srNewViewport*/) noexcept -{ - return S_OK; -} - // Routine Description: // - Currently unused by this renderer // Arguments: @@ -2143,14 +2152,24 @@ CATCH_RETURN(); // - newTitle: the new string to use for the title of the window // Return Value: // - S_OK -[[nodiscard]] HRESULT DxEngine::_DoUpdateTitle() noexcept +[[nodiscard]] HRESULT DxEngine::_UpdateTitle(const std::wstring_view newTitle) noexcept +try { - if (_hwndTarget != INVALID_HANDLE_VALUE) + HRESULT hr = S_FALSE; + if (newTitle != _lastFrameTitle) { - return PostMessageW(_hwndTarget, CM_UPDATE_TITLE, 0, 0) ? S_OK : E_FAIL; + if (_hwndTarget != INVALID_HANDLE_VALUE) + { + return PostMessageW(_hwndTarget, CM_UPDATE_TITLE, 0, 0) ? S_OK : E_FAIL; + } + + _lastFrameTitle = newTitle; + _titleChanged = false; + hr = S_OK; } - return S_FALSE; + return hr; } +CATCH_RETURN(); void DxEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) { @@ -2222,7 +2241,7 @@ void DxEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) const auto thisPointPatterns = pData->GetPatternId(thisPoint); if (color != it->TextAttr() || patternIds != thisPointPatterns) { - auto newAttr{ it->TextAttr() }; + const auto newAttr{ it->TextAttr() }; // foreground doesn't matter for runs of spaces (!) // if we trick it . . . we call Paint far fewer times for cmatrix if (!s_IsAllSpaces(it->Chars()) || !newAttr.HasIdenticalVisualRepresentationForBlankSpace(color, renderData.globalInvert) || patternIds != thisPointPatterns) @@ -2293,8 +2312,8 @@ void DxEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) // Do that in the future if some WPR trace points you to this spot as super bad. for (auto colsPainted = 0u; colsPainted < cols; ++colsPainted, ++lineIt, ++lineTarget.X) { - auto lines = lineIt->TextAttr(); - auto gridLines = _CalculateGridLines(pData, lines, lineTarget); + const auto lines = lineIt->TextAttr(); + const auto gridLines = _CalculateGridLines(pData, lines, lineTarget); // Return early if there are no lines to paint. if (gridLines != IRenderEngine::GridLines::None) { @@ -2308,7 +2327,7 @@ void DxEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) else { // If nothing exciting is going on, draw the lines in bulk. - auto gridLines = _CalculateGridLines(pData, currentRunColor, screenPoint); + const auto gridLines = _CalculateGridLines(pData, currentRunColor, screenPoint); // Return early if there are no lines to paint. if (gridLines != IRenderEngine::GridLines::None) { diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index 251edfb2d14..bdc024678f9 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -76,8 +76,8 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept override; - [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept override; - [[nodiscard]] HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept override; + [[nodiscard]] HRESULT TriggerSelection(IRenderData* pData) noexcept override; + [[nodiscard]] HRESULT TriggerScroll(const COORD* const pcoordDelta) noexcept override; [[nodiscard]] HRESULT InvalidateAll() noexcept override; [[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept override; [[nodiscard]] HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept override; @@ -107,7 +107,6 @@ namespace Microsoft::Console::Render const bool isSettingDefaultBrushes) noexcept; [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo) noexcept override; [[nodiscard]] HRESULT UpdateDpi(int const iDpi) noexcept override; - [[nodiscard]] HRESULT UpdateViewport(const SMALL_RECT srNewViewport) noexcept override; [[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override; @@ -128,8 +127,8 @@ namespace Microsoft::Console::Render void UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept; protected: - [[nodiscard]] HRESULT _DoUpdateTitle() noexcept; void _PaintBufferLineHelper(const BufferLineRenderData& renderData); + [[nodiscard]] HRESULT _UpdateTitle(const std::wstring_view newTitle) noexcept; [[nodiscard]] HRESULT _PaintTerminalEffects() noexcept; [[nodiscard]] bool _FullRepaintNeeded() const noexcept; diff --git a/src/renderer/gdi/gdirenderer.hpp b/src/renderer/gdi/gdirenderer.hpp index 3368c07514c..c58de087baa 100644 --- a/src/renderer/gdi/gdirenderer.hpp +++ b/src/renderer/gdi/gdirenderer.hpp @@ -26,8 +26,8 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT SetHwnd(const HWND hwnd) noexcept; - [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept override; - [[nodiscard]] HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept override; + [[nodiscard]] HRESULT TriggerSelection(IRenderData* pData) noexcept override; + [[nodiscard]] HRESULT TriggerScroll(const COORD* const pcoordDelta) noexcept override; [[nodiscard]] HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept override; [[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept override; @@ -66,7 +66,6 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override; [[nodiscard]] HRESULT UpdateDpi(const int iDpi) noexcept override; - [[nodiscard]] HRESULT UpdateViewport(const SMALL_RECT srNewViewport) noexcept override; [[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& FontDesired, _Out_ FontInfo& Font, @@ -79,7 +78,7 @@ namespace Microsoft::Console::Render protected: void GdiEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData); - [[nodiscard]] HRESULT _DoUpdateTitle() noexcept; + [[nodiscard]] HRESULT _UpdateTitle(const std::wstring_view newTitle) noexcept; private: HWND _hwndTargetWindow; diff --git a/src/renderer/gdi/invalidate.cpp b/src/renderer/gdi/invalidate.cpp index ca77b03cd12..67dbfa4bc62 100644 --- a/src/renderer/gdi/invalidate.cpp +++ b/src/renderer/gdi/invalidate.cpp @@ -28,7 +28,7 @@ HRESULT GdiEngine::InvalidateSystem(const RECT* const prcDirtyClient) noexcept // - pcoordDelta - Pointer to character dimension (COORD) of the distance the console would like us to move while scrolling. // Return Value: // - HRESULT S_OK, GDI-based error code, or safemath error -HRESULT GdiEngine::InvalidateScroll(const COORD* const pcoordDelta) noexcept +HRESULT GdiEngine::TriggerScroll(const COORD* const pcoordDelta) noexcept { if (pcoordDelta->X != 0 || pcoordDelta->Y != 0) { @@ -45,6 +45,8 @@ HRESULT GdiEngine::InvalidateScroll(const COORD* const pcoordDelta) noexcept _szInvalidScroll = szInvalidScrollNew; } + _ScrollPreviousSelection(*pcoordDelta); + return S_OK; } @@ -54,13 +56,22 @@ HRESULT GdiEngine::InvalidateScroll(const COORD* const pcoordDelta) noexcept // - rectangles - Vector of rectangles to draw, line by line // Return Value: // - HRESULT S_OK or GDI-based error code -HRESULT GdiEngine::InvalidateSelection(const std::vector& rectangles) noexcept +HRESULT GdiEngine::TriggerSelection(IRenderData* pData) noexcept { + const auto rectangles = _CalculateCurrentSelection(pData); + + for (const auto& rect : _previousSelection) + { + RETURN_IF_FAILED(Invalidate(&rect)); + } + for (const auto& rect : rectangles) { RETURN_IF_FAILED(Invalidate(&rect)); } + _previousSelection = rectangles; + return S_OK; } diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index 9178cab90b4..f5582b6d228 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -90,7 +90,7 @@ using namespace Microsoft::Console::Render; } // Paint window title - RETURN_IF_FAILED(_DoUpdateTitle()); + RETURN_IF_FAILED(_UpdateTitle(pData->GetConsoleTitle())); return S_OK; } diff --git a/src/renderer/gdi/state.cpp b/src/renderer/gdi/state.cpp index 14835f38178..4e05a9f7e60 100644 --- a/src/renderer/gdi/state.cpp +++ b/src/renderer/gdi/state.cpp @@ -437,18 +437,6 @@ GdiEngine::~GdiEngine() return S_OK; } -// Method Description: -// - This method will update our internal reference for how big the viewport is. -// Does nothing for GDI. -// Arguments: -// - srNewViewport - The bounds of the new viewport. -// Return Value: -// - HRESULT S_OK -[[nodiscard]] HRESULT GdiEngine::UpdateViewport(const SMALL_RECT /*srNewViewport*/) noexcept -{ - return S_OK; -} - // Routine Description: // - This method will figure out what the new font should be given the starting font information and a DPI. // - When the final font is determined, the FontInfo structure given will be updated with the actual resulting font chosen as the nearest match. @@ -639,10 +627,18 @@ void GdiEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) // title must be updated on the main window's windowproc thread. // Return Value: // - S_OK if PostMessageW succeeded, otherwise E_FAIL -[[nodiscard]] HRESULT GdiEngine::_DoUpdateTitle() noexcept +[[nodiscard]] HRESULT GdiEngine::_UpdateTitle(const std::wstring_view newTitle) noexcept { - // the CM_UPDATE_TITLE handler in windowproc will query the updated title. - return PostMessageW(_hwndTargetWindow, CM_UPDATE_TITLE, 0, (LPARAM) nullptr) ? S_OK : E_FAIL; + HRESULT hr = S_FALSE; + if (newTitle != _lastFrameTitle) + { + // the CM_UPDATE_TITLE handler in windowproc will query the updated title. + hr = PostMessageW(_hwndTargetWindow, CM_UPDATE_TITLE, 0, (LPARAM) nullptr) ? S_OK : E_FAIL; + _lastFrameTitle = newTitle; + _titleChanged = false; + hr = S_OK; + } + return hr; } // Routine Description: diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index 2d5e3ab9f8b..5accde94224 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -19,6 +19,7 @@ Author(s): #include "FontInfoDesired.hpp" #include "IRenderData.hpp" #include "../../buffer/out/LineRendition.hpp" +#include "../../types/inc/viewport.hpp" namespace Microsoft::Console::Render { @@ -64,11 +65,11 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept = 0; - [[nodiscard]] virtual HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept = 0; + [[nodiscard]] virtual bool TriggerRedraw(IRenderData* pData, const Microsoft::Console::Types::Viewport& region) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept = 0; - [[nodiscard]] virtual HRESULT InvalidateSelection(const std::vector& rectangles) noexcept = 0; - [[nodiscard]] virtual HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept = 0; + [[nodiscard]] virtual HRESULT TriggerSelection(IRenderData* pData) noexcept = 0; + [[nodiscard]] virtual HRESULT TriggerScroll(const COORD* const pcoordDelta) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateAll() noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept = 0; @@ -77,7 +78,7 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept = 0; [[nodiscard]] virtual HRESULT UpdateDpi(const int iDpi) noexcept = 0; - [[nodiscard]] virtual HRESULT UpdateViewport(const SMALL_RECT srNewViewport) noexcept = 0; + virtual COORD UpdateViewport(IRenderData* pData) noexcept = 0; [[nodiscard]] virtual HRESULT GetProposedFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo, diff --git a/src/renderer/inc/RenderEngineBase.hpp b/src/renderer/inc/RenderEngineBase.hpp index e76c6a16e89..e788b2174f4 100644 --- a/src/renderer/inc/RenderEngineBase.hpp +++ b/src/renderer/inc/RenderEngineBase.hpp @@ -49,6 +49,7 @@ namespace Microsoft::Console::Render protected: RenderEngineBase(); + RenderEngineBase(const Microsoft::Console::Types::Viewport initialViewport); RenderEngineBase(const RenderEngineBase&) = default; RenderEngineBase(RenderEngineBase&&) = default; RenderEngineBase& operator=(const RenderEngineBase&) = default; @@ -65,9 +66,12 @@ namespace Microsoft::Console::Render static GridLines s_GetGridlines(const TextAttribute& textAttribute) noexcept; - std::vector _GetSelectionRects(IRenderData* pData) const; + std::vector _GetSelectionRects(IRenderData* pData) noexcept; + std::vector _CalculateCurrentSelection(IRenderData* pData) noexcept; - static bool s_IsAllSpaces(const std::wstring_view v) + void _ScrollPreviousSelection(const til::point delta); + + static constexpr bool s_IsAllSpaces(const std::wstring_view v) noexcept { // first non-space char is not found (is npos) return v.find_first_not_of(L" ") == decltype(v)::npos; @@ -75,12 +79,18 @@ namespace Microsoft::Console::Render public: [[nodiscard]] HRESULT InvalidateTitle(const std::wstring_view proposedTitle) noexcept override; + COORD UpdateViewport(IRenderData* pData) noexcept override; + [[nodiscard]] bool TriggerRedraw(IRenderData* pData, const Microsoft::Console::Types::Viewport& region) noexcept override; + [[nodiscard]] HRESULT TriggerSelection(IRenderData* /*pData*/) noexcept override { return S_OK; }; + [[nodiscard]] HRESULT TriggerScroll(const COORD* const /*pcoordDelta*/) noexcept override { return S_OK; }; [[nodiscard]] virtual bool RequiresContinuousRedraw() noexcept override; + [[nodiscard]] virtual HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept = 0; + [[nodiscard]] virtual HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept = 0; void WaitUntilCanRender() noexcept override; - void UpdateLastHoveredInterval(const std::optional::interval>& newInterval) + void UpdateLastHoveredInterval(const std::optional::interval>& newInterval) noexcept { _hoveredInterval = newInterval; } @@ -91,6 +101,11 @@ namespace Microsoft::Console::Render std::vector _clusterBuffer; std::optional::interval> _hoveredInterval; + + Microsoft::Console::Types::Viewport _viewport; + std::vector _previousSelection; + + static constexpr float _shrinkThreshold = 0.8f; }; inline Microsoft::Console::Render::RenderEngineBase::~RenderEngineBase() {} diff --git a/src/renderer/uia/UiaRenderer.cpp b/src/renderer/uia/UiaRenderer.cpp index 20ef3bb401c..458b562de90 100644 --- a/src/renderer/uia/UiaRenderer.cpp +++ b/src/renderer/uia/UiaRenderer.cpp @@ -20,7 +20,6 @@ UiaEngine::UiaEngine(IUiaEventDispatcher* dispatcher) : _textBufferChanged{ false }, _cursorChanged{ false }, _isEnabled{ true }, - _prevSelection{}, _prevCursorRegion{}, RenderEngineBase() { @@ -104,15 +103,17 @@ CATCH_RETURN(); // - rectangles - One or more rectangles describing character positions on the grid // Return Value: // - S_OK -[[nodiscard]] HRESULT UiaEngine::InvalidateSelection(const std::vector& rectangles) noexcept +[[nodiscard]] HRESULT UiaEngine::TriggerSelection(IRenderData* pData) noexcept { + const auto rectangles = _CalculateCurrentSelection(pData); + // early exit: different number of rows - if (_prevSelection.size() != rectangles.size()) + if (_previousSelection.size() != rectangles.size()) { try { _selectionChanged = true; - _prevSelection = rectangles; + _previousSelection = rectangles; } CATCH_LOG_RETURN_HR(E_FAIL); return S_OK; @@ -122,14 +123,14 @@ CATCH_RETURN(); { try { - const auto prevRect = _prevSelection.at(i); + const auto prevRect = _previousSelection.at(i); const auto newRect = rectangles.at(i); // if any value is different, selection has changed if (prevRect.Top != newRect.Top || prevRect.Right != newRect.Right || prevRect.Left != newRect.Left || prevRect.Bottom != newRect.Bottom) { _selectionChanged = true; - _prevSelection = rectangles; + _previousSelection = rectangles; return S_OK; } } @@ -141,19 +142,6 @@ CATCH_RETURN(); return S_OK; } -// Routine Description: -// - Scrolls the existing dirty region (if it exists) and -// invalidates the area that is uncovered in the window. -// Arguments: -// - pcoordDelta - The number of characters to move and uncover. -// - -Y is up, Y is down, -X is left, X is right. -// Return Value: -// - S_OK -[[nodiscard]] HRESULT UiaEngine::InvalidateScroll(const COORD* const /*pcoordDelta*/) noexcept -{ - return S_FALSE; -} - // Routine Description: // - Notifies to repaint everything. // - NOTE: Use sparingly. Only use when something that could affect the entire @@ -303,17 +291,6 @@ CATCH_RETURN(); return S_FALSE; } -// Method Description: -// - This method will update our internal reference for how big the viewport is. -// Arguments: -// - srNewViewport - The bounds of the new viewport. -// Return Value: -// - HRESULT S_OK -[[nodiscard]] HRESULT UiaEngine::UpdateViewport(const SMALL_RECT /*srNewViewport*/) noexcept -{ - return S_FALSE; -} - // Routine Description: // - Currently unused by this renderer // Arguments: diff --git a/src/renderer/uia/UiaRenderer.hpp b/src/renderer/uia/UiaRenderer.hpp index 86436baf8c0..30561ef7f0f 100644 --- a/src/renderer/uia/UiaRenderer.hpp +++ b/src/renderer/uia/UiaRenderer.hpp @@ -44,14 +44,12 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept override; - [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept override; - [[nodiscard]] HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept override; + [[nodiscard]] HRESULT TriggerSelection(IRenderData* pData) noexcept override; [[nodiscard]] HRESULT InvalidateAll() noexcept override; [[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept override; [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo) noexcept override; [[nodiscard]] HRESULT UpdateDpi(int const iDpi) noexcept override; - [[nodiscard]] HRESULT UpdateViewport(const SMALL_RECT srNewViewport) noexcept override; [[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override; @@ -68,7 +66,6 @@ namespace Microsoft::Console::Render Microsoft::Console::Types::IUiaEventDispatcher* _dispatcher; - std::vector _prevSelection; SMALL_RECT _prevCursorRegion; }; } diff --git a/src/renderer/vt/XtermEngine.cpp b/src/renderer/vt/XtermEngine.cpp index a948acd827e..e10921a045e 100644 --- a/src/renderer/vt/XtermEngine.cpp +++ b/src/renderer/vt/XtermEngine.cpp @@ -380,7 +380,7 @@ try // statement here. // Move the cursor to the bottom of the current viewport - const short bottom = _lastViewport.BottomInclusive(); + const short bottom = _viewport.BottomInclusive(); RETURN_IF_FAILED(_MoveCursor({ 0, bottom })); // Emit some number of newlines to create space in the buffer. RETURN_IF_FAILED(_Write(std::string(absDy, '\n'))); @@ -429,7 +429,7 @@ try // changes _above_ the wrapped line, that we maintain the wrap state in // the Terminal. const til::rectangle lastCellOfWrappedRow{ - til::point{ _lastViewport.RightInclusive(), _wrappedRow.value() }, + til::point{ _viewport.RightInclusive(), _wrappedRow.value() }, til::size{ 1, 1 } }; _trace.TraceInvalidate(lastCellOfWrappedRow); @@ -465,7 +465,7 @@ CATCH_RETURN(); // console would like us to move while scrolling. // Return Value: // - S_OK if we succeeded, else an appropriate HRESULT for safemath failure -[[nodiscard]] HRESULT XtermEngine::InvalidateScroll(const COORD* const pcoordDelta) noexcept +[[nodiscard]] HRESULT XtermEngine::TriggerScroll(const COORD* const pcoordDelta) noexcept try { const til::point delta{ *pcoordDelta }; @@ -480,6 +480,8 @@ try _scrollDelta += delta; } + _ScrollPreviousSelection(*pcoordDelta); + return S_OK; } CATCH_RETURN(); @@ -541,19 +543,28 @@ CATCH_RETURN(); // - newTitle: the new string to use for the title of the window // Return Value: // - S_OK -[[nodiscard]] HRESULT XtermEngine::_DoUpdateTitle(const std::wstring_view newTitle) noexcept +[[nodiscard]] HRESULT XtermEngine::_UpdateTitle(const std::wstring_view newTitle) noexcept { - // inbox telnet uses xterm-ascii as its mode. If we're in ascii mode, don't - // do anything, to maintain compatibility. - if (_fUseAsciiOnly) + HRESULT hr = S_FALSE; + if (newTitle != _lastFrameTitle) { - return S_OK; - } + // inbox telnet uses xterm-ascii as its mode. If we're in ascii mode, don't + // do anything, to maintain compatibility. + if (_fUseAsciiOnly) + { + hr = S_OK; + } - try - { - const auto converted = ConvertToA(CP_UTF8, newTitle); - return VtEngine::_ChangeTitle(converted); + try + { + const auto converted = ConvertToA(CP_UTF8, newTitle); + hr = VtEngine::_ChangeTitle(converted); + } + CATCH_RETURN(); + + _lastFrameTitle = newTitle; + _titleChanged = false; + hr = S_OK; } - CATCH_RETURN(); + return hr; } diff --git a/src/renderer/vt/XtermEngine.hpp b/src/renderer/vt/XtermEngine.hpp index a491547a702..e211e1cd51c 100644 --- a/src/renderer/vt/XtermEngine.hpp +++ b/src/renderer/vt/XtermEngine.hpp @@ -47,7 +47,7 @@ namespace Microsoft::Console::Render const bool lineWrapped) noexcept override; [[nodiscard]] HRESULT ScrollFrame() noexcept override; - [[nodiscard]] HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept override; + [[nodiscard]] HRESULT TriggerScroll(const COORD* const pcoordDelta) noexcept override; [[nodiscard]] HRESULT WriteTerminalW(const std::wstring_view str) noexcept override; @@ -59,7 +59,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT _MoveCursor(const COORD coord) noexcept override; - [[nodiscard]] HRESULT _DoUpdateTitle(const std::wstring_view newTitle) noexcept; + [[nodiscard]] HRESULT _UpdateTitle(const std::wstring_view newTitle) noexcept; #ifdef UNIT_TESTING friend class VtRendererTest; diff --git a/src/renderer/vt/invalidate.cpp b/src/renderer/vt/invalidate.cpp index 1856ac4cdd4..7c05ecc9634 100644 --- a/src/renderer/vt/invalidate.cpp +++ b/src/renderer/vt/invalidate.cpp @@ -24,21 +24,6 @@ using namespace Microsoft::Console::Render; return S_OK; } -// Routine Description: -// - Notifies us that the console has changed the selection region and would -// like it updated -// Arguments: -// - rectangles - Vector of rectangles to draw, line by line -// Return Value: -// - S_OK -[[nodiscard]] HRESULT VtEngine::InvalidateSelection(const std::vector& /*rectangles*/) noexcept -{ - // Selection shouldn't be handled bt the VT Renderer Host, it should be - // handled by the client. - - return S_OK; -} - // Routine Description: // - Notifies us that the console has changed the character region specified. // - NOTE: This typically triggers on cursor or text buffer changes @@ -91,7 +76,7 @@ CATCH_RETURN(); [[nodiscard]] HRESULT VtEngine::InvalidateAll() noexcept try { - _trace.TraceInvalidateAll(_lastViewport.ToOrigin().ToInclusive()); + _trace.TraceInvalidateAll(_viewport.ToOrigin().ToInclusive()); _invalidMap.set_all(); return S_OK; } diff --git a/src/renderer/vt/paint.cpp b/src/renderer/vt/paint.cpp index 9cce08fab3c..b8a6c41e440 100644 --- a/src/renderer/vt/paint.cpp +++ b/src/renderer/vt/paint.cpp @@ -35,7 +35,7 @@ using namespace Microsoft::Console::Types; _quickReturn = !somethingToDo; _trace.TraceStartPaint(_quickReturn, _invalidMap, - _lastViewport.ToInclusive(), + _viewport.ToInclusive(), _scrollDelta, _cursorMoved, _wrappedRow); @@ -521,7 +521,7 @@ void VtEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) const bool useEraseChar = (optimalToUseECH) && (!_newBottomLine) && (!_clearedAllThisFrame); - const bool printingBottomLine = coord.Y == _lastViewport.BottomInclusive(); + const bool printingBottomLine = coord.Y == _viewport.BottomInclusive(); // GH#5502 - If the background color of the "new bottom line" is different // than when we emitted the line, we can't optimize out the spaces from it. @@ -604,7 +604,7 @@ void VtEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) // GH#1245: This needs to be RightExclusive, _not_ inclusive. Otherwise, we // won't update our internal cursor position tracker correctly at the last // character of the row. - if (_lastText.X < _lastViewport.RightExclusive()) + if (_lastText.X < _viewport.RightExclusive()) { _lastText.X += static_cast(columnsActual); } @@ -614,7 +614,7 @@ void VtEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) // Mark that we're in the delayed EOL wrap state - we don't want to be // clever about how we move the cursor in this state, since different // terminals will handle a backspace differently in this state. - if (_lastText.X >= _lastViewport.RightInclusive()) + if (_lastText.X >= _viewport.RightInclusive()) { _delayedEolWrap = true; } @@ -636,7 +636,7 @@ void VtEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) // before we need to print new text. _deferredCursorPos = { _lastText.X + sNumSpaces, _lastText.Y }; - if (_deferredCursorPos.X <= _lastViewport.RightInclusive()) + if (_deferredCursorPos.X <= _viewport.RightInclusive()) { RETURN_IF_FAILED(_EraseCharacter(sNumSpaces)); } diff --git a/src/renderer/vt/state.cpp b/src/renderer/vt/state.cpp index 5fc18533891..63fb1938967 100644 --- a/src/renderer/vt/state.cpp +++ b/src/renderer/vt/state.cpp @@ -27,10 +27,9 @@ const COORD VtEngine::INVALID_COORDS = { -1, -1 }; // - An instance of a Renderer. VtEngine::VtEngine(_In_ wil::unique_hfile pipe, const Viewport initialViewport) : - RenderEngineBase(), + RenderEngineBase(initialViewport), _hFile(std::move(pipe)), _lastTextAttributes(INVALID_COLOR, INVALID_COLOR), - _lastViewport(initialViewport), _pool(til::pmr::get_default_resource()), _invalidMap(initialViewport.Dimensions(), false, &_pool), _lastText({ 0 }), @@ -213,13 +212,23 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe, // - srNewViewport - The bounds of the new viewport. // Return Value: // - HRESULT S_OK -[[nodiscard]] HRESULT VtEngine::UpdateViewport(const SMALL_RECT srNewViewport) noexcept +COORD VtEngine::UpdateViewport(IRenderData* pData) noexcept +{ + SMALL_RECT const srNewViewport = pData->GetViewport().ToInclusive(); + return UpdateViewport(srNewViewport); +} + +COORD VtEngine::UpdateViewport(const SMALL_RECT srNewViewport) noexcept { HRESULT hr = S_OK; - const Viewport oldView = _lastViewport; + const Viewport oldView = _viewport; const Viewport newView = Viewport::FromInclusive(srNewViewport); - _lastViewport = newView; + COORD coordDelta; + coordDelta.X = oldView.Left() - newView.Left(); + coordDelta.Y = oldView.Top() - newView.Top(); + + _viewport = newView; if ((oldView.Height() != newView.Height()) || (oldView.Width() != newView.Width())) { @@ -258,15 +267,14 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe, // Viewport is smaller now - just update it all. if (oldView.Height() > newView.Height() || oldView.Width() > newView.Width()) { - hr = InvalidateAll(); + LOG_IF_FAILED(InvalidateAll()); } } } - return hr; + return coordDelta; } - -// Method Description: + // Method Description: // - This method will figure out what the new font should be given the starting font information and a DPI. // - When the final font is determined, the FontInfo structure given will be updated with the actual resulting font chosen as the nearest match. // - NOTE: It is left up to the underling rendering system to choose the nearest font. Please ask for the font dimensions if they are required using the interface. Do not use the size you requested with this structure. diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index ce9b439f3b4..28fb61f65d4 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -45,8 +45,6 @@ namespace Microsoft::Console::Render virtual ~VtEngine() override = default; - [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept override; - [[nodiscard]] virtual HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept = 0; [[nodiscard]] HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept override; [[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept override; @@ -75,7 +73,8 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& pfiFontInfoDesired, _Out_ FontInfo& pfiFontInfo) noexcept override; [[nodiscard]] HRESULT UpdateDpi(const int iDpi) noexcept override; - [[nodiscard]] HRESULT UpdateViewport(const SMALL_RECT srNewViewport) noexcept override; + COORD UpdateViewport(IRenderData* pData) noexcept override; + COORD UpdateViewport(const SMALL_RECT srNewViewport) noexcept; [[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& FontDesired, _Out_ FontInfo& Font, @@ -113,8 +112,6 @@ namespace Microsoft::Console::Render TextAttribute _lastTextAttributes; - Microsoft::Console::Types::Viewport _lastViewport; - std::pmr::unsynchronized_pool_resource _pool; til::pmr::bitmap _invalidMap; From 13d386a748c3b2bf3ce5427ded01d7263b29fdb9 Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Thu, 22 Jul 2021 09:42:42 +0800 Subject: [PATCH 9/9] code format & spelling --- src/renderer/base/RenderEngineBase.cpp | 276 ++++++++++++------------- src/renderer/dx/DxRenderer.cpp | 4 +- src/renderer/dx/DxRenderer.hpp | 2 +- src/renderer/gdi/state.cpp | 2 +- src/renderer/inc/IRenderEngine.hpp | 2 +- src/renderer/inc/RenderEngineBase.hpp | 2 +- src/renderer/vt/paint.cpp | 2 +- src/renderer/vt/state.cpp | 2 +- src/renderer/vt/vtrenderer.hpp | 2 +- 9 files changed, 147 insertions(+), 147 deletions(-) diff --git a/src/renderer/base/RenderEngineBase.cpp b/src/renderer/base/RenderEngineBase.cpp index 67a12ae925e..6552c5a58e7 100644 --- a/src/renderer/base/RenderEngineBase.cpp +++ b/src/renderer/base/RenderEngineBase.cpp @@ -329,8 +329,9 @@ void RenderEngineBase::_LoopSelection(IRenderData* pData, std::function RenderEngineBase::_GetSelectionRects(IRenderData* pData) noexcept - { - const auto& buffer = pData->GetTextBuffer(); - auto rects = pData->GetSelectionRects(); - // Adjust rectangles to viewport - Viewport view = pData->GetViewport(); - - std::vector result; - - for (auto rect : rects) - { - // Convert buffer offsets to the equivalent range of screen cells - // expected by callers, taking line rendition into account. - const auto lineRendition = buffer.GetLineRendition(rect.Top()); - rect = Viewport::FromInclusive(BufferToScreenLine(rect.ToInclusive(), lineRendition)); - - auto sr = view.ConvertToOrigin(rect).ToInclusive(); - - // hopefully temporary, we should be receiving the right selection sizes without correction. - sr.Right++; - sr.Bottom++; - - result.emplace_back(sr); - } - - return result; - } - - +// - Generates a IRenderEngine::GridLines structure from the values in the +// provided textAttribute +// Arguments: +// - textAttribute: the TextAttribute to generate GridLines from. +// Return Value: +// - a GridLines containing all the gridline info from the TextAttribute +IRenderEngine::GridLines RenderEngineBase::s_GetGridlines(const TextAttribute& textAttribute) noexcept +{ + // Convert console grid line representations into rendering engine enum representations. + IRenderEngine::GridLines lines = IRenderEngine::GridLines::None; + + if (textAttribute.IsTopHorizontalDisplayed()) + { + lines |= IRenderEngine::GridLines::Top; + } + + if (textAttribute.IsBottomHorizontalDisplayed()) + { + lines |= IRenderEngine::GridLines::Bottom; + } + + if (textAttribute.IsLeftVerticalDisplayed()) + { + lines |= IRenderEngine::GridLines::Left; + } + + if (textAttribute.IsRightVerticalDisplayed()) + { + lines |= IRenderEngine::GridLines::Right; + } + + if (textAttribute.IsCrossedOut()) + { + lines |= IRenderEngine::GridLines::Strikethrough; + } + + if (textAttribute.IsUnderlined()) + { + lines |= IRenderEngine::GridLines::Underline; + } + + if (textAttribute.IsDoublyUnderlined()) + { + lines |= IRenderEngine::GridLines::DoubleUnderline; + } + + if (textAttribute.IsHyperlink()) + { + lines |= IRenderEngine::GridLines::HyperlinkUnderline; + } + return lines; +} + +// Routine Description: +// - Helper to determine the selected region of the buffer. +// Return Value: +// - A vector of rectangles representing the regions to select, line by line. +std::vector RenderEngineBase::_GetSelectionRects(IRenderData* pData) noexcept +{ + const auto& buffer = pData->GetTextBuffer(); + auto rects = pData->GetSelectionRects(); + // Adjust rectangles to viewport + Viewport view = pData->GetViewport(); + + std::vector result; + + for (auto rect : rects) + { + // Convert buffer offsets to the equivalent range of screen cells + // expected by callers, taking line rendition into account. + const auto lineRendition = buffer.GetLineRendition(rect.Top()); + rect = Viewport::FromInclusive(BufferToScreenLine(rect.ToInclusive(), lineRendition)); + + auto sr = view.ConvertToOrigin(rect).ToInclusive(); + + // hopefully temporary, we should be receiving the right selection sizes without correction. + sr.Right++; + sr.Bottom++; + + result.emplace_back(sr); + } + + return result; +} + std::vector RenderEngineBase::_CalculateCurrentSelection(IRenderData* pData) noexcept - { - // Get selection rectangles - const auto rects = _GetSelectionRects(pData); - - // Restrict all previous selection rectangles to inside the current viewport bounds - for (auto& sr : _previousSelection) - { - // Make the exclusive SMALL_RECT into a til::rectangle. - til::rectangle rc{ Viewport::FromExclusive(sr).ToInclusive() }; - - // Make a viewport representing the coordinates that are currently presentable. - const til::rectangle viewport{ til::size{ pData->GetViewport().Dimensions() } }; - - // Intersect them so we only invalidate things that are still visible. - rc &= viewport; - - // Convert back into the exclusive SMALL_RECT and store in the vector. - sr = Viewport::FromInclusive(rc).ToExclusive(); - } - - return rects; - } - - // Method Description: - // - Offsets all of the selection rectangles we might be holding onto - // as the previously selected area. If the whole viewport scrolls, - // we need to scroll these areas also to ensure they're invalidated - // properly when the selection further changes. - // Arguments: - // - delta - The scroll delta - // Return Value: - // - - Updates internal state instead. - void RenderEngineBase::_ScrollPreviousSelection(const til::point delta) - { - if (delta != til::point{ 0, 0 }) - { - for (auto& sr : _previousSelection) - { - // Get a rectangle representing this piece of the selection. - til::rectangle rc = Viewport::FromExclusive(sr).ToInclusive(); - - // Offset the entire existing rectangle by the delta. - rc += delta; - - // Store it back into the vector. - sr = Viewport::FromInclusive(rc).ToExclusive(); - } - } - } +{ + // Get selection rectangles + const auto rects = _GetSelectionRects(pData); + + // Restrict all previous selection rectangles to inside the current viewport bounds + for (auto& sr : _previousSelection) + { + // Make the exclusive SMALL_RECT into a til::rectangle. + til::rectangle rc{ Viewport::FromExclusive(sr).ToInclusive() }; + + // Make a viewport representing the coordinates that are currently presentable. + const til::rectangle viewport{ til::size{ pData->GetViewport().Dimensions() } }; + + // Intersect them so we only invalidate things that are still visible. + rc &= viewport; + + // Convert back into the exclusive SMALL_RECT and store in the vector. + sr = Viewport::FromInclusive(rc).ToExclusive(); + } + + return rects; +} + +// Method Description: +// - Offsets all of the selection rectangles we might be holding onto +// as the previously selected area. If the whole viewport scrolls, +// we need to scroll these areas also to ensure they're invalidated +// properly when the selection further changes. +// Arguments: +// - delta - The scroll delta +// Return Value: +// - - Updates internal state instead. +void RenderEngineBase::_ScrollPreviousSelection(const til::point delta) +{ + if (delta != til::point{ 0, 0 }) + { + for (auto& sr : _previousSelection) + { + // Get a rectangle representing this piece of the selection. + til::rectangle rc = Viewport::FromExclusive(sr).ToInclusive(); + + // Offset the entire existing rectangle by the delta. + rc += delta; + + // Store it back into the vector. + sr = Viewport::FromInclusive(rc).ToExclusive(); + } + } +} diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index a52402c70f9..75d5c0a5ff6 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -1372,7 +1372,7 @@ CATCH_RETURN() // - // Return Value: // - Any DirectX error, a memory error, etc. -[[nodiscard]] HRESULT DxEngine::PaintFrame(IRenderData *pData) noexcept +[[nodiscard]] HRESULT DxEngine::PaintFrame(IRenderData* pData) noexcept try { if (pData == nullptr) @@ -1407,7 +1407,7 @@ try } CATCH_RETURN(); - // Routine Description: +// Routine Description: // - Ends batch drawing and captures any state necessary for presentation // Arguments: // - diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index bdc024678f9..ff53cb1e705 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -83,7 +83,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept override; [[nodiscard]] HRESULT StartPaint() noexcept override; - [[nodiscard]] HRESULT PaintFrame(IRenderData* pdata) noexcept override; + [[nodiscard]] HRESULT PaintFrame(IRenderData* pData) noexcept override; [[nodiscard]] HRESULT EndPaint() noexcept override; [[nodiscard]] bool RequiresContinuousRedraw() noexcept override; diff --git a/src/renderer/gdi/state.cpp b/src/renderer/gdi/state.cpp index 4e05a9f7e60..8f979d4b58c 100644 --- a/src/renderer/gdi/state.cpp +++ b/src/renderer/gdi/state.cpp @@ -458,7 +458,7 @@ void GdiEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) { if (renderData.needLineTransformation) { - LOG_IF_FAILED(PrepareLineTransform(renderData.lineRendention, renderData.screenPosition.Y, renderData.visible.Left())); + LOG_IF_FAILED(PrepareLineTransform(renderData.lineRendition, renderData.screenPosition.Y, renderData.visible.Left())); } // Retrieve the cell information iterator limited to just this line we want to redraw. diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index 5accde94224..0c95a93118a 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -55,7 +55,7 @@ namespace Microsoft::Console::Render public: [[nodiscard]] virtual HRESULT StartPaint() noexcept = 0; - [[nodiscard]] virtual HRESULT PaintFrame(IRenderData *pData) noexcept = 0; + [[nodiscard]] virtual HRESULT PaintFrame(IRenderData* pData) noexcept = 0; [[nodiscard]] virtual HRESULT EndPaint() noexcept = 0; diff --git a/src/renderer/inc/RenderEngineBase.hpp b/src/renderer/inc/RenderEngineBase.hpp index e788b2174f4..869bc108e4b 100644 --- a/src/renderer/inc/RenderEngineBase.hpp +++ b/src/renderer/inc/RenderEngineBase.hpp @@ -34,7 +34,7 @@ namespace Microsoft::Console::Render // Used by GDI Engine. Microsoft::Console::Types::Viewport visible; // Used by GDI Engine. - LineRendition lineRendention; + LineRendition lineRendition; // Used by GDI Engine. bool needLineTransformation; bool lineWrapped; diff --git a/src/renderer/vt/paint.cpp b/src/renderer/vt/paint.cpp index b8a6c41e440..67d6053462e 100644 --- a/src/renderer/vt/paint.cpp +++ b/src/renderer/vt/paint.cpp @@ -266,7 +266,7 @@ void VtEngine::_PaintBufferLineHelper(const BufferLineRenderData& renderData) THROW_IF_FAILED(PaintBufferLine({ _clusterBuffer.data(), _clusterBuffer.size() }, screenPoint, trimLeft, renderData.lineWrapped)); } } - // Routine Description: +// Routine Description: // - Write a VT sequence to change the current colors of text. Writes true RGB // color sequences. // Arguments: diff --git a/src/renderer/vt/state.cpp b/src/renderer/vt/state.cpp index efe9d659ba3..cd8dbdaeda8 100644 --- a/src/renderer/vt/state.cpp +++ b/src/renderer/vt/state.cpp @@ -272,7 +272,7 @@ COORD VtEngine::UpdateViewport(const SMALL_RECT srNewViewport) noexcept return coordDelta; } - // Method Description: +// Method Description: // - This method will figure out what the new font should be given the starting font information and a DPI. // - When the final font is determined, the FontInfo structure given will be updated with the actual resulting font chosen as the nearest match. // - NOTE: It is left up to the underling rendering system to choose the nearest font. Please ask for the font dimensions if they are required using the interface. Do not use the size you requested with this structure. diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index 28fb61f65d4..d5cec87a517 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -53,7 +53,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept override; [[nodiscard]] virtual HRESULT StartPaint() noexcept override; - [[nodiscard]] virtual HRESULT PaintFrame(IRenderData* pdata) noexcept override; + [[nodiscard]] virtual HRESULT PaintFrame(IRenderData* pData) noexcept override; [[nodiscard]] virtual HRESULT EndPaint() noexcept override; [[nodiscard]] virtual HRESULT Present() noexcept override;