diff --git a/src/host/_stream.cpp b/src/host/_stream.cpp index e434e413ebd..4c588db4dde 100644 --- a/src/host/_stream.cpp +++ b/src/host/_stream.cpp @@ -97,17 +97,6 @@ static void AdjustCursorPosition(SCREEN_INFORMATION& screenInfo, _In_ til::point coordCursor.y = bufferSize.height - 1; } - const auto cursorMovedPastViewport = coordCursor.y > screenInfo.GetViewport().BottomInclusive(); - - // if at right or bottom edge of window, scroll right or down one char. - if (cursorMovedPastViewport) - { - til::point WindowOrigin; - WindowOrigin.x = 0; - WindowOrigin.y = coordCursor.y - screenInfo.GetViewport().BottomInclusive(); - LOG_IF_FAILED(screenInfo.SetViewportOrigin(false, WindowOrigin, true)); - } - LOG_IF_FAILED(screenInfo.SetCursorPosition(coordCursor, false)); } @@ -167,6 +156,8 @@ void WriteCharsLegacy(SCREEN_INFORMATION& screenInfo, const std::wstring_view& t auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); auto writer = gci.GetVtWriterForBuffer(&screenInfo); + const auto snap = screenInfo.SnapOnOutput(); + // If we enter this if condition, then someone wrote text in VT mode and now switched to non-VT mode. // Since the Console APIs don't support delayed EOL wrapping, we need to first put the cursor back // to a position that the Console APIs expect (= not delayed). @@ -340,6 +331,8 @@ void WriteCharsVT(SCREEN_INFORMATION& screenInfo, const std::wstring_view& str) // may change, so get the VtIo reference now, just in case. auto writer = gci.GetVtWriterForBuffer(&screenInfo); + const auto snap = screenInfo.SnapOnOutput(); + stateMachine.ProcessString(str); if (writer) diff --git a/src/host/input.cpp b/src/host/input.cpp index fd92154020c..d56c49b2e71 100644 --- a/src/host/input.cpp +++ b/src/host/input.cpp @@ -108,7 +108,7 @@ bool ShouldTakeOverKeyboardShortcuts() void HandleGenericKeyEvent(INPUT_RECORD event, const bool generateBreak) { auto& keyEvent = event.Event.KeyEvent; - const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); auto ContinueProcessing = true; if (WI_IsAnyFlagSet(keyEvent.dwControlKeyState, CTRL_PRESSED) && @@ -167,6 +167,11 @@ void HandleGenericKeyEvent(INPUT_RECORD event, const bool generateBreak) keyEvent.bKeyDown = false; gci.pInputBuffer->Write(event); } + + if (gci.HasActiveOutputBuffer()) + { + gci.GetActiveOutputBuffer().SnapOnInput(keyEvent.wVirtualKeyCode); + } } } diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index 8d5e9217246..a894ff25a80 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -1668,39 +1668,58 @@ void SCREEN_INFORMATION::SetCursorDBMode(const bool DoubleCursor) return STATUS_SUCCESS; } -void SCREEN_INFORMATION::MakeCursorVisible(const til::point CursorPosition) +static constexpr bool IsInputKey(WORD vkey) { - til::point WindowOrigin; + return vkey != VK_CONTROL && + vkey != VK_LCONTROL && + vkey != VK_RCONTROL && + vkey != VK_MENU && + vkey != VK_LMENU && + vkey != VK_RMENU && + vkey != VK_SHIFT && + vkey != VK_LSHIFT && + vkey != VK_RSHIFT && + vkey != VK_LWIN && + vkey != VK_RWIN && + vkey != VK_SNAPSHOT; +} - if (CursorPosition.x > _viewport.RightInclusive()) - { - WindowOrigin.x = CursorPosition.x - _viewport.RightInclusive(); - } - else if (CursorPosition.x < _viewport.Left()) - { - WindowOrigin.x = CursorPosition.x - _viewport.Left(); - } - else - { - WindowOrigin.x = 0; - } +void SCREEN_INFORMATION::MakeCursorVisible(til::point position) +{ + const auto viewportOrigin = _viewport.Origin(); + const auto viewportSize = _viewport.Dimensions(); + const auto bufferSize = _textBuffer->GetSize().Dimensions(); + auto origin = viewportOrigin; - if (CursorPosition.y > _viewport.BottomInclusive()) - { - WindowOrigin.y = CursorPosition.y - _viewport.BottomInclusive(); - } - else if (CursorPosition.y < _viewport.Top()) + // Ensure the given position is in bounds. + position.x = std::clamp(position.x, 0, bufferSize.width - 1); + position.y = std::clamp(position.y, 0, bufferSize.height - 1); + + origin.y = std::min(origin.y, position.y); // shift up if above + origin.y = std::max(origin.y, position.y - (viewportSize.height - 1)); // shift down if below + + origin.x = std::min(origin.x, position.x); // shift left if left + origin.x = std::max(origin.x, position.x - (viewportSize.width - 1)); // shift right if right + + if (origin != viewportOrigin) { - WindowOrigin.y = CursorPosition.y - _viewport.Top(); + std::ignore = SetViewportOrigin(true, origin, false); } - else +} + +void SCREEN_INFORMATION::SnapOnInput(const WORD vkey) +{ + if (IsInputKey(vkey)) { - WindowOrigin.y = 0; + _makeCursorVisible(); } +} - if (WindowOrigin.x != 0 || WindowOrigin.y != 0) +void SCREEN_INFORMATION::_makeCursorVisible() +{ + if (_textBuffer->GetCursor().IsOn()) { - LOG_IF_FAILED(SetViewportOrigin(false, WindowOrigin, false)); + MakeCursorVisible(_textBuffer->GetCursor().GetPosition()); } } diff --git a/src/host/screenInfo.hpp b/src/host/screenInfo.hpp index 4ecd1a4e3e9..8c870edc31f 100644 --- a/src/host/screenInfo.hpp +++ b/src/host/screenInfo.hpp @@ -71,6 +71,18 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console void GetRequiredConsoleSizeInPixels(_Out_ til::size* const pRequiredSize) const; void MakeCurrentCursorVisible(); + void MakeCursorVisible(til::point position); + void SnapOnInput(WORD vkey); + auto SnapOnOutput() + { + const auto inBounds = _viewport.IsInBounds(_textBuffer->GetCursor().GetPosition()); + return wil::scope_exit([this, inBounds]() { + if (inBounds) + { + _makeCursorVisible(); + } + }); + } void ClipToScreenBuffer(_Inout_ til::inclusive_rect* const psrClip) const; @@ -184,8 +196,6 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console void SetCursorDBMode(const bool DoubleCursor); [[nodiscard]] NTSTATUS SetCursorPosition(const til::point Position, const bool TurnOn); - void MakeCursorVisible(const til::point CursorPosition); - [[nodiscard]] NTSTATUS UseAlternateScreenBuffer(const TextAttribute& initAttributes); void UseMainScreenBuffer(); @@ -232,6 +242,7 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console void _CalculateViewportSize(const til::rect* const prcClientArea, _Out_ til::size* const pcoordSize); void _AdjustViewportSize(const til::rect* const prcClientNew, const til::rect* const prcClientOld, const til::size* const pcoordSize); void _InternalSetViewportSize(const til::size* pcoordSize, const bool fResizeFromTop, const bool fResizeFromLeft); + void _makeCursorVisible(); static void s_CalculateScrollbarVisibility(const til::rect* const prcClientArea, const til::size* const pcoordBufferSize, diff --git a/src/interactivity/win32/Clipboard.cpp b/src/interactivity/win32/Clipboard.cpp index 8b72f01b188..03951493839 100644 --- a/src/interactivity/win32/Clipboard.cpp +++ b/src/interactivity/win32/Clipboard.cpp @@ -162,6 +162,11 @@ void Clipboard::StringPaste(_In_reads_(cchData) const wchar_t* const pData, const auto bracketedPasteMode = gci.GetBracketedPasteMode(); auto inEvents = TextToKeyEvents(pData, cchData, vtInputMode && bracketedPasteMode); gci.pInputBuffer->Write(inEvents); + + if (gci.HasActiveOutputBuffer()) + { + gci.GetActiveOutputBuffer().SnapOnInput(0); + } } catch (...) {