Merge remote-tracking branch 'origin/main' into feature/llm

This commit is contained in:
Console Service Bot 2025-06-26 01:31:31 +00:00
commit 7e192f5e0c
10 changed files with 69 additions and 37 deletions

View File

@ -563,13 +563,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
}
void ConptyConnection::ClearBuffer()
void ConptyConnection::ClearBuffer(bool keepCursorRow)
{
// If we haven't connected yet, then we really don't need to do
// anything. The connection should already start clear!
if (_isConnected())
{
THROW_IF_FAILED(ConptyClearPseudoConsole(_hPC.get()));
THROW_IF_FAILED(ConptyClearPseudoConsole(_hPC.get(), keepCursorRow));
}
}

View File

@ -25,7 +25,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
void Resize(uint32_t rows, uint32_t columns);
void ResetSize();
void Close() noexcept;
void ClearBuffer();
void ClearBuffer(bool keepCursorRow);
void ShowHide(const bool show);

View File

@ -15,7 +15,7 @@ namespace Microsoft.Terminal.TerminalConnection
UInt16 ShowWindow { get; };
void ResetSize();
void ClearBuffer();
void ClearBuffer(Boolean keepCursorRow);
void ShowHide(Boolean show);

View File

@ -2264,23 +2264,42 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none>
void ControlCore::ClearBuffer(Control::ClearBufferType clearType)
{
std::wstring_view command;
switch (clearType)
{
case ClearBufferType::Screen:
command = L"\x1b[H\x1b[2J";
break;
case ClearBufferType::Scrollback:
command = L"\x1b[3J";
break;
case ClearBufferType::All:
command = L"\x1b[H\x1b[2J\x1b[3J";
break;
}
{
const auto lock = _terminal->LockForWriting();
_terminal->Write(command);
// In absolute buffer coordinates, including the scrollback (= Y is offset by the scrollback height).
const auto viewport = _terminal->GetViewport();
// The absolute cursor coordinate.
const auto cursor = _terminal->GetViewportRelativeCursorPosition();
// GH#18732: Users want the row the cursor is on to be preserved across clears.
std::wstring sequence;
if (clearType == ClearBufferType::Scrollback || clearType == ClearBufferType::All)
{
sequence.append(L"\x1b[3J");
}
if (clearType == ClearBufferType::Screen || clearType == ClearBufferType::All)
{
// Erase any viewport contents below (but not including) the cursor row.
if (viewport.Height() - cursor.y > 1)
{
fmt::format_to(std::back_inserter(sequence), FMT_COMPILE(L"\x1b[{};1H\x1b[J"), cursor.y + 2);
}
// Erase any viewport contents above (but not including) the cursor row.
if (cursor.y > 0)
{
// An SU sequence would be simpler than this DL sequence,
// but SU isn't well standardized between terminals.
// Generally speaking, it's best avoiding it.
fmt::format_to(std::back_inserter(sequence), FMT_COMPILE(L"\x1b[H\x1b[{}M"), cursor.y);
}
fmt::format_to(std::back_inserter(sequence), FMT_COMPILE(L"\x1b[1;{}H"), cursor.x + 1);
}
_terminal->Write(sequence);
}
if (clearType == Control::ClearBufferType::Screen || clearType == Control::ClearBufferType::All)
@ -2289,8 +2308,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
// Since the clearing of ConPTY occurs asynchronously, this call can result weird issues,
// where a console application still sees contents that we've already deleted, etc.
// The correct way would be for ConPTY to emit the appropriate CSI n J sequences.
conpty.ClearBuffer();
conpty.ClearBuffer(true);
}
}
}

View File

@ -248,7 +248,7 @@ namespace ControlUnitTests
_standardInit(core);
Log::Comment(L"Print 40 rows of 'Foo', and a single row of 'Bar' "
L"(leaving the cursor afer 'Bar')");
L"(leaving the cursor after 'Bar')");
for (auto i = 0; i < 40; ++i)
{
conn->WriteInput(winrt_wstring_to_array_view(L"Foo\r\n"));
@ -285,7 +285,7 @@ namespace ControlUnitTests
_standardInit(core);
Log::Comment(L"Print 40 rows of 'Foo', and a single row of 'Bar' "
L"(leaving the cursor afer 'Bar')");
L"(leaving the cursor after 'Bar')");
for (auto i = 0; i < 40; ++i)
{
conn->WriteInput(winrt_wstring_to_array_view(L"Foo\r\n"));
@ -304,9 +304,9 @@ namespace ControlUnitTests
Log::Comment(L"Check the buffer after the clear");
VERIFY_ARE_EQUAL(20, core->_terminal->GetViewport().Height());
VERIFY_ARE_EQUAL(41, core->ScrollOffset());
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
VERIFY_ARE_EQUAL(20, core->ViewHeight());
VERIFY_ARE_EQUAL(61, core->BufferHeight());
VERIFY_ARE_EQUAL(41, core->BufferHeight());
// In this test, we can't actually check if we cleared the buffer
// contents. ConPTY will handle the actual clearing of the buffer
@ -322,7 +322,7 @@ namespace ControlUnitTests
_standardInit(core);
Log::Comment(L"Print 40 rows of 'Foo', and a single row of 'Bar' "
L"(leaving the cursor afer 'Bar')");
L"(leaving the cursor after 'Bar')");
for (auto i = 0; i < 40; ++i)
{
conn->WriteInput(winrt_wstring_to_array_view(L"Foo\r\n"));

View File

@ -124,7 +124,13 @@ try
}
case PtySignal::ClearBuffer:
{
_DoClearBuffer();
ClearBufferData msg = { 0 };
if (!_GetData(&msg, sizeof(msg)))
{
return S_OK;
}
_DoClearBuffer(msg.keepCursorRow != 0);
break;
}
case PtySignal::ResizeWindow:
@ -180,7 +186,7 @@ void PtySignalInputThread::_DoResizeWindow(const ResizeWindowData& data)
_api.ResizeWindow(data.sx, data.sy);
}
void PtySignalInputThread::_DoClearBuffer() const
void PtySignalInputThread::_DoClearBuffer(const bool keepCursorRow) const
{
LockConsole();
auto Unlock = wil::scope_exit([&] { UnlockConsole(); });
@ -196,8 +202,11 @@ void PtySignalInputThread::_DoClearBuffer() const
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
auto& screenInfo = gci.GetActiveOutputBuffer();
auto& stateMachine = screenInfo.GetStateMachine();
stateMachine.ProcessString(L"\x1b[H\x1b[2J");
auto& tb = screenInfo.GetTextBuffer();
const auto cursor = tb.GetCursor().GetPosition();
tb.ClearScrollback(cursor.y, keepCursorRow ? 1 : 0);
tb.GetCursor().SetPosition({ keepCursorRow ? cursor.x : 0, 0 });
}
void PtySignalInputThread::_DoShowHide(const ShowHideData& data)

View File

@ -55,6 +55,11 @@ namespace Microsoft::Console
unsigned short show; // used as a bool, but passed as a ushort
};
struct ClearBufferData
{
unsigned short keepCursorRow;
};
struct SetParentData
{
uint64_t handle;
@ -64,7 +69,7 @@ namespace Microsoft::Console
[[nodiscard]] bool _GetData(_Out_writes_bytes_(cbBuffer) void* const pBuffer, const DWORD cbBuffer);
void _DoResizeWindow(const ResizeWindowData& data);
void _DoSetWindowParent(const SetParentData& data);
void _DoClearBuffer() const;
void _DoClearBuffer(bool keepCursorRow) const;
void _DoShowHide(const ShowHideData& data);
void _Shutdown();

View File

@ -38,7 +38,7 @@ CONPTY_EXPORT HRESULT WINAPI ConptyCreatePseudoConsole(COORD size, HANDLE hInput
CONPTY_EXPORT HRESULT WINAPI ConptyCreatePseudoConsoleAsUser(HANDLE hToken, COORD size, HANDLE hInput, HANDLE hOutput, DWORD dwFlags, HPCON* phPC);
CONPTY_EXPORT HRESULT WINAPI ConptyResizePseudoConsole(HPCON hPC, COORD size);
CONPTY_EXPORT HRESULT WINAPI ConptyClearPseudoConsole(HPCON hPC);
CONPTY_EXPORT HRESULT WINAPI ConptyClearPseudoConsole(HPCON hPC, BOOL keepCursorRow);
CONPTY_EXPORT HRESULT WINAPI ConptyShowHidePseudoConsole(HPCON hPC, bool show);
CONPTY_EXPORT HRESULT WINAPI ConptyReparentPseudoConsole(HPCON hPC, HWND newParent);
CONPTY_EXPORT HRESULT WINAPI ConptyReleasePseudoConsole(HPCON hPC);

View File

@ -278,15 +278,16 @@ HRESULT _ResizePseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const CO
// Return Value:
// - S_OK if the call succeeded, else an appropriate HRESULT for failing to
// write the clear message to the pty.
HRESULT _ClearPseudoConsole(_In_ const PseudoConsole* const pPty)
static HRESULT _ClearPseudoConsole(_In_ const PseudoConsole* const pPty, BOOL keepCursorRow) noexcept
{
if (pPty == nullptr)
{
return E_INVALIDARG;
}
unsigned short signalPacket[1];
unsigned short signalPacket[2];
signalPacket[0] = PTY_SIGNAL_CLEAR_WINDOW;
signalPacket[1] = keepCursorRow ? 1 : 0;
const auto fSuccess = WriteFile(pPty->hSignal, signalPacket, sizeof(signalPacket), nullptr, nullptr);
return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError());
@ -492,13 +493,13 @@ extern "C" HRESULT WINAPI ConptyResizePseudoConsole(_In_ HPCON hPC, _In_ COORD s
// - This is used exclusively by ConPTY to support GH#1193, GH#1882. This allows
// a terminal to clear the contents of the ConPTY buffer, which is important
// if the user would like to be able to clear the terminal-side buffer.
extern "C" HRESULT WINAPI ConptyClearPseudoConsole(_In_ HPCON hPC)
extern "C" HRESULT WINAPI ConptyClearPseudoConsole(_In_ HPCON hPC, BOOL keepCursorRow)
{
const PseudoConsole* const pPty = (PseudoConsole*)hPC;
auto hr = pPty == nullptr ? E_INVALIDARG : S_OK;
if (SUCCEEDED(hr))
{
hr = _ClearPseudoConsole(pPty);
hr = _ClearPseudoConsole(pPty, keepCursorRow);
}
return hr;
}

View File

@ -68,7 +68,6 @@ HRESULT _CreatePseudoConsole(const HANDLE hToken,
_Inout_ PseudoConsole* pPty);
HRESULT _ResizePseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const COORD size);
HRESULT _ClearPseudoConsole(_In_ const PseudoConsole* const pPty);
HRESULT _ShowHidePseudoConsole(_In_ const PseudoConsole* const pPty, const bool show);
HRESULT _ReparentPseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const HWND newParent);
void _ClosePseudoConsoleMembers(_In_ PseudoConsole* pPty);