-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Don't reflow the alt buffer on resize #12719
Changes from all commits
83466a4
e94e08c
dd702c7
1209fa4
bf24cdd
dff1b94
60d2c2e
57280d8
d08d216
6529579
5d8c0d0
54dc304
57094b7
69d0973
0a1ed70
9453aa5
e658431
4c96fc0
0cfb463
bf3d79e
7237fce
65b4571
c8f0a5e
0163878
7c3e808
324aa2d
9eb0658
3b1ced3
0e711c0
31f9904
0ad1478
48d47f8
d7bce5a
2285ea5
0e8a917
764237c
3a52eab
4c1404d
4a05760
002fbbc
f209d98
66fa2dc
554fb8f
2a9eae3
f2677bd
098f6e3
e14bba4
6b89642
bea906d
fe35515
f6da747
3360201
23c6bf8
d157184
93dbb9e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -228,12 +228,38 @@ void Terminal::UpdateAppearance(const ICoreAppearance& appearance) | |
// appropriate HRESULT for failing to resize. | ||
[[nodiscard]] HRESULT Terminal::UserResize(const COORD viewportSize) noexcept | ||
{ | ||
const auto oldDimensions = _mutableViewport.Dimensions(); | ||
const auto oldDimensions = _GetMutableViewport().Dimensions(); | ||
if (viewportSize == oldDimensions) | ||
{ | ||
return S_FALSE; | ||
} | ||
|
||
// Shortcut: if we're in the alt buffer, just resize the | ||
// alt buffer and put off resizing the main buffer till we switch back. Fortunately, this is easy. We don't need to | ||
// worry about the viewport and scrollback at all! The alt buffer never has | ||
// any scrollback, so we just need to resize it and presto, we're done. | ||
if (_inAltBuffer()) | ||
{ | ||
// stash this resize for the future. | ||
_deferredResize = til::size{ viewportSize }; | ||
|
||
_altBuffer->GetCursor().StartDeferDrawing(); | ||
// we're capturing `this` here because when we exit, we want to EndDefer on the (newly created) active buffer. | ||
auto endDefer = wil::scope_exit([this]() noexcept { _altBuffer->GetCursor().EndDeferDrawing(); }); | ||
|
||
// GH#3494: We don't need to reflow the alt buffer. Apps that use the | ||
// alt buffer will redraw themselves. This prevents graphical artifacts. | ||
// | ||
// This is consistent with VTE | ||
RETURN_IF_FAILED(_altBuffer->ResizeTraditional(viewportSize)); | ||
|
||
// Since the _mutableViewport is no longer the size of the actual | ||
// viewport, then update our _altBufferSize tracker we're using to help | ||
// us out here. | ||
_altBufferSize = til::size{ viewportSize }; | ||
return S_OK; | ||
} | ||
|
||
const auto dx = ::base::ClampSub(viewportSize.X, oldDimensions.X); | ||
const short newBufferHeight = ::base::ClampAdd(viewportSize.Y, _scrollbackLines); | ||
|
||
|
@@ -415,48 +441,6 @@ void Terminal::UpdateAppearance(const ICoreAppearance& appearance) | |
// before, and shouldn't be now either. | ||
_scrollOffset = originalOffsetWasZero ? 0 : static_cast<int>(::base::ClampSub(_mutableViewport.Top(), newVisibleTop)); | ||
|
||
// Now that we've finished the hard work of resizing the main buffer and | ||
// getting the viewport back into the right spot, we need to ALSO resize the | ||
// alt buffer, if one exists. Fortunately, this is easy. We don't need to | ||
// worry about the viewport and scrollback at all! The alt buffer never has | ||
// any scrollback, so we just need to resize it and presto, we're done. | ||
if (_inAltBuffer()) | ||
{ | ||
_altBuffer->GetCursor().StartDeferDrawing(); | ||
// we're capturing `this` here because when we exit, we want to EndDefer on the (newly created) active buffer. | ||
auto endDefer = wil::scope_exit([this]() noexcept { _altBuffer->GetCursor().EndDeferDrawing(); }); | ||
|
||
// First allocate a new text buffer to take the place of the current one. | ||
std::unique_ptr<TextBuffer> newTextBuffer; | ||
try | ||
{ | ||
// GH#3848 - Stash away the current attributes | ||
const auto oldBufferAttributes = _altBuffer->GetCurrentAttributes(); | ||
newTextBuffer = std::make_unique<TextBuffer>(bufferSize, | ||
TextAttribute{}, | ||
0, // temporarily set size to 0 so it won't render. | ||
true, | ||
_mainBuffer->GetRenderer()); | ||
|
||
// start defer drawing on the new buffer | ||
newTextBuffer->GetCursor().StartDeferDrawing(); | ||
|
||
// We don't need any fancy position information. We're just gonna | ||
// resize the buffer, it's gonna be in exactly the place it is now. | ||
// There's no scrollback to worry about! | ||
|
||
RETURN_IF_FAILED(TextBuffer::Reflow(*_altBuffer.get(), | ||
*newTextBuffer.get(), | ||
_GetMutableViewport(), | ||
std::nullopt)); | ||
|
||
// Restore the active text attributes | ||
newTextBuffer->SetCurrentAttributes(oldBufferAttributes); | ||
_altBuffer.swap(newTextBuffer); | ||
} | ||
CATCH_RETURN(); | ||
} | ||
|
||
// GH#5029 - make sure to InvalidateAll here, so that we'll paint the entire visible viewport. | ||
try | ||
{ | ||
|
@@ -723,7 +707,7 @@ bool Terminal::SendMouseEvent(const COORD viewportPos, const unsigned int uiButt | |
// clamp them to be within the range [(0, 0), (W, H)]. | ||
#pragma warning(suppress : 26496) // analysis can't tell we're assigning through a reference below | ||
auto clampedPos{ viewportPos }; | ||
_mutableViewport.ToOrigin().Clamp(clampedPos); | ||
_GetMutableViewport().ToOrigin().Clamp(clampedPos); | ||
return _terminalInput->HandleMouse(clampedPos, uiButton, GET_KEYSTATE_WPARAM(states.Value()), wheelDelta, state); | ||
} | ||
|
||
|
@@ -951,7 +935,10 @@ WORD Terminal::_TakeVirtualKeyFromLastKeyEvent(const WORD scanCode) noexcept | |
|
||
Viewport Terminal::_GetMutableViewport() const noexcept | ||
{ | ||
return _inAltBuffer() ? Viewport::FromDimensions(_mutableViewport.Dimensions()) : | ||
// GH#3493: if we're in the alt buffer, then it's possible that the mutable | ||
// viewport's size hasn't been updated yet. In that case, use the | ||
// temporarily stashed _altBufferSize instead. | ||
return _inAltBuffer() ? Viewport::FromDimensions(_altBufferSize.to_win32_coord()) : | ||
_mutableViewport; | ||
} | ||
|
||
|
@@ -968,7 +955,7 @@ int Terminal::ViewStartIndex() const noexcept | |
|
||
int Terminal::ViewEndIndex() const noexcept | ||
{ | ||
return _inAltBuffer() ? _mutableViewport.Height() - 1 : _mutableViewport.BottomInclusive(); | ||
return _inAltBuffer() ? _altBufferSize.height : _mutableViewport.BottomInclusive(); | ||
} | ||
|
||
// _VisibleStartIndex is the first visible line of the buffer | ||
|
@@ -986,9 +973,14 @@ int Terminal::_VisibleEndIndex() const noexcept | |
|
||
Viewport Terminal::_GetVisibleViewport() const noexcept | ||
{ | ||
// GH#3493: if we're in the alt buffer, then it's possible that the mutable | ||
// viewport's size hasn't been updated yet. In that case, use the | ||
// temporarily stashed _altBufferSize instead. | ||
const COORD origin{ 0, gsl::narrow<short>(_VisibleStartIndex()) }; | ||
const COORD size{ _inAltBuffer() ? _altBufferSize.to_win32_coord() : | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is almost aching for another helper ... or, seems like you could use the Dimensions off the GetMutableViewport helper's return value? I'd prefer concentrating the magic in one place. :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't we make a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bear in mind that we'll probably need to support more buffers than just main and alt in the long term. So I'd expect properties like this would ultimately be part of the text buffer itself, and then you could just do something like |
||
_mutableViewport.Dimensions() }; | ||
return Viewport::FromDimensions(origin, | ||
_mutableViewport.Dimensions()); | ||
size); | ||
} | ||
|
||
// Writes a string of text to the buffer, then moves the cursor (and viewport) | ||
|
@@ -1121,46 +1113,50 @@ void Terminal::_AdjustCursorPosition(const COORD proposedPosition) | |
cursor.SetPosition(proposedCursorPosition); | ||
|
||
// Move the viewport down if the cursor moved below the viewport. | ||
bool updatedViewport = false; | ||
const auto scrollAmount = std::max(0, proposedCursorPosition.Y - _mutableViewport.BottomInclusive()); | ||
if (scrollAmount > 0) | ||
// Obviously, don't need to do this in the alt buffer. | ||
if (!_inAltBuffer()) | ||
{ | ||
const auto newViewTop = std::max(0, proposedCursorPosition.Y - (_mutableViewport.Height() - 1)); | ||
// In the alt buffer, we never need to adjust _mutableViewport, which is the viewport of the main buffer. | ||
if (!_inAltBuffer() && newViewTop != _mutableViewport.Top()) | ||
bool updatedViewport = false; | ||
const auto scrollAmount = std::max(0, proposedCursorPosition.Y - _mutableViewport.BottomInclusive()); | ||
if (scrollAmount > 0) | ||
{ | ||
_mutableViewport = Viewport::FromDimensions({ 0, gsl::narrow<short>(newViewTop) }, | ||
_mutableViewport.Dimensions()); | ||
updatedViewport = true; | ||
const auto newViewTop = std::max(0, proposedCursorPosition.Y - (_mutableViewport.Height() - 1)); | ||
// In the alt buffer, we never need to adjust _mutableViewport, which is the viewport of the main buffer. | ||
if (newViewTop != _mutableViewport.Top()) | ||
{ | ||
_mutableViewport = Viewport::FromDimensions({ 0, gsl::narrow<short>(newViewTop) }, | ||
_mutableViewport.Dimensions()); | ||
updatedViewport = true; | ||
} | ||
} | ||
} | ||
|
||
// If the viewport moved, or we circled the buffer, we might need to update | ||
// our _scrollOffset | ||
if (!_inAltBuffer() && (updatedViewport || newRows != 0)) | ||
{ | ||
const auto oldScrollOffset = _scrollOffset; | ||
// If the viewport moved, or we circled the buffer, we might need to update | ||
// our _scrollOffset | ||
if (updatedViewport || newRows != 0) | ||
{ | ||
const auto oldScrollOffset = _scrollOffset; | ||
|
||
// scroll if... | ||
// - no selection is active | ||
// - viewport is already at the bottom | ||
const bool scrollToOutput = !IsSelectionActive() && _scrollOffset == 0; | ||
// scroll if... | ||
// - no selection is active | ||
// - viewport is already at the bottom | ||
const bool scrollToOutput = !IsSelectionActive() && _scrollOffset == 0; | ||
|
||
_scrollOffset = scrollToOutput ? 0 : _scrollOffset + scrollAmount + newRows; | ||
_scrollOffset = scrollToOutput ? 0 : _scrollOffset + scrollAmount + newRows; | ||
|
||
// Clamp the range to make sure that we don't scroll way off the top of the buffer | ||
_scrollOffset = std::clamp(_scrollOffset, | ||
0, | ||
_activeBuffer().GetSize().Height() - _mutableViewport.Height()); | ||
// Clamp the range to make sure that we don't scroll way off the top of the buffer | ||
_scrollOffset = std::clamp(_scrollOffset, | ||
0, | ||
_activeBuffer().GetSize().Height() - _mutableViewport.Height()); | ||
|
||
// If the new scroll offset is different, then we'll still want to raise a scroll event | ||
updatedViewport = updatedViewport || (oldScrollOffset != _scrollOffset); | ||
} | ||
// If the new scroll offset is different, then we'll still want to raise a scroll event | ||
updatedViewport = updatedViewport || (oldScrollOffset != _scrollOffset); | ||
} | ||
|
||
// If the viewport moved, then send a scrolling notification. | ||
if (updatedViewport) | ||
{ | ||
_NotifyScrollEvent(); | ||
// If the viewport moved, then send a scrolling notification. | ||
if (updatedViewport) | ||
{ | ||
_NotifyScrollEvent(); | ||
} | ||
} | ||
|
||
if (rowsPushedOffTopOfBuffer != 0) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -328,6 +328,9 @@ class Microsoft::Terminal::Core::Terminal final : | |
SHORT _scrollbackLines; | ||
bool _detectURLs{ false }; | ||
|
||
til::size _altBufferSize; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah that makes sense. In hindsight, it's obvious that it was easier to mess this up when both buffers were sharing. |
||
std::optional<til::size> _deferredResize{ std::nullopt }; | ||
|
||
// _scrollOffset is the number of lines above the viewport that are currently visible | ||
// If _scrollOffset is 0, then the visible region of the buffer is the viewport. | ||
int _scrollOffset; | ||
|
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.
Beautiful.