-
Notifications
You must be signed in to change notification settings - Fork 8.5k
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
Correct fill attributes when scrolling and erasing #3100
Conversation
…ributes correctly.
…tions fill with the correct attributes.
…r rows scrolled into view.
…e new rows with the correct attributes.
…ributes correctly, and make sure the full buffer width is covered.
…es, and cover the full buffer width.
…t the fill attributes correctly, and make sure the full buffer width is affected.
…or the erase and hard reset operations.
…ribute and ScrollConsoleScreenBufferW methods from the ConGetSet interface.
…FillConsoleOutputAttributeImpl and ScrollConsoleScreenBufferWImpl implementations.
src/buffer/out/textBuffer.cpp
Outdated
// The VT standard requires that the new row is initialized with | ||
// the current background color, but with no meta attributes set. | ||
auto fillAttributes = _currentAttributes; | ||
fillAttributes.SetMetaAttributes(0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concerned about this one, actually -- this is used for traditional Windows console apps as well, and I don't know what the expected behavior is for new rows outside of global VT processing mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes. I should have mentioned I did actually have a look into that. I wrote a little test app that set the attributes with SetConsoleTextAttribute
(using COMMON_LVB_UNDERSCORE
and COMMON_LVB_REVERSE_VIDEO
) and then output some text that would cause the screen to scroll.
In both the legacy console and the v2 console, a line feed doesn't actually fill the new row with the current attributes most of the time - only when it reaches the end of the buffer. That's just plain weird, but it seems that's the way it has always been, and that hasn't changed.
In the legacy console the underscore and reverse attributes don't seem to have any effect anywhere, so naturally that means the new rows filled when scrolling past the end of the buffer will also not have an underscore or have their attributes reversed. So in some ways this update more closely matches that legacy behaviour. It is a change from the current v2 behaviour though.
The bottom line is I'd be very surprised if anyone is relying on this behaviour, considering how strange and inconsistent it is. However, I know backward compatibility is important to you, so if you think it's a concern, I can probably add a check that only resets the attributes when in vt mode or something of the sort.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've now made it so this only applies in the specific case of the line feed scrolling when in vt mode. There are probably more situations where it should be applied, but for now this is the most conservative fix for this particular issue.
There are two other issues I should mention that I'm concerned about in this PR:
|
…, if that is the result of a line feed scroll in vt mode.
It is necessary to lock the scroll operation. We are supposed to lock any operation that moves data around in either the input or output buffers. The fact that it's a global lock pains us, but it's just the way it's been. The latter case with Adding a
That appears to be another mistake that has slipped through the cracks. We're supposed to be notifying eventing when the contents of the buffer changes in a way that is visible on the screen. Because that's so prone to error, I've asked @carlos-zamora to start down the path of turning accessibility into a renderer instead of having to separately event it all over the place. The events that accessibility needs to know are pretty much the same things that one needs to know to draw on the screen anyway (and we already have a robust system for knowing and doing that). However, he's doing it for UIA. Where the In the short term, it would make sense to propagate the In the long term, we should also book moving MSAA to a renderer like @carlos-zamora is doing for UIA. I appear to have mentioned that briefly in #410 as a performance concern as well, but I don't see an issue on the tracker to do it. Maybe @DHowett-MSFT has better GitHub-search-fu than I do. |
I think you're right about that. I added a check to see if that function was ever called without the lock being held and I couldn't get it to fail, so I think it's probably OK. When it's triggered as a result of a write operation it would already have been locked by one of the But that does suggest that the lock probably shouldn't be needed in any of the private VT methods, since they should already have been locked by the
I've added a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I apologize for not getting back to this PR earlier. I'm happy with this change overall, but I think it might need a little bit of toughing up to deal with the ExtendedAttributes
that merged after this PR was introduced.
Once that's fixed, I'll be happy with accepting this PR.
…tandard erase operations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for setting all those meta attributes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Sorry it took a while!
@msftbot make sure @miniksa signs off |
Hello @carlos-zamora! Because you've given me some instructions on how to help merge this pull request, I'll be modifying my merge approach. Here's how I understand your requirements for merging this pull request:
If this doesn't seem right to you, you can tell me to cancel these instructions and use the auto-merge policy that has been configured for this repository. Try telling me "forget everything I just told you". |
OK, I intend to read this one after lunch today. Sorry for the delay. I've been swamped with internal deliverables. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks pretty good to me. I just had two small comments before we final approve and merge.
src/buffer/out/textBuffer.cpp
Outdated
@@ -542,14 +542,22 @@ bool TextBuffer::NewlineCursor() | |||
// - <none> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arguments comment not updated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I totally overlooked that. Added in commit 171a4f9
src/host/screenInfo.cpp
Outdated
// The buffer needs to be initialized with the standard erase attributes, | ||
// i.e. the current background color, but with no meta attributes set. | ||
auto initAttributes = GetAttributes(); | ||
initAttributes.SetExtendedAttributes(ExtendedAttributes::Normal); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like this pattern (SetExtendedAttributes to Normal, set Meta to 0) is common enough that perhaps there should just be a .SetStandardErase() method on the Attributes that does both of these instead of having this pattern everywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very happy to do this. Makes things a lot neater. Added in commit 786cc36
…the operations that need that attribute state.
…ularBuffer method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent, thank you!
// erased area be filled with the current background color, but with | ||
// no additional meta attributes set. For all other cases, we just | ||
// fill with the default attributes. | ||
auto fillAttrs = TextAttribute{}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: TextAttribute fillAttrs{};
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ARGH i apparently wrote this comment eighteen months ago and never published it
literally don't care now
😄
// We don't use the _EraseAreaHelper here because _EraseSingleLineDistanceHelper does it all in one operation | ||
fSuccess = _EraseSingleLineDistanceHelper(coordBelowStartPosition, dwTotalAreaBelow, csbiex.wAttributes); | ||
// Again we need to use the default attributes, hence standardFillAttrs is false. | ||
fSuccess = _conApi->PrivateFillRegion(coordBelowStartPosition, dwTotalAreaBelow, L' ', false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit
fSuccess = _conApi->PrivateFillRegion(coordBelowStartPosition, dwTotalAreaBelow, L' ', false); | |
fSuccess = _conApi->PrivateFillRegion(coordBelowStartPosition, dwTotalAreaBelow, UNICODE_SPACE, false); |
if we can see UNICODE_SPACE here?
@j4james excellent as usual. There's a couple missing |
Hello @DHowett-MSFT! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
Just has a check, and it seems |
🎉 Handy links: |
Summary of the Pull Request
Operations that erase areas of the screen are typically meant to do so using the current color attributes, but with the rendition attributes reset (what we refer to as meta attributes). This also includes scroll operations that have to clear the area of the screen that has scrolled into view. The only exception is the Erase Scrollback operation, which needs to reset the buffer with the default attributes. This PR updates all of these cases to apply the correct attributes when scrolling and erasing.
PR Checklist
Detailed Description of the Pull Request / Additional comments
My initial plan was to use a special case legacy attribute value to indicate the "standard erase attribute" which could safely be passed through the legacy APIs. But this wouldn't cover the cases that required default attributes to be used. And then with the changes in PR #2668 and #2987, it became clear that our requirements could be better achieved with a couple of new private APIs that wouldn't have to depend on legacy attribute hacks at all.
To that end, I've added the
PrivateFillRegion
andPrivateScrollRegion
APIs to theConGetSet
interface. These are just thin wrappers around the existingSCREEN_INFORMATION::Write
method and theScrollRegion
function respectively, but with a simple boolean parameter to choose between filling with default attributes or the standard erase attributes (i.e the current colors but with meta attributes reset).With those new APIs in place, I could then update most scroll operations to use
PrivateScrollRegion
, and most erase operations to usePrivateFillRegion
.The functions affected by scrolling included:
DoSrvPrivateReverseLineFeed
(the RI command)DoSrvPrivateModifyLinesImpl
(the IL and DL commands)AdaptDispatch::_InsertDeleteHelper
(the ICH and DCH commands)AdaptDispatch::_ScrollMovement
(the SU and SD commands)The functions affected by erasing included:
AdaptDispatch::_EraseSingleLineHelper
(the EL command, and most ED variants)AdaptDispatch::EraseCharacters
(the ECH command)While updating these erase methods, I noticed that both of them also required boundary fixes similar to those in PR #2505 (i.e. the horizontal extent of the erase operation should apply to the full width of the buffer, and not just the current viewport width), so I've addressed that at the same time.
In addition to the changes above, there were also a few special cases, the first being the line feed handling, which required updating in a number of places to use the correct erase attributes:
SCREEN_INFORMATION::InitializeCursorRowAttributes
- this is used to initialise the rows that pan into view when the viewport is moved down the buffer.TextBuffer::IncrementCircularBuffer
- this occurs when we scroll passed the very end of the buffer, and a recycled row now needs to be reinitialised.AdjustCursorPosition
- when within margin boundaries, this relies on a couple of direct calls toScrollRegion
which needed to be passed the correct fill attributes.The second special case was the full screen erase sequence (
ESC 2 J
), which is handled separately from the other ED sequences. This required updating theSCREEN_INFORMATION::VtEraseAll
method to use the standard erase attributes, and also required changes to the horizontal extent of the filled area, since it should have been clearing the full buffer width (the same issue as the other erase operations mentioned above).Finally, there was the
AdaptDispatch::_EraseScrollback
method, which uses both scroll and fill operations, which could now be handled by the newPrivateScrollRegion
andPrivateFillRegion
APIs. But in this case we needed to fill with the default attributes rather than the standard erase attributes. And again this implementation needed some changes to make sure the full width of the active area was retained after the erase, similar to the horizontal boundary issues with the other erase operations.Once all these changes were made, there were a few areas of the code that could then be simplified quite a bit. The
FillConsoleOutputCharacterW
,FillConsoleOutputAttribute
, andScrollConsoleScreenBufferW
were no longer needed in theConGetSet
interface, so all of that code could now be removed. The_EraseSingleLineDistanceHelper
and_EraseAreaHelper
methods in theAdaptDispatch
class were also no longer required and could be removed.Then there were the hacks to handle legacy default colors in the
FillConsoleOutputAttributeImpl
andScrollConsoleScreenBufferWImpl
implementations. Since those hacks were only needed for VT operations, and the VT code no longer calls those methods, there was no longer a need to retain that behaviour (in fact there are probably some edge cases where that behaviour might have been considered a bug when reached via the public console APIs).Validation Steps Performed
For most of the scrolling operations there were already existing tests in place, and those could easily be extended to check that the meta attributes were correctly reset when filling the revealed lines of the scrolling region.
In the screen buffer tests, I made updates of that sort to the
ScrollOperations
method (handling SU, SD, IL, DL, and RI), theInsertChars
andDeleteChars
methods (ICH and DCH), and theVtNewlinePastViewport
method (LF). I also added a newVtNewlinePastEndOfBuffer
test to check the case where the line feed causes the viewport to pan past the end of the buffer.The erase operations, however, were being covered by adapter tests, and those aren't really suited for this kind of functionality (the same sort of issue came up in PR #2505). As a result I've had to reimplement those tests as screen buffer tests.
Most of the erase operations are covered by the
EraseTests
method, except the for the scrollback erase which has a dedicatedEraseScrollbackTests
method. I've also had to replace theHardReset
adapter test, but that was already mostly covered by theHardResetBuffer
screen buffer test, which I've now extended slightly (it could do with some more checks, but I think that can wait for a future PR when we're fixing other RIS issues).