Add missing lock guards on Terminal access (#15894)

`Terminal` is used concurrently by at least 4 threads. The table
below lists the class members and the threads that access them
to the best of my knowledge. Where:
* UI: UI Thread
* BG: Background worker threads (`winrt::resume_background`)
* RD: Render thread
* VT: VT connection thread

|                                    | UI | BG | RD | VT |
|------------------------------------|----|----|----|----|
| `_pfnWriteInput`                   | x  | x  |    | x  |
| `_pfnWarningBell`                  |    |    |    | x  |
| `_pfnTitleChanged`                 |    |    |    | x  |
| `_pfnCopyToClipboard`              |    |    |    | x  |
| `_pfnScrollPositionChanged`        | x  | x  |    | x  |
| `_pfnCursorPositionChanged`        |    |    |    | x  |
| `_pfnTaskbarProgressChanged`       |    |    |    | x  |
| `_pfnShowWindowChanged`            |    |    |    | x  |
| `_pfnPlayMidiNote`                 |    |    |    | x  |
| `_pfnCompletionsChanged`           |    |    |    | x  |
| `_renderSettings`                  | x  |    | x  | x  |
| `_stateMachine`                    | x  |    |    | x  |
| `_terminalInput`                   | x  |    |    | x  |
| `_title`                           | x  |    | x  | x  |
| `_startingTitle`                   | x  |    | x  |    |
| `_startingTabColor`                | x  |    |    |    |
| `_defaultCursorShape`              | x  |    |    | x  |
| `_systemMode`                      |    | x  | x  | x  |
| `_snapOnInput`                     | x  | x  |    |    |
| `_altGrAliasing`                   | x  |    |    |    |
| `_suppressApplicationTitle`        | x  |    |    | x  |
| `_trimBlockSelection`              | x  |    |    |    |
| `_autoMarkPrompts`                 | x  |    |    |    |
| `_taskbarState`                    | x  |    |    | x  |
| `_taskbarProgress`                 | x  |    |    | x  |
| `_workingDirectory`                | x  |    |    | x  |
| `_fontInfo`                        | x  |    | x  |    |
| `_selection`                       | x  | x  | x  | x  |
| `_blockSelection`                  | x  | x  | x  |    |
| `_wordDelimiters`                  | x  | x  |    |    |
| `_multiClickSelectionMode`         | x  | x  | x  |    |
| `_selectionMode`                   | x  | x  | x  |    |
| `_selectionIsTargetingUrl`         | x  | x  | x  |    |
| `_selectionEndpoint`               | x  | x  | x  |    |
| `_anchorInactiveSelectionEndpoint` | x  | x  | x  |    |
| `_mainBuffer`                      | x  | x  | x  | x  |
| `_altBuffer`                       | x  | x  | x  | x  |
| `_mutableViewport`                 | x  |    | x  | x  |
| `_scrollbackLines`                 | x  |    |    |    |
| `_detectURLs`                      | x  |    |    |    |
| `_altBufferSize`                   | x  | x  | x  | x  |
| `_deferredResize`                  | x  |    |    | x  |
| `_scrollOffset`                    | x  | x  | x  | x  |
| `_patternIntervalTree`             | x  | x  | x  | x  |
| `_lastKeyEventCodes`               | x  |    |    |    |
| `_currentPromptState`              | x  |    |    | x  |

Only 7 members are specific to one thread and don't require locking.
All other members require some for of locking to be safe for use.

To address the issue this changeset adds `LockForReading/LockForWriting`
calls everywhere `_terminal` is accessed in `ControlCore/HwndTerminal`.
Additionally, to ensure these issues don't pop up anymore, it adds to
all `Terminal` functions a debug assertion that the lock is being held.

Finally, because this changeset started off rather modest, it contains
changes that I initially made without being aware about the extent of
the issue. It simplifies the access around `_patternIntervalTree` by
making `_InvalidatePatternTree()` directly use that member.
Furthermore, it simplifies `_terminal->SetCursorOn(!IsCursorOn())` to
`BlinkCursor()`, allowing the code to be shared with `HwndTerminal`.

Ideally `Terminal` should not be that much of a class so that we don't
need such coarse locking. Splitting out selection and rendering state
should allow deduplicating code with conhost and use finer locking.

Closes #9617

## Validation Steps Performed
I tried to use as many Windows Terminal features as I could and fixed
every occurrence of `_assertLocked()` failures.
This commit is contained in:
Leonard Hecker
2023-09-19 18:59:39 +02:00
committed by GitHub
parent f494d68f11
commit 4370da9549
9 changed files with 443 additions and 269 deletions

View File

@@ -28,6 +28,7 @@ CYICON
Dacl
dataobject
dcomp
debugbreak
delayimp
DERR
dlldata

View File

@@ -43,13 +43,13 @@ try
return DefWindowProc(hwnd, WM_NCCREATE, wParam, lParam);
}
#pragma warning(suppress : 26490) // Win32 APIs can only store void*, have to use reinterpret_cast
auto terminal = reinterpret_cast<HwndTerminal*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
const auto publicTerminal = reinterpret_cast<HwndTerminal*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
if (terminal)
if (publicTerminal)
{
if (_IsMouseMessage(uMsg))
{
if (terminal->_CanSendVTMouseInput() && terminal->_SendMouseEvent(uMsg, wParam, lParam))
if (publicTerminal->_CanSendVTMouseInput() && publicTerminal->_SendMouseEvent(uMsg, wParam, lParam))
{
// GH#6401: Capturing the mouse ensures that we get drag/release events
// even if the user moves outside the window.
@@ -81,14 +81,14 @@ try
case WM_GETOBJECT:
if (lParam == UiaRootObjectId)
{
return UiaReturnRawElementProvider(hwnd, wParam, lParam, terminal->_GetUiaProvider());
return UiaReturnRawElementProvider(hwnd, wParam, lParam, publicTerminal->_GetUiaProvider());
}
break;
case WM_LBUTTONDOWN:
LOG_IF_FAILED(terminal->_StartSelection(lParam));
LOG_IF_FAILED(publicTerminal->_StartSelection(lParam));
return 0;
case WM_LBUTTONUP:
terminal->_singleClickTouchdownPos = std::nullopt;
publicTerminal->_singleClickTouchdownPos = std::nullopt;
[[fallthrough]];
case WM_MBUTTONUP:
case WM_RBUTTONUP:
@@ -97,30 +97,31 @@ try
case WM_MOUSEMOVE:
if (WI_IsFlagSet(wParam, MK_LBUTTON))
{
LOG_IF_FAILED(terminal->_MoveSelection(lParam));
LOG_IF_FAILED(publicTerminal->_MoveSelection(lParam));
return 0;
}
break;
case WM_RBUTTONDOWN:
if (const auto& termCore{ terminal->_terminal }; termCore && termCore->IsSelectionActive())
if (publicTerminal->_terminal && publicTerminal->_terminal->IsSelectionActive())
{
try
{
const auto bufferData = termCore->RetrieveSelectedTextFromBuffer(false);
LOG_IF_FAILED(terminal->_CopyTextToSystemClipboard(bufferData, true));
TerminalClearSelection(terminal);
const auto lock = publicTerminal->_terminal->LockForWriting();
const auto bufferData = publicTerminal->_terminal->RetrieveSelectedTextFromBuffer(false);
LOG_IF_FAILED(publicTerminal->_CopyTextToSystemClipboard(bufferData, true));
publicTerminal->_ClearSelection();
}
CATCH_LOG();
}
else
{
terminal->_PasteTextFromClipboard();
publicTerminal->_PasteTextFromClipboard();
}
return 0;
case WM_DESTROY:
// Release Terminal's hwnd so Teardown doesn't try to destroy it again
terminal->_hwnd.release();
terminal->Teardown();
publicTerminal->_hwnd.release();
publicTerminal->Teardown();
return 0;
default:
break;
@@ -195,6 +196,8 @@ HwndTerminal::~HwndTerminal()
HRESULT HwndTerminal::Initialize()
{
_terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
const auto lock = _terminal->LockForWriting();
auto renderThread = std::make_unique<::Microsoft::Console::Render::RenderThread>();
auto* const localPointerToThread = renderThread.get();
auto& renderSettings = _terminal->GetRenderSettings();
@@ -304,7 +307,6 @@ void HwndTerminal::_UpdateFont(int newDpi)
return;
}
_currentDpi = newDpi;
auto lock = _terminal->LockForWriting();
// TODO: MSFT:20895307 If the font doesn't exist, this doesn't
// actually fail. We need a way to gracefully fallback.
@@ -323,10 +325,10 @@ IRawElementProviderSimple* HwndTerminal::_GetUiaProvider() noexcept
{
return nullptr;
}
auto lock = _terminal->LockForWriting();
LOG_IF_FAILED(::Microsoft::WRL::MakeAndInitialize<HwndTerminalAutomationPeer>(&_uiaProvider, this->GetRenderData(), this));
_uiaEngine = std::make_unique<::Microsoft::Console::Render::UiaEngine>(_uiaProvider.Get());
LOG_IF_FAILED(_uiaEngine->Enable());
const auto lock = _terminal->LockForWriting();
_renderer->AddRenderEngine(_uiaEngine.get());
}
catch (...)
@@ -344,7 +346,7 @@ HRESULT HwndTerminal::Refresh(const til::size windowSize, _Out_ til::size* dimen
RETURN_HR_IF_NULL(E_NOT_VALID_STATE, _terminal);
RETURN_HR_IF_NULL(E_INVALIDARG, dimensions);
auto lock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
_terminal->ClearSelection();
@@ -381,37 +383,45 @@ void HwndTerminal::SendOutput(std::wstring_view data)
{
return;
}
const auto lock = _terminal->LockForWriting();
_terminal->Write(data);
}
HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal)
{
auto _terminal = std::make_unique<HwndTerminal>(parentHwnd);
RETURN_IF_FAILED(_terminal->Initialize());
auto publicTerminal = std::make_unique<HwndTerminal>(parentHwnd);
*hwnd = _terminal->GetHwnd();
*terminal = _terminal.release();
RETURN_IF_FAILED(publicTerminal->Initialize());
*hwnd = publicTerminal->GetHwnd();
*terminal = publicTerminal.release();
return S_OK;
}
void _stdcall TerminalRegisterScrollCallback(void* terminal, void __stdcall callback(int, int, int))
try
{
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
publicTerminal->RegisterScrollCallback(callback);
}
CATCH_LOG()
void _stdcall TerminalRegisterWriteCallback(void* terminal, const void __stdcall callback(wchar_t*))
try
{
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
publicTerminal->RegisterWriteCallback(callback);
}
CATCH_LOG()
void _stdcall TerminalSendOutput(void* terminal, LPCWSTR data)
try
{
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
publicTerminal->SendOutput(data);
}
CATCH_LOG()
/// <summary>
/// Triggers a terminal resize using the new width and height in pixel.
@@ -446,13 +456,18 @@ HRESULT _stdcall TerminalTriggerResize(_In_ void* terminal, _In_ til::CoordType
/// <param name="dimensionsInPixels">Out parameter with the new size of the renderer.</param>
/// <returns>HRESULT of the attempted resize.</returns>
HRESULT _stdcall TerminalTriggerResizeWithDimension(_In_ void* terminal, _In_ til::size dimensionsInCharacters, _Out_ til::size* dimensionsInPixels)
try
{
RETURN_HR_IF_NULL(E_INVALIDARG, dimensionsInPixels);
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
const auto viewInCharacters = Viewport::FromDimensions(dimensionsInCharacters);
const auto viewInPixels = publicTerminal->_renderEngine->GetViewportInPixels(viewInCharacters);
Viewport viewInPixels;
{
const auto viewInCharacters = Viewport::FromDimensions(dimensionsInCharacters);
const auto lock = publicTerminal->_terminal->LockForReading();
viewInPixels = publicTerminal->_renderEngine->GetViewportInPixels(viewInCharacters);
}
dimensionsInPixels->width = viewInPixels.Width();
dimensionsInPixels->height = viewInPixels.Height();
@@ -461,6 +476,7 @@ HRESULT _stdcall TerminalTriggerResizeWithDimension(_In_ void* terminal, _In_ ti
return TerminalTriggerResize(terminal, viewInPixels.Width(), viewInPixels.Height(), &unused);
}
CATCH_RETURN()
/// <summary>
/// Calculates the amount of rows and columns that fit in the provided width and height.
@@ -471,10 +487,12 @@ HRESULT _stdcall TerminalTriggerResizeWithDimension(_In_ void* terminal, _In_ ti
/// <param name="dimensions">Out parameter containing the columns and rows that fit the new size.</param>
/// <returns>HRESULT of the calculation.</returns>
HRESULT _stdcall TerminalCalculateResize(_In_ void* terminal, _In_ til::CoordType width, _In_ til::CoordType height, _Out_ til::size* dimensions)
try
{
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
const auto viewInPixels = Viewport::FromDimensions({ width, height });
const auto lock = publicTerminal->_terminal->LockForReading();
const auto viewInCharacters = publicTerminal->_renderEngine->GetViewportInCharacters(viewInPixels);
dimensions->width = viewInCharacters.Width();
@@ -482,20 +500,27 @@ HRESULT _stdcall TerminalCalculateResize(_In_ void* terminal, _In_ til::CoordTyp
return S_OK;
}
CATCH_RETURN()
void _stdcall TerminalDpiChanged(void* terminal, int newDpi)
try
{
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
const auto lock = publicTerminal->_terminal->LockForWriting();
publicTerminal->_UpdateFont(newDpi);
}
CATCH_LOG()
void _stdcall TerminalUserScroll(void* terminal, int viewTop)
try
{
if (const auto publicTerminal = static_cast<const HwndTerminal*>(terminal); publicTerminal && publicTerminal->_terminal)
{
const auto lock = publicTerminal->_terminal->LockForWriting();
publicTerminal->_terminal->UserScrollViewport(viewTop);
}
}
CATCH_LOG()
const unsigned int HwndTerminal::_NumberOfClicks(til::point point, std::chrono::steady_clock::time_point timestamp) noexcept
{
@@ -522,7 +547,7 @@ try
GET_Y_LPARAM(lParam),
};
auto lock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
const auto altPressed = GetKeyState(VK_MENU) < 0;
const til::size fontSize{ this->_actualFont.GetSize() };
@@ -566,7 +591,7 @@ try
GET_Y_LPARAM(lParam),
};
auto lock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
const til::size fontSize{ this->_actualFont.GetSize() };
RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.area() == 0); // either dimension = 0, area == 0
@@ -596,45 +621,57 @@ try
}
CATCH_RETURN();
void HwndTerminal::_ClearSelection() noexcept
try
void HwndTerminal::_ClearSelection()
{
if (!_terminal)
{
return;
}
auto lock{ _terminal->LockForWriting() };
_terminal->ClearSelection();
_renderer->TriggerSelection();
}
CATCH_LOG();
void _stdcall TerminalClearSelection(void* terminal)
try
{
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
const auto lock = publicTerminal->_terminal->LockForWriting();
publicTerminal->_ClearSelection();
}
CATCH_LOG()
bool _stdcall TerminalIsSelectionActive(void* terminal)
try
{
if (const auto publicTerminal = static_cast<const HwndTerminal*>(terminal); publicTerminal && publicTerminal->_terminal)
{
const auto lock = publicTerminal->_terminal->LockForReading();
return publicTerminal->_terminal->IsSelectionActive();
}
return false;
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return false;
}
// Returns the selected text in the terminal.
const wchar_t* _stdcall TerminalGetSelection(void* terminal)
try
{
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
if (!publicTerminal || !publicTerminal->_terminal)
{
return nullptr;
}
const auto bufferData = publicTerminal->_terminal->RetrieveSelectedTextFromBuffer(false);
publicTerminal->_ClearSelection();
TextBuffer::TextAndColor bufferData;
{
const auto lock = publicTerminal->_terminal->LockForWriting();
bufferData = publicTerminal->_terminal->RetrieveSelectedTextFromBuffer(false);
publicTerminal->_ClearSelection();
}
// convert text: vector<string> --> string
std::wstring selectedText;
@@ -646,6 +683,11 @@ const wchar_t* _stdcall TerminalGetSelection(void* terminal)
auto returnText = wil::make_cotaskmem_string_nothrow(selectedText.c_str());
return returnText.release();
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return nullptr;
}
static ControlKeyStates getControlKeyState() noexcept
{
@@ -683,6 +725,7 @@ bool HwndTerminal::_CanSendVTMouseInput() const noexcept
{
// Only allow the transit of mouse events if shift isn't pressed.
const auto shiftPressed = GetKeyState(VK_SHIFT) < 0;
const auto lock = _terminal->LockForReading();
return !shiftPressed && _focused && _terminal && _terminal->IsTrackingMouseInput();
}
@@ -715,6 +758,7 @@ try
WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed)
};
const auto lock = _terminal->LockForWriting();
return _terminal->SendMouseEvent(cursorPosition / fontSize, uMsg, getControlKeyState(), wheelDelta, state);
}
catch (...)
@@ -740,6 +784,7 @@ try
{
_uiaProvider->RecordKeyEvent(vkey);
}
const auto lock = _terminal->LockForWriting();
_terminal->SendKeyEvent(vkey, scanCode, modifiers, keyDown);
}
CATCH_LOG();
@@ -751,7 +796,10 @@ try
{
return;
}
else if (_terminal->IsSelectionActive())
const auto lock = _terminal->LockForWriting();
if (_terminal->IsSelectionActive())
{
_ClearSelection();
if (ch == UNICODE_ESC)
@@ -778,16 +826,20 @@ try
CATCH_LOG();
void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, WORD flags, bool keyDown)
try
{
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
publicTerminal->_SendKeyEvent(vkey, scanCode, flags, keyDown);
}
CATCH_LOG()
void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode, WORD flags)
try
{
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
publicTerminal->_SendCharEvent(ch, scanCode, flags);
}
CATCH_LOG()
void _stdcall DestroyTerminal(void* terminal)
{
@@ -803,8 +855,9 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font
{
return;
}
{
auto lock = publicTerminal->_terminal->LockForWriting();
const auto lock = publicTerminal->_terminal->LockForWriting();
auto& renderSettings = publicTerminal->_terminal->GetRenderSettings();
renderSettings.SetColorTableEntry(TextColor::DEFAULT_FOREGROUND, theme.DefaultForeground);
@@ -818,13 +871,13 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font
// It's using gsl::at to check the index is in bounds, but the analyzer still calls this array-to-pointer-decay
[[gsl::suppress(bounds .3)]] renderSettings.SetColorTableEntry(tableIndex, gsl::at(theme.ColorTable, tableIndex));
}
publicTerminal->_terminal->SetCursorStyle(static_cast<DispatchTypes::CursorStyle>(theme.CursorStyle));
publicTerminal->_desiredFont = { fontFamily, 0, DEFAULT_FONT_WEIGHT, static_cast<float>(fontSize), CP_UTF8 };
publicTerminal->_UpdateFont(newDpi);
}
publicTerminal->_terminal->SetCursorStyle(static_cast<DispatchTypes::CursorStyle>(theme.CursorStyle));
publicTerminal->_desiredFont = { fontFamily, 0, DEFAULT_FONT_WEIGHT, static_cast<float>(fontSize), CP_UTF8 };
publicTerminal->_UpdateFont(newDpi);
// When the font changes the terminal dimensions need to be recalculated since the available row and column
// space will have changed.
RECT windowRect;
@@ -836,29 +889,35 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font
}
void _stdcall TerminalBlinkCursor(void* terminal)
{
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
if (!publicTerminal || !publicTerminal->_terminal || (!publicTerminal->_terminal->IsCursorBlinkingAllowed() && publicTerminal->_terminal->IsCursorVisible()))
{
return;
}
publicTerminal->_terminal->SetCursorOn(!publicTerminal->_terminal->IsCursorOn());
}
void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible)
try
{
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
if (!publicTerminal || !publicTerminal->_terminal)
{
return;
}
const auto lock = publicTerminal->_terminal->LockForWriting();
publicTerminal->_terminal->BlinkCursor();
}
CATCH_LOG()
void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible)
try
{
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
if (!publicTerminal || !publicTerminal->_terminal)
{
return;
}
const auto lock = publicTerminal->_terminal->LockForWriting();
publicTerminal->_terminal->SetCursorOn(visible);
}
CATCH_LOG()
void __stdcall TerminalSetFocus(void* terminal)
{
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
publicTerminal->_focused = true;
if (auto uiaEngine = publicTerminal->_uiaEngine.get())
{
@@ -868,7 +927,7 @@ void __stdcall TerminalSetFocus(void* terminal)
void __stdcall TerminalKillFocus(void* terminal)
{
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
publicTerminal->_focused = false;
if (auto uiaEngine = publicTerminal->_uiaEngine.get())
{
@@ -923,7 +982,11 @@ try
{
const auto& fontData = _actualFont;
const int iFontHeightPoints = fontData.GetUnscaledSize().height; // this renderer uses points already
const auto bgColor = _terminal->GetAttributeColors({}).second;
COLORREF bgColor;
{
const auto lock = _terminal->LockForReading();
bgColor = _terminal->GetAttributeColors({}).second;
}
auto HTMLToPlaceOnClip = TextBuffer::GenHTML(rows, iFontHeightPoints, fontData.GetFaceName(), bgColor);
_CopyToSystemClipboard(HTMLToPlaceOnClip, L"HTML Format");
@@ -993,30 +1056,16 @@ void HwndTerminal::_PasteTextFromClipboard() noexcept
return;
}
auto pwstr = static_cast<PCWCH>(GlobalLock(ClipboardDataHandle));
_StringPaste(pwstr);
if (const auto pwstr = static_cast<PCWCH>(GlobalLock(ClipboardDataHandle)))
{
_WriteTextToConnection(pwstr);
}
GlobalUnlock(ClipboardDataHandle);
CloseClipboard();
}
void HwndTerminal::_StringPaste(const wchar_t* const pData) noexcept
{
if (pData == nullptr)
{
return;
}
try
{
std::wstring text(pData);
_WriteTextToConnection(text);
}
CATCH_LOG();
}
til::size HwndTerminal::GetFontSize() const noexcept
{
return _actualFont.GetSize();
@@ -1045,6 +1094,7 @@ void HwndTerminal::ChangeViewport(const til::inclusive_rect& NewWindow)
{
return;
}
const auto lock = _terminal->LockForWriting();
_terminal->UserScrollViewport(NewWindow.top);
}

View File

@@ -112,14 +112,13 @@ private:
HRESULT _CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows, const bool fAlsoCopyFormatting);
HRESULT _CopyToSystemClipboard(std::string stringToCopy, LPCWSTR lpszFormat);
void _PasteTextFromClipboard() noexcept;
void _StringPaste(const wchar_t* const pData) noexcept;
const unsigned int _NumberOfClicks(til::point clickPos, std::chrono::steady_clock::time_point clickTime) noexcept;
HRESULT _StartSelection(LPARAM lParam) noexcept;
HRESULT _MoveSelection(LPARAM lParam) noexcept;
IRawElementProviderSimple* _GetUiaProvider() noexcept;
void _ClearSelection() noexcept;
void _ClearSelection();
bool _CanSendVTMouseInput() const noexcept;
bool _SendMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept;

View File

@@ -86,6 +86,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_settings = winrt::make_self<implementation::ControlSettings>(settings, unfocusedAppearance);
_terminal = std::make_shared<::Microsoft::Terminal::Core::Terminal>();
const auto lock = _terminal->LockForWriting();
_setupDispatcherAndCallbacks();
@@ -197,7 +198,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
[weakTerminal = std::weak_ptr{ _terminal }]() {
if (const auto t = weakTerminal.lock())
{
auto lock = t->LockForWriting();
const auto lock = t->LockForWriting();
t->UpdatePatternsUnderLock();
}
});
@@ -282,6 +283,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Get our current size in rows/cols, and hook them up to
// this connection too.
{
const auto lock = _terminal->LockForReading();
const auto vp = _terminal->GetViewport();
const auto width = vp.Width();
const auto height = vp.Height();
@@ -318,7 +320,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_compositionScale = compositionScale;
{ // scope for terminalLock
auto terminalLock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
if (_initializedTerminal.load(std::memory_order_relaxed))
{
@@ -427,6 +429,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
if (_initializedTerminal.load(std::memory_order_relaxed))
{
const auto lock = _terminal->LockForWriting();
_renderer->EnablePainting();
}
}
@@ -489,6 +492,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_handleControlC();
}
const auto lock = _terminal->LockForWriting();
return _terminal->SendCharEvent(ch, scanCode, modifiers);
}
@@ -517,18 +521,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// modifier key. We'll wait for a real keystroke to dismiss the
// GH #7395 - don't update selection when taking PrintScreen
// selection.
return HasSelection() && ::Microsoft::Terminal::Core::Terminal::IsInputKey(vkey);
return _terminal->IsSelectionActive() && ::Microsoft::Terminal::Core::Terminal::IsInputKey(vkey);
}
bool ControlCore::TryMarkModeKeybinding(const WORD vkey,
const ::Microsoft::Terminal::Core::ControlKeyStates mods)
{
auto lock = _terminal->LockForWriting();
if (_shouldTryUpdateSelection(vkey) && _terminal->SelectionMode() == ::Terminal::SelectionInteractionMode::Mark)
{
if (vkey == 'A' && !mods.IsAltPressed() && !mods.IsShiftPressed() && mods.IsCtrlPressed())
{
// Ctrl + A --> Select all
auto lock = _terminal->LockForWriting();
_terminal->SelectAll();
_updateSelectionUI();
return true;
@@ -536,7 +541,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
else if (vkey == VK_TAB && !mods.IsAltPressed() && !mods.IsCtrlPressed() && _settings->DetectURLs())
{
// [Shift +] Tab --> next/previous hyperlink
auto lock = _terminal->LockForWriting();
const auto direction = mods.IsShiftPressed() ? ::Terminal::SearchDirection::Backward : ::Terminal::SearchDirection::Forward;
_terminal->SelectHyperlink(direction);
_updateSelectionUI();
@@ -545,14 +549,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
else if (vkey == VK_RETURN && mods.IsCtrlPressed() && !mods.IsAltPressed() && !mods.IsShiftPressed())
{
// Ctrl + Enter --> Open URL
auto lock = _terminal->LockForReading();
if (const auto uri = _terminal->GetHyperlinkAtBufferPosition(_terminal->GetSelectionAnchor()); !uri.empty())
{
lock.unlock();
_OpenHyperlinkHandlers(*this, winrt::make<OpenHyperlinkEventArgs>(winrt::hstring{ uri }));
}
else
{
const auto selectedText = _terminal->GetTextBuffer().GetPlainText(_terminal->GetSelectionAnchor(), _terminal->GetSelectionEnd());
lock.unlock();
_OpenHyperlinkHandlers(*this, winrt::make<OpenHyperlinkEventArgs>(winrt::hstring{ selectedText }));
}
return true;
@@ -560,7 +565,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
else if (vkey == VK_RETURN && !mods.IsCtrlPressed() && !mods.IsAltPressed())
{
// [Shift +] Enter --> copy text
// Don't lock here! CopySelectionToClipboard already locks for you!
CopySelectionToClipboard(mods.IsShiftPressed(), nullptr);
_terminal->ClearSelection();
_updateSelectionUI();
@@ -575,7 +579,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
else if (const auto updateSlnParams{ _terminal->ConvertKeyEventToUpdateSelectionParams(mods, vkey) })
{
// try to update the selection
auto lock = _terminal->LockForWriting();
_terminal->UpdateSelection(updateSlnParams->first, updateSlnParams->second, mods);
_updateSelectionUI();
return true;
@@ -599,6 +602,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const ControlKeyStates modifiers,
const bool keyDown)
{
const auto lock = _terminal->LockForWriting();
// Update the selection, if it's present
// GH#8522, GH#3758 - Only modify the selection on key _down_. If we
// modify on key up, then there's chance that we'll immediately dismiss
@@ -608,7 +613,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// try to update the selection
if (const auto updateSlnParams{ _terminal->ConvertKeyEventToUpdateSelectionParams(modifiers, vkey) })
{
auto lock = _terminal->LockForWriting();
_terminal->UpdateSelection(updateSlnParams->first, updateSlnParams->second, modifiers);
_updateSelectionUI();
return true;
@@ -646,17 +650,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const short wheelDelta,
const TerminalInput::MouseButtonState state)
{
const auto lock = _terminal->LockForWriting();
return _terminal->SendMouseEvent(viewportPos, uiButton, states, wheelDelta, state);
}
void ControlCore::UserScrollViewport(const int viewTop)
{
// Clear the regex pattern tree so the renderer does not try to render them while scrolling
_terminal->ClearPatternTree();
// This is a scroll event that wasn't initiated by the terminal
// itself - it was initiated by the mouse wheel, or the scrollbar.
_terminal->UserScrollViewport(viewTop);
{
// This is a scroll event that wasn't initiated by the terminal
// itself - it was initiated by the mouse wheel, or the scrollbar.
const auto lock = _terminal->LockForWriting();
_terminal->UserScrollViewport(viewTop);
}
const auto shared = _shared.lock_shared();
if (shared->updatePatternLocations)
@@ -696,6 +701,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// cleartype -> grayscale if the BG is transparent / acrylic.
if (_renderEngine)
{
const auto lock = _terminal->LockForWriting();
_renderEngine->EnableTransparentBackground(_isBackgroundTransparent());
_renderer->NotifyPaintFrame();
}
@@ -707,7 +713,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::ToggleShaderEffects()
{
const auto path = _settings->PixelShaderPath();
auto lock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
// Originally, this action could be used to enable the retro effects
// even when they're set to `false` in the settings. If the user didn't
// specify a custom pixel shader, manually enable the legacy retro
@@ -755,7 +761,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
decltype(_terminal->GetHyperlinkIntervalFromViewportPosition({})) newInterval{ std::nullopt };
if (terminalPosition.has_value())
{
auto lock = _terminal->LockForReading(); // Lock for the duration of our reads.
const auto lock = _terminal->LockForReading();
newId = _terminal->GetHyperlinkIdAtViewportPosition(*terminalPosition);
newInterval = _terminal->GetHyperlinkIntervalFromViewportPosition(*terminalPosition);
}
@@ -770,7 +776,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// wouldn't be able to ask us about the hyperlink text/position
// without deadlocking us.
{
auto lock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
_lastHoveredId = newId;
_lastHoveredInterval = newInterval;
@@ -785,16 +791,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::hstring ControlCore::GetHyperlink(const Core::Point pos) const
{
// Lock for the duration of our reads.
auto lock = _terminal->LockForReading();
const auto lock = _terminal->LockForReading();
return winrt::hstring{ _terminal->GetHyperlinkAtViewportPosition(til::point{ pos }) };
}
winrt::hstring ControlCore::HoveredUriText() const
{
auto lock = _terminal->LockForReading(); // Lock for the duration of our reads.
if (_lastHoveredCell.has_value())
{
const auto lock = _terminal->LockForReading();
auto uri{ _terminal->GetHyperlinkAtViewportPosition(*_lastHoveredCell) };
uri.resize(std::min<size_t>(1024u, uri.size())); // Truncate for display
return winrt::hstring{ uri };
@@ -814,7 +819,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_settings = winrt::make_self<implementation::ControlSettings>(settings, newAppearance);
auto lock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
_cellWidth = CSSLengthPercentage::FromString(_settings->CellWidth().c_str());
_cellHeight = CSSLengthPercentage::FromString(_settings->CellHeight().c_str());
@@ -856,7 +861,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - INVARIANT: This method can only be called if the caller DOES NOT HAVE writing lock on the terminal.
void ControlCore::ApplyAppearance(const bool& focused)
{
auto lock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
const auto& newAppearance{ focused ? _settings->FocusedAppearance() : _settings->UnfocusedAppearance() };
// Update the terminal core with its new Core settings
_terminal->UpdateAppearance(*newAppearance);
@@ -1101,7 +1106,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_panelHeight = height;
_compositionScale = scale;
auto lock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
if (scaleChanged)
{
// _updateFont relies on the new _compositionScale set above
@@ -1112,7 +1117,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::SetSelectionAnchor(const til::point position)
{
auto lock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
_terminal->SetSelectionAnchor(position);
}
@@ -1123,7 +1128,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// to throw it all in a struct and pass it along.
Control::SelectionData ControlCore::SelectionInfo() const
{
auto lock = _terminal->LockForReading();
const auto lock = _terminal->LockForReading();
Control::SelectionData info;
const auto start{ _terminal->SelectionStartForRendering() };
@@ -1146,15 +1151,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - position: the point in terminal coordinates (in cells, not pixels)
void ControlCore::SetEndSelectionPoint(const til::point position)
{
const auto lock = _terminal->LockForWriting();
if (!_terminal->IsSelectionActive())
{
return;
}
// Have to take the lock because the renderer will not draw correctly if
// you move its endpoints while it is generating a frame.
auto lock = _terminal->LockForWriting();
til::point terminalPosition{
std::clamp(position.x, 0, _terminal->GetViewport().Width() - 1),
std::clamp(position.y, 0, _terminal->GetViewport().Height() - 1)
@@ -1182,6 +1185,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool ControlCore::CopySelectionToClipboard(bool singleLine,
const Windows::Foundation::IReference<CopyFormat>& formats)
{
const auto lock = _terminal->LockForWriting();
// no selection --> nothing to copy
if (!_terminal->IsSelectionActive())
{
@@ -1231,21 +1236,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::SelectAll()
{
auto lock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
_terminal->SelectAll();
_updateSelectionUI();
}
void ControlCore::ClearSelection()
{
auto lock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
_terminal->ClearSelection();
_updateSelectionUI();
}
bool ControlCore::ToggleBlockSelection()
{
auto lock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
if (_terminal->IsSelectionActive())
{
_terminal->SetBlockSelection(!_terminal->IsBlockSelection());
@@ -1260,7 +1265,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::ToggleMarkMode()
{
auto lock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
_terminal->ToggleMarkMode();
_updateSelectionUI();
}
@@ -1272,6 +1277,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool ControlCore::SwitchSelectionEndpoint()
{
const auto lock = _terminal->LockForWriting();
if (_terminal->IsSelectionActive())
{
_terminal->SwitchSelectionEndpoint();
@@ -1283,6 +1289,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool ControlCore::ExpandSelectionToWord()
{
const auto lock = _terminal->LockForWriting();
if (_terminal->IsSelectionActive())
{
_terminal->ExpandSelectionToWord();
@@ -1297,6 +1304,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// before sending it over the terminal's connection.
void ControlCore::PasteText(const winrt::hstring& hstr)
{
const auto lock = _terminal->LockForWriting();
_terminal->WritePastedText(hstr);
_terminal->ClearSelection();
_updateSelectionUI();
@@ -1346,21 +1354,25 @@ namespace winrt::Microsoft::Terminal::Control::implementation
hstring ControlCore::Title()
{
const auto lock = _terminal->LockForReading();
return hstring{ _terminal->GetConsoleTitle() };
}
hstring ControlCore::WorkingDirectory() const
{
const auto lock = _terminal->LockForReading();
return hstring{ _terminal->GetWorkingDirectory() };
}
bool ControlCore::BracketedPasteEnabled() const noexcept
{
const auto lock = _terminal->LockForReading();
return _terminal->IsXtermBracketedPasteModeEnabled();
}
Windows::Foundation::IReference<winrt::Windows::UI::Color> ControlCore::TabColor() noexcept
{
const auto lock = _terminal->LockForReading();
auto coreColor = _terminal->GetTabColor();
return coreColor.has_value() ? Windows::Foundation::IReference<winrt::Windows::UI::Color>{ static_cast<winrt::Windows::UI::Color>(coreColor.value()) } :
nullptr;
@@ -1373,6 +1385,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::color ControlCore::BackgroundColor() const
{
const auto lock = _terminal->LockForReading();
return _terminal->GetRenderSettings().GetColorAlias(ColorAlias::DefaultBackground);
}
@@ -1382,6 +1395,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - The taskbar state of this control
const size_t ControlCore::TaskbarState() const noexcept
{
const auto lock = _terminal->LockForReading();
return _terminal->GetTaskbarState();
}
@@ -1391,11 +1405,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - The taskbar progress of this control
const size_t ControlCore::TaskbarProgress() const noexcept
{
const auto lock = _terminal->LockForReading();
return _terminal->GetTaskbarProgress();
}
int ControlCore::ScrollOffset()
{
const auto lock = _terminal->LockForReading();
return _terminal->GetScrollOffset();
}
@@ -1406,6 +1422,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - The height of the terminal in lines of text
int ControlCore::ViewHeight() const
{
const auto lock = _terminal->LockForReading();
return _terminal->GetViewport().Height();
}
@@ -1416,6 +1433,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - The height of the terminal in lines of text
int ControlCore::BufferHeight() const
{
const auto lock = _terminal->LockForReading();
return _terminal->GetBufferHeight();
}
@@ -1463,12 +1481,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}
// Clear the regex pattern tree so the renderer does not try to render them while scrolling
// We're **NOT** taking the lock here unlike _scrollbarChangeHandler because
// we are already under lock (since this usually happens as a result of writing).
// TODO GH#9617: refine locking around pattern tree
_terminal->ClearPatternTree();
// Start the throttled update of our scrollbar.
auto update{ winrt::make<ScrollPositionChangedArgs>(viewTop,
viewHeight,
@@ -1527,6 +1539,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool ControlCore::HasSelection() const
{
const auto lock = _terminal->LockForReading();
return _terminal->IsSelectionActive();
}
@@ -1538,6 +1551,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Windows::Foundation::Collections::IVector<winrt::hstring> ControlCore::SelectedText(bool trimTrailingWhitespace) const
{
// RetrieveSelectedTextFromBuffer will lock while it's reading
const auto lock = _terminal->LockForReading();
const auto internalResult{ _terminal->RetrieveSelectedTextFromBuffer(trimTrailingWhitespace).text };
auto result = winrt::single_threaded_vector<winrt::hstring>();
@@ -1565,7 +1579,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none>
void ControlCore::Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive)
{
auto lock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
if (_searcher.ResetIfStale(*GetRenderData(), text, !goForward, !caseSensitive))
{
@@ -1690,7 +1704,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::BlinkAttributeTick()
{
auto lock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
auto& renderSettings = _terminal->GetRenderSettings();
renderSettings.ToggleBlinkRendition(*_renderer);
@@ -1698,13 +1712,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::BlinkCursor()
{
if (!_terminal->IsCursorBlinkingAllowed() &&
_terminal->IsCursorVisible())
{
return;
}
// SetCursorOn will take the write lock for you.
_terminal->SetCursorOn(!_terminal->IsCursorOn());
const auto lock = _terminal->LockForWriting();
_terminal->BlinkCursor();
}
bool ControlCore::CursorOn() const
@@ -1714,22 +1723,26 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::CursorOn(const bool isCursorOn)
{
const auto lock = _terminal->LockForWriting();
_terminal->SetCursorOn(isCursorOn);
}
void ControlCore::ResumeRendering()
{
const auto lock = _terminal->LockForWriting();
_renderer->ResetErrorStateAndResume();
}
bool ControlCore::IsVtMouseModeEnabled() const
{
return _terminal != nullptr && _terminal->IsTrackingMouseInput();
const auto lock = _terminal->LockForWriting();
return _terminal->IsTrackingMouseInput();
}
bool ControlCore::ShouldSendAlternateScroll(const unsigned int uiButton,
const int32_t delta) const
{
return _terminal != nullptr && _terminal->ShouldSendAlternateScroll(uiButton, delta);
const auto lock = _terminal->LockForWriting();
return _terminal->ShouldSendAlternateScroll(uiButton, delta);
}
Core::Point ControlCore::CursorPosition() const
@@ -1740,7 +1753,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return { 0, 0 };
}
auto lock = _terminal->LockForReading();
const auto lock = _terminal->LockForReading();
return _terminal->GetViewportRelativeCursorPosition().to_core_point();
}
@@ -1756,7 +1769,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const bool isOnOriginalPosition,
bool& selectionNeedsToBeCopied)
{
auto lock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
// handle ALT key
_terminal->SetBlockSelection(altEnabled);
@@ -1782,14 +1795,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// the selection (we need to reset selection on double-click or
// triple-click, so it captures the word or the line, rather than
// extending the selection)
if (HasSelection() && (!shiftEnabled || isOnOriginalPosition))
if (_terminal->IsSelectionActive() && (!shiftEnabled || isOnOriginalPosition))
{
// Reset the selection
_terminal->ClearSelection();
selectionNeedsToBeCopied = false; // there's no selection, so there's nothing to update
}
if (shiftEnabled && HasSelection())
if (shiftEnabled && _terminal->IsSelectionActive())
{
// If shift is pressed and there is a selection we extend it using
// the selection mode (expand the "end" selection point)
@@ -1878,6 +1891,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - Updates the renderer's representation of the selection as well as the selection marker overlay in TermControl
void ControlCore::_updateSelectionUI()
{
const auto lock = _terminal->LockForWriting();
_renderer->TriggerSelection();
// only show the markers if we're doing a keyboard selection or in mark mode
const bool showMarkers{ _terminal->SelectionMode() >= ::Microsoft::Terminal::Core::Terminal::SelectionInteractionMode::Keyboard };
@@ -1887,10 +1901,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::AttachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine)
{
// _renderer will always exist since it's introduced in the ctor
const auto lock = _terminal->LockForWriting();
_renderer->AddRenderEngine(pEngine);
}
void ControlCore::DetachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine)
{
const auto lock = _terminal->LockForWriting();
_renderer->RemoveRenderEngine(pEngine);
}
@@ -1918,7 +1934,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
try
{
_terminal->Write(hstr);
{
const auto lock = _terminal->LockForWriting();
_terminal->Write(hstr);
}
// Start the throttled update of where our hyperlinks are.
const auto shared = _shared.lock_shared();
@@ -1959,6 +1978,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
if (clearType == Control::ClearBufferType::Scrollback || clearType == Control::ClearBufferType::All)
{
const auto lock = _terminal->LockForWriting();
_terminal->EraseScrollback();
}
@@ -1976,7 +1996,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
hstring ControlCore::ReadEntireBuffer() const
{
auto terminalLock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
const auto& textBuffer = _terminal->GetTextBuffer();
@@ -2004,7 +2024,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Get all of our recent commands. This will only really work if the user has enabled shell integration.
Control::CommandHistoryContext ControlCore::CommandHistory() const
{
auto terminalLock = _terminal->LockForWriting();
const auto lock = _terminal->LockForWriting();
const auto& textBuffer = _terminal->GetTextBuffer();
std::vector<winrt::hstring> commands;
@@ -2089,6 +2109,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
else
{
const auto lock = _terminal->LockForReading();
s = _terminal->GetColorScheme();
}
@@ -2112,8 +2133,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none>
void ControlCore::ColorScheme(const Core::Scheme& scheme)
{
auto l{ _terminal->LockForWriting() };
_settings->FocusedAppearance()->DefaultForeground(scheme.Foreground);
_settings->FocusedAppearance()->DefaultBackground(scheme.Background);
_settings->FocusedAppearance()->CursorColor(scheme.CursorColor);
@@ -2136,10 +2155,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_settings->FocusedAppearance()->SetColorTableEntry(14, scheme.BrightCyan);
_settings->FocusedAppearance()->SetColorTableEntry(15, scheme.BrightWhite);
const auto lock = _terminal->LockForWriting();
_terminal->ApplyScheme(scheme);
_renderEngine->SetSelectionBackground(til::color{ _settings->SelectionBackground() });
_renderer->TriggerRedrawAll(true);
}
@@ -2211,6 +2229,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// focused.
const auto previous = std::exchange(_isReadOnly, false);
const auto restore = wil::scope_exit([&]() { _isReadOnly = previous; });
const auto lock = _terminal->LockForWriting();
_terminal->FocusChanged(focused);
}
@@ -2245,7 +2264,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Windows::Foundation::Collections::IVector<Control::ScrollMark> ControlCore::ScrollMarks() const
{
auto internalMarks{ _terminal->GetScrollMarks() };
const auto lock = _terminal->LockForReading();
const auto& internalMarks{ _terminal->GetScrollMarks() };
auto v = winrt::single_threaded_observable_vector<Control::ScrollMark>();
for (const auto& mark : internalMarks)
{
@@ -2270,6 +2290,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::AddMark(const Control::ScrollMark& mark)
{
const auto lock = _terminal->LockForReading();
::ScrollMark m{};
if (mark.Color.HasValue)
@@ -2277,7 +2298,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
m.color = til::color{ mark.Color.Color };
}
if (HasSelection())
if (_terminal->IsSelectionActive())
{
m.start = til::point{ _terminal->GetSelectionAnchor() };
m.end = til::point{ _terminal->GetSelectionEnd() };
@@ -2291,11 +2312,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// set the start & end to the cursor position.
_terminal->AddMark(m, m.start, m.end, true);
}
void ControlCore::ClearMark() { _terminal->ClearMark(); }
void ControlCore::ClearAllMarks() { _terminal->ClearAllMarks(); }
void ControlCore::ClearMark()
{
const auto lock = _terminal->LockForWriting();
_terminal->ClearMark();
}
void ControlCore::ClearAllMarks()
{
const auto lock = _terminal->LockForWriting();
_terminal->ClearAllMarks();
}
void ControlCore::ScrollToMark(const Control::ScrollToMarkDirection& direction)
{
const auto lock = _terminal->LockForWriting();
const auto currentOffset = ScrollOffset();
const auto& marks{ _terminal->GetScrollMarks() };
@@ -2402,15 +2434,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto bufferSize{ _terminal->GetTextBuffer().GetSize() };
bufferSize.DecrementInBounds(s.end);
auto lock = _terminal->LockForWriting();
_terminal->SelectNewRegion(s.start, s.end);
_renderer->TriggerSelection();
}
void ControlCore::SelectCommand(const bool goUp)
{
const til::point start = HasSelection() ? (goUp ? _terminal->GetSelectionAnchor() : _terminal->GetSelectionEnd()) :
_terminal->GetTextBuffer().GetCursor().GetPosition();
const auto lock = _terminal->LockForWriting();
const til::point start = _terminal->IsSelectionActive() ? (goUp ? _terminal->GetSelectionAnchor() : _terminal->GetSelectionEnd()) :
_terminal->GetTextBuffer().GetCursor().GetPosition();
std::optional<::ScrollMark> nearest{ std::nullopt };
const auto& marks{ _terminal->GetScrollMarks() };
@@ -2449,8 +2482,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::SelectOutput(const bool goUp)
{
const til::point start = HasSelection() ? (goUp ? _terminal->GetSelectionAnchor() : _terminal->GetSelectionEnd()) :
_terminal->GetTextBuffer().GetCursor().GetPosition();
const auto lock = _terminal->LockForWriting();
const til::point start = _terminal->IsSelectionActive() ? (goUp ? _terminal->GetSelectionAnchor() : _terminal->GetSelectionEnd()) :
_terminal->GetTextBuffer().GetCursor().GetPosition();
std::optional<::ScrollMark> nearest{ std::nullopt };
const auto& marks{ _terminal->GetScrollMarks() };
@@ -2483,7 +2518,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::ColorSelection(const Control::SelectionColor& fg, const Control::SelectionColor& bg, Core::MatchMode matchMode)
{
if (HasSelection())
const auto lock = _terminal->LockForWriting();
if (_terminal->IsSelectionActive())
{
const auto pForeground = winrt::get_self<implementation::SelectionColor>(fg);
const auto pBackground = winrt::get_self<implementation::SelectionColor>(bg);
@@ -2521,6 +2558,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
// viewportRelativeCharacterPosition is relative to the current
// viewport, so adjust for that:
const auto lock = _terminal->LockForReading();
_contextMenuBufferPosition = _terminal->GetViewport().Origin() + viewportRelativeCharacterPosition;
}
@@ -2529,6 +2567,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool (*filter)(const ::ScrollMark&),
til::point_span (*getSpan)(const ::ScrollMark&))
{
const auto lock = _terminal->LockForWriting();
// Do nothing if the caller didn't give us a way to get the span to select for this mark.
if (!getSpan)
{
@@ -2575,6 +2615,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const til::point& pos,
bool (*filter)(const ::ScrollMark&))
{
const auto lock = _terminal->LockForWriting();
// Don't show this if the click was on the selection
if (_terminal->IsSelectionActive() &&
_terminal->GetSelectionAnchor() <= pos &&

View File

@@ -87,15 +87,15 @@ void Terminal::UpdateSettings(ICoreSettings settings)
_trimBlockSelection = settings.TrimBlockSelection();
_autoMarkPrompts = settings.AutoMarkPrompts();
_terminalInput.ForceDisableWin32InputMode(settings.ForceVTInput());
_getTerminalInput().ForceDisableWin32InputMode(settings.ForceVTInput());
if (settings.TabColor() == nullptr)
{
_renderSettings.SetColorTableEntry(TextColor::FRAME_BACKGROUND, INVALID_COLOR);
GetRenderSettings().SetColorTableEntry(TextColor::FRAME_BACKGROUND, INVALID_COLOR);
}
else
{
_renderSettings.SetColorTableEntry(TextColor::FRAME_BACKGROUND, til::color{ settings.TabColor().Value() });
GetRenderSettings().SetColorTableEntry(TextColor::FRAME_BACKGROUND, til::color{ settings.TabColor().Value() });
}
if (!_startingTabColor && settings.StartingTabColor())
@@ -125,35 +125,37 @@ void Terminal::UpdateSettings(ICoreSettings settings)
// - appearance: an ICoreAppearance with new settings values for us to use.
void Terminal::UpdateAppearance(const ICoreAppearance& appearance)
{
_renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBold, appearance.IntenseIsBold());
_renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBright, appearance.IntenseIsBright());
auto& renderSettings = GetRenderSettings();
renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBold, appearance.IntenseIsBold());
renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBright, appearance.IntenseIsBright());
switch (appearance.AdjustIndistinguishableColors())
{
case AdjustTextMode::Always:
_renderSettings.SetRenderMode(RenderSettings::Mode::IndexedDistinguishableColors, false);
_renderSettings.SetRenderMode(RenderSettings::Mode::AlwaysDistinguishableColors, true);
renderSettings.SetRenderMode(RenderSettings::Mode::IndexedDistinguishableColors, false);
renderSettings.SetRenderMode(RenderSettings::Mode::AlwaysDistinguishableColors, true);
break;
case AdjustTextMode::Indexed:
_renderSettings.SetRenderMode(RenderSettings::Mode::IndexedDistinguishableColors, true);
_renderSettings.SetRenderMode(RenderSettings::Mode::AlwaysDistinguishableColors, false);
renderSettings.SetRenderMode(RenderSettings::Mode::IndexedDistinguishableColors, true);
renderSettings.SetRenderMode(RenderSettings::Mode::AlwaysDistinguishableColors, false);
break;
case AdjustTextMode::Never:
_renderSettings.SetRenderMode(RenderSettings::Mode::IndexedDistinguishableColors, false);
_renderSettings.SetRenderMode(RenderSettings::Mode::AlwaysDistinguishableColors, false);
renderSettings.SetRenderMode(RenderSettings::Mode::IndexedDistinguishableColors, false);
renderSettings.SetRenderMode(RenderSettings::Mode::AlwaysDistinguishableColors, false);
break;
}
const til::color newBackgroundColor{ appearance.DefaultBackground() };
_renderSettings.SetColorAlias(ColorAlias::DefaultBackground, TextColor::DEFAULT_BACKGROUND, newBackgroundColor);
renderSettings.SetColorAlias(ColorAlias::DefaultBackground, TextColor::DEFAULT_BACKGROUND, newBackgroundColor);
const til::color newForegroundColor{ appearance.DefaultForeground() };
_renderSettings.SetColorAlias(ColorAlias::DefaultForeground, TextColor::DEFAULT_FOREGROUND, newForegroundColor);
renderSettings.SetColorAlias(ColorAlias::DefaultForeground, TextColor::DEFAULT_FOREGROUND, newForegroundColor);
const til::color newCursorColor{ appearance.CursorColor() };
_renderSettings.SetColorTableEntry(TextColor::CURSOR_COLOR, newCursorColor);
renderSettings.SetColorTableEntry(TextColor::CURSOR_COLOR, newCursorColor);
for (auto i = 0; i < 16; i++)
{
_renderSettings.SetColorTableEntry(i, til::color{ appearance.GetColorTableEntry(i) });
renderSettings.SetColorTableEntry(i, til::color{ appearance.GetColorTableEntry(i) });
}
auto cursorShape = CursorType::VerticalBar;
@@ -445,17 +447,15 @@ std::wstring_view Terminal::GetWorkingDirectory() noexcept
try
{
_activeBuffer().TriggerRedrawAll();
_NotifyScrollEvent();
}
CATCH_LOG();
_NotifyScrollEvent();
return S_OK;
}
void Terminal::Write(std::wstring_view stringView)
{
auto lock = LockForWriting();
const auto& cursor = _activeBuffer().GetCursor();
const til::point cursorPosBefore{ cursor.GetPosition() };
@@ -501,7 +501,6 @@ void Terminal::TrySnapOnInput()
{
if (_snapOnInput && _scrollOffset != 0)
{
auto lock = LockForWriting();
_scrollOffset = 0;
_NotifyScrollEvent();
}
@@ -515,7 +514,7 @@ void Terminal::TrySnapOnInput()
// - true, if we are tracking mouse input. False, otherwise
bool Terminal::IsTrackingMouseInput() const noexcept
{
return _terminalInput.IsTrackingMouseInput();
return _getTerminalInput().IsTrackingMouseInput();
}
// Routine Description:
@@ -529,7 +528,7 @@ bool Terminal::IsTrackingMouseInput() const noexcept
bool Terminal::ShouldSendAlternateScroll(const unsigned int uiButton,
const int32_t delta) const noexcept
{
return _terminalInput.ShouldSendAlternateScroll(uiButton, ::base::saturated_cast<short>(delta));
return _getTerminalInput().ShouldSendAlternateScroll(uiButton, ::base::saturated_cast<short>(delta));
}
// Method Description:
@@ -716,7 +715,7 @@ bool Terminal::SendKeyEvent(const WORD vkey,
}
const auto keyEv = SynthesizeKeyEvent(keyDown, 1, vkey, sc, ch, states.Value());
return _handleTerminalInputResult(_terminalInput.HandleKey(keyEv));
return _handleTerminalInputResult(_getTerminalInput().HandleKey(keyEv));
}
// Method Description:
@@ -740,7 +739,7 @@ bool Terminal::SendMouseEvent(til::point viewportPos, const unsigned int uiButto
// We shouldn't throw away perfectly good events when they're offscreen, so we just
// clamp them to be within the range [(0, 0), (W, H)].
_GetMutableViewport().ToOrigin().Clamp(viewportPos);
return _handleTerminalInputResult(_terminalInput.HandleMouse(viewportPos, uiButton, GET_KEYSTATE_WPARAM(states.Value()), wheelDelta, state));
return _handleTerminalInputResult(_getTerminalInput().HandleMouse(viewportPos, uiButton, GET_KEYSTATE_WPARAM(states.Value()), wheelDelta, state));
}
// Method Description:
@@ -793,7 +792,7 @@ bool Terminal::SendCharEvent(const wchar_t ch, const WORD scanCode, const Contro
}
const auto keyDown = SynthesizeKeyEvent(true, 1, vkey, scanCode, ch, states.Value());
return _handleTerminalInputResult(_terminalInput.HandleKey(keyDown));
return _handleTerminalInputResult(_getTerminalInput().HandleKey(keyDown));
}
// Method Description:
@@ -806,22 +805,21 @@ bool Terminal::SendCharEvent(const wchar_t ch, const WORD scanCode, const Contro
// - none
void Terminal::FocusChanged(const bool focused)
{
_handleTerminalInputResult(_terminalInput.HandleFocus(focused));
_handleTerminalInputResult(_getTerminalInput().HandleFocus(focused));
}
// Method Description:
// - Invalidates the regions described in the given pattern tree for the rendering purposes
// Arguments:
// - The interval tree containing regions that need to be invalidated
void Terminal::_InvalidatePatternTree(const interval_tree::IntervalTree<til::point, size_t>& tree)
void Terminal::_InvalidatePatternTree()
{
const auto vis = _VisibleStartIndex();
auto invalidate = [=](const PointTree::interval& interval) {
_patternIntervalTree.visit_all([&](const PointTree::interval& interval) {
const til::point startCoord{ interval.start.x, interval.start.y + vis };
const til::point endCoord{ interval.stop.x, interval.stop.y + vis };
_InvalidateFromCoords(startCoord, endCoord);
};
tree.visit_all(invalidate);
});
}
// Method Description:
@@ -978,14 +976,29 @@ WORD Terminal::_TakeVirtualKeyFromLastKeyEvent(const WORD scanCode) noexcept
return codes.ScanCode == scanCode ? codes.VirtualKey : 0;
}
void Terminal::_assertLocked() const noexcept
{
#ifndef NDEBUG
if (!_readWriteLock.is_locked())
{
// __debugbreak() has the benefit over assert() that the debugger jumps right here to this line.
// That way there's no need to first click any dialogues, etc. The disadvantage of course is that the
// application just crashes if no debugger is attached. But hey, that's a great incentive to fix the bug!
__debugbreak();
}
#endif
}
// Method Description:
// - Acquire a read lock on the terminal.
// Return Value:
// - a shared_lock which can be used to unlock the terminal. The shared_lock
// will release this lock when it's destructed.
[[nodiscard]] std::unique_lock<til::recursive_ticket_lock> Terminal::LockForReading()
[[nodiscard]] std::unique_lock<til::recursive_ticket_lock> Terminal::LockForReading() const noexcept
{
return std::unique_lock{ _readWriteLock };
#pragma warning(suppress : 26447) // The function is declared 'noexcept' but calls function 'recursive_ticket_lock>()' which may throw exceptions (f.6).
#pragma warning(suppress : 26492) // Don't use const_cast to cast away const or volatile
return std::unique_lock{ const_cast<til::recursive_ticket_lock&>(_readWriteLock) };
}
// Method Description:
@@ -993,8 +1006,9 @@ WORD Terminal::_TakeVirtualKeyFromLastKeyEvent(const WORD scanCode) noexcept
// Return Value:
// - a unique_lock which can be used to unlock the terminal. The unique_lock
// will release this lock when it's destructed.
[[nodiscard]] std::unique_lock<til::recursive_ticket_lock> Terminal::LockForWriting()
[[nodiscard]] std::unique_lock<til::recursive_ticket_lock> Terminal::LockForWriting() noexcept
{
#pragma warning(suppress : 26447) // The function is declared 'noexcept' but calls function 'recursive_ticket_lock>()' which may throw exceptions (f.6).
return std::unique_lock{ _readWriteLock };
}
@@ -1032,6 +1046,30 @@ int Terminal::ViewEndIndex() const noexcept
return _inAltBuffer() ? _altBufferSize.height - 1 : _mutableViewport.BottomInclusive();
}
RenderSettings& Terminal::GetRenderSettings() noexcept
{
_assertLocked();
return _renderSettings;
}
const RenderSettings& Terminal::GetRenderSettings() const noexcept
{
_assertLocked();
return _renderSettings;
}
TerminalInput& Terminal::_getTerminalInput() noexcept
{
_assertLocked();
return _terminalInput;
}
const TerminalInput& Terminal::_getTerminalInput() const noexcept
{
_assertLocked();
return _terminalInput;
}
// _VisibleStartIndex is the first visible line of the buffer
int Terminal::_VisibleStartIndex() const noexcept
{
@@ -1072,14 +1110,14 @@ void Terminal::_PreserveUserScrollOffset(const int viewportDelta) noexcept
void Terminal::UserScrollViewport(const int viewTop)
{
// Clear the regex pattern tree so the renderer does not try to render them while scrolling
_clearPatternTree();
if (_inAltBuffer())
{
return;
}
// we're going to modify state here that the renderer could be reading.
auto lock = LockForWriting();
const auto clampedNewTop = std::max(0, viewTop);
const auto realTop = ViewStartIndex();
const auto newDelta = realTop - clampedNewTop;
@@ -1098,9 +1136,11 @@ int Terminal::GetScrollOffset() noexcept
return _VisibleStartIndex();
}
void Terminal::_NotifyScrollEvent() noexcept
try
void Terminal::_NotifyScrollEvent()
{
// See UserScrollViewport().
_clearPatternTree();
if (_pfnScrollPositionChanged)
{
const auto visible = _GetVisibleViewport();
@@ -1110,7 +1150,6 @@ try
_pfnScrollPositionChanged(top, height, bottom);
}
}
CATCH_LOG()
void Terminal::_NotifyTerminalCursorPositionChanged() noexcept
{
@@ -1182,6 +1221,18 @@ void Terminal::SetPlayMidiNoteCallback(std::function<void(const int, const int,
_pfnPlayMidiNote.swap(pfn);
}
void Terminal::BlinkCursor() noexcept
{
if (_selectionMode != SelectionInteractionMode::Mark)
{
auto& cursor = _activeBuffer().GetCursor();
if (cursor.IsBlinkingAllowed() && cursor.IsVisible())
{
cursor.SetIsOn(!cursor.IsOn());
}
}
}
// Method Description:
// - Sets the cursor to be currently on. On/Off is tracked independently of
// cursor visibility (hidden/visible). On/off is controlled by the cursor
@@ -1190,18 +1241,11 @@ void Terminal::SetPlayMidiNoteCallback(std::function<void(const int, const int,
// Visible, then it will immediately become visible.
// Arguments:
// - isVisible: whether the cursor should be visible
void Terminal::SetCursorOn(const bool isOn)
void Terminal::SetCursorOn(const bool isOn) noexcept
{
auto lock = LockForWriting();
_activeBuffer().GetCursor().SetIsOn(isOn);
}
bool Terminal::IsCursorBlinkingAllowed() const noexcept
{
const auto& cursor = _activeBuffer().GetCursor();
return _selectionMode != SelectionInteractionMode::Mark && cursor.IsBlinkingAllowed();
}
// Method Description:
// - Update our internal knowledge about where regex patterns are on the screen
// - This is called by TerminalControl (through a throttled function) when the visible
@@ -1209,20 +1253,23 @@ bool Terminal::IsCursorBlinkingAllowed() const noexcept
// - INVARIANT: this function can only be called if the caller has the writing lock on the terminal
void Terminal::UpdatePatternsUnderLock()
{
_InvalidatePatternTree(_patternIntervalTree);
_InvalidatePatternTree();
_patternIntervalTree = _getPatterns(_VisibleStartIndex(), _VisibleEndIndex());
_InvalidatePatternTree(_patternIntervalTree);
_InvalidatePatternTree();
}
// Method Description:
// - Clears and invalidates the interval pattern tree
// - This is called to prevent the renderer from rendering patterns while the
// visible region is changing
void Terminal::ClearPatternTree()
void Terminal::_clearPatternTree()
{
auto oldTree = _patternIntervalTree;
_patternIntervalTree = {};
_InvalidatePatternTree(oldTree);
_assertLocked();
if (!_patternIntervalTree.empty())
{
_InvalidatePatternTree();
_patternIntervalTree = {};
}
}
// Method Description:
@@ -1236,7 +1283,7 @@ const std::optional<til::color> Terminal::GetTabColor() const
}
else
{
const auto tabColor = _renderSettings.GetColorAlias(ColorAlias::FrameBackground);
const auto tabColor = GetRenderSettings().GetColorAlias(ColorAlias::FrameBackground);
return tabColor == INVALID_COLOR ? std::nullopt : std::optional<til::color>{ tabColor };
}
}
@@ -1266,56 +1313,59 @@ void Microsoft::Terminal::Core::Terminal::CompletionsChangedCallback(std::functi
Scheme Terminal::GetColorScheme() const
{
Scheme s;
const auto& renderSettings = GetRenderSettings();
s.Foreground = til::color{ _renderSettings.GetColorAlias(ColorAlias::DefaultForeground) };
s.Background = til::color{ _renderSettings.GetColorAlias(ColorAlias::DefaultBackground) };
Scheme s;
s.Foreground = til::color{ renderSettings.GetColorAlias(ColorAlias::DefaultForeground) };
s.Background = til::color{ renderSettings.GetColorAlias(ColorAlias::DefaultBackground) };
// SelectionBackground is stored in the ControlAppearance
s.CursorColor = til::color{ _renderSettings.GetColorTableEntry(TextColor::CURSOR_COLOR) };
s.CursorColor = til::color{ renderSettings.GetColorTableEntry(TextColor::CURSOR_COLOR) };
s.Black = til::color{ _renderSettings.GetColorTableEntry(TextColor::DARK_BLACK) };
s.Red = til::color{ _renderSettings.GetColorTableEntry(TextColor::DARK_RED) };
s.Green = til::color{ _renderSettings.GetColorTableEntry(TextColor::DARK_GREEN) };
s.Yellow = til::color{ _renderSettings.GetColorTableEntry(TextColor::DARK_YELLOW) };
s.Blue = til::color{ _renderSettings.GetColorTableEntry(TextColor::DARK_BLUE) };
s.Purple = til::color{ _renderSettings.GetColorTableEntry(TextColor::DARK_MAGENTA) };
s.Cyan = til::color{ _renderSettings.GetColorTableEntry(TextColor::DARK_CYAN) };
s.White = til::color{ _renderSettings.GetColorTableEntry(TextColor::DARK_WHITE) };
s.BrightBlack = til::color{ _renderSettings.GetColorTableEntry(TextColor::BRIGHT_BLACK) };
s.BrightRed = til::color{ _renderSettings.GetColorTableEntry(TextColor::BRIGHT_RED) };
s.BrightGreen = til::color{ _renderSettings.GetColorTableEntry(TextColor::BRIGHT_GREEN) };
s.BrightYellow = til::color{ _renderSettings.GetColorTableEntry(TextColor::BRIGHT_YELLOW) };
s.BrightBlue = til::color{ _renderSettings.GetColorTableEntry(TextColor::BRIGHT_BLUE) };
s.BrightPurple = til::color{ _renderSettings.GetColorTableEntry(TextColor::BRIGHT_MAGENTA) };
s.BrightCyan = til::color{ _renderSettings.GetColorTableEntry(TextColor::BRIGHT_CYAN) };
s.BrightWhite = til::color{ _renderSettings.GetColorTableEntry(TextColor::BRIGHT_WHITE) };
s.Black = til::color{ renderSettings.GetColorTableEntry(TextColor::DARK_BLACK) };
s.Red = til::color{ renderSettings.GetColorTableEntry(TextColor::DARK_RED) };
s.Green = til::color{ renderSettings.GetColorTableEntry(TextColor::DARK_GREEN) };
s.Yellow = til::color{ renderSettings.GetColorTableEntry(TextColor::DARK_YELLOW) };
s.Blue = til::color{ renderSettings.GetColorTableEntry(TextColor::DARK_BLUE) };
s.Purple = til::color{ renderSettings.GetColorTableEntry(TextColor::DARK_MAGENTA) };
s.Cyan = til::color{ renderSettings.GetColorTableEntry(TextColor::DARK_CYAN) };
s.White = til::color{ renderSettings.GetColorTableEntry(TextColor::DARK_WHITE) };
s.BrightBlack = til::color{ renderSettings.GetColorTableEntry(TextColor::BRIGHT_BLACK) };
s.BrightRed = til::color{ renderSettings.GetColorTableEntry(TextColor::BRIGHT_RED) };
s.BrightGreen = til::color{ renderSettings.GetColorTableEntry(TextColor::BRIGHT_GREEN) };
s.BrightYellow = til::color{ renderSettings.GetColorTableEntry(TextColor::BRIGHT_YELLOW) };
s.BrightBlue = til::color{ renderSettings.GetColorTableEntry(TextColor::BRIGHT_BLUE) };
s.BrightPurple = til::color{ renderSettings.GetColorTableEntry(TextColor::BRIGHT_MAGENTA) };
s.BrightCyan = til::color{ renderSettings.GetColorTableEntry(TextColor::BRIGHT_CYAN) };
s.BrightWhite = til::color{ renderSettings.GetColorTableEntry(TextColor::BRIGHT_WHITE) };
return s;
}
void Terminal::ApplyScheme(const Scheme& colorScheme)
{
_renderSettings.SetColorAlias(ColorAlias::DefaultForeground, TextColor::DEFAULT_FOREGROUND, til::color{ colorScheme.Foreground });
_renderSettings.SetColorAlias(ColorAlias::DefaultBackground, TextColor::DEFAULT_BACKGROUND, til::color{ colorScheme.Background });
auto& renderSettings = GetRenderSettings();
_renderSettings.SetColorTableEntry(TextColor::DARK_BLACK, til::color{ colorScheme.Black });
_renderSettings.SetColorTableEntry(TextColor::DARK_RED, til::color{ colorScheme.Red });
_renderSettings.SetColorTableEntry(TextColor::DARK_GREEN, til::color{ colorScheme.Green });
_renderSettings.SetColorTableEntry(TextColor::DARK_YELLOW, til::color{ colorScheme.Yellow });
_renderSettings.SetColorTableEntry(TextColor::DARK_BLUE, til::color{ colorScheme.Blue });
_renderSettings.SetColorTableEntry(TextColor::DARK_MAGENTA, til::color{ colorScheme.Purple });
_renderSettings.SetColorTableEntry(TextColor::DARK_CYAN, til::color{ colorScheme.Cyan });
_renderSettings.SetColorTableEntry(TextColor::DARK_WHITE, til::color{ colorScheme.White });
_renderSettings.SetColorTableEntry(TextColor::BRIGHT_BLACK, til::color{ colorScheme.BrightBlack });
_renderSettings.SetColorTableEntry(TextColor::BRIGHT_RED, til::color{ colorScheme.BrightRed });
_renderSettings.SetColorTableEntry(TextColor::BRIGHT_GREEN, til::color{ colorScheme.BrightGreen });
_renderSettings.SetColorTableEntry(TextColor::BRIGHT_YELLOW, til::color{ colorScheme.BrightYellow });
_renderSettings.SetColorTableEntry(TextColor::BRIGHT_BLUE, til::color{ colorScheme.BrightBlue });
_renderSettings.SetColorTableEntry(TextColor::BRIGHT_MAGENTA, til::color{ colorScheme.BrightPurple });
_renderSettings.SetColorTableEntry(TextColor::BRIGHT_CYAN, til::color{ colorScheme.BrightCyan });
_renderSettings.SetColorTableEntry(TextColor::BRIGHT_WHITE, til::color{ colorScheme.BrightWhite });
renderSettings.SetColorAlias(ColorAlias::DefaultForeground, TextColor::DEFAULT_FOREGROUND, til::color{ colorScheme.Foreground });
renderSettings.SetColorAlias(ColorAlias::DefaultBackground, TextColor::DEFAULT_BACKGROUND, til::color{ colorScheme.Background });
_renderSettings.SetColorTableEntry(TextColor::CURSOR_COLOR, til::color{ colorScheme.CursorColor });
renderSettings.SetColorTableEntry(TextColor::DARK_BLACK, til::color{ colorScheme.Black });
renderSettings.SetColorTableEntry(TextColor::DARK_RED, til::color{ colorScheme.Red });
renderSettings.SetColorTableEntry(TextColor::DARK_GREEN, til::color{ colorScheme.Green });
renderSettings.SetColorTableEntry(TextColor::DARK_YELLOW, til::color{ colorScheme.Yellow });
renderSettings.SetColorTableEntry(TextColor::DARK_BLUE, til::color{ colorScheme.Blue });
renderSettings.SetColorTableEntry(TextColor::DARK_MAGENTA, til::color{ colorScheme.Purple });
renderSettings.SetColorTableEntry(TextColor::DARK_CYAN, til::color{ colorScheme.Cyan });
renderSettings.SetColorTableEntry(TextColor::DARK_WHITE, til::color{ colorScheme.White });
renderSettings.SetColorTableEntry(TextColor::BRIGHT_BLACK, til::color{ colorScheme.BrightBlack });
renderSettings.SetColorTableEntry(TextColor::BRIGHT_RED, til::color{ colorScheme.BrightRed });
renderSettings.SetColorTableEntry(TextColor::BRIGHT_GREEN, til::color{ colorScheme.BrightGreen });
renderSettings.SetColorTableEntry(TextColor::BRIGHT_YELLOW, til::color{ colorScheme.BrightYellow });
renderSettings.SetColorTableEntry(TextColor::BRIGHT_BLUE, til::color{ colorScheme.BrightBlue });
renderSettings.SetColorTableEntry(TextColor::BRIGHT_MAGENTA, til::color{ colorScheme.BrightPurple });
renderSettings.SetColorTableEntry(TextColor::BRIGHT_CYAN, til::color{ colorScheme.BrightCyan });
renderSettings.SetColorTableEntry(TextColor::BRIGHT_WHITE, til::color{ colorScheme.BrightWhite });
renderSettings.SetColorTableEntry(TextColor::CURSOR_COLOR, til::color{ colorScheme.CursorColor });
// Tell the control that the scrollbar has somehow changed. Used as a
// workaround to force the control to redraw any scrollbar marks whose color
@@ -1325,6 +1375,7 @@ void Terminal::ApplyScheme(const Scheme& colorScheme)
bool Terminal::_inAltBuffer() const noexcept
{
_assertLocked();
return _altBuffer != nullptr;
}
@@ -1341,7 +1392,7 @@ void Terminal::_updateUrlDetection()
}
else
{
ClearPatternTree();
_clearPatternTree();
}
}
@@ -1497,7 +1548,7 @@ void Terminal::ClearMark()
// workaround to force the control to redraw any scrollbar marks
_NotifyScrollEvent();
}
void Terminal::ClearAllMarks() noexcept
void Terminal::ClearAllMarks()
{
_activeBuffer().ClearAllMarks();
// Tell the control that the scrollbar has somehow changed. Used as a
@@ -1521,28 +1572,30 @@ til::color Terminal::GetColorForMark(const ScrollMark& mark) const
return *mark.color;
}
const auto& renderSettings = GetRenderSettings();
switch (mark.category)
{
case MarkCategory::Prompt:
{
return _renderSettings.GetColorAlias(ColorAlias::DefaultForeground);
return renderSettings.GetColorAlias(ColorAlias::DefaultForeground);
}
case MarkCategory::Error:
{
return _renderSettings.GetColorTableEntry(TextColor::BRIGHT_RED);
return renderSettings.GetColorTableEntry(TextColor::BRIGHT_RED);
}
case MarkCategory::Warning:
{
return _renderSettings.GetColorTableEntry(TextColor::BRIGHT_YELLOW);
return renderSettings.GetColorTableEntry(TextColor::BRIGHT_YELLOW);
}
case MarkCategory::Success:
{
return _renderSettings.GetColorTableEntry(TextColor::BRIGHT_GREEN);
return renderSettings.GetColorTableEntry(TextColor::BRIGHT_GREEN);
}
default:
case MarkCategory::Info:
{
return _renderSettings.GetColorAlias(ColorAlias::DefaultForeground);
return renderSettings.GetColorAlias(ColorAlias::DefaultForeground);
}
}
}

View File

@@ -101,8 +101,8 @@ public:
// WritePastedText comes from our input and goes back to the PTY's input channel
void WritePastedText(std::wstring_view stringView);
[[nodiscard]] std::unique_lock<til::recursive_ticket_lock> LockForReading();
[[nodiscard]] std::unique_lock<til::recursive_ticket_lock> LockForWriting();
[[nodiscard]] std::unique_lock<til::recursive_ticket_lock> LockForReading() const noexcept;
[[nodiscard]] std::unique_lock<til::recursive_ticket_lock> LockForWriting() noexcept;
til::recursive_ticket_lock_suspension SuspendLock() noexcept;
til::CoordType GetBufferHeight() const noexcept;
@@ -110,8 +110,8 @@ public:
int ViewStartIndex() const noexcept;
int ViewEndIndex() const noexcept;
RenderSettings& GetRenderSettings() noexcept { return _renderSettings; };
const RenderSettings& GetRenderSettings() const noexcept { return _renderSettings; };
RenderSettings& GetRenderSettings() noexcept;
const RenderSettings& GetRenderSettings() const noexcept;
const std::vector<ScrollMark>& GetScrollMarks() const noexcept;
void AddMark(const ScrollMark& mark,
@@ -162,7 +162,7 @@ public:
#pragma endregion
void ClearMark();
void ClearAllMarks() noexcept;
void ClearAllMarks();
til::color GetColorForMark(const ScrollMark& mark) const;
#pragma region ITerminalInput
@@ -233,11 +233,10 @@ public:
void SetPlayMidiNoteCallback(std::function<void(const int, const int, const std::chrono::microseconds)> pfn) noexcept;
void CompletionsChangedCallback(std::function<void(std::wstring_view, unsigned int)> pfn) noexcept;
void SetCursorOn(const bool isOn);
bool IsCursorBlinkingAllowed() const noexcept;
void BlinkCursor() noexcept;
void SetCursorOn(const bool isOn) noexcept;
void UpdatePatternsUnderLock();
void ClearPatternTree();
const std::optional<til::color> GetTabColor() const;
@@ -406,7 +405,8 @@ private:
// Either way, we should make this behavior controlled by a setting.
interval_tree::IntervalTree<til::point, size_t> _patternIntervalTree;
void _InvalidatePatternTree(const interval_tree::IntervalTree<til::point, size_t>& tree);
void _clearPatternTree();
void _InvalidatePatternTree();
void _InvalidateFromCoords(const til::point start, const til::point end);
// Since virtual keys are non-zero, you assume that this field is empty/invalid if it is.
@@ -435,6 +435,10 @@ private:
void _StoreKeyEvent(const WORD vkey, const WORD scanCode) noexcept;
WORD _TakeVirtualKeyFromLastKeyEvent(const WORD scanCode) noexcept;
void _assertLocked() const noexcept;
Console::VirtualTerminal::TerminalInput& _getTerminalInput() noexcept;
const Console::VirtualTerminal::TerminalInput& _getTerminalInput() const noexcept;
int _VisibleStartIndex() const noexcept;
int _VisibleEndIndex() const noexcept;
@@ -443,7 +447,7 @@ private:
void _PreserveUserScrollOffset(const int viewportDelta) noexcept;
void _NotifyScrollEvent() noexcept;
void _NotifyScrollEvent();
void _NotifyTerminalCursorPositionChanged() noexcept;

View File

@@ -44,6 +44,7 @@ til::rect Terminal::GetViewport() const noexcept
}
void Terminal::SetViewportPosition(const til::point position) noexcept
try
{
// The viewport is fixed at 0,0 for the alt buffer, so this is a no-op.
if (!_inAltBuffer())
@@ -55,6 +56,7 @@ void Terminal::SetViewportPosition(const til::point position) noexcept
_NotifyScrollEvent();
}
}
CATCH_LOG()
void Terminal::SetTextAttributes(const TextAttribute& attrs) noexcept
{
@@ -63,11 +65,13 @@ void Terminal::SetTextAttributes(const TextAttribute& attrs) noexcept
void Terminal::SetSystemMode(const Mode mode, const bool enabled) noexcept
{
_assertLocked();
_systemMode.set(mode, enabled);
}
bool Terminal::GetSystemMode(const Mode mode) const noexcept
{
_assertLocked();
return _systemMode.test(mode);
}
@@ -78,6 +82,7 @@ void Terminal::WarningBell()
void Terminal::SetWindowTitle(const std::wstring_view title)
{
_assertLocked();
if (!_suppressApplicationTitle)
{
_title.emplace(title);
@@ -87,6 +92,7 @@ void Terminal::SetWindowTitle(const std::wstring_view title)
CursorType Terminal::GetUserDefaultCursorStyle() const noexcept
{
_assertLocked();
return _defaultCursorShape;
}
@@ -121,6 +127,8 @@ void Terminal::CopyToClipboard(std::wstring_view content)
// - <none>
void Terminal::SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress)
{
_assertLocked();
_taskbarState = static_cast<size_t>(state);
switch (state)
@@ -164,6 +172,8 @@ void Terminal::SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::D
void Terminal::SetWorkingDirectory(std::wstring_view uri)
{
_assertLocked();
static bool logged = false;
if (!logged)
{
@@ -187,6 +197,8 @@ void Terminal::PlayMidiNote(const int noteNumber, const int velocity, const std:
void Terminal::UseAlternateScreenBuffer(const TextAttribute& attrs)
{
_assertLocked();
// the new alt buffer is exactly the size of the viewport.
_altBufferSize = _mutableViewport.Dimensions();
@@ -222,7 +234,7 @@ void Terminal::UseAlternateScreenBuffer(const TextAttribute& attrs)
// GH#3321: Make sure we let the TerminalInput know that we switched
// buffers. This might affect how we interpret certain mouse events.
_terminalInput.UseAlternateScreenBuffer();
_getTerminalInput().UseAlternateScreenBuffer();
// Update scrollbars
_NotifyScrollEvent();
@@ -275,7 +287,7 @@ void Terminal::UseMainScreenBuffer()
// GH#3321: Make sure we let the TerminalInput know that we switched
// buffers. This might affect how we interpret certain mouse events.
_terminalInput.UseMainScreenBuffer();
_getTerminalInput().UseMainScreenBuffer();
// Update scrollbars
_NotifyScrollEvent();
@@ -291,6 +303,8 @@ void Terminal::UseMainScreenBuffer()
// NOTE: This is the version of AddMark that comes from VT
void Terminal::MarkPrompt(const ScrollMark& mark)
{
_assertLocked();
static bool logged = false;
if (!logged)
{
@@ -315,6 +329,8 @@ void Terminal::MarkPrompt(const ScrollMark& mark)
void Terminal::MarkCommandStart()
{
_assertLocked();
const til::point cursorPos{ _activeBuffer().GetCursor().GetPosition() };
if ((_currentPromptState == PromptState::Prompt) &&
@@ -341,6 +357,8 @@ void Terminal::MarkCommandStart()
void Terminal::MarkOutputStart()
{
_assertLocked();
const til::point cursorPos{ _activeBuffer().GetCursor().GetPosition() };
if ((_currentPromptState == PromptState::Command) &&
@@ -367,6 +385,8 @@ void Terminal::MarkOutputStart()
void Terminal::MarkCommandFinish(std::optional<unsigned int> error)
{
_assertLocked();
const til::point cursorPos{ _activeBuffer().GetCursor().GetPosition() };
auto category = MarkCategory::Prompt;
if (error.has_value())

View File

@@ -92,6 +92,7 @@ std::vector<til::point_span> Terminal::_GetSelectionSpans() const noexcept
// - None
const til::point Terminal::GetSelectionAnchor() const noexcept
{
_assertLocked();
return _selection->start;
}
@@ -103,6 +104,7 @@ const til::point Terminal::GetSelectionAnchor() const noexcept
// - None
const til::point Terminal::GetSelectionEnd() const noexcept
{
_assertLocked();
return _selection->end;
}
@@ -155,11 +157,13 @@ const Terminal::SelectionEndpoint Terminal::SelectionEndpointTarget() const noex
// - bool representing if selection is active. Used to decide copy/paste on right click
const bool Terminal::IsSelectionActive() const noexcept
{
_assertLocked();
return _selection.has_value();
}
const bool Terminal::IsBlockSelection() const noexcept
{
_assertLocked();
return _blockSelection;
}
@@ -188,6 +192,8 @@ void Terminal::MultiClickSelection(const til::point viewportPos, SelectionExpans
// - position: the (x,y) coordinate on the visible viewport
void Terminal::SetSelectionAnchor(const til::point viewportPos)
{
_assertLocked();
_selection = SelectionAnchors{};
_selection->pivot = _ConvertToBufferCell(viewportPos);
@@ -205,7 +211,7 @@ void Terminal::SetSelectionAnchor(const til::point viewportPos)
// - newExpansionMode: overwrites the _multiClickSelectionMode for this function call. Used for ShiftClick
void Terminal::SetSelectionEnd(const til::point viewportPos, std::optional<SelectionExpansion> newExpansionMode)
{
if (!_selection.has_value())
if (!IsSelectionActive())
{
// capture a log for spurious endpoint sets without an active selection
LOG_HR(E_ILLEGAL_STATE_CHANGE);
@@ -817,6 +823,7 @@ void Terminal::_MoveByBuffer(SelectionDirection direction, til::point& pos) noex
#pragma warning(disable : 26440) // changing this to noexcept would require a change to ConHost's selection model
void Terminal::ClearSelection()
{
_assertLocked();
_selection = std::nullopt;
_selectionMode = SelectionInteractionMode::None;
_selectionIsTargetingUrl = false;
@@ -832,8 +839,6 @@ void Terminal::ClearSelection()
// - wstring text from buffer. If extended to multiple lines, each line is separated by \r\n
const TextBuffer::TextAndColor Terminal::RetrieveSelectedTextFromBuffer(bool singleLine)
{
auto lock = LockForReading();
const auto selectionRects = _GetSelectionRects();
const auto GetAttributeColors = [&](const auto& attr) {

View File

@@ -29,11 +29,13 @@ const TextBuffer& Terminal::GetTextBuffer() const noexcept
const FontInfo& Terminal::GetFontInfo() const noexcept
{
_assertLocked();
return _fontInfo;
}
void Terminal::SetFontInfo(const FontInfo& fontInfo)
{
_assertLocked();
_fontInfo = fontInfo;
}
@@ -105,6 +107,8 @@ const std::wstring Microsoft::Terminal::Core::Terminal::GetHyperlinkCustomId(uin
// - The pattern IDs of the location
const std::vector<size_t> Terminal::GetPatternId(const til::point location) const
{
_assertLocked();
// Look through our interval tree for this location
const auto intervals = _patternIntervalTree.findOverlapping({ location.x + 1, location.y }, location);
if (intervals.size() == 0)
@@ -125,7 +129,7 @@ const std::vector<size_t> Terminal::GetPatternId(const til::point location) cons
std::pair<COLORREF, COLORREF> Terminal::GetAttributeColors(const TextAttribute& attr) const noexcept
{
return _renderSettings.GetAttributeColors(attr);
return GetRenderSettings().GetAttributeColors(attr);
}
std::vector<Microsoft::Console::Types::Viewport> Terminal::GetSelectionRects() noexcept
@@ -185,19 +189,14 @@ void Terminal::SelectNewRegion(const til::point coordStart, const til::point coo
}
const std::wstring_view Terminal::GetConsoleTitle() const noexcept
try
{
_assertLocked();
if (_title.has_value())
{
return _title.value();
return *_title;
}
return _startingTitle;
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return {};
}
// Method Description:
// - Lock the terminal for reading the contents of the buffer. Ensures that the
@@ -223,5 +222,6 @@ const bool Terminal::IsUiaDataInitialized() const noexcept
// when a screen reader requests it. However, the terminal might not be fully
// initialized yet. So we use this to check if any crucial components of
// UiaData are not yet initialized.
_assertLocked();
return !!_mainBuffer;
}