From e6acccfe15bc9fa8553ea1f8a8f716f55b8f6c99 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 11 Dec 2024 21:34:38 +0100 Subject: [PATCH] Can we have infinite scrollback? --- doc/cascadia/profiles.schema.json | 2 +- src/buffer/out/OutputCellRect.cpp | 6 +- src/buffer/out/Row.cpp | 4 +- src/buffer/out/Row.hpp | 8 +- src/buffer/out/textBuffer.cpp | 125 ++++++++++-------- src/buffer/out/textBuffer.hpp | 28 ++-- src/buffer/out/textBufferCellIterator.hpp | 2 +- src/cascadia/TerminalCore/Terminal.cpp | 5 +- .../Profiles_Advanced.cpp | 4 + .../Profiles_Advanced.xaml | 3 +- .../TerminalSettingsModel/defaults.json | 4 +- src/host/_output.cpp | 43 +++--- src/host/getset.cpp | 27 ++-- src/host/screenInfo.cpp | 8 +- src/host/utils.cpp | 4 +- src/inc/DefaultSettings.h | 2 +- src/inc/til/point.h | 4 + src/renderer/atlas/AtlasEngine.api.cpp | 118 ++++++++--------- src/renderer/atlas/AtlasEngine.cpp | 12 +- src/renderer/atlas/AtlasEngine.h | 35 +++-- src/renderer/atlas/AtlasEngine.r.cpp | 4 +- src/renderer/atlas/BackendD2D.cpp | 14 +- src/renderer/atlas/BackendD2D.h | 2 +- src/renderer/atlas/BackendD3D.cpp | 14 +- src/renderer/atlas/BackendD3D.h | 4 +- src/renderer/atlas/common.h | 34 ++--- src/types/viewport.cpp | 4 +- 27 files changed, 270 insertions(+), 250 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 09be784c159..a4a02ffb155 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -2994,7 +2994,7 @@ "type": "boolean" }, "historySize": { - "default": 9001, + "default": 65536, "description": "The number of lines above the ones displayed in the window you can scroll back to.", "minimum": -1, "type": "integer" diff --git a/src/buffer/out/OutputCellRect.cpp b/src/buffer/out/OutputCellRect.cpp index 18ea859568a..2b6fad772be 100644 --- a/src/buffer/out/OutputCellRect.cpp +++ b/src/buffer/out/OutputCellRect.cpp @@ -25,7 +25,7 @@ OutputCellRect::OutputCellRect(const til::CoordType rows, const til::CoordType c _rows(rows), _cols(cols) { - _storage.resize(gsl::narrow(rows * cols)); + _storage.resize(gsl::narrow(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(row * _cols)); + return &_storage.at(gsl::narrow(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(row * _cols)); + return &_storage.at(gsl::narrow(til::HugeCoordType{ row } * _cols)); } // Routine Description: diff --git a/src/buffer/out/Row.cpp b/src/buffer/out/Row.cpp index 7a46215b6fc..7b4021c9c16 100644 --- a/src/buffer/out/Row.cpp +++ b/src/buffer/out/Row.cpp @@ -937,12 +937,12 @@ void ROW::_resizeChars(uint16_t colEndDirty, uint16_t chBegDirty, size_t chEndDi } } -til::small_rle& ROW::Attributes() noexcept +ROW::AttributesType& ROW::Attributes() noexcept { return _attr; } -const til::small_rle& ROW::Attributes() const noexcept +const ROW::AttributesType& ROW::Attributes() const noexcept { return _attr; } diff --git a/src/buffer/out/Row.hpp b/src/buffer/out/Row.hpp index d2c19036baf..7c17f31a16b 100644 --- a/src/buffer/out/Row.hpp +++ b/src/buffer/out/Row.hpp @@ -92,6 +92,8 @@ struct CharToColumnMapper class ROW final { public: + using AttributesType = til::small_rle; + // The implicit agreement between ROW and TextBuffer is that the `charsBuffer` and `charOffsetsBuffer` // arrays have a minimum alignment of 16 Bytes and a size of `rowWidth+1`. The former is used to // implement Reset() efficiently via SIMD and the latter is used to store the past-the-end offset @@ -148,8 +150,8 @@ class ROW final void ReplaceText(RowWriteState& state); void CopyTextFrom(RowCopyTextFromState& state); - til::small_rle& Attributes() noexcept; - const til::small_rle& Attributes() const noexcept; + AttributesType& Attributes() noexcept; + const AttributesType& Attributes() const noexcept; TextAttribute GetAttrByColumn(til::CoordType column) const; std::vector GetHyperlinks() const; ImageSlice* SetImageSlice(ImageSlice::Pointer imageSlice) noexcept; @@ -297,7 +299,7 @@ class ROW final std::span _charOffsets; // _attr is a run-length-encoded vector of TextAttribute with a decompressed // length equal to _columnCount (= 1 TextAttribute per column). - til::small_rle _attr; + AttributesType _attr; // The width of the row in visual columns. uint16_t _columnCount = 0; // Stores double-width/height (DECSWL/DECDWL/DECDHL) attributes. diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index a920854b732..c49cfcdd7ba 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -59,9 +59,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); } @@ -69,7 +66,7 @@ TextBuffer::~TextBuffer() { if (_buffer) { - _destroy(); + _destroy(_buffer.get()); } } @@ -88,10 +85,11 @@ TextBuffer::~TextBuffer() // with our huge allocation, as well as to be able to reduce the private working set of // the application by only committing what we actually need. This reduces conhost's // memory usage from ~7MB down to just ~2MB at startup in the general case. -void TextBuffer::_reserve(til::size screenBufferSize, const TextAttribute& defaultAttributes) +__declspec(noinline) void TextBuffer::_reserve(til::size screenBufferSize, const TextAttribute& defaultAttributes) { - const auto w = gsl::narrow(screenBufferSize.width); - const auto h = gsl::narrow(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 + UINT16_MAX); constexpr auto rowSize = ROW::CalculateRowSize(); const auto charsBufferSize = ROW::CalculateCharsBufferSize(w); @@ -102,13 +100,13 @@ 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(h) + 1; + const auto rowCount = gsl::narrow_cast(h) + 1; const auto allocSize = gsl::narrow(rowCount * rowStride); // NOTE: Modifications to this block of code might have to be mirrored over to ResizeTraditional(). // It constructs a temporary TextBuffer and then extracts the members below, overwriting itself. - _buffer = wil::unique_virtualalloc_ptr{ - static_cast(THROW_LAST_ERROR_IF_NULL(VirtualAlloc(nullptr, allocSize, MEM_RESERVE, PAGE_READWRITE))) + _buffer = wil::unique_virtualalloc_ptr{ + static_cast(THROW_LAST_ERROR_IF_NULL(VirtualAlloc(nullptr, allocSize, MEM_RESERVE, PAGE_READWRITE))) }; _bufferEnd = _buffer.get() + allocSize; _commitWatermark = _buffer.get(); @@ -126,7 +124,7 @@ void TextBuffer::_reserve(til::size screenBufferSize, const TextAttribute& defau // Declaring this function as noinline allows _getRowByOffsetDirect() to be inlined, // which improves overall TextBuffer performance by ~6%. And all it cost is this annotation. // The compiler doesn't understand the likelihood of our branches. (PGO does, but that's imperfect.) -__declspec(noinline) void TextBuffer::_commit(const std::byte* row) +__declspec(noinline) void TextBuffer::_commit(const uint8_t* row) { assert(row >= _commitWatermark); @@ -143,29 +141,62 @@ __declspec(noinline) void TextBuffer::_commit(const std::byte* row) // Destructs and MEM_DECOMMITs all previously constructed ROWs. // You can use this (or rather the Reset() method) to fully clear the TextBuffer. -void TextBuffer::_decommit() noexcept +void TextBuffer::_decommit(til::CoordType keep) noexcept { - _destroy(); - VirtualFree(_buffer.get(), 0, MEM_DECOMMIT); - _commitWatermark = _buffer.get(); + SYSTEM_INFO si; + GetSystemInfo(&si); + + keep = std::clamp(keep, 0, _height); + keep += _commitReadAheadRowCount; + keep = std::min(keep, _height); + + // Amount of bytes that have been allocated with MEM_COMMIT so far. + const auto commitBytes = gsl::narrow_cast(_commitWatermark - _buffer.get()); + // 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, keep * _bufferRowStride); + const auto 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(si.dwPageSize) - 1; + const auto 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) + { + VirtualFree(_buffer.get() + pageOffset, commitBytes - pageOffset, MEM_DECOMMIT); + } + + _commitWatermark = newWatermark; } // Constructs ROWs between [_commitWatermark,until). -void TextBuffer::_construct(const std::byte* until) noexcept +void TextBuffer::_construct(const uint8_t* until) noexcept { - for (; _commitWatermark < until; _commitWatermark += _bufferRowStride) + // _width has been validated to fit into uint16_t during reserve(). + const auto width = gsl::narrow_cast(_width); + auto wm = _commitWatermark; + + for (; wm < until; wm += _bufferRowStride) { - const auto row = reinterpret_cast(_commitWatermark); - const auto chars = reinterpret_cast(_commitWatermark + _bufferOffsetChars); - const auto indices = reinterpret_cast(_commitWatermark + _bufferOffsetCharOffsets); - std::construct_at(row, chars, indices, _width, _initialAttributes); + const auto row = reinterpret_cast(wm); + const auto chars = reinterpret_cast(wm + _bufferOffsetChars); + const auto indices = reinterpret_cast(wm + _bufferOffsetCharOffsets); + std::construct_at(row, chars, indices, width, _initialAttributes); } + + _commitWatermark = wm; } -// Destructs ROWs between [_buffer,_commitWatermark). -void TextBuffer::_destroy() const noexcept +// Destructs ROWs between [it,_commitWatermark). +void TextBuffer::_destroy(uint8_t* it) const noexcept { - for (auto it = _buffer.get(); it < _commitWatermark; it += _bufferRowStride) + for (; it < _commitWatermark; it += _bufferRowStride) { std::destroy_at(reinterpret_cast(it)); } @@ -973,7 +1004,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,29 +1019,20 @@ 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); - } + _decommit(rowsToKeep); } // Routine Description: @@ -2015,7 +2037,7 @@ std::string TextBuffer::GenHTML(const CopyRequest& req, const auto [rowBeg, rowEnd, addLineBreak] = _RowCopyHelper(req, iRow, row); const auto rowBegU16 = gsl::narrow_cast(rowBeg); const auto rowEndU16 = gsl::narrow_cast(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) @@ -2265,7 +2287,7 @@ std::string TextBuffer::GenRTF(const CopyRequest& req, const auto [rowBeg, rowEnd, addLineBreak] = _RowCopyHelper(req, iRow, row); const auto rowBegU16 = gsl::narrow_cast(rowBeg); const auto rowEndU16 = gsl::narrow_cast(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) @@ -2457,7 +2479,7 @@ void TextBuffer::_SerializeRow(const ROW& row, const til::CoordType startX, cons const auto startXU16 = gsl::narrow_cast(startX); const auto endXU16 = gsl::narrow_cast(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(); @@ -3246,7 +3268,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) @@ -3273,7 +3294,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) { @@ -3316,8 +3337,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; @@ -3350,7 +3369,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) { diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index c97de9e7523..a16d05d6ef9 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -311,10 +311,10 @@ class TextBuffer final private: void _reserve(til::size screenBufferSize, const TextAttribute& defaultAttributes); - void _commit(const std::byte* row); - void _decommit() noexcept; - void _construct(const std::byte* until) noexcept; - void _destroy() const noexcept; + void _commit(const uint8_t* row); + void _decommit(til::CoordType keep) noexcept; + void _construct(const uint8_t* until) noexcept; + void _destroy(uint8_t* it) const noexcept; ROW& _getRowByOffsetDirect(size_t offset); ROW& _getRow(til::CoordType y) const; til::CoordType _estimateOffsetOfLastCommittedRow() const noexcept; @@ -357,9 +357,9 @@ class TextBuffer final // Padding may exist for alignment purposes. // // The base (start) address of the memory arena. - wil::unique_virtualalloc_ptr _buffer; + wil::unique_virtualalloc_ptr _buffer; // The past-the-end pointer of the memory arena. - std::byte* _bufferEnd = nullptr; + uint8_t* _bufferEnd = nullptr; // The range between _buffer (inclusive) and _commitWatermark (exclusive) is the range of // memory that has already been committed via MEM_COMMIT and contains ready-to-use ROWs. // @@ -375,15 +375,15 @@ class TextBuffer final // _commitWatermark will always be a multiple of _bufferRowStride away from _buffer. // 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. + uint8_t* _commitWatermark = nullptr; + // 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) = 120KB chunks, 4.1MB buffer at 9001 rows + // * 120 columns (the most common) = 160KB chunks, 5.6MB buffer at 9001 rows + // * 400 columns (the usual maximum) = 440KB chunks, 15.5MB 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; @@ -397,9 +397,9 @@ class TextBuffer final 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) diff --git a/src/buffer/out/textBufferCellIterator.hpp b/src/buffer/out/textBufferCellIterator.hpp index 30276f2e13c..1754fdbbb04 100644 --- a/src/buffer/out/textBufferCellIterator.hpp +++ b/src/buffer/out/textBufferCellIterator.hpp @@ -53,7 +53,7 @@ class TextBufferCellIterator void _GenerateView() noexcept; static const ROW* s_GetRow(const TextBuffer& buffer, const til::point pos); - til::small_rle::const_iterator _attrIter; + ROW::AttributesType::const_iterator _attrIter; OutputCellView _view; const ROW* _pRow; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index c77ab1ab764..eb126402794 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -43,8 +43,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(bufferSize, attr, cursorSize, true, &renderer); @@ -66,7 +65,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, 0x3FFFFFFF), renderer); UpdateSettings(settings); } diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.cpp b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.cpp index 00bdff6d0b6..6f417271bda 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.cpp +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.cpp @@ -17,6 +17,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation Profiles_Advanced::Profiles_Advanced() { InitializeComponent(); + // The XAML -> C++ converter seems to use floats instead of doubles, + // which means it can't represent such a large number. + // As such we have to set the property manually. + HistorySizeBox().Maximum(1073741823.0); } void Profiles_Advanced::OnNavigatedTo(const NavigationEventArgs& e) diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml index 15505919307..3a2cfcacfd2 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml @@ -72,7 +72,8 @@ ClearSettingValue="{x:Bind Profile.ClearHistorySize}" HasSettingValue="{x:Bind Profile.HasHistorySize, Mode=OneWay}" SettingOverrideSource="{x:Bind Profile.HistorySizeOverrideSource, Mode=OneWay}"> - ()); - 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()); + 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; } } @@ -444,20 +440,21 @@ 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()); - 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()); + const auto startedAtOrigin = startingCoordinate == til::point{ 0, 0 }; + const auto wroteSpaces = character == UNICODE_SPACE; - if (wroteWholeBuffer && startedAtOrigin && wroteSpaces) + if (wroteWholeBuffer && startedAtOrigin && wroteSpaces) + { + WriteClearScreen(OutContext); + if (writer) { - WriteClearScreen(OutContext); writer.Submit(); - cellsModified = lengthToWrite; - return S_OK; } + cellsModified = lengthToWrite; + return S_OK; } } diff --git a/src/host/getset.cpp b/src/host/getset.cpp index b8109bca718..a81bf51b647 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -980,23 +980,26 @@ 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(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); diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index 3e1ae49d4b3..8e4bd972d60 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -2114,10 +2114,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(std::clamp(viewportRect.width(), 1, coordScreenBufferSize.width)); - const auto cy = gsl::narrow_cast(std::clamp(viewportRect.height(), 1, coordScreenBufferSize.height)); - const auto x = gsl::narrow_cast(std::clamp(viewportRect.left, 0, coordScreenBufferSize.width - cx)); - const auto y = gsl::narrow_cast(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 }); diff --git a/src/host/utils.cpp b/src/host/utils.cpp index d5ac1fc90d4..33a0ac3aad7 100644 --- a/src/host/utils.cpp +++ b/src/host/utils.cpp @@ -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(std::clamp(retVal, INT_MIN, INT_MAX)); } // Routine Description: diff --git a/src/inc/DefaultSettings.h b/src/inc/DefaultSettings.h index bfec082513f..5bc9dce16bd 100644 --- a/src/inc/DefaultSettings.h +++ b/src/inc/DefaultSettings.h @@ -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 = 65536; #pragma warning(push) #pragma warning(disable : 26426) diff --git a/src/inc/til/point.h b/src/inc/til/point.h index 17bc9d1c861..ce3582a3ed9 100644 --- a/src/inc/til/point.h +++ b/src/inc/til/point.h @@ -9,6 +9,10 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" inline constexpr CoordType CoordTypeMin = INT32_MIN; inline constexpr CoordType CoordTypeMax = INT32_MAX; + using HugeCoordType = int64_t; + inline constexpr HugeCoordType HugeCoordTypeMin = INT64_MIN; + inline constexpr HugeCoordType HugeCoordTypeMax = INT64_MAX; + namespace details { template diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index a80ae930ba9..7ae256ee8bd 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -48,8 +48,8 @@ constexpr HRESULT vec2_narrow(U x, U y, vec2& out) noexcept //assert(psrRegion->top < psrRegion->bottom && psrRegion->top >= 0 && psrRegion->bottom <= _api.cellCount.y); // BeginPaint() protects against invalid out of bounds numbers. - _api.invalidatedRows.start = std::min(_api.invalidatedRows.start, gsl::narrow_cast(psrRegion->top)); - _api.invalidatedRows.end = std::max(_api.invalidatedRows.end, gsl::narrow_cast(psrRegion->bottom)); + _api.invalidatedRows.start = std::min(_api.invalidatedRows.start, gsl::narrow_cast(psrRegion->top)); + _api.invalidatedRows.end = std::max(_api.invalidatedRows.end, gsl::narrow_cast(psrRegion->bottom)); return S_OK; } @@ -58,10 +58,10 @@ constexpr HRESULT vec2_narrow(U x, U y, vec2& out) noexcept //assert(psrRegion->left <= psrRegion->right && psrRegion->left >= 0 && psrRegion->right <= _api.cellCount.x); //assert(psrRegion->top <= psrRegion->bottom && psrRegion->top >= 0 && psrRegion->bottom <= _api.cellCount.y); - const auto left = gsl::narrow_cast(psrRegion->left); - const auto top = gsl::narrow_cast(psrRegion->top); - const auto right = gsl::narrow_cast(psrRegion->right); - const auto bottom = gsl::narrow_cast(psrRegion->bottom); + const auto left = gsl::narrow_cast(psrRegion->left); + const auto top = gsl::narrow_cast(psrRegion->top); + const auto right = gsl::narrow_cast(psrRegion->right); + const auto bottom = gsl::narrow_cast(psrRegion->bottom); // BeginPaint() protects against invalid out of bounds numbers. _api.invalidatedCursorArea.left = std::min(_api.invalidatedCursorArea.left, left); @@ -96,8 +96,8 @@ void AtlasEngine::_invalidateSpans(std::span spans, const til::rect rect{ beg, row, end + 1, row + 1 }; rect = rect.to_origin(viewportOrigin); rect &= viewport; - _api.invalidatedRows.start = gsl::narrow_cast(std::min(_api.invalidatedRows.start, std::max(0, rect.top))); - _api.invalidatedRows.end = gsl::narrow_cast(std::max(_api.invalidatedRows.end, std::max(0, rect.bottom))); + _api.invalidatedRows.start = std::min(_api.invalidatedRows.start, std::max(0, rect.top)); + _api.invalidatedRows.end = std::max(_api.invalidatedRows.end, std::max(0, rect.bottom)); }); } } @@ -107,8 +107,8 @@ void AtlasEngine::_invalidateSpans(std::span spans, const if (!selections.empty()) { // INVARIANT: This assumes that `selections` is sorted by increasing Y - _api.invalidatedRows.start = gsl::narrow_cast(std::min(_api.invalidatedRows.start, std::max(0, selections.front().top))); - _api.invalidatedRows.end = gsl::narrow_cast(std::max(_api.invalidatedRows.end, std::max(0, selections.back().bottom))); + _api.invalidatedRows.start = std::min(_api.invalidatedRows.start, std::max(0, selections.front().top)); + _api.invalidatedRows.end = std::max(_api.invalidatedRows.end, std::max(0, selections.back().bottom)); } return S_OK; } @@ -127,28 +127,26 @@ void AtlasEngine::_invalidateSpans(std::span spans, const if (const auto delta = pcoordDelta->x) { - _api.invalidatedCursorArea.left = gsl::narrow_cast(clamp(_api.invalidatedCursorArea.left + delta, u16min, u16max)); - _api.invalidatedCursorArea.right = gsl::narrow_cast(clamp(_api.invalidatedCursorArea.right + delta, u16min, u16max)); - + _api.invalidatedCursorArea.left = clamp(_api.invalidatedCursorArea.left + delta, 0, i32SafeMax); + _api.invalidatedCursorArea.right = clamp(_api.invalidatedCursorArea.right + delta, 0, i32SafeMax); _api.invalidatedRows = invalidatedRowsAll; } if (const auto delta = pcoordDelta->y) { - _api.scrollOffset = gsl::narrow_cast(clamp(_api.scrollOffset + delta, i16min, i16max)); - - _api.invalidatedCursorArea.top = gsl::narrow_cast(clamp(_api.invalidatedCursorArea.top + delta, u16min, u16max)); - _api.invalidatedCursorArea.bottom = gsl::narrow_cast(clamp(_api.invalidatedCursorArea.bottom + delta, u16min, u16max)); + _api.scrollOffset = clamp(_api.scrollOffset + delta, -i32SafeMax, i32SafeMax); + _api.invalidatedCursorArea.top = clamp(_api.invalidatedCursorArea.top + delta, 0, i32SafeMax); + _api.invalidatedCursorArea.bottom = clamp(_api.invalidatedCursorArea.bottom + delta, 0, i32SafeMax); if (delta < 0) { - _api.invalidatedRows.start = gsl::narrow_cast(clamp(_api.invalidatedRows.start + delta, u16min, u16max)); + _api.invalidatedRows.start = clamp(_api.invalidatedRows.start + delta, 0, i32SafeMax); _api.invalidatedRows.end = _api.s->viewportCellCount.y; } else { _api.invalidatedRows.start = 0; - _api.invalidatedRows.end = gsl::narrow_cast(clamp(_api.invalidatedRows.end + delta, u16min, u16max)); + _api.invalidatedRows.end = clamp(_api.invalidatedRows.end + delta, 0, i32SafeMax); } } @@ -188,7 +186,7 @@ void AtlasEngine::_invalidateSpans(std::span spans, const [[nodiscard]] HRESULT AtlasEngine::UpdateDpi(const int dpi) noexcept { - u16 newDPI; + i32 newDPI; RETURN_IF_FAILED(api_narrow(dpi, newDPI)); if (_api.s->font->dpi != newDPI) @@ -202,13 +200,13 @@ void AtlasEngine::_invalidateSpans(std::span spans, const [[nodiscard]] HRESULT AtlasEngine::UpdateViewport(const til::inclusive_rect& srNewViewport) noexcept try { - const u16x2 viewportCellCount{ - gsl::narrow(std::max(1, srNewViewport.right - srNewViewport.left + 1)), - gsl::narrow(std::max(1, srNewViewport.bottom - srNewViewport.top + 1)), + const i32x2 viewportCellCount{ + gsl::narrow(std::max(1, srNewViewport.right - srNewViewport.left + 1)), + gsl::narrow(std::max(1, srNewViewport.bottom - srNewViewport.top + 1)), }; - const u16x2 viewportOffset{ - gsl::narrow(srNewViewport.left), - gsl::narrow(srNewViewport.top), + const i32x2 viewportOffset{ + gsl::narrow(srNewViewport.left), + gsl::narrow(srNewViewport.top), }; if (_api.s->viewportCellCount != viewportCellCount) @@ -462,10 +460,7 @@ void AtlasEngine::SetWarningCallback(std::function(clamp(pixels.width, 1, u16max)), - gsl::narrow_cast(clamp(pixels.height, 1, u16max)), - }; + const i32x2 newSize{ pixels.width, pixels.height }; if (_api.s->targetSize != newSize) { @@ -806,8 +801,8 @@ void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, Fo // Our cells can't overlap each other so we additionally clamp the bottom line to be inside the cell boundaries. doubleUnderlinePosBottom = std::min(doubleUnderlinePosBottom, adjustedHeight - doubleUnderlineWidth); - const auto cellWidth = gsl::narrow(lrintf(adjustedWidth)); - const auto cellHeight = gsl::narrow(lrintf(adjustedHeight)); + const auto cellWidth = gsl::narrow(lrintf(adjustedWidth)); + const auto cellHeight = gsl::narrow(lrintf(adjustedHeight)); { til::size coordSize; @@ -827,22 +822,21 @@ void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, Fo if (fontMetrics) { - const auto fontWeightU16 = gsl::narrow_cast(requestedWeight); - const auto advanceWidthU16 = gsl::narrow_cast(lrintf(advanceWidth)); - const auto baselineU16 = gsl::narrow_cast(lrintf(baseline)); - const auto descenderU16 = gsl::narrow_cast(cellHeight - baselineU16); - const auto thinLineWidthU16 = gsl::narrow_cast(lrintf(thinLineWidth)); + const auto advanceWidthI32 = static_cast(lrintf(advanceWidth)); + const auto baselineI32 = static_cast(lrintf(baseline)); + const auto descenderI32 = cellHeight - baselineI32; + const auto thinLineWidthI32 = static_cast(lrintf(thinLineWidth)); - const auto gridBottomPositionU16 = gsl::narrow_cast(cellHeight - thinLineWidth); - const auto gridRightPositionU16 = gsl::narrow_cast(cellWidth - thinLineWidth); + const auto gridBottomPositionI32 = static_cast(lrintf(cellHeight - thinLineWidth)); + const auto gridRightPositionI32 = static_cast(lrintf(cellWidth - thinLineWidth)); - const auto underlinePosU16 = gsl::narrow_cast(lrintf(underlinePos)); - const auto underlineWidthU16 = gsl::narrow_cast(lrintf(underlineWidth)); - const auto strikethroughPosU16 = gsl::narrow_cast(lrintf(strikethroughPos)); - const auto strikethroughWidthU16 = gsl::narrow_cast(lrintf(strikethroughWidth)); - const auto doubleUnderlinePosTopU16 = gsl::narrow_cast(lrintf(doubleUnderlinePosTop)); - const auto doubleUnderlinePosBottomU16 = gsl::narrow_cast(lrintf(doubleUnderlinePosBottom)); - const auto doubleUnderlineWidthU16 = gsl::narrow_cast(lrintf(doubleUnderlineWidth)); + const auto underlinePosI32 = static_cast(lrintf(underlinePos)); + const auto underlineWidthI32 = static_cast(lrintf(underlineWidth)); + const auto strikethroughPosI32 = static_cast(lrintf(strikethroughPos)); + const auto strikethroughWidthI32 = static_cast(lrintf(strikethroughWidth)); + const auto doubleUnderlinePosTopI32 = static_cast(lrintf(doubleUnderlinePosTop)); + const auto doubleUnderlinePosBottomI32 = static_cast(lrintf(doubleUnderlinePosBottom)); + const auto doubleUnderlineWidthI32 = static_cast(lrintf(doubleUnderlineWidth)); // NOTE: From this point onward no early returns or throwing code should exist, // as we might cause _api to be in an inconsistent state otherwise. @@ -853,22 +847,22 @@ void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, Fo fontMetrics->fontName = std::move(primaryFontName); fontMetrics->fontSize = fontSizeInPx; fontMetrics->cellSize = { cellWidth, cellHeight }; - fontMetrics->fontWeight = fontWeightU16; - fontMetrics->advanceWidth = advanceWidthU16; - fontMetrics->baseline = baselineU16; - fontMetrics->descender = descenderU16; - fontMetrics->thinLineWidth = thinLineWidthU16; - - fontMetrics->gridTop = { 0, thinLineWidthU16 }; - fontMetrics->gridBottom = { gridBottomPositionU16, thinLineWidthU16 }; - fontMetrics->gridLeft = { 0, thinLineWidthU16 }; - fontMetrics->gridRight = { gridRightPositionU16, thinLineWidthU16 }; - - fontMetrics->underline = { underlinePosU16, underlineWidthU16 }; - fontMetrics->strikethrough = { strikethroughPosU16, strikethroughWidthU16 }; - fontMetrics->doubleUnderline[0] = { doubleUnderlinePosTopU16, doubleUnderlineWidthU16 }; - fontMetrics->doubleUnderline[1] = { doubleUnderlinePosBottomU16, doubleUnderlineWidthU16 }; - fontMetrics->overline = { 0, underlineWidthU16 }; + fontMetrics->fontWeight = requestedWeight; + fontMetrics->advanceWidth = advanceWidthI32; + fontMetrics->baseline = baselineI32; + fontMetrics->descender = descenderI32; + fontMetrics->thinLineWidth = thinLineWidthI32; + + fontMetrics->gridTop = { 0, thinLineWidthI32 }; + fontMetrics->gridBottom = { gridBottomPositionI32, thinLineWidthI32 }; + fontMetrics->gridLeft = { 0, thinLineWidthI32 }; + fontMetrics->gridRight = { gridRightPositionI32, thinLineWidthI32 }; + + fontMetrics->underline = { underlinePosI32, underlineWidthI32 }; + fontMetrics->strikethrough = { strikethroughPosI32, strikethroughWidthI32 }; + fontMetrics->doubleUnderline[0] = { doubleUnderlinePosTopI32, doubleUnderlineWidthI32 }; + fontMetrics->doubleUnderline[1] = { doubleUnderlinePosBottomI32, doubleUnderlineWidthI32 }; + fontMetrics->overline = { 0, underlineWidthI32 }; fontMetrics->builtinGlyphs = fontInfoDesired.GetEnableBuiltinGlyphs(); fontMetrics->colorGlyphs = fontInfoDesired.GetEnableColorGlyphs(); diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index 0de92b62373..9f1249d2aba 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -95,8 +95,8 @@ try } if (_api.scrollOffset) { - const auto limit = gsl::narrow_cast(_p.s->viewportCellCount.y & 0x7fff); - const auto offset = gsl::narrow_cast(clamp(_api.scrollOffset, -limit, limit)); + const auto limit = _p.s->viewportCellCount.y; + const auto offset = clamp(_api.scrollOffset, -limit, limit); const auto nothingInvalid = _api.invalidatedRows.start == _api.invalidatedRows.end; _api.scrollOffset = offset; @@ -104,13 +104,13 @@ try // Mark the newly scrolled in rows as invalidated if (offset < 0) { - const u16 begRow = _p.s->viewportCellCount.y + offset; + const auto begRow = _p.s->viewportCellCount.y + offset; _api.invalidatedRows.start = nothingInvalid ? begRow : std::min(_api.invalidatedRows.start, begRow); _api.invalidatedRows.end = _p.s->viewportCellCount.y; } else { - const u16 endRow = offset; + const auto endRow = offset; _api.invalidatedRows.start = 0; _api.invalidatedRows.end = nothingInvalid ? endRow : std::max(_api.invalidatedRows.end, endRow); } @@ -142,7 +142,7 @@ try // the contents of the entire swap chain is redundant, but more importantly because the scroll rect // is the subset of the contents that are being scrolled into. If you scroll the entire viewport // then the scroll rect is empty, which Present1() will loudly complain about. - if (_p.invalidatedRows == range{ 0, _p.s->viewportCellCount.y }) + if (_p.invalidatedRows == range{ 0, _p.s->viewportCellCount.y }) { _p.MarkAllAsDirty(); } @@ -398,7 +398,7 @@ void AtlasEngine::_fillColorBitmap(const size_t y, const size_t x1, const size_t // - bgColor: the background highlight color // Returns: // - S_OK if we painted successfully, else an appropriate HRESULT error code -[[nodiscard]] HRESULT AtlasEngine::_drawHighlighted(std::span& highlights, const u16 row, const u16 begX, const u16 endX, const u32 fgColor, const u32 bgColor) noexcept +[[nodiscard]] HRESULT AtlasEngine::_drawHighlighted(std::span& highlights, const i32 row, const i32 begX, const i32 endX, const u32 fgColor, const u32 bgColor) noexcept try { if (highlights.empty()) diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index af77dcfe697..3c023bb01b1 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -36,13 +36,13 @@ namespace Microsoft::Console::Render::Atlas [[nodiscard]] HRESULT InvalidateScroll(const til::point* pcoordDelta) noexcept override; [[nodiscard]] HRESULT InvalidateAll() noexcept override; [[nodiscard]] HRESULT InvalidateTitle(std::wstring_view proposedTitle) noexcept override; - [[nodiscard]] HRESULT NotifyNewText(const std::wstring_view newText) noexcept override; + [[nodiscard]] HRESULT NotifyNewText(std::wstring_view newText) noexcept override; [[nodiscard]] HRESULT PrepareRenderInfo(RenderFrameInfo info) noexcept override; [[nodiscard]] HRESULT ResetLineTransform() noexcept override; [[nodiscard]] HRESULT PrepareLineTransform(LineRendition lineRendition, til::CoordType targetRow, til::CoordType viewportLeft) noexcept override; [[nodiscard]] HRESULT PaintBackground() noexcept override; [[nodiscard]] HRESULT PaintBufferLine(std::span clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept override; - [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override; + [[nodiscard]] HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF gridlineColor, COLORREF underlineColor, size_t cchLine, til::point coordTarget) noexcept override; [[nodiscard]] HRESULT PaintImageSlice(const ImageSlice& imageSlice, til::CoordType targetRow, til::CoordType viewportLeft) noexcept override; [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override; [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; @@ -66,7 +66,7 @@ namespace Microsoft::Console::Render::Atlas // setter void SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept; void SetCallback(std::function pfn) noexcept; - void EnableTransparentBackground(const bool isTransparent) noexcept; + void EnableTransparentBackground(bool isTransparent) noexcept; [[nodiscard]] HRESULT SetHwnd(HWND hwnd) noexcept; void SetPixelShaderPath(std::wstring_view value) noexcept; void SetPixelShaderImagePath(std::wstring_view value) noexcept; @@ -89,8 +89,8 @@ namespace Microsoft::Console::Render::Atlas void _mapCharacters(const wchar_t* text, u32 textLength, u32* mappedLength, IDWriteFontFace2** mappedFontFace) const; void _mapComplex(IDWriteFontFace2* mappedFontFace, u32 idx, u32 length, ShapedRow& row); ATLAS_ATTR_COLD void _mapReplacementCharacter(u32 from, u32 to, ShapedRow& row); - void _fillColorBitmap(const size_t y, const size_t x1, const size_t x2, const u32 fgColor, const u32 bgColor) noexcept; - [[nodiscard]] HRESULT _drawHighlighted(std::span& highlights, const u16 row, const u16 begX, const u16 endX, const u32 fgColor, const u32 bgColor) noexcept; + void _fillColorBitmap(size_t y, size_t x1, size_t x2, u32 fgColor, u32 bgColor) noexcept; + [[nodiscard]] HRESULT _drawHighlighted(std::span& highlights, i32 row, i32 begX, i32 endX, u32 fgColor, u32 bgColor) noexcept; // AtlasEngine.api.cpp void _resolveTransparencySettings() noexcept; @@ -110,13 +110,10 @@ namespace Microsoft::Console::Render::Atlas void _waitUntilCanRender() noexcept; void _present(); - static constexpr u16 u16min = 0x0000; - static constexpr u16 u16max = 0xffff; - static constexpr i16 i16min = -0x8000; - static constexpr i16 i16max = 0x7fff; - static constexpr u16r invalidatedAreaNone = { u16max, u16max, u16min, u16min }; - static constexpr range invalidatedRowsNone{ u16max, u16min }; - static constexpr range invalidatedRowsAll{ u16min, u16max }; + static constexpr i32 i32SafeMax = INT32_MAX / 2; + static constexpr i32r invalidatedAreaNone = { i32SafeMax, i32SafeMax, 0, 0 }; + static constexpr range invalidatedRowsNone{ i32SafeMax, 0 }; + static constexpr range invalidatedRowsAll{ 0, i32SafeMax }; static constexpr u32 highlightBg = 0xff00ffff; static constexpr u32 highlightFg = 0xff000000; @@ -140,7 +137,7 @@ namespace Microsoft::Console::Render::Atlas AntialiasingMode antialiasingMode = DefaultAntialiasingMode; std::vector bufferLine; - std::vector bufferLineColumn; + std::vector bufferLineColumn; std::array, 4> textFormatAxes; std::vector analysisResults; @@ -163,9 +160,9 @@ namespace Microsoft::Console::Render::Atlas u32 currentBackground = 0; u32 currentForeground = 0; FontRelevantAttributes attributes = FontRelevantAttributes::None; - u16x2 lastPaintBufferLineCoord{}; + u32x2 lastPaintBufferLineCoord{}; // UpdateHyperlinkHoveredId() - u16 hyperlinkHoveredId = 0; + u32 hyperlinkHoveredId = 0; // These tracks the highlighted regions on the screen that are yet to be painted. std::span searchHighlights; @@ -175,12 +172,12 @@ namespace Microsoft::Console::Render::Atlas // dirtyRect is a computed value based on invalidatedRows. til::rect dirtyRect; // These "invalidation" fields are reset in EndPaint() - u16r invalidatedCursorArea = invalidatedAreaNone; - range invalidatedRows = invalidatedRowsNone; // x is treated as "top" and y as "bottom" - i16 scrollOffset = 0; + i32r invalidatedCursorArea = invalidatedAreaNone; + range invalidatedRows = invalidatedRowsNone; // x is treated as "top" and y as "bottom" + i32 scrollOffset = 0; // The position of the viewport inside the text buffer (in cells). - u16x2 viewportOffset{ 0, 0 }; + i32x2 viewportOffset{ 0, 0 }; } _api; }; } diff --git a/src/renderer/atlas/AtlasEngine.r.cpp b/src/renderer/atlas/AtlasEngine.r.cpp index 2df96f0c620..9c997396231 100644 --- a/src/renderer/atlas/AtlasEngine.r.cpp +++ b/src/renderer/atlas/AtlasEngine.r.cpp @@ -319,8 +319,8 @@ void AtlasEngine::_createSwapChain() _destroySwapChain(); DXGI_SWAP_CHAIN_DESC1 desc{ - .Width = _p.s->targetSize.x, - .Height = _p.s->targetSize.y, + .Width = static_cast(_p.s->targetSize.x), + .Height = static_cast(_p.s->targetSize.y), .Format = DXGI_FORMAT_B8G8R8A8_UNORM, .SampleDesc = { .Count = 1 }, .BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT, diff --git a/src/renderer/atlas/BackendD2D.cpp b/src/renderer/atlas/BackendD2D.cpp index c05dd1f21e3..2724a11b520 100644 --- a/src/renderer/atlas/BackendD2D.cpp +++ b/src/renderer/atlas/BackendD2D.cpp @@ -344,9 +344,9 @@ void BackendD2D::_drawText(RenderingPayload& p) f32 BackendD2D::_drawBuiltinGlyphs(const RenderingPayload& p, const ShapedRow* row, const FontMapping& m, f32 baselineY, f32 baselineX) { - const f32 cellTop = baselineY - p.s->font->baseline; - const f32 cellBottom = cellTop + p.s->font->cellSize.y; - const f32 cellWidth = p.s->font->cellSize.x; + const auto cellTop = baselineY - p.s->font->baseline; + const auto cellBottom = cellTop + p.s->font->cellSize.y; + const auto cellWidth = static_cast(p.s->font->cellSize.x); _prepareBuiltinGlyphRenderTarget(p); @@ -628,8 +628,8 @@ void BackendD2D::_drawGridlineRow(const RenderingPayload& p, const ShapedRow* ro D2D1_POINT_2F point0{ 0, cellCenter }; D2D1_POINT_2F point1{ 0, cellCenter + cellHeight }; const auto brush = _brushWithColor(r.gridlineColor); - const f32 w = pos.height; - const f32 hw = w * 0.5f; + const auto w = static_cast(pos.height); + const auto hw = w * 0.5f; for (; x < to; x += cellWidth) { @@ -644,8 +644,8 @@ void BackendD2D::_drawGridlineRow(const RenderingPayload& p, const ShapedRow* ro const auto to = r.to * scaledCellWidth; const auto brush = _brushWithColor(color); - const f32 w = pos.height; - const f32 centerY = cellCenter + pos.position + w * 0.5f; + const auto w = static_cast(pos.height); + const auto centerY = cellCenter + pos.position + w * 0.5f; const D2D1_POINT_2F point0{ from, centerY }; const D2D1_POINT_2F point1{ to, centerY }; _renderTarget->DrawLine(point0, point1, brush, w, strokeStyle); diff --git a/src/renderer/atlas/BackendD2D.h b/src/renderer/atlas/BackendD2D.h index da4991bc417..b1847be3837 100644 --- a/src/renderer/atlas/BackendD2D.h +++ b/src/renderer/atlas/BackendD2D.h @@ -68,7 +68,7 @@ namespace Microsoft::Console::Render::Atlas til::generation_t _fontGeneration; til::generation_t _cursorGeneration; til::generation_t _miscGeneration; - u16x2 _viewportCellCount{}; + i32x2 _viewportCellCount{}; #if ATLAS_DEBUG_SHOW_DIRTY i32r _presentRects[9]{}; diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp index 2acb1285917..75a6880e21c 100644 --- a/src/renderer/atlas/BackendD3D.cpp +++ b/src/renderer/atlas/BackendD3D.cpp @@ -536,8 +536,8 @@ void BackendD3D::_recreateCustomRenderTargetView(const RenderingPayload& p) _customOffscreenTextureView.reset(); const D3D11_TEXTURE2D_DESC desc{ - .Width = p.s->targetSize.x, - .Height = p.s->targetSize.y, + .Width = static_cast(p.s->targetSize.x), + .Height = static_cast(p.s->targetSize.y), .MipLevels = 1, .ArraySize = 1, .Format = DXGI_FORMAT_B8G8R8A8_UNORM, @@ -556,8 +556,8 @@ void BackendD3D::_recreateBackgroundColorBitmap(const RenderingPayload& p) _backgroundBitmapView.reset(); const D3D11_TEXTURE2D_DESC desc{ - .Width = p.s->viewportCellCount.x, - .Height = p.s->viewportCellCount.y, + .Width = static_cast(p.s->viewportCellCount.x), + .Height = static_cast(p.s->viewportCellCount.y), .MipLevels = 1, .ArraySize = 1, .Format = DXGI_FORMAT_R8G8B8A8_UNORM, @@ -1072,7 +1072,7 @@ void BackendD3D::_drawBackground(const RenderingPayload& p) _appendQuad() = { .shadingType = static_cast(ShadingType::Background), - .size = p.s->targetSize, + .size = { static_cast(p.s->targetSize.x), static_cast(p.s->targetSize.y) }, }; } @@ -1789,7 +1789,7 @@ void BackendD3D::_drawGridlines(const RenderingPayload& p, u16 y) _appendQuad() = { .shadingType = static_cast(ShadingType::SolidLine), .position = { static_cast(posX), rowTop }, - .size = { width, p.s->font->cellSize.y }, + .size = { width, static_cast(p.s->font->cellSize.y) }, .color = r.gridlineColor, }; } @@ -1956,7 +1956,7 @@ void BackendD3D::_drawCursorBackground(const RenderingPayload& p) }; const u16x2 size{ static_cast(p.s->font->cellSize.x * (x1 - x0)), - p.s->font->cellSize.y, + static_cast(p.s->font->cellSize.y), }; auto background = cursorColor; auto foreground = bg; diff --git a/src/renderer/atlas/BackendD3D.h b/src/renderer/atlas/BackendD3D.h index 962d691b01d..112cf3e3370 100644 --- a/src/renderer/atlas/BackendD3D.h +++ b/src/renderer/atlas/BackendD3D.h @@ -319,8 +319,8 @@ namespace Microsoft::Console::Render::Atlas til::generation_t _generation; til::generation_t _fontGeneration; til::generation_t _miscGeneration; - u16x2 _targetSize{}; - u16x2 _viewportCellCount{}; + i32x2 _targetSize{}; + i32x2 _viewportCellCount{}; ShadingType _textShadingType = ShadingType::Default; // An empty-box cursor spanning a wide glyph that has different diff --git a/src/renderer/atlas/common.h b/src/renderer/atlas/common.h index 3f70ae6748e..3ea7a4dc05d 100644 --- a/src/renderer/atlas/common.h +++ b/src/renderer/atlas/common.h @@ -342,8 +342,8 @@ namespace Microsoft::Console::Render::Atlas struct FontDecorationPosition { - u16 position = 0; - u16 height = 0; + i32 position = 0; + i32 height = 0; }; struct FontSettings @@ -355,12 +355,12 @@ namespace Microsoft::Console::Render::Atlas std::vector fontFeatures; std::vector fontAxisValues; f32 fontSize = 0; - u16x2 cellSize; - u16 fontWeight = 0; - u16 advanceWidth = 0; - u16 baseline = 0; - u16 descender = 0; - u16 thinLineWidth = 0; + i32x2 cellSize; + i32 fontWeight = 0; + i32 advanceWidth = 0; + i32 baseline = 0; + i32 descender = 0; + i32 thinLineWidth = 0; FontDecorationPosition gridTop; FontDecorationPosition gridBottom; @@ -372,7 +372,7 @@ namespace Microsoft::Console::Render::Atlas FontDecorationPosition doubleUnderline[2]; FontDecorationPosition overline; - u16 dpi = 96; + i32 dpi = 96; AntialiasingMode antialiasingMode = DefaultAntialiasingMode; bool builtinGlyphs = false; bool colorGlyphs = true; @@ -386,8 +386,8 @@ namespace Microsoft::Console::Render::Atlas ATLAS_POD_OPS(CursorSettings) u32 cursorColor = 0xffffffff; - u16 cursorType = 0; - u16 heightPercentage = 20; + i32 cursorType = 0; + i32 heightPercentage = 20; }; struct MiscellaneousSettings @@ -408,9 +408,9 @@ namespace Microsoft::Console::Render::Atlas til::generational cursor; til::generational misc; // Size of the viewport / swap chain in pixel. - u16x2 targetSize{ 0, 0 }; + i32x2 targetSize{ 0, 0 }; // Size of the portion of the text buffer that we're drawing on the screen. - u16x2 viewportCellCount{ 0, 0 }; + i32x2 viewportCellCount{ 0, 0 }; }; using GenerationalSettings = til::generational; @@ -467,7 +467,7 @@ namespace Microsoft::Console::Render::Atlas struct ShapedRow { - void Clear(u16 y, u16 cellHeight) noexcept + void Clear(i32 y, i32 cellHeight) noexcept { mappings.clear(); glyphIndices.clear(); @@ -526,7 +526,7 @@ namespace Microsoft::Console::Render::Atlas til::generation_t generation; til::generation_t targetGeneration; til::generation_t fontGeneration; - u16x2 targetSize{}; + i32x2 targetSize{}; bool waitForPresentation = false; } swapChain; wil::com_ptr device; @@ -579,11 +579,11 @@ namespace Microsoft::Console::Render::Atlas // whenever glyphs go out of bounds. `AtlasEngine::_present()` will clamp it. i32r dirtyRectInPx{}; // In rows. - range invalidatedRows{}; + range invalidatedRows{}; // In columns. i32 scrollOffsetX = 0; // In pixel. - i16 scrollDeltaY = 0; + i32 scrollDeltaY = 0; void MarkAllAsDirty() noexcept { diff --git a/src/types/viewport.cpp b/src/types/viewport.cpp index 30c5307dd14..f72303144e9 100644 --- a/src/types/viewport.cpp +++ b/src/types/viewport.cpp @@ -254,7 +254,7 @@ 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 = (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. @@ -267,7 +267,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(std::clamp(retVal, INT_MIN, INT_MAX)); } // Method Description: