mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-06 14:19:45 +00:00
Compare commits
8 Commits
dev/cazamo
...
dev/lhecke
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2aca3103a6 | ||
|
|
9841d1feaa | ||
|
|
0143683891 | ||
|
|
8835876b89 | ||
|
|
7cb5a4bc78 | ||
|
|
c9d27e779f | ||
|
|
5e68a08f80 | ||
|
|
971dba737b |
2
.github/actions/spelling/expect/expect.txt
vendored
2
.github/actions/spelling/expect/expect.txt
vendored
@@ -393,6 +393,7 @@ DECNKM
|
||||
DECNRCM
|
||||
DECOM
|
||||
decommit
|
||||
decommitting
|
||||
DECPCCM
|
||||
DECPCTERM
|
||||
DECPS
|
||||
@@ -1820,6 +1821,7 @@ uwa
|
||||
uwp
|
||||
uwu
|
||||
uxtheme
|
||||
VADs
|
||||
Vanara
|
||||
vararg
|
||||
vclib
|
||||
|
||||
@@ -3035,7 +3035,7 @@
|
||||
"type": "boolean"
|
||||
},
|
||||
"historySize": {
|
||||
"default": 9001,
|
||||
"default": 16384,
|
||||
"description": "The number of lines above the ones displayed in the window you can scroll back to.",
|
||||
"minimum": -1,
|
||||
"type": "integer"
|
||||
|
||||
@@ -25,7 +25,7 @@ OutputCellRect::OutputCellRect(const til::CoordType rows, const til::CoordType c
|
||||
_rows(rows),
|
||||
_cols(cols)
|
||||
{
|
||||
_storage.resize(gsl::narrow<size_t>(rows * cols));
|
||||
_storage.resize(gsl::narrow<size_t>(static_cast<til::HugeCoordType>(rows) * cols));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -61,7 +61,7 @@ OutputCellIterator OutputCellRect::GetRowIter(const til::CoordType row) const
|
||||
// - Pointer to the location in the rectangle that represents the start of the requested row.
|
||||
OutputCell* OutputCellRect::_FindRowOffset(const til::CoordType row)
|
||||
{
|
||||
return &_storage.at(gsl::narrow_cast<size_t>(row * _cols));
|
||||
return &_storage.at(gsl::narrow<size_t>(static_cast<til::HugeCoordType>(row) * _cols));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -73,7 +73,7 @@ OutputCell* OutputCellRect::_FindRowOffset(const til::CoordType row)
|
||||
// - Pointer to the location in the rectangle that represents the start of the requested row.
|
||||
const OutputCell* OutputCellRect::_FindRowOffset(const til::CoordType row) const
|
||||
{
|
||||
return &_storage.at(gsl::narrow_cast<size_t>(row * _cols));
|
||||
return &_storage.at(gsl::narrow<size_t>(static_cast<til::HugeCoordType>(row) * _cols));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
||||
@@ -60,9 +60,6 @@ TextBuffer::TextBuffer(til::size screenBufferSize,
|
||||
_cursor{ cursorSize, *this },
|
||||
_isActiveBuffer{ isActiveBuffer }
|
||||
{
|
||||
// Guard against resizing the text buffer to 0 columns/rows, which would break being able to insert text.
|
||||
screenBufferSize.width = std::max(screenBufferSize.width, 1);
|
||||
screenBufferSize.height = std::max(screenBufferSize.height, 1);
|
||||
_reserve(screenBufferSize, defaultAttributes);
|
||||
}
|
||||
|
||||
@@ -70,7 +67,7 @@ TextBuffer::~TextBuffer()
|
||||
{
|
||||
if (_buffer)
|
||||
{
|
||||
_destroy();
|
||||
_destroy(_buffer.get());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,8 +88,9 @@ TextBuffer::~TextBuffer()
|
||||
// memory usage from ~7MB down to just ~2MB at startup in the general case.
|
||||
void TextBuffer::_reserve(til::size screenBufferSize, const TextAttribute& defaultAttributes)
|
||||
{
|
||||
const auto w = gsl::narrow<uint16_t>(screenBufferSize.width);
|
||||
const auto h = gsl::narrow<uint16_t>(screenBufferSize.height);
|
||||
// Guard against resizing the text buffer to 0 columns/rows, which would break being able to insert text.
|
||||
const auto w = std::clamp(screenBufferSize.width, 1, 0xffff);
|
||||
const auto h = std::clamp(screenBufferSize.height, 1, til::CoordTypeMax / 2);
|
||||
|
||||
constexpr auto rowSize = ROW::CalculateRowSize();
|
||||
const auto charsBufferSize = ROW::CalculateCharsBufferSize(w);
|
||||
@@ -103,7 +101,7 @@ void TextBuffer::_reserve(til::size screenBufferSize, const TextAttribute& defau
|
||||
// 65535*65535 cells would result in a allocSize of 8GiB.
|
||||
// --> Use uint64_t so that we can safely do our calculations even on x86.
|
||||
// We allocate 1 additional row, which will be used for GetScratchpadRow().
|
||||
const auto rowCount = ::base::strict_cast<uint64_t>(h) + 1;
|
||||
const auto rowCount = gsl::narrow_cast<uint64_t>(h) + 1;
|
||||
const auto allocSize = gsl::narrow<size_t>(rowCount * rowStride);
|
||||
|
||||
// NOTE: Modifications to this block of code might have to be mirrored over to ResizeTraditional().
|
||||
@@ -142,31 +140,71 @@ __declspec(noinline) void TextBuffer::_commit(const std::byte* row)
|
||||
_construct(_commitWatermark + size);
|
||||
}
|
||||
|
||||
// Destructs and MEM_DECOMMITs all previously constructed ROWs.
|
||||
// Destructs and MEM_DECOMMITs all rows between [rowsToKeep,_commitWatermark).
|
||||
// You can use this (or rather the Reset() method) to fully clear the TextBuffer.
|
||||
void TextBuffer::_decommit() noexcept
|
||||
void TextBuffer::_decommit(til::CoordType rowsToKeep) noexcept
|
||||
{
|
||||
_destroy();
|
||||
VirtualFree(_buffer.get(), 0, MEM_DECOMMIT);
|
||||
_commitWatermark = _buffer.get();
|
||||
// Amount of bytes that have been allocated with MEM_COMMIT so far.
|
||||
const auto commitBytes = gsl::narrow_cast<size_t>(_commitWatermark - _buffer.get());
|
||||
auto newWatermark = _buffer.get();
|
||||
size_t pageOffset = 0;
|
||||
|
||||
if (rowsToKeep > 0)
|
||||
{
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
|
||||
rowsToKeep = std::min(rowsToKeep, _height);
|
||||
|
||||
// +1 for the scratchpad row at offset 0.
|
||||
const auto rows = gsl::narrow_cast<size_t>(rowsToKeep) + 1;
|
||||
|
||||
// Offset in bytes to the first row that we were asked to destroy.
|
||||
// We must ensure that the offset is not past the end of the current _commitWatermark,
|
||||
// since we don't want to finish with a watermark that's somehow larger than what we started with.
|
||||
const auto byteOffset = std::min(commitBytes, rows * _bufferRowStride);
|
||||
newWatermark = _buffer.get() + byteOffset;
|
||||
|
||||
// Since the last row we were asked to keep may reside in the middle
|
||||
// of a page, we must round the offset up to the next page boundary.
|
||||
// That offset will tell us the offset at which we will MEM_DECOMMIT memory.
|
||||
const auto pageMask = gsl::narrow_cast<size_t>(si.dwPageSize) - 1;
|
||||
pageOffset = (byteOffset + pageMask) & ~pageMask;
|
||||
}
|
||||
|
||||
// _destroy() takes care to check that the given pointer is valid.
|
||||
_destroy(newWatermark);
|
||||
|
||||
// MEM_DECOMMIT the memory that we don't need anymore.
|
||||
if (pageOffset < commitBytes)
|
||||
{
|
||||
// The analyzer can't know that we're intentionally decommitting memory here.
|
||||
#pragma warning(suppress : 6250) // Calling 'VirtualFree' without the MEM_RELEASE flag may free memory but not address descriptors (VADs). This causes address space leaks.
|
||||
VirtualFree(_buffer.get() + pageOffset, commitBytes - pageOffset, MEM_DECOMMIT);
|
||||
}
|
||||
|
||||
_commitWatermark = newWatermark;
|
||||
}
|
||||
|
||||
// Constructs ROWs between [_commitWatermark,until).
|
||||
void TextBuffer::_construct(const std::byte* until) noexcept
|
||||
{
|
||||
// _width has been validated to fit into uint16_t during reserve().
|
||||
const auto width = gsl::narrow_cast<uint16_t>(_width);
|
||||
|
||||
for (; _commitWatermark < until; _commitWatermark += _bufferRowStride)
|
||||
{
|
||||
const auto row = reinterpret_cast<ROW*>(_commitWatermark);
|
||||
const auto chars = reinterpret_cast<wchar_t*>(_commitWatermark + _bufferOffsetChars);
|
||||
const auto indices = reinterpret_cast<uint16_t*>(_commitWatermark + _bufferOffsetCharOffsets);
|
||||
std::construct_at(row, chars, indices, _width, _initialAttributes);
|
||||
std::construct_at(row, chars, indices, width, _initialAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
// Destructs ROWs between [_buffer,_commitWatermark).
|
||||
void TextBuffer::_destroy() const noexcept
|
||||
// Destructs ROWs between [it,_commitWatermark).
|
||||
void TextBuffer::_destroy(std::byte* it) const noexcept
|
||||
{
|
||||
for (auto it = _buffer.get(); it < _commitWatermark; it += _bufferRowStride)
|
||||
for (; it < _commitWatermark; it += _bufferRowStride)
|
||||
{
|
||||
std::destroy_at(reinterpret_cast<ROW*>(it));
|
||||
}
|
||||
@@ -974,7 +1012,7 @@ til::point TextBuffer::BufferToScreenPosition(const til::point position) const
|
||||
// and the default current color attributes
|
||||
void TextBuffer::Reset() noexcept
|
||||
{
|
||||
_decommit();
|
||||
_decommit(0);
|
||||
_initialAttributes = _currentAttributes;
|
||||
}
|
||||
|
||||
@@ -988,30 +1026,22 @@ void TextBuffer::ClearScrollback(const til::CoordType newFirstRow, const til::Co
|
||||
{
|
||||
return;
|
||||
}
|
||||
// The new viewport should keep 0 rows? Then just reset everything.
|
||||
if (rowsToKeep <= 0)
|
||||
|
||||
if (rowsToKeep > 0)
|
||||
{
|
||||
_decommit();
|
||||
return;
|
||||
// Our goal is to move the viewport to the absolute start of the underlying memory buffer so that we can
|
||||
// MEM_DECOMMIT the remaining memory. _firstRow is used to make the TextBuffer behave like a circular buffer.
|
||||
// The newFirstRow parameter is relative to the _firstRow. The trick to get the content to the absolute start
|
||||
// is to simply add _firstRow ourselves and then reset it to 0. This causes ScrollRows() to write into
|
||||
// the absolute start while reading from relative coordinates. This works because GetRowByOffset()
|
||||
// operates modulo the buffer height and so the possibly-too-large startAbsolute won't be an issue.
|
||||
const auto startAbsolute = _firstRow + newFirstRow;
|
||||
_firstRow = 0;
|
||||
ScrollRows(startAbsolute, rowsToKeep, -startAbsolute);
|
||||
}
|
||||
|
||||
ClearMarksInRange(til::point{ 0, 0 }, til::point{ _width, std::max(0, newFirstRow - 1) });
|
||||
|
||||
// Our goal is to move the viewport to the absolute start of the underlying memory buffer so that we can
|
||||
// MEM_DECOMMIT the remaining memory. _firstRow is used to make the TextBuffer behave like a circular buffer.
|
||||
// The newFirstRow parameter is relative to the _firstRow. The trick to get the content to the absolute start
|
||||
// is to simply add _firstRow ourselves and then reset it to 0. This causes ScrollRows() to write into
|
||||
// the absolute start while reading from relative coordinates. This works because GetRowByOffset()
|
||||
// operates modulo the buffer height and so the possibly-too-large startAbsolute won't be an issue.
|
||||
const auto startAbsolute = _firstRow + newFirstRow;
|
||||
_firstRow = 0;
|
||||
ScrollRows(startAbsolute, rowsToKeep, -startAbsolute);
|
||||
|
||||
const auto end = _estimateOffsetOfLastCommittedRow();
|
||||
for (auto y = rowsToKeep; y <= end; ++y)
|
||||
{
|
||||
GetMutableRowByOffset(y).Reset(_initialAttributes);
|
||||
}
|
||||
// NOTE: If rowsToKeep is 0 we'll fall through to here and just decommit everything.
|
||||
_decommit(rowsToKeep);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -1993,7 +2023,7 @@ std::vector<til::point_span> TextBuffer::GetTextSpans(til::point start, til::poi
|
||||
const auto rects = GetTextRects(start, end, /*blockSelection*/ true, bufferCoordinates);
|
||||
textSpans.reserve(rects.size());
|
||||
|
||||
for (auto rect : rects)
|
||||
for (const auto& rect : rects)
|
||||
{
|
||||
const til::point first = { rect.left, rect.top };
|
||||
const til::point second = { rect.right, rect.bottom };
|
||||
@@ -2260,7 +2290,7 @@ std::string TextBuffer::GenHTML(const CopyRequest& req,
|
||||
const auto [rowBeg, rowEnd, addLineBreak] = _RowCopyHelper(req, iRow, row);
|
||||
const auto rowBegU16 = gsl::narrow_cast<uint16_t>(rowBeg);
|
||||
const auto rowEndU16 = gsl::narrow_cast<uint16_t>(rowEnd);
|
||||
const auto runs = row.Attributes().slice(rowBegU16, rowEndU16).runs();
|
||||
const auto& runs = row.Attributes().slice(rowBegU16, rowEndU16).runs();
|
||||
|
||||
auto x = rowBegU16;
|
||||
for (const auto& [attr, length] : runs)
|
||||
@@ -2510,7 +2540,7 @@ std::string TextBuffer::GenRTF(const CopyRequest& req,
|
||||
const auto [rowBeg, rowEnd, addLineBreak] = _RowCopyHelper(req, iRow, row);
|
||||
const auto rowBegU16 = gsl::narrow_cast<uint16_t>(rowBeg);
|
||||
const auto rowEndU16 = gsl::narrow_cast<uint16_t>(rowEnd);
|
||||
const auto runs = row.Attributes().slice(rowBegU16, rowEndU16).runs();
|
||||
const auto& runs = row.Attributes().slice(rowBegU16, rowEndU16).runs();
|
||||
|
||||
auto x = rowBegU16;
|
||||
for (auto& [attr, length] : runs)
|
||||
@@ -2699,7 +2729,7 @@ void TextBuffer::_SerializeRow(const ROW& row, const til::CoordType startX, cons
|
||||
|
||||
const auto startXU16 = gsl::narrow_cast<uint16_t>(startX);
|
||||
const auto endXU16 = gsl::narrow_cast<uint16_t>(endX);
|
||||
const auto runs = row.Attributes().slice(startXU16, endXU16).runs();
|
||||
const auto& runs = row.Attributes().slice(startXU16, endXU16).runs();
|
||||
|
||||
const auto beg = runs.begin();
|
||||
const auto end = runs.end();
|
||||
@@ -3292,7 +3322,7 @@ void TextBuffer::RemoveHyperlinkFromMap(uint16_t id) noexcept
|
||||
// - The custom ID if there was one, empty string otherwise
|
||||
std::wstring TextBuffer::GetCustomIdFromId(uint16_t id) const
|
||||
{
|
||||
for (auto customIdPair : _hyperlinkCustomIdMap)
|
||||
for (const auto& customIdPair : _hyperlinkCustomIdMap)
|
||||
{
|
||||
if (customIdPair.second == id)
|
||||
{
|
||||
@@ -3488,7 +3518,6 @@ MarkExtents TextBuffer::_scrollMarkExtentForRow(const til::CoordType rowOffset,
|
||||
bool startedPrompt = false;
|
||||
bool startedCommand = false;
|
||||
bool startedOutput = false;
|
||||
MarkKind lastMarkKind = MarkKind::Output;
|
||||
|
||||
const auto endThisMark = [&](auto x, auto y) {
|
||||
if (startedOutput)
|
||||
@@ -3515,7 +3544,7 @@ MarkExtents TextBuffer::_scrollMarkExtentForRow(const til::CoordType rowOffset,
|
||||
// Output attribute.
|
||||
|
||||
const auto& row = GetRowByOffset(y);
|
||||
const auto runs = row.Attributes().runs();
|
||||
const auto& runs = row.Attributes().runs();
|
||||
x = 0;
|
||||
for (const auto& [attr, length] : runs)
|
||||
{
|
||||
@@ -3558,8 +3587,6 @@ MarkExtents TextBuffer::_scrollMarkExtentForRow(const til::CoordType rowOffset,
|
||||
|
||||
endThisMark(lastMarkedText.x, lastMarkedText.y);
|
||||
}
|
||||
// Otherwise, we've changed from any state -> any state, and it doesn't really matter.
|
||||
lastMarkKind = markKind;
|
||||
}
|
||||
// advance to next run of text
|
||||
x = nextX;
|
||||
@@ -3592,7 +3619,7 @@ std::wstring TextBuffer::_commandForRow(const til::CoordType rowOffset,
|
||||
// Command attributes. Collect up all of those, till we get to the next
|
||||
// Output attribute.
|
||||
const auto& row = GetRowByOffset(y);
|
||||
const auto runs = row.Attributes().runs();
|
||||
const auto& runs = row.Attributes().runs();
|
||||
auto x = 0;
|
||||
for (const auto& [attr, length] : runs)
|
||||
{
|
||||
|
||||
@@ -318,9 +318,9 @@ public:
|
||||
private:
|
||||
void _reserve(til::size screenBufferSize, const TextAttribute& defaultAttributes);
|
||||
void _commit(const std::byte* row);
|
||||
void _decommit() noexcept;
|
||||
void _decommit(til::CoordType keep) noexcept;
|
||||
void _construct(const std::byte* until) noexcept;
|
||||
void _destroy() const noexcept;
|
||||
void _destroy(std::byte* it) const noexcept;
|
||||
ROW& _getRowByOffsetDirect(size_t offset);
|
||||
ROW& _getRow(til::CoordType y) const;
|
||||
til::CoordType _estimateOffsetOfLastCommittedRow() const noexcept;
|
||||
@@ -384,14 +384,14 @@ private:
|
||||
// In other words, _commitWatermark itself will either point exactly onto the next ROW
|
||||
// that should be committed or be equal to _bufferEnd when all ROWs are committed.
|
||||
std::byte* _commitWatermark = nullptr;
|
||||
// This will MEM_COMMIT 128 rows more than we need, to avoid us from having to call VirtualAlloc too often.
|
||||
// This will MEM_COMMIT 256 rows more than we need, to avoid us from having to call VirtualAlloc too often.
|
||||
// This equates to roughly the following commit chunk sizes at these column counts:
|
||||
// * 80 columns (the usual minimum) = 60KB chunks, 4.1MB buffer at 9001 rows
|
||||
// * 120 columns (the most common) = 80KB chunks, 5.6MB buffer at 9001 rows
|
||||
// * 400 columns (the usual maximum) = 220KB chunks, 15.5MB buffer at 9001 rows
|
||||
// * 80 columns (the usual minimum) = 131KB chunks, 4.6MB buffer at 9001 rows
|
||||
// * 120 columns (the most common) = 172KB chunks, 6.0MB buffer at 9001 rows
|
||||
// * 400 columns (the usual maximum) = 459KB chunks, 16.1MB buffer at 9001 rows
|
||||
// There's probably a better metric than this. (This comment was written when ROW had both,
|
||||
// a _chars array containing text and a _charOffsets array contain column-to-text indices.)
|
||||
static constexpr size_t _commitReadAheadRowCount = 128;
|
||||
static constexpr size_t _commitReadAheadRowCount = 256;
|
||||
// Before TextBuffer was made to use virtual memory it initialized the entire memory arena with the initial
|
||||
// attributes right away. To ensure it continues to work the way it used to, this stores these initial attributes.
|
||||
TextAttribute _initialAttributes;
|
||||
@@ -405,9 +405,9 @@ private:
|
||||
size_t _bufferOffsetChars = 0;
|
||||
size_t _bufferOffsetCharOffsets = 0;
|
||||
// The width of the buffer in columns.
|
||||
uint16_t _width = 0;
|
||||
til::CoordType _width = 0;
|
||||
// The height of the buffer in rows, excluding the scratchpad row.
|
||||
uint16_t _height = 0;
|
||||
til::CoordType _height = 0;
|
||||
|
||||
TextAttribute _currentAttributes;
|
||||
til::CoordType _firstRow = 0; // indexes top row (not necessarily 0)
|
||||
|
||||
@@ -44,8 +44,7 @@ void Terminal::Create(til::size viewportSize, til::CoordType scrollbackLines, Re
|
||||
{
|
||||
_mutableViewport = Viewport::FromDimensions({ 0, 0 }, viewportSize);
|
||||
_scrollbackLines = scrollbackLines;
|
||||
const til::size bufferSize{ viewportSize.width,
|
||||
Utils::ClampToShortMax(viewportSize.height + scrollbackLines, 1) };
|
||||
const til::size bufferSize{ viewportSize.width, viewportSize.height + scrollbackLines };
|
||||
const TextAttribute attr{};
|
||||
const UINT cursorSize = 12;
|
||||
_mainBuffer = std::make_unique<TextBuffer>(bufferSize, attr, cursorSize, true, &renderer);
|
||||
@@ -67,7 +66,7 @@ void Terminal::CreateFromSettings(ICoreSettings settings,
|
||||
Utils::ClampToShortMax(settings.InitialRows(), 1) };
|
||||
|
||||
// TODO:MSFT:20642297 - Support infinite scrollback here, if HistorySize is -1
|
||||
Create(viewportSize, Utils::ClampToShortMax(settings.HistorySize(), 0), renderer);
|
||||
Create(viewportSize, std::clamp(settings.HistorySize(), 0, til::CoordTypeMax / 2), renderer);
|
||||
|
||||
UpdateSettings(settings);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
InitializeComponent();
|
||||
|
||||
Automation::AutomationProperties::SetName(AddBellSoundButton(), RS_(L"Profile_AddBellSound/Text"));
|
||||
|
||||
// The XAML -> C++ converter seems to use floats instead of doubles, which means it
|
||||
// can't represent large numbers accurately (lol!). So, we set the property manually.
|
||||
HistorySizeBox().Maximum(static_cast<double>(til::CoordTypeMax / 2));
|
||||
}
|
||||
|
||||
void Profiles_Advanced::OnNavigatedTo(const NavigationEventArgs& e)
|
||||
|
||||
@@ -71,7 +71,8 @@
|
||||
ClearSettingValue="{x:Bind Profile.ClearHistorySize}"
|
||||
HasSettingValue="{x:Bind Profile.HasHistorySize, Mode=OneWay}"
|
||||
SettingOverrideSource="{x:Bind Profile.HistorySizeOverrideSource, Mode=OneWay}">
|
||||
<muxc:NumberBox x:Uid="Profile_HistorySizeBox"
|
||||
<muxc:NumberBox x:Name="HistorySizeBox"
|
||||
x:Uid="Profile_HistorySizeBox"
|
||||
LargeChange="100"
|
||||
Minimum="0"
|
||||
SmallChange="10"
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"fontFace": "Cascadia Mono",
|
||||
"fontSize": 12,
|
||||
"hidden": false,
|
||||
"historySize": 9001,
|
||||
"historySize": 16384,
|
||||
"padding": "8, 8, 8, 8",
|
||||
"snapOnInput": true,
|
||||
"altGrAliasing": true,
|
||||
@@ -66,7 +66,7 @@
|
||||
"fontFace": "Cascadia Mono",
|
||||
"fontSize": 12,
|
||||
"hidden": false,
|
||||
"historySize": 9001,
|
||||
"historySize": 16384,
|
||||
"padding": "8, 8, 8, 8",
|
||||
"snapOnInput": true,
|
||||
"altGrAliasing": true,
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace TerminalCoreUnitTests
|
||||
TEST_CLASS(ScreenSizeLimitsTest);
|
||||
|
||||
TEST_METHOD(ScreenWidthAndHeightAreClampedToBounds);
|
||||
TEST_METHOD(ScrollbackHistorySizeIsClampedToBounds);
|
||||
|
||||
TEST_METHOD(ResizeIsClampedToBounds);
|
||||
};
|
||||
@@ -54,46 +53,6 @@ void ScreenSizeLimitsTest::ScreenWidthAndHeightAreClampedToBounds()
|
||||
VERIFY_ARE_EQUAL(actualDimensions.width, SHRT_MAX, L"Column count clamped to SHRT_MAX == " WCS(SHRT_MAX));
|
||||
}
|
||||
|
||||
void ScreenSizeLimitsTest::ScrollbackHistorySizeIsClampedToBounds()
|
||||
{
|
||||
// What is actually clamped is the number of rows in the internal history buffer,
|
||||
// which is the *sum* of the history size plus the number of rows
|
||||
// actually visible on screen at the moment.
|
||||
|
||||
static constexpr til::CoordType visibleRowCount = 100;
|
||||
|
||||
// Zero history size is acceptable.
|
||||
auto noHistorySettings = winrt::make<MockTermSettings>(0, visibleRowCount, 100);
|
||||
Terminal noHistoryTerminal{ Terminal::TestDummyMarker{} };
|
||||
DummyRenderer renderer{ &noHistoryTerminal };
|
||||
noHistoryTerminal.CreateFromSettings(noHistorySettings, renderer);
|
||||
VERIFY_ARE_EQUAL(noHistoryTerminal.GetTextBuffer().TotalRowCount(), visibleRowCount, L"History size of 0 is accepted");
|
||||
|
||||
// Negative history sizes are clamped to zero.
|
||||
auto negativeHistorySizeSettings = winrt::make<MockTermSettings>(-100, visibleRowCount, 100);
|
||||
Terminal negativeHistorySizeTerminal{ Terminal::TestDummyMarker{} };
|
||||
negativeHistorySizeTerminal.CreateFromSettings(negativeHistorySizeSettings, renderer);
|
||||
VERIFY_ARE_EQUAL(negativeHistorySizeTerminal.GetTextBuffer().TotalRowCount(), visibleRowCount, L"Negative history size is clamped to 0");
|
||||
|
||||
// History size + initial visible rows == SHRT_MAX is acceptable.
|
||||
auto maxHistorySizeSettings = winrt::make<MockTermSettings>(SHRT_MAX - visibleRowCount, visibleRowCount, 100);
|
||||
Terminal maxHistorySizeTerminal{ Terminal::TestDummyMarker{} };
|
||||
maxHistorySizeTerminal.CreateFromSettings(maxHistorySizeSettings, renderer);
|
||||
VERIFY_ARE_EQUAL(maxHistorySizeTerminal.GetTextBuffer().TotalRowCount(), SHRT_MAX, L"History size == SHRT_MAX - initial row count is accepted");
|
||||
|
||||
// History size + initial visible rows == SHRT_MAX + 1 will be clamped slightly.
|
||||
auto justTooBigHistorySizeSettings = winrt::make<MockTermSettings>(SHRT_MAX - visibleRowCount + 1, visibleRowCount, 100);
|
||||
Terminal justTooBigHistorySizeTerminal{ Terminal::TestDummyMarker{} };
|
||||
justTooBigHistorySizeTerminal.CreateFromSettings(justTooBigHistorySizeSettings, renderer);
|
||||
VERIFY_ARE_EQUAL(justTooBigHistorySizeTerminal.GetTextBuffer().TotalRowCount(), SHRT_MAX, L"History size == 1 + SHRT_MAX - initial row count is clamped to SHRT_MAX - initial row count");
|
||||
|
||||
// Ridiculously large history sizes are also clamped.
|
||||
auto farTooBigHistorySizeSettings = winrt::make<MockTermSettings>(99999999, visibleRowCount, 100);
|
||||
Terminal farTooBigHistorySizeTerminal{ Terminal::TestDummyMarker{} };
|
||||
farTooBigHistorySizeTerminal.CreateFromSettings(farTooBigHistorySizeSettings, renderer);
|
||||
VERIFY_ARE_EQUAL(farTooBigHistorySizeTerminal.GetTextBuffer().TotalRowCount(), SHRT_MAX, L"History size that is far too large is clamped to SHRT_MAX - initial row count");
|
||||
}
|
||||
|
||||
void ScreenSizeLimitsTest::ResizeIsClampedToBounds()
|
||||
{
|
||||
// What is actually clamped is the number of rows in the internal history buffer,
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace TerminalCoreUnitTests
|
||||
|
||||
TEST_METHOD(OverflowTests)
|
||||
{
|
||||
const til::point maxCoord = { SHRT_MAX, SHRT_MAX };
|
||||
const til::point maxCoord{ til::CoordTypeMax, til::CoordTypeMax };
|
||||
|
||||
// Test SetSelectionAnchor(til::point) and SetSelectionEnd(til::point)
|
||||
// Behavior: clamp coord to viewport.
|
||||
@@ -132,8 +132,8 @@ namespace TerminalCoreUnitTests
|
||||
Log::Comment(L"Triple click selection with NO scrollback value");
|
||||
ValidateTripleClickSelection(0, { 0, 9 }, { 10, 9 });
|
||||
|
||||
// Test with max scrollback
|
||||
const til::CoordType expected_row = SHRT_MAX - 1;
|
||||
// Test with large scrollback
|
||||
const til::CoordType expected_row = (SHRT_MAX - 1) + 10; // +10 viewport size
|
||||
Log::Comment(L"Single click selection with MAXIMUM scrollback value");
|
||||
ValidateSingleClickSelection(SHRT_MAX, { 10, expected_row }, { 10, expected_row });
|
||||
Log::Comment(L"Double click selection with MAXIMUM scrollback value");
|
||||
|
||||
@@ -400,20 +400,16 @@ static FillConsoleResult FillConsoleImpl(SCREEN_INFORMATION& screenInfo, FillCon
|
||||
// See FillConsoleOutputCharacterWImpl and its identical code.
|
||||
if (enablePowershellShim)
|
||||
{
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
if (const auto writer = gci.GetVtWriterForBuffer(&OutContext))
|
||||
{
|
||||
const auto currentBufferDimensions{ OutContext.GetBufferSize().Dimensions() };
|
||||
const auto wroteWholeBuffer = lengthToWrite == (currentBufferDimensions.area<size_t>());
|
||||
const auto startedAtOrigin = startingCoordinate == til::point{ 0, 0 };
|
||||
const auto wroteSpaces = attribute == (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
|
||||
const auto currentBufferDimensions{ OutContext.GetBufferSize().Dimensions() };
|
||||
const auto wroteWholeBuffer = lengthToWrite == (currentBufferDimensions.area<size_t>());
|
||||
const auto startedAtOrigin = startingCoordinate == til::point{ 0, 0 };
|
||||
const auto wroteSpaces = attribute == (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
|
||||
|
||||
if (wroteWholeBuffer && startedAtOrigin && wroteSpaces)
|
||||
{
|
||||
// PowerShell has previously called FillConsoleOutputCharacterW() which triggered a call to WriteClearScreen().
|
||||
cellsModified = lengthToWrite;
|
||||
return S_OK;
|
||||
}
|
||||
if (wroteWholeBuffer && startedAtOrigin && wroteSpaces)
|
||||
{
|
||||
// PowerShell has previously called FillConsoleOutputCharacterW() which triggered a call to WriteClearScreen().
|
||||
cellsModified = lengthToWrite;
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,20 +454,25 @@ static FillConsoleResult FillConsoleImpl(SCREEN_INFORMATION& screenInfo, FillCon
|
||||
if (enablePowershellShim)
|
||||
{
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
if (auto writer = gci.GetVtWriterForBuffer(&OutContext))
|
||||
{
|
||||
const auto currentBufferDimensions{ OutContext.GetBufferSize().Dimensions() };
|
||||
const auto wroteWholeBuffer = lengthToWrite == (currentBufferDimensions.area<size_t>());
|
||||
const auto startedAtOrigin = startingCoordinate == til::point{ 0, 0 };
|
||||
const auto wroteSpaces = character == UNICODE_SPACE;
|
||||
auto writer = gci.GetVtWriterForBuffer(&OutContext);
|
||||
const auto currentBufferDimensions{ OutContext.GetBufferSize().Dimensions() };
|
||||
const auto wroteWholeBuffer = lengthToWrite == (currentBufferDimensions.area<size_t>());
|
||||
const auto startedAtOrigin = startingCoordinate == til::point{ 0, 0 };
|
||||
const auto wroteSpaces = character == UNICODE_SPACE;
|
||||
|
||||
if (wroteWholeBuffer && startedAtOrigin && wroteSpaces)
|
||||
if (wroteWholeBuffer && startedAtOrigin && wroteSpaces)
|
||||
{
|
||||
// WriteClearScreen uses VT sequences and is more efficient at clearing the buffer than FillConsoleImpl.
|
||||
// For this reason, we use it no matter whether we have a VT writer (= ConPTY) or not.
|
||||
WriteClearScreen(OutContext);
|
||||
|
||||
if (writer)
|
||||
{
|
||||
WriteClearScreen(OutContext);
|
||||
writer.Submit();
|
||||
cellsModified = lengthToWrite;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
cellsModified = lengthToWrite;
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -982,23 +982,30 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont
|
||||
// A null character will get translated to whitespace.
|
||||
fillCharacter = Microsoft::Console::VirtualTerminal::VtIo::SanitizeUCS2(fillCharacter);
|
||||
|
||||
if (writer)
|
||||
// GH#3126 - This is a shim for cmd's `cls` function. In the
|
||||
// legacy console, `cls` is supposed to clear the entire buffer.
|
||||
// We always use a VT sequence, even if ConPTY isn't used, because those are faster nowadays.
|
||||
if (enableCmdShim &&
|
||||
source.left <= 0 && source.top <= 0 &&
|
||||
source.right >= bufferSize.RightInclusive() && source.bottom >= bufferSize.BottomInclusive() &&
|
||||
target.x == 0 && target.y <= -bufferSize.BottomExclusive() &&
|
||||
!clip &&
|
||||
fillCharacter == UNICODE_SPACE && fillAttribute == buffer.GetAttributes().GetLegacyAttributes())
|
||||
{
|
||||
// GH#3126 - This is a shim for cmd's `cls` function. In the
|
||||
// legacy console, `cls` is supposed to clear the entire buffer.
|
||||
// We always use a VT sequence, even if ConPTY isn't used, because those are faster nowadays.
|
||||
if (enableCmdShim &&
|
||||
source.left <= 0 && source.top <= 0 &&
|
||||
source.right >= bufferSize.RightInclusive() && source.bottom >= bufferSize.BottomInclusive() &&
|
||||
target.x == 0 && target.y <= -bufferSize.BottomExclusive() &&
|
||||
!clip &&
|
||||
fillCharacter == UNICODE_SPACE && fillAttribute == buffer.GetAttributes().GetLegacyAttributes())
|
||||
// WriteClearScreen uses VT sequences and is more efficient at clearing the buffer than FillConsoleImpl.
|
||||
// For this reason, we use it no matter whether we have a VT writer (= ConPTY) or not.
|
||||
WriteClearScreen(context);
|
||||
|
||||
if (writer)
|
||||
{
|
||||
WriteClearScreen(context);
|
||||
writer.Submit();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if (writer)
|
||||
{
|
||||
const auto clipViewport = clip ? Viewport::FromInclusive(*clip).Clamp(bufferSize) : bufferSize;
|
||||
const auto sourceViewport = Viewport::FromInclusive(source);
|
||||
const auto fillViewport = sourceViewport.Clamp(clipViewport);
|
||||
|
||||
@@ -2120,10 +2120,10 @@ void SCREEN_INFORMATION::SetViewport(const Viewport& newViewport,
|
||||
// MSFT-33471786, GH#13193:
|
||||
// newViewport may reside anywhere outside of the valid coordScreenBufferSize.
|
||||
// For instance it might be scrolled down more than our text buffer allows to be scrolled.
|
||||
const auto cx = gsl::narrow_cast<SHORT>(std::clamp(viewportRect.width(), 1, coordScreenBufferSize.width));
|
||||
const auto cy = gsl::narrow_cast<SHORT>(std::clamp(viewportRect.height(), 1, coordScreenBufferSize.height));
|
||||
const auto x = gsl::narrow_cast<SHORT>(std::clamp(viewportRect.left, 0, coordScreenBufferSize.width - cx));
|
||||
const auto y = gsl::narrow_cast<SHORT>(std::clamp(viewportRect.top, 0, coordScreenBufferSize.height - cy));
|
||||
const auto cx = std::clamp(viewportRect.width(), 1, coordScreenBufferSize.width);
|
||||
const auto cy = std::clamp(viewportRect.height(), 1, coordScreenBufferSize.height);
|
||||
const auto x = std::clamp(viewportRect.left, 0, coordScreenBufferSize.width - cx);
|
||||
const auto y = std::clamp(viewportRect.top, 0, coordScreenBufferSize.height - cy);
|
||||
|
||||
_viewport = Viewport::FromExclusive({ x, y, x + cx, y + cy });
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ int Utils::s_CompareCoords(const til::size bufferSize, const til::point coordFir
|
||||
// First set the distance vertically
|
||||
// If first is on row 4 and second is on row 6, first will be -2 rows behind second * an 80 character row would be -160.
|
||||
// For the same row, it'll be 0 rows * 80 character width = 0 difference.
|
||||
auto retVal = (coordFirst.y - coordSecond.y) * cRowWidth;
|
||||
auto retVal = (til::HugeCoordType{ coordFirst.y } - coordSecond.y) * cRowWidth;
|
||||
|
||||
// Now adjust for horizontal differences
|
||||
// If first is in position 15 and second is in position 30, first is -15 left in relation to 30.
|
||||
@@ -193,7 +193,7 @@ int Utils::s_CompareCoords(const til::size bufferSize, const til::point coordFir
|
||||
// Step one will set the retVal as -80 as first is one row behind the second.
|
||||
// Step two will then see that first is 79 - 0 = +79 right of second and add 79
|
||||
// The total is -80 + 79 = -1.
|
||||
return retVal;
|
||||
return gsl::narrow_cast<int>(std::clamp<til::HugeCoordType>(retVal, INT_MIN, INT_MAX));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
||||
@@ -21,7 +21,7 @@ constexpr COLORREF OPACITY_OPAQUE = 0xff000000;
|
||||
constexpr auto DEFAULT_FOREGROUND = COLOR_WHITE;
|
||||
constexpr auto DEFAULT_BACKGROUND = COLOR_BLACK;
|
||||
|
||||
constexpr short DEFAULT_HISTORY_SIZE = 9001;
|
||||
constexpr til::CoordType DEFAULT_HISTORY_SIZE = 16384;
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26426)
|
||||
|
||||
@@ -259,11 +259,11 @@ int Viewport::CompareInBounds(const til::point first, const til::point second, b
|
||||
// First set the distance vertically
|
||||
// If first is on row 4 and second is on row 6, first will be -2 rows behind second * an 80 character row would be -160.
|
||||
// For the same row, it'll be 0 rows * 80 character width = 0 difference.
|
||||
auto retVal = (first.y - second.y) * Width();
|
||||
auto retVal = (gsl::narrow_cast<til::HugeCoordType>(first.y) - second.y) * Width();
|
||||
|
||||
// Now adjust for horizontal differences
|
||||
// If first is in position 15 and second is in position 30, first is -15 left in relation to 30.
|
||||
retVal += (first.x - second.x);
|
||||
retVal += gsl::narrow_cast<til::HugeCoordType>(first.x) - second.x;
|
||||
|
||||
// Further notes:
|
||||
// If we already moved behind one row, this will help correct for when first is right of second.
|
||||
@@ -272,7 +272,7 @@ int Viewport::CompareInBounds(const til::point first, const til::point second, b
|
||||
// Step one will set the retVal as -80 as first is one row behind the second.
|
||||
// Step two will then see that first is 79 - 0 = +79 right of second and add 79
|
||||
// The total is -80 + 79 = -1.
|
||||
return retVal;
|
||||
return gsl::narrow_cast<int>(std::clamp<til::HugeCoordType>(retVal, INT_MIN, INT_MAX));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
Reference in New Issue
Block a user