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

VT renderer works incorrectly when cursor is moved between writes #17270

Closed
alabuzhev opened this issue May 15, 2024 · 10 comments · Fixed by #17290
Closed

VT renderer works incorrectly when cursor is moved between writes #17270

alabuzhev opened this issue May 15, 2024 · 10 comments · Fixed by #17290
Labels
Area-Rendering Text rendering, emoji, complex glyph & font-fallback issues Issue-Bug It either shouldn't be doing this or needs an investigation. Needs-Tag-Fix Doesn't match tag requirements Product-Conpty For console issues specifically related to conpty

Comments

@alabuzhev
Copy link
Contributor

Windows Terminal version

Latest source

Windows build number

10.0.19045.4355

Other Software

No

Steps to reproduce

The original issue is described here: FarGroup/FarManager#841
According to the comments, it regressed somewhere between 1.18.3181.0 - 1.19.11213.0

MRE: VtRendererBug.zip
STR: compile, run, follow instructions

Expected Behavior

The vertical bar moves right without affecting the underlying text.

It works as expected in OpenConsole.

Actual Behavior

The vertical bar and the underlying text are broken.
The third line is written into the second.

It doesn't work as expected in WT and other terminals that use OpenConsole, e.g. WezTerm.


I've managed to trace it down to

std::string seq = "\r\n";
hr = _Write(seq);

It looks like specifying the position explicitly fixes the issue:

-                std::string seq = "\r\n";
-                hr = _Write(seq);
+                hr = _CursorPosition(coord);

Which could mean that the cursor position in _lastText is tracked incorrectly / unreliably.

@alabuzhev alabuzhev added Issue-Bug It either shouldn't be doing this or needs an investigation. Needs-Triage It's a new issue that the core contributor team needs to triage at the next triage meeting labels May 15, 2024
Copy link

Hi I'm an AI powered bot that finds similar issues based off the issue title.

Please view the issues below to see if they solve your problem, and if the issue describes your problem please consider closing this one and thumbs upping the other issue to help us prioritize it. Thank you!

Closed similar issues:

Note: You can give me feedback by thumbs upping or thumbs downing this comment.

@carlos-zamora carlos-zamora added Area-Rendering Text rendering, emoji, complex glyph & font-fallback issues Product-Conpty For console issues specifically related to conpty and removed Needs-Triage It's a new issue that the core contributor team needs to triage at the next triage meeting labels May 15, 2024
@carlos-zamora carlos-zamora added this to the Terminal v1.22 milestone May 15, 2024
@alabuzhev
Copy link
Contributor Author

MRE: VtRendererBug.zip
STR: compile, run, follow instructions

P.S. I think the provided example reproduces the same issue.
Just in case if not, STR for the original one:

  1. Get Far - https://github.com/FarGroup/FarManager/releases/
  2. Run it.
  3. In F9 - Options - Interface settings
    uncheck:
    [ ] Use Virtual Terminal for rendering
    [ ] Fullwidth-aware rendering
    check:
    [x] ClearType-friendly redraw (can be slow)
  4. Edit any file (F4)
  5. In F11 - FarColorer - Configure - Main settings
    check:
    [x] Cross
    set Cross style to both
  6. Press CtrlHome to move the cursor to the upper left corner and then Right a few times.

@lhecker

This comment was marked as outdated.

@j4james
Copy link
Collaborator

j4james commented May 16, 2024

FYI, I did a git bisect with the VtRendererBug test case, and for me the issue first showed up in commit 72b4488. However, I suspect it may be one of those things that's timing dependent, and that commit just exposed a bug that has always existed.

@j4james
Copy link
Collaborator

j4james commented May 16, 2024

I've just confirmed that reverting commit 72b4488 on the main branch is enough to fix the issue for me (i.e. moving the WaitUntilCanRender call back down to the bottom of the loop, before PaintFrame). And I've also now tested the cursor movement in Far itself, and the same fix works there too.

Again I'm not sure that's a real solution to the problem, but it might be worth considering as a quick fix, assuming it works for everyone else and not just me.

@alabuzhev
Copy link
Contributor Author

Thanks James, reverting 72b4488 fixed it for me as well.

@j4james
Copy link
Collaborator

j4james commented May 16, 2024

As for what's going wrong, this is what I've been able to establish:

  1. When you write a line of text that extends to the last column, we set the _wrapForced flag, even though the line hasn't actually wrapped (this is partially tracked in Remove SetWrapForced from AdaptDispatch::_WriteToBuffer #15602, although that's referencing the AdaptDispatch class, while this particular case is in one of the console APIs).

  2. When the VtEngine outputs that line, and it sees that flag is set, it records the apparent wrapped state in the _wrappedRow field, again despite the fact that it hasn't actually wrapped.

  3. At this point in time, the cursor is meant to be at the start of line 2, because that's where it was manually positioned, and the WriteConsoleOutput API doesn't change that. So at the end of the frame, the VtEngine tries to move the cursor there. However, because of _wrappedRow being set to the line above, it thinks there is nothing to do (the previousLineWrapped test here).

  4. Even though the _MoveCursor method didn't do anything in the previous step, it still updates the _lastText field to where it mistakenly thought the cursor was (it thinks it's at the start of line 2, when it's actually at the end of line 1). This is why the subsequent output ends up the wrong location.

Ignoring bug number 1 for the moment, it seems to me that point 3 is also a bug, because the _wrappedRow field being set does not actually indicate that the cursor is on the next line, only that it doesn't need to be moved there if you're about to write a character. If you're just painting the cursor at that point, it needs to be moved explicitly.

So another possible way to fix this issue would be to set _wrappedRow = std::nullopt in the VtEngine::PaintCursor method before calling _MoveCursor.

That said, the conpty code is complicated, and I really don't know it very well, so there may be more to it than that. I'm just throwing this out there as potential solution.

@j4james
Copy link
Collaborator

j4james commented May 16, 2024

Another simple test case that can be run from a WSL bash shell:

printf '\033[2J\033[1;999H*'; sleep 1; printf '\033[2H\033[65;1;1;1;999$x'; sleep 1; printf '\033[3HThis should be line 3\n'

The text "This should be line 3", should be on line 3 😄, but it usually ends up on line 2.

This one is not fixed by reverting 72b4488, but it is fixed by the _wrappedRow patch suggested above.

@DHowett
Copy link
Member

DHowett commented May 16, 2024

Whichever the fix ends up being, I'm going to defer it until the next servicing wave for 1.21 - thanks for all the great investigations, everyone 😄

@j4james
Copy link
Collaborator

j4james commented May 25, 2024

I've just realised that issue #17013 is another variant of this bug. And that scenario is also fixed by PR #17290.

github-merge-queue bot pushed a commit that referenced this issue May 30, 2024
## Summary of the Pull Request

If the VT render engine is moving the cursor to the start of a row, and
the previous row was marked as wrapped, it will assume that it doesn't
need to do anything, because the next output should automatically move
the cursor to the correct position anyway.

However, if that cursor movement is coming from the final `PaintCursor`
call for the frame, there isn't going to be any more output, so the
cursor will be left in the wrong position.

This PR fixes that issue by clearing the `_wrappedRow` field before the
`_MoveCursor` call in the `PaintCursor` method.

## Validation Steps Performed

I've confirmed that this fixes all the test cases mentioned in issue
#17270, and issue #17013, and I've added a unit test to check the new
behavior is working as expected.

However, this change does break a couple of `ConptyRoundtripTests` that
were expecting the terminal row to be marked as wrapped when writing a
wrapped line in two parts using `WriteCharsLegacy`. This is because the
legacy way of wrapping a line isn't the same as a VT delayed wrap, so it
has to be emulated with cursor movement, and that can end up resetting
the wrap flag.

It's possible that could be fixed, but it's already broken in a number
of other ways, so I don't think this makes things much worse. For now,
I've just made the affected test cases skip the wrapping check.

## PR Checklist
- [x] Closes #17013
- [x] Closes #17270
- [x] Tests added/passed
@microsoft-github-policy-service microsoft-github-policy-service bot added the Needs-Tag-Fix Doesn't match tag requirements label May 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Rendering Text rendering, emoji, complex glyph & font-fallback issues Issue-Bug It either shouldn't be doing this or needs an investigation. Needs-Tag-Fix Doesn't match tag requirements Product-Conpty For console issues specifically related to conpty
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants