terminal/src/renderer/vt/invalidate.cpp
Michael Niksa ef80f665d3
Correct scrolling invalidation region for tmux in pty w/ bitmap (#5122)
Correct scrolling invalidation region for tmux in pty w/ bitmap

Add tracing for circling and scrolling operations. Fix improper
invalidation within AdjustCursorPosition routine in the subsection about
scrolling down at the bottom with a set of margins enabled.

## References
- Introduced with #5024 

## Detailed Description of the Pull Request / Additional comments
- This occurs when there is a scroll region restriction applied and a
  newline operation is performed to attempt to spin the contents of just
  the scroll region. This is a frequent behavior of tmux.
- Right now, the Terminal doesn't support any sort of "scroll content"
  operation, so what happens here generally speaking is that the PTY in
  the ConHost will repaint everything when this happens.
- The PTY when doing `AdjustCursorPosition` with a scroll region
  restriction would do the following things:

1. Slide literally everything in the direction it needed to go to take
   advantage of rotating the circular buffer. (This would force a
   repaint in PTY as the PTY always forces repaint when the buffer
   circles.)
2. Copy the lines that weren't supposed to move back to where they were
   supposed to go.
3. Backfill the "revealed" region that encompasses what was supposed to
   be the newline.

- The invalidations for the three operations above were:

1. Invalidate the number of rows of the delta at the top of the buffer
   (this part was wrong)
2. Invalidate the lines that got copied back into position (probably
   unnecessary, but OK)
3. Invalidate the revealed/filled-with-spaces line (this is good).

- When we were using a simple single rectangle for invalidation, the
  union of the top row of the buffer from 1 and the bottom row of the
  buffer from 2 (and 3 was irrelevant as it was already unioned it)
  resulted in repainting the entire buffer and all was good.

- When we switched to a bitmap, it dutifully only repainted the top line
  and the bottom two lines as the middle ones weren't a consequence of
  intersect.

- The logic was wrong. We shouldn't be invalidating rows-from-the-top
  for the amount of the delta. The 1 part should be invalidating
  everything BUT the lines that were invalidated in parts 2 and 3.
  (Arguably part 2 shouldn't be happening at all, but I'm not optimizing
  for that right now.)

- So this solves it by restoring an entire screen repaint for this sort
  of slide data operation by giving the correct number of invalidated
  lines to the bitmap.

## Validation Steps Performed
- Manual validation with the steps described in #5104
- Automatic test `ConptyRoundtripTests::ScrollWithMargins`.

Closes #5104
2020-03-27 22:37:23 +00:00

142 lines
4.6 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "vtrenderer.hpp"
using namespace Microsoft::Console::Types;
#pragma hdrstop
using namespace Microsoft::Console::Render;
// Routine Description:
// - Notifies us that the system has requested a particular pixel area of the
// client rectangle should be redrawn. (On WM_PAINT)
// For VT, this doesn't mean anything. So do nothing.
// Arguments:
// - prcDirtyClient - Pointer to pixel area (RECT) of client region the system
// believes is dirty
// Return Value:
// - S_OK
[[nodiscard]] HRESULT VtEngine::InvalidateSystem(const RECT* const /*prcDirtyClient*/) noexcept
{
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<SMALL_RECT>& /*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
// Arguments:
// - psrRegion - Character region (SMALL_RECT) that has been changed
// Return Value:
// - S_OK, else an appropriate HRESULT for failing to allocate or write.
[[nodiscard]] HRESULT VtEngine::Invalidate(const SMALL_RECT* const psrRegion) noexcept
try
{
const til::rectangle rect{ Viewport::FromExclusive(*psrRegion).ToInclusive() };
_trace.TraceInvalidate(rect);
_invalidMap.set(rect);
return S_OK;
}
CATCH_RETURN();
// Routine Description:
// - Notifies us that the console has changed the position of the cursor.
// Arguments:
// - pcoordCursor - the new position of the cursor
// Return Value:
// - S_OK
[[nodiscard]] HRESULT VtEngine::InvalidateCursor(const COORD* const pcoordCursor) noexcept
{
// If we just inherited the cursor, we're going to get an InvalidateCursor
// for both where the old cursor was, and where the new cursor is
// (the inherited location). (See Cursor.cpp:Cursor::SetPosition)
// We should ignore the first one, but after that, if the client application
// is moving the cursor around in the viewport, move our virtual top
// up to meet their changes.
if (!_skipCursor && _virtualTop > pcoordCursor->Y)
{
_virtualTop = pcoordCursor->Y;
}
_skipCursor = false;
_cursorMoved = true;
return S_OK;
}
// Routine Description:
// - Notifies to repaint everything.
// - NOTE: Use sparingly. Only use when something that could affect the entire
// frame simultaneously occurs.
// Arguments:
// - <none>
// Return Value:
// - S_OK, else an appropriate HRESULT for failing to allocate or write.
[[nodiscard]] HRESULT VtEngine::InvalidateAll() noexcept
try
{
_trace.TraceInvalidateAll(_lastViewport.ToOrigin().ToInclusive());
_invalidMap.set_all();
return S_OK;
}
CATCH_RETURN();
// Method Description:
// - Notifies us that we're about to circle the buffer, giving us a chance to
// force a repaint before the buffer contents are lost. The VT renderer
// needs to be able to render all text before it's lost, so we return true.
// Arguments:
// - Receives a bool indicating if we should force the repaint.
// Return Value:
// - S_OK
[[nodiscard]] HRESULT VtEngine::InvalidateCircling(_Out_ bool* const pForcePaint) noexcept
{
// If we're in the middle of a resize request, don't try to immediately start a frame.
if (_inResizeRequest)
{
*pForcePaint = false;
}
else
{
*pForcePaint = true;
// Keep track of the fact that we circled, we'll need to do some work on
// end paint to specifically handle this.
_circled = true;
}
_trace.TraceTriggerCircling(*pForcePaint);
return S_OK;
}
// Method Description:
// - Notifies us that we're about to be torn down. This gives us a last chance
// to force a repaint before the buffer contents are lost. The VT renderer
// needs to be able to render all text before it's lost, so we return true.
// Arguments:
// - Receives a bool indicating if we should force the repaint.
// Return Value:
// - S_OK
[[nodiscard]] HRESULT VtEngine::PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept
{
*pForcePaint = true;
return S_OK;
}