Skip to content

Commit 4b1a5cc

Browse files
committed
Merge branch 'main' into dev/cazamor/selfhost/11-18
2 parents 704c8dc + 19a8501 commit 4b1a5cc

File tree

14 files changed

+178
-33
lines changed

14 files changed

+178
-33
lines changed

.github/actions/spelling/expect/expect.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@ DSBUFFERDESC
495495
DSBVOLUME
496496
dsm
497497
dsound
498+
DSRCPR
498499
DSSCL
499500
DSwap
500501
DTo

src/cascadia/TerminalControl/ControlCore.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,6 +1787,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
17871787
void ControlCore::ClearSearch()
17881788
{
17891789
const auto lock = _terminal->LockForWriting();
1790+
1791+
// GH #19358: select the focused search result before clearing search
1792+
if (const auto focusedSearchResult = _terminal->GetSearchHighlightFocused())
1793+
{
1794+
_terminal->SetSelectionAnchor(focusedSearchResult->start);
1795+
_terminal->SetSelectionEnd(focusedSearchResult->end);
1796+
_renderer->TriggerSelection();
1797+
}
1798+
17901799
_terminal->SetSearchHighlights({});
17911800
_terminal->SetSearchHighlightFocused(0);
17921801
_renderer->TriggerSearchHighlight(_searcher.Results());

src/cascadia/TerminalCore/TerminalSelection.cpp

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -362,10 +362,30 @@ void Terminal::ToggleMarkMode()
362362
{
363363
// Enter Mark Mode
364364
// NOTE: directly set cursor state. We already should have locked before calling this function.
365-
if (!IsSelectionActive())
365+
if (IsSelectionActive())
366+
{
367+
// Selection already existed --> just target "end"
368+
if (WI_AreAllFlagsClear(_selectionEndpoint, SelectionEndpoint::Start | SelectionEndpoint::End))
369+
{
370+
WI_SetFlag(_selectionEndpoint, SelectionEndpoint::End);
371+
}
372+
}
373+
else if (const auto focusedSearchResult = GetSearchHighlightFocused())
374+
{
375+
// We have a focused search result --> treat it as selection
376+
*_selection.write() = SelectionInfo{
377+
.start = focusedSearchResult->start,
378+
.end = focusedSearchResult->end,
379+
.pivot = focusedSearchResult->start,
380+
.blockSelection = false,
381+
.active = true,
382+
};
383+
WI_SetFlag(_selectionEndpoint, SelectionEndpoint::End);
384+
}
385+
else
366386
{
367387
// If we're scrolled up, use the viewport origin as the selection start.
368-
// Otherwise, use the cursor position.
388+
// Otherwise, just use the cursor position.
369389
const auto cursorPos = _scrollOffset != 0 ? _GetVisibleViewport().Origin() :
370390
_activeBuffer().GetCursor().GetPosition();
371391
*_selection.write() = SelectionInfo{
@@ -377,11 +397,6 @@ void Terminal::ToggleMarkMode()
377397
};
378398
WI_SetAllFlags(_selectionEndpoint, SelectionEndpoint::Start | SelectionEndpoint::End);
379399
}
380-
else if (WI_AreAllFlagsClear(_selectionEndpoint, SelectionEndpoint::Start | SelectionEndpoint::End))
381-
{
382-
// Selection already existed
383-
WI_SetFlag(_selectionEndpoint, SelectionEndpoint::End);
384-
}
385400
_ScrollToPoint(_selection->start);
386401
_selectionMode = SelectionInteractionMode::Mark;
387402
_selectionIsTargetingUrl = false;

src/host/VtInputThread.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ using namespace Microsoft::Console::VirtualTerminal;
2121
// - hPipe - a handle to the file representing the read end of the VT pipe.
2222
// - inheritCursor - a bool indicating if the state machine should expect a
2323
// cursor positioning sequence. See MSFT:15681311.
24-
VtInputThread::VtInputThread(_In_ wil::unique_hfile hPipe, const bool inheritCursor) :
24+
VtInputThread::VtInputThread(_In_ wil::unique_hfile hPipe) :
2525
_hFile{ std::move(hPipe) }
2626
{
2727
THROW_HR_IF(E_HANDLE, _hFile.get() == INVALID_HANDLE_VALUE);
2828

2929
auto dispatch = std::make_unique<InteractDispatch>();
30-
auto engine = std::make_unique<InputStateMachineEngine>(std::move(dispatch), inheritCursor);
30+
auto engine = std::make_unique<InputStateMachineEngine>(std::move(dispatch));
3131
_pInputStateMachine = std::make_unique<StateMachine>(std::move(engine));
3232
}
3333

@@ -185,8 +185,14 @@ void VtInputThread::_InputThread()
185185
return S_OK;
186186
}
187187

188+
void VtInputThread::CaptureNextCursorPositionReport() const noexcept
189+
{
190+
auto& engine = static_cast<InputStateMachineEngine&>(_pInputStateMachine->Engine());
191+
engine.CaptureNextCursorPositionReport();
192+
}
193+
188194
til::enumset<DeviceAttribute, uint64_t> VtInputThread::WaitUntilDA1(DWORD timeout) const noexcept
189195
{
190-
const auto& engine = static_cast<InputStateMachineEngine&>(_pInputStateMachine->Engine());
196+
auto& engine = static_cast<InputStateMachineEngine&>(_pInputStateMachine->Engine());
191197
return engine.WaitUntilDA1(timeout);
192198
}

src/host/VtInputThread.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ namespace Microsoft::Console
2626
class VtInputThread
2727
{
2828
public:
29-
VtInputThread(_In_ wil::unique_hfile hPipe, const bool inheritCursor);
29+
explicit VtInputThread(_In_ wil::unique_hfile hPipe);
3030

3131
[[nodiscard]] HRESULT Start();
32+
void CaptureNextCursorPositionReport() const noexcept;
3233
til::enumset<VirtualTerminal::DeviceAttribute, uint64_t> WaitUntilDA1(DWORD timeout) const noexcept;
3334

3435
private:

src/host/VtIo.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ bool VtIo::IsUsingVt() const
155155
{
156156
if (IsValidHandle(_hInput.get()))
157157
{
158-
_pVtInputThread = std::make_unique<VtInputThread>(std::move(_hInput), _lookingForCursorPosition);
158+
_pVtInputThread = std::make_unique<VtInputThread>(std::move(_hInput));
159159
}
160160
}
161161
CATCH_RETURN();
@@ -177,7 +177,7 @@ bool VtIo::IsUsingVt() const
177177
// wait for the DA1 response below and effectively wait for both.
178178
if (_lookingForCursorPosition)
179179
{
180-
writer.WriteUTF8("\x1b[6n"); // Cursor Position Report (DSR CPR)
180+
writer.WriteDSRCPR();
181181
}
182182

183183
// GH#4999 - Send a sequence to the connected terminal to request
@@ -720,6 +720,19 @@ void VtIo::Writer::WriteASB(bool enabled) const
720720
_io->_back.append(&buf[0], std::size(buf) - 1);
721721
}
722722

723+
// DSR CPR: Cursor Position Report
724+
bool VtIo::Writer::WriteDSRCPR() const
725+
{
726+
if (!_io->_pVtInputThread)
727+
{
728+
return false;
729+
}
730+
731+
_io->_back.append("\x1b[6n");
732+
_io->_pVtInputThread->CaptureNextCursorPositionReport();
733+
return true;
734+
}
735+
723736
void VtIo::Writer::WriteWindowVisibility(bool visible) const
724737
{
725738
char buf[] = "\x1b[1t";

src/host/VtIo.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ namespace Microsoft::Console::VirtualTerminal
4040
void WriteSGR1006(bool enabled) const;
4141
void WriteDECAWM(bool enabled) const;
4242
void WriteASB(bool enabled) const;
43+
bool WriteDSRCPR() const;
4344
void WriteWindowVisibility(bool visible) const;
4445
void WriteWindowTitle(std::wstring_view title) const;
4546
void WriteAttributes(const TextAttribute& attributes) const;

src/host/screenInfo.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,6 +1404,8 @@ NT_CATCH_RETURN()
14041404

14051405
if (SUCCEEDED_NTSTATUS(status))
14061406
{
1407+
SetConptyCursorPositionMayBeWrong();
1408+
14071409
// Fire off an event to let accessibility apps know the layout has changed.
14081410
if (IsActiveScreenBuffer())
14091411
{
@@ -1422,6 +1424,86 @@ NT_CATCH_RETURN()
14221424
return status;
14231425
}
14241426

1427+
// If we're ConPTY, our copy of the buffer may be out of sync with the terminal,
1428+
// because our VT, resize reflow, etc., implementation may be different.
1429+
// For some operations we set a flag to indicate the cursor position may be wrong.
1430+
// For GetConsoleScreenBufferInfo(Ex) we then fetch the latest position from the terminal.
1431+
// This fixes some of the most glaring out of sync issues. See GH#18725.
1432+
bool SCREEN_INFORMATION::ConptyCursorPositionMayBeWrong() const noexcept
1433+
{
1434+
return _conptyCursorPositionMayBeWrong.load(std::memory_order_relaxed);
1435+
}
1436+
1437+
// This should be called whenever we do something that may desynchronize
1438+
// our cursor position from the terminal's, e.g. a buffer resize.
1439+
// See ConptyCursorPositionMayBeWrong().
1440+
void SCREEN_INFORMATION::SetConptyCursorPositionMayBeWrong() noexcept
1441+
{
1442+
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
1443+
assert(gci.IsConsoleLocked());
1444+
1445+
if (gci.IsInVtIoMode())
1446+
{
1447+
_conptyCursorPositionMayBeWrong.store(true, std::memory_order_relaxed);
1448+
}
1449+
}
1450+
1451+
// This should be called whenever we've synchronized our cursor position again.
1452+
// See ConptyCursorPositionMayBeWrong().
1453+
void SCREEN_INFORMATION::ResetConptyCursorPositionMayBeWrong() noexcept
1454+
{
1455+
_conptyCursorPositionMayBeWrong.store(false, std::memory_order_relaxed);
1456+
til::atomic_notify_all(_conptyCursorPositionMayBeWrong);
1457+
}
1458+
1459+
// Call this to synchronously wait until the ConPTY cursor position
1460+
// is known to be correct again. To do so, this emits a DSR CPR sequence
1461+
// and waits for a response from the terminal.
1462+
// See ConptyCursorPositionMayBeWrong().
1463+
void SCREEN_INFORMATION::WaitForConptyCursorPositionToBeSynchronized() noexcept
1464+
{
1465+
if (!_conptyCursorPositionMayBeWrong.load(std::memory_order_relaxed))
1466+
{
1467+
return;
1468+
}
1469+
1470+
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
1471+
1472+
{
1473+
gci.LockConsole();
1474+
const auto exit = wil::scope_exit([&] { gci.UnlockConsole(); });
1475+
auto writer = gci.GetVtWriterForBuffer(this);
1476+
1477+
if (!writer || !writer.WriteDSRCPR())
1478+
{
1479+
_conptyCursorPositionMayBeWrong.store(false, std::memory_order_relaxed);
1480+
return;
1481+
}
1482+
1483+
writer.Submit();
1484+
}
1485+
1486+
// If you were to hold the lock, the ConPTY input thread couldn't
1487+
// process any input and thus couldn't update the cursor position.
1488+
assert(!gci.IsConsoleLocked());
1489+
1490+
for (;;)
1491+
{
1492+
if (!_conptyCursorPositionMayBeWrong.load(std::memory_order::relaxed))
1493+
{
1494+
break;
1495+
}
1496+
1497+
// atomic_wait() returns false when the timeout expires.
1498+
// Technically we should decrement the timeout with each iteration,
1499+
// but I suspect infinite spurious wake-ups are a theoretical problem.
1500+
if (!til::atomic_wait(_conptyCursorPositionMayBeWrong, true, 500))
1501+
{
1502+
break;
1503+
}
1504+
}
1505+
}
1506+
14251507
// Routine Description:
14261508
// - Given a rectangle containing screen buffer coordinates (character-level positioning, not pixel)
14271509
// This method will trim the rectangle to ensure it is within the buffer.

src/host/screenInfo.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,11 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console
222222
[[nodiscard]] NTSTATUS ResizeWithReflow(const til::size coordnewScreenSize);
223223
[[nodiscard]] NTSTATUS ResizeTraditional(const til::size coordNewScreenSize);
224224

225+
bool ConptyCursorPositionMayBeWrong() const noexcept;
226+
void SetConptyCursorPositionMayBeWrong() noexcept;
227+
void ResetConptyCursorPositionMayBeWrong() noexcept;
228+
void WaitForConptyCursorPositionToBeSynchronized() noexcept;
229+
225230
private:
226231
SCREEN_INFORMATION(_In_ Microsoft::Console::Interactivity::IWindowMetrics* pMetrics,
227232
const TextAttribute popupAttributes,
@@ -280,6 +285,7 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console
280285
til::CoordType _virtualBottom;
281286

282287
std::optional<til::size> _deferredPtyResize{ std::nullopt };
288+
std::atomic<bool> _conptyCursorPositionMayBeWrong = false;
283289

284290
static void _handleDeferredResize(SCREEN_INFORMATION& siMain);
285291

src/server/ApiDispatchers.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,12 @@ constexpr T saturate(auto val)
584584
SCREEN_INFORMATION* pObj;
585585
RETURN_IF_FAILED(pObjectHandle->GetScreenBuffer(GENERIC_READ, &pObj));
586586

587+
// See ConptyCursorPositionMayBeWrong() for details.
588+
if (pObj->ConptyCursorPositionMayBeWrong())
589+
{
590+
pObj->WaitForConptyCursorPositionToBeSynchronized();
591+
}
592+
587593
m->_pApiRoutines->GetConsoleScreenBufferInfoExImpl(*pObj, ex);
588594

589595
a->FullscreenSupported = !!ex.bFullscreenSupported;

0 commit comments

Comments
 (0)