Files
terminal/src/cascadia/TerminalCore/Terminal.hpp
Leonard Hecker 81cdb07646 Separate pruning of elevated/unelevated session buffers (#19546)
Previously, launching an unelevated session after an elevated one would
delete the latter's persisted buffers, and vice versa of course. Also,
elevated buffers didn't have an ACL forbidding access to unelevated
users. That's also fixed now.

Closes #19526

## Validation Steps Performed
* Unelevated/elevated WT doesn't erase each other's buffers 
* Old buffers named `buffer_` are renamed to `elevated_` if needed 
2025-11-20 11:49:14 -08:00

490 lines
22 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include <conattrs.hpp>
#include "../../inc/DefaultSettings.h"
#include "../../buffer/out/textBuffer.hpp"
#include "../../renderer/inc/IRenderData.hpp"
#include "../../terminal/adapter/ITerminalApi.hpp"
#include "../../terminal/parser/StateMachine.hpp"
#include "../../terminal/input/terminalInput.hpp"
#include "../../types/inc/Viewport.hpp"
#include "../../types/inc/GlyphWidth.hpp"
#include "../../cascadia/terminalcore/ITerminalInput.hpp"
#include <til/generational.h>
#include <til/ticket_lock.h>
#include <til/winrt.h>
inline constexpr size_t TaskbarMinProgress{ 10 };
// You have to forward decl the ICoreSettings here, instead of including the header.
// If you include the header, there will be compilation errors with other
// headers that include Terminal.hpp
namespace winrt::Microsoft::Terminal::Core
{
struct ICoreSettings;
struct ICoreAppearance;
enum class MatchMode;
}
namespace Microsoft::Console::VirtualTerminal
{
class AdaptDispatch;
}
namespace Microsoft::Terminal::Core
{
class Terminal;
}
// fwdecl unittest classes
#ifdef UNIT_TESTING
namespace TerminalCoreUnitTests
{
class TerminalBufferTests;
class TerminalApiTest;
class ScrollTest;
};
#endif
class Microsoft::Terminal::Core::Terminal final :
public Microsoft::Console::VirtualTerminal::ITerminalApi,
public Microsoft::Terminal::Core::ITerminalInput,
public Microsoft::Console::Render::IRenderData
{
using RenderSettings = Microsoft::Console::Render::RenderSettings;
public:
struct TestDummyMarker
{
};
static constexpr bool IsInputKey(WORD vkey)
{
return vkey != VK_CONTROL &&
vkey != VK_LCONTROL &&
vkey != VK_RCONTROL &&
vkey != VK_MENU &&
vkey != VK_LMENU &&
vkey != VK_RMENU &&
vkey != VK_SHIFT &&
vkey != VK_LSHIFT &&
vkey != VK_RSHIFT &&
vkey != VK_LWIN &&
vkey != VK_RWIN &&
vkey != VK_SNAPSHOT;
}
Terminal();
Terminal(TestDummyMarker);
void Create(til::size viewportSize,
til::CoordType scrollbackLines,
Microsoft::Console::Render::Renderer& renderer);
void CreateFromSettings(winrt::Microsoft::Terminal::Core::ICoreSettings settings,
Microsoft::Console::Render::Renderer& renderer);
void UpdateSettings(winrt::Microsoft::Terminal::Core::ICoreSettings settings);
void UpdateAppearance(const winrt::Microsoft::Terminal::Core::ICoreAppearance& appearance);
void UpdateColorScheme(const winrt::Microsoft::Terminal::Core::ICoreScheme& scheme);
void SetHighContrastMode(bool hc) noexcept;
void SetFontInfo(const FontInfo& fontInfo);
void SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle);
void SetOptionalFeatures(winrt::Microsoft::Terminal::Core::ICoreSettings settings);
bool IsXtermBracketedPasteModeEnabled() const noexcept;
std::wstring_view GetWorkingDirectory() noexcept;
til::point GetViewportRelativeCursorPosition() const noexcept;
// Write comes from the PTY and goes to our parser to be stored in the output buffer
void Write(std::wstring_view stringView);
void _assertLocked() const noexcept;
void _assertUnlocked() const noexcept;
[[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;
int ViewStartIndex() const noexcept;
int ViewEndIndex() const noexcept;
bool IsFocused() const noexcept;
RenderSettings& GetRenderSettings() noexcept;
const RenderSettings& GetRenderSettings() const noexcept;
std::vector<ScrollMark> GetMarkRows() const;
std::vector<MarkExtents> GetMarkExtents() const;
void AddMarkFromUI(ScrollbarData mark, til::CoordType y);
til::property<bool> AlwaysNotifyOnBufferRotation;
std::wstring CurrentCommand() const;
void SerializeMainBuffer(HANDLE handle) const;
#pragma region ITerminalApi
// These methods are defined in TerminalApi.cpp
void ReturnResponse(const std::wstring_view response) override;
Microsoft::Console::VirtualTerminal::StateMachine& GetStateMachine() noexcept override;
BufferState GetBufferAndViewport() noexcept override;
void SetViewportPosition(const til::point position) noexcept override;
void SetSystemMode(const Mode mode, const bool enabled) noexcept override;
bool GetSystemMode(const Mode mode) const noexcept override;
void ReturnAnswerback() override;
void WarningBell() override;
void SetWindowTitle(const std::wstring_view title) override;
CursorType GetUserDefaultCursorStyle() const noexcept override;
bool ResizeWindow(const til::CoordType width, const til::CoordType height) override;
void SetCodePage(const unsigned int codepage) noexcept override;
void ResetCodePage() noexcept override;
unsigned int GetOutputCodePage() const noexcept override;
unsigned int GetInputCodePage() const noexcept override;
void CopyToClipboard(wil::zwstring_view content) override;
void SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) override;
void SetWorkingDirectory(std::wstring_view uri) override;
void PlayMidiNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration) override;
void ShowWindow(bool showOrHide) override;
void UseAlternateScreenBuffer(const TextAttribute& attrs) override;
void UseMainScreenBuffer() override;
bool IsVtInputEnabled() const noexcept override;
void NotifyBufferRotation(const int delta) override;
void NotifyShellIntegrationMark() override;
void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override;
void SearchMissingCommand(const std::wstring_view command) override;
#pragma endregion
void ClearMark();
void ClearAllMarks();
til::color GetColorForMark(const ScrollbarData& markData) const;
#pragma region ITerminalInput
// These methods are defined in Terminal.cpp
[[nodiscard]] ::Microsoft::Console::VirtualTerminal::TerminalInput::OutputType SendKeyEvent(const WORD vkey, const WORD scanCode, const Microsoft::Terminal::Core::ControlKeyStates states, const bool keyDown) override;
[[nodiscard]] ::Microsoft::Console::VirtualTerminal::TerminalInput::OutputType SendMouseEvent(const til::point viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state) override;
[[nodiscard]] ::Microsoft::Console::VirtualTerminal::TerminalInput::OutputType SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) override;
[[nodiscard]] ::Microsoft::Console::VirtualTerminal::TerminalInput::OutputType FocusChanged(const bool focused) override;
[[nodiscard]] HRESULT UserResize(const til::size viewportSize) noexcept override;
void UserScrollViewport(const int viewTop) override;
int GetScrollOffset() noexcept override;
void TrySnapOnInput() override;
bool IsTrackingMouseInput() const noexcept;
bool ShouldSendAlternateScroll(const unsigned int uiButton, const int32_t delta) const noexcept;
std::wstring GetHyperlinkAtViewportPosition(const til::point viewportPos);
std::wstring GetHyperlinkAtBufferPosition(const til::point bufferPos);
uint16_t GetHyperlinkIdAtViewportPosition(const til::point viewportPos);
std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> GetHyperlinkIntervalFromViewportPosition(const til::point viewportPos);
#pragma endregion
#pragma region IRenderData
Microsoft::Console::Types::Viewport GetViewport() noexcept override;
til::point GetTextBufferEndPosition() const noexcept override;
TextBuffer& GetTextBuffer() const noexcept override;
const FontInfo& GetFontInfo() const noexcept override;
void LockConsole() noexcept override;
void UnlockConsole() noexcept override;
// These methods are defined in TerminalRenderData.cpp
Microsoft::Console::Render::TimerDuration GetBlinkInterval() noexcept override;
ULONG GetCursorPixelWidth() const noexcept override;
bool IsGridLineDrawingAllowed() noexcept override;
std::wstring GetHyperlinkUri(uint16_t id) const override;
std::wstring GetHyperlinkCustomId(uint16_t id) const override;
std::vector<size_t> GetPatternId(const til::point location) const override;
std::pair<COLORREF, COLORREF> GetAttributeColors(const TextAttribute& attr) const noexcept override;
std::span<const til::point_span> GetSelectionSpans() const noexcept override;
std::span<const til::point_span> GetSearchHighlights() const noexcept override;
const til::point_span* GetSearchHighlightFocused() const noexcept override;
bool IsSelectionActive() const noexcept override;
bool IsBlockSelection() const noexcept override;
void ClearSelection() override;
void SelectNewRegion(const til::point coordStart, const til::point coordEnd) override;
til::point GetSelectionAnchor() const noexcept override;
til::point GetSelectionEnd() const noexcept override;
std::wstring_view GetConsoleTitle() const noexcept override;
bool IsUiaDataInitialized() const noexcept override;
#pragma endregion
void SetWriteInputCallback(std::function<void(std::wstring_view)> pfn) noexcept;
void SetWarningBellCallback(std::function<void()> pfn) noexcept;
void SetTitleChangedCallback(std::function<void(std::wstring_view)> pfn) noexcept;
void SetCopyToClipboardCallback(std::function<void(wil::zwstring_view)> pfn) noexcept;
void SetScrollPositionChangedCallback(std::function<void(const int, const int, const int)> pfn) noexcept;
void TaskbarProgressChangedCallback(std::function<void()> pfn) noexcept;
void SetShowWindowCallback(std::function<void(bool)> pfn) noexcept;
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 SetSearchMissingCommandCallback(std::function<void(std::wstring_view, const til::CoordType)> pfn) noexcept;
void SetClearQuickFixCallback(std::function<void()> pfn) noexcept;
void SetWindowSizeChangedCallback(std::function<void(int32_t, int32_t)> pfn) noexcept;
void SetSearchHighlights(const std::vector<til::point_span>& highlights) noexcept;
void SetSearchHighlightFocused(size_t focusedIdx) noexcept;
void ScrollToSearchHighlight(til::CoordType searchScrollOffset);
void UpdatePatternsUnderLock();
const std::optional<til::color> GetTabColor() const;
const size_t GetTaskbarState() const noexcept;
const size_t GetTaskbarProgress() const noexcept;
void ColorSelection(const TextAttribute& attr, winrt::Microsoft::Terminal::Core::MatchMode matchMode);
void PreviewText(std::wstring_view input);
#pragma region TextSelection
// These methods are defined in TerminalSelection.cpp
enum class SelectionInteractionMode
{
None,
Mouse,
Keyboard,
Mark
};
enum class SelectionDirection
{
Left,
Right,
Up,
Down
};
enum class SearchDirection
{
Forward,
Backward
};
enum class SelectionExpansion
{
Char,
Word,
Line, // Mouse selection only!
Viewport,
Buffer
};
enum class SelectionEndpoint
{
None = 0,
Start = 0x1,
End = 0x2
};
struct TextCopyData
{
std::wstring plainText;
std::string html;
std::string rtf;
};
void MultiClickSelection(const til::point viewportPos, SelectionExpansion expansionMode);
void SetSelectionAnchor(const til::point position);
void SetSelectionEnd(const til::point position, std::optional<SelectionExpansion> newExpansionMode = std::nullopt);
void SetBlockSelection(const bool isEnabled) noexcept;
void UpdateSelection(SelectionDirection direction, SelectionExpansion mode, ControlKeyStates mods);
void SelectAll();
SelectionInteractionMode SelectionMode() const noexcept;
void SwitchSelectionEndpoint() noexcept;
void ExpandSelectionToWord();
void ToggleMarkMode();
void SelectHyperlink(const SearchDirection dir);
using UpdateSelectionParams = std::optional<std::pair<SelectionDirection, SelectionExpansion>>;
UpdateSelectionParams ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey) const noexcept;
til::point SelectionStartForRendering() const;
til::point SelectionEndForRendering() const;
SelectionEndpoint SelectionEndpointTarget() const noexcept;
TextCopyData RetrieveSelectedTextFromBuffer(const bool singleLine, const bool withControlSequences = false, const bool html = false, const bool rtf = false) const;
#pragma endregion
#ifndef NDEBUG
bool _suppressLockChecks = false;
#endif
private:
std::function<void(std::wstring_view)> _pfnWriteInput;
std::function<void()> _pfnWarningBell;
std::function<void(std::wstring_view)> _pfnTitleChanged;
std::function<void(wil::zwstring_view)> _pfnCopyToClipboard;
// I've specifically put this instance here as it requires
// alignas(std::hardware_destructive_interference_size)
// for best performance.
//
// But we can abuse the fact that the surrounding members rarely change and are huge
// (std::function is like 64 bytes) to create some natural padding without wasting space.
til::recursive_ticket_lock _readWriteLock;
std::function<void(const int, const int, const int)> _pfnScrollPositionChanged;
std::function<void()> _pfnTaskbarProgressChanged;
std::function<void(bool)> _pfnShowWindowChanged;
std::function<void(const int, const int, const std::chrono::microseconds)> _pfnPlayMidiNote;
std::function<void(std::wstring_view, unsigned int)> _pfnCompletionsChanged;
std::function<void(std::wstring_view, const til::CoordType)> _pfnSearchMissingCommand;
std::function<void()> _pfnClearQuickFix;
std::function<void(int32_t, int32_t)> _pfnWindowSizeChanged;
RenderSettings _renderSettings;
std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine;
::Microsoft::Console::VirtualTerminal::TerminalInput _terminalInput;
std::optional<std::wstring> _title;
std::wstring _startingTitle;
std::optional<til::color> _startingTabColor;
std::vector<til::point_span> _searchHighlights;
size_t _searchHighlightFocused = 0;
mutable std::vector<til::point_span> _lastSelectionSpans;
mutable til::generation_t _lastSelectionGeneration{};
CursorType _defaultCursorShape = CursorType::Legacy;
std::optional<Microsoft::Console::Render::TimerDuration> _cursorBlinkInterval;
til::enumset<Mode> _systemMode{ Mode::AutoWrap };
bool _focused = false;
bool _snapOnInput = true;
bool _altGrAliasing = true;
bool _suppressApplicationTitle = false;
bool _trimBlockSelection = true;
bool _autoMarkPrompts = false;
bool _rainbowSuggestions = false;
size_t _taskbarState = 0;
size_t _taskbarProgress = 0;
size_t _hyperlinkPatternId = 0;
std::wstring _answerbackMessage;
std::wstring _workingDirectory;
bool _highContrastMode = false;
// This default fake font value is only used to check if the font is a raster font.
// Otherwise, the font is changed to a real value with the renderer via TriggerFontChange.
FontInfo _fontInfo{ DEFAULT_FONT_FACE, TMPF_TRUETYPE, 10, { 0, DEFAULT_FONT_SIZE }, CP_UTF8, false };
#pragma region Text Selection
// a selection is represented as a range between two COORDs (start and end)
// the pivot is the til::point that remains selected when you extend a selection in any direction
// this is particularly useful when a word selection is extended over its starting point
// see TerminalSelection.cpp for more information
struct SelectionInfo
{
til::point start;
til::point end;
til::point pivot;
bool blockSelection = false;
bool active = false;
};
til::generational<SelectionInfo> _selection{};
std::wstring _wordDelimiters;
SelectionExpansion _multiClickSelectionMode = SelectionExpansion::Char;
SelectionInteractionMode _selectionMode = SelectionInteractionMode::None;
bool _selectionIsTargetingUrl = false;
SelectionEndpoint _selectionEndpoint = SelectionEndpoint::None;
bool _anchorInactiveSelectionEndpoint = false;
#pragma endregion
std::unique_ptr<TextBuffer> _mainBuffer;
std::unique_ptr<TextBuffer> _altBuffer;
Microsoft::Console::Types::Viewport _mutableViewport;
til::CoordType _scrollbackLines = 0;
bool _detectURLs = false;
bool _clipboardOperationsAllowed = true;
til::size _altBufferSize;
std::optional<til::size> _deferredResize;
// _scrollOffset is the number of lines above the viewport that are currently visible
// If _scrollOffset is 0, then the visible region of the buffer is the viewport.
til::CoordType _scrollOffset = 0;
// TODO this might not be the value we want to store.
// We might want to store the height in the scrollback that's currently visible.
// Think on this some more.
// For example: While looking at the scrollback, we probably want the visible region to "stick"
// to the region they scrolled to. If that were the case, then every time we move _mutableViewport,
// we'd also need to update _offset.
// However, if we just stored it as a _visibleTop, then that point would remain fixed -
// Though if _visibleTop == _mutableViewport.top, then we'd need to make sure to update
// _visibleTop as well.
// Additionally, maybe some people want to scroll into the history, then have that scroll out from
// underneath them, while others would prefer to anchor it in place.
// Either way, we should make this behavior controlled by a setting.
interval_tree::IntervalTree<til::point, size_t> _patternIntervalTree;
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.
struct KeyEventCodes
{
WORD VirtualKey;
WORD ScanCode;
};
std::optional<KeyEventCodes> _lastKeyEventCodes;
static WORD _ScanCodeFromVirtualKey(const WORD vkey) noexcept;
static WORD _VirtualKeyFromScanCode(const WORD scanCode) noexcept;
static WORD _VirtualKeyFromCharacter(const wchar_t ch) noexcept;
static wchar_t _CharacterFromKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates states) noexcept;
void _StoreKeyEvent(const WORD vkey, const WORD scanCode) noexcept;
WORD _TakeVirtualKeyFromLastKeyEvent(const WORD scanCode) noexcept;
Console::VirtualTerminal::TerminalInput& _getTerminalInput() noexcept;
const Console::VirtualTerminal::TerminalInput& _getTerminalInput() const noexcept;
int _VisibleStartIndex() const noexcept;
int _VisibleEndIndex() const noexcept;
Microsoft::Console::Types::Viewport _GetMutableViewport() const noexcept;
Microsoft::Console::Types::Viewport _GetVisibleViewport() const noexcept;
void _PreserveUserScrollOffset(const int viewportDelta) noexcept;
til::CoordType _ScrollToPoints(const til::point coordStart, const til::point coordEnd);
void _NotifyScrollEvent();
bool _inAltBuffer() const noexcept;
TextBuffer& _activeBuffer() const noexcept;
void _updateUrlDetection();
interval_tree::IntervalTree<til::point, size_t> _getPatterns(til::CoordType beg, til::CoordType end) const;
#pragma region TextSelection
// These methods are defined in TerminalSelection.cpp
std::vector<til::point_span> _GetSelectionSpans() const noexcept;
std::pair<til::point, til::point> _PivotSelection(const til::point targetPos, bool& targetStart) const noexcept;
std::pair<til::point, til::point> _ExpandSelectionAnchors(std::pair<til::point, til::point> anchors) const;
til::point _ConvertToBufferCell(const til::point viewportPos, bool allowRightExclusive) const;
void _ScrollToPoint(const til::point pos);
void _MoveByChar(SelectionDirection direction, til::point& pos);
void _MoveByWord(SelectionDirection direction, til::point& pos);
void _MoveByViewport(SelectionDirection direction, til::point& pos) noexcept;
void _MoveByBuffer(SelectionDirection direction, til::point& pos) noexcept;
void _SetSelectionEnd(SelectionInfo* selection, const til::point position, std::optional<SelectionExpansion> newExpansionMode = std::nullopt);
#pragma endregion
#ifdef UNIT_TESTING
friend class TerminalCoreUnitTests::TerminalBufferTests;
friend class TerminalCoreUnitTests::TerminalApiTest;
friend class TerminalCoreUnitTests::ScrollTest;
#endif
};