Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix double width cursor for CJK characters #2932

Merged
merged 1 commit into from
Sep 27, 2019

Conversation

skyline75489
Copy link
Collaborator

@skyline75489 skyline75489 commented Sep 27, 2019

Summary of the Pull Request

Fix double width cursor for CJK characters

References

#2713

PR Checklist

Detailed Description of the Pull Request / Additional comments

The code was copied from https://github.com/microsoft/terminal/blob/master/src/host/renderData.cpp#L266

Validation Steps Performed

图片

图片

Copy link
Contributor

@DHowett-MSFT DHowett-MSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks right to me. Thanks for tracking this down!

@DHowett-MSFT DHowett-MSFT added the Needs-Second It's a PR that needs another sign-off label Sep 27, 2019
@zadjii-msft zadjii-msft merged commit eafa884 into microsoft:master Sep 27, 2019
@skyline75489
Copy link
Collaborator Author

This seems to be causing a use-after-free in TextBufferCellIterator constructor. I'm trying to find out why but I'm really quite new to the codebase. Sorry for any inconvience. Feel free to revert this in case it stops you guys from working.

@zadjii-msft
Copy link
Member

Oh no, that's probably #2375. The use-after-free crash only comes when you close a tab first, right?

@skyline75489
Copy link
Collaborator Author

I think it's a worse one, the _attrIter was freed in

. The dereferncing will cause a crash.

@skyline75489
Copy link
Collaborator Author

skyline75489 commented Sep 27, 2019

I think I figured out the reason. Coincidently it relates to my other PR #2937 .

The ATTR_ROW::InsertAttrRuns method replaces _list with another vector, causing the original _list and all its iterator unavailable. This PR introduced IsCursorDoubleWidth is using an old iterator and trying to dereference it in race condition. A quick fix would be changing the last few lines in ATTR_ROW::InsertAttrRuns here:

-    _list.swap(newRun);
+    _list.resize(newRun.size());
+    for (auto i = 0; i < _list.size(); i++)
+    {
+        _list[i] = newRun[i];
+    }

I think there could be a better way to do this.

It's easy to reproduce the crash. Just open Vim and quickly navigating up and down.

@zadjii-msft
Copy link
Member

@miniksa thoughts here? I know we've done a lot of optimization on InsertAttrRuns in the past, esp. w.r.t. std::swap, but I don't remember those specifics anymore. You probably remember better than I.

@skyline75489
Copy link
Collaborator Author

For what I've learned so far, #2937 is a must-have. It dramastically reduce the overhead of creating a vector and replace _list with it. With the help of #2937 and further optimization, the choice of using std::swap or plain old copy doesn't really matter any more.

@skyline75489
Copy link
Collaborator Author

skyline75489 commented Sep 29, 2019

This PR also exposed another problem. When the windows is maximized and I'm navigationg up and down in vim, an assert failure was raised here:

THROW_HR_IF(E_INVALIDARG, !limits.IsInBounds(pos));

I'm seeing pos.X is 209 and limit is 208, thus the failure.

I found a possible fix. It seems the cursor updating code here is the root cause:

cursor.SetPosition(proposedCursorPosition);

I added the border validation code like this:

+        if (proposedCursorPosition.X > bufferSize.Width() - 1)
+       
+            proposedCursorPosition.X = bufferSize.Width() - 1;
+        }
+
         // This section is essentially equivalent to `AdjustCursorPosition`
         // Update Cursor Position
         cursor.SetPosition(proposedCursorPosition);

DHowett-MSFT pushed a commit that referenced this pull request Oct 3, 2019
@skyline75489 skyline75489 deleted the fix/doubleWidthCursor branch October 21, 2019 07:01
DHowett-MSFT pushed a commit that referenced this pull request Oct 22, 2019
Revert "Fix cursor redrawing crash (#2965)"
This reverts commit 926a2e3.

Revert "Fix double width cursor for CJK characters (#2932)"
This reverts commit eafa884.

Fixes #3277.
Fully reverts #2965, #2932.
miniksa pushed a commit that referenced this pull request Oct 22, 2019
Revert "Fix cursor redrawing crash (#2965)"
This reverts commit 926a2e3.

Revert "Fix double width cursor for CJK characters (#2932)"
This reverts commit eafa884.

Fixes #3277.
Fully reverts #2965, #2932.
ghost pushed a commit that referenced this pull request Apr 15, 2020
# Summary of the Pull Request
This PR will allow the cursor to be double width when on top of a double width character. This required changing `IsCursorDoubleWidth` to check whether the glyph the cursor's on top of is double width. This code is exactly the same as the original PR that addressed this issue in #2932. That one got reverted at some point due to the crashes related to it, but due to a combination of Terminal having come further since that PR and other changes to address use-after-frees, some of the crashes may/may not be relevant now. The ones that seemed to be relevant/repro-able, I attempt to address in this PR.

The `IsCursorDoubleWidth` check would fail during the `TextBuffer::Reflow` call inside of `Terminal::UserResize` occasionally, particularly when `newCursor.EndDeferDrawing()` is called. This is because when we tell the newCursor to `EndDefer`, the renderer will attempt to redraw the cursor. As part of this redraw, it'll ask if `IsCursorDoubleWidth`, and if the renderer managed to ask this before `UserResize` swapped out the old buffer with the new one from `Reflow`, the renderer will be asking the old buffer if its out-of-bounds cursor is double width. This was pretty easily repro'd using `cmatrix -u0` and resizing the window like a madman.

As a solution, I've moved the Start/End DeferDrawing calls out of `Reflow` and into `UserResize`. This way, I can "clamp" the portion of the code where the newBuffer is getting created and reflowed and swapped into the Terminal buffer, and only allow the renderer to draw once the swap is done. This also means that ConHost's `ResizeWithReflow` needed to change slightly.

In addition, I've added a WriteLock to `SetCursorOn`. It was mentioned as a fix for a crash in #2965 (although I can't repro), and I also figured it would be good to try to emulate where ConHost locks with regards to Cursor operations, and this seemed to be one that we were missing.

## PR Checklist
* [x] Closes #2713
* [x] CLA signed
* [x] Tests added/passed

## Validation Steps Performed
Manual validation that the cursor is indeed chonky, added a test case to check that we are correctly saying that the cursor is double width (not too sure if I put it in the right place). Also open to other test case ideas and thoughts on what else I should be careful for since I am quite nervous about what other crashes might occur.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs-Second It's a PR that needs another sign-off
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature Request - filledBox cursor should cover the whole unicode character
4 participants