Repository: microsoft/terminal (language: cpp)

Issue:
Cursor droppings on wide chars in Windows Terminal
### Windows Terminal version

1.16.2641.0

### Windows build number

10.0.19044.2364

### Other Software

_No response_

### Steps to reproduce

1. Open a WSL bash shell in Windows Terminal.
2. Execute `printf "\e[2 q\e[2J\e[1;999H\e[D\u306F"; sleep 1; printf "\e[B\n"`

This sequence is performing the following steps:

1. The cursor style is set to a non-blinking block (just to make the bug more obvious).
2. The screen is cleared, and the cursor moved to the top right corner.
3. The cursor is then moved one column back and a wide character (`は`) is written out.
5. We wait for a second, to give the cursor a chance to render.
6. The cursor is moved down a line.
7. We output a newline (just so the shell prompt is back at the start of the line).

### Expected Behavior

At the end of all that, there should only be one cursor visible on the screen.

### Actual Behavior

In addition to the real cursor at the shell prompt, there is a second cursor left behind overlapping the `は` character.

![image](https://user-images.githubusercontent.com/4181424/211568625-b393f0a2-7efb-4b0a-9e9b-5373563a7e7c.png)

I realise this is probably not a typical scenario that you're likely to encounter, but it'll become more common once Windows Terminal implements delayed EOL wrap correctly (which is one of the thing I'm hoping to fix in PR #14640).



Discussion / hints:
Note that this is not the same thing as #12739, which was the result of deferred cursor drawing. This is the result of a miscalculation in the cursor bounds checking, as explained in https://github.com/microsoft/terminal/pull/13001#discussion_r862125419.

Fix patch:
diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp
index 0fba0990990..8f7332597c8 100644
--- a/src/renderer/base/renderer.cpp
+++ b/src/renderer/base/renderer.cpp
@@ -276,15 +276,16 @@ void Renderer::TriggerRedrawCursor(const til::point* const pcoord)
         // cells for the cursor, taking line rendition into account.
         const auto lineRendition = buffer.GetLineRendition(pcoord->y);
         const auto cursorWidth = _pData->IsCursorDoubleWidth() ? 2 : 1;
-        const til::inclusive_rect cursorRect = { pcoord->x, pcoord->y, pcoord->x + cursorWidth - 1, pcoord->y };
-        auto cursorView = Viewport::FromInclusive(BufferToScreenLine(cursorRect, lineRendition));
+        til::inclusive_rect cursorRect = { pcoord->x, pcoord->y, pcoord->x + cursorWidth - 1, pcoord->y };
+        cursorRect = BufferToScreenLine(cursorRect, lineRendition);
 
-        // The region is clamped within the viewport boundaries and we only
-        // trigger a redraw if the region is not empty.
+        // That region is then clipped within the viewport boundaries and we
+        // only trigger a redraw if the resulting region is not empty.
         auto view = _pData->GetViewport();
-        if (view.IsInBounds(cursorView))
+        auto updateRect = til::rect{ cursorRect };
+        if (view.TrimToViewport(&updateRect))
         {
-            const auto updateRect = view.ConvertToOrigin(cursorView).ToExclusive();
+            view.ConvertToOrigin(&updateRect);
             FOREACH_ENGINE(pEngine)
             {
                 LOG_IF_FAILED(pEngine->InvalidateCursor(&updateRect));


Test patch: