mirror of
https://github.com/microsoft/terminal.git
synced 2026-05-20 13:57:43 +00:00
Compare commits
1 Commits
dev/duhowe
...
dev/lhecke
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cfbae30b0 |
1
.github/actions/spelling/expect/expect.txt
vendored
1
.github/actions/spelling/expect/expect.txt
vendored
@@ -1116,7 +1116,6 @@ msrc
|
|||||||
MSVCRTD
|
MSVCRTD
|
||||||
MTSM
|
MTSM
|
||||||
Munged
|
Munged
|
||||||
munges
|
|
||||||
murmurhash
|
murmurhash
|
||||||
muxes
|
muxes
|
||||||
myapplet
|
myapplet
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
#include "OutputCell.hpp"
|
#include "OutputCell.hpp"
|
||||||
|
|
||||||
#include "../../types/inc/GlyphWidth.hpp"
|
|
||||||
#include "../../types/inc/convert.hpp"
|
#include "../../types/inc/convert.hpp"
|
||||||
#include "../../inc/conattrs.hpp"
|
#include "../../inc/conattrs.hpp"
|
||||||
|
|
||||||
|
|||||||
@@ -8,27 +8,10 @@
|
|||||||
#include <til/unicode.h>
|
#include <til/unicode.h>
|
||||||
|
|
||||||
#include "../../types/inc/convert.hpp"
|
#include "../../types/inc/convert.hpp"
|
||||||
#include "../../types/inc/GlyphWidth.hpp"
|
|
||||||
#include "../../inc/conattrs.hpp"
|
#include "../../inc/conattrs.hpp"
|
||||||
|
|
||||||
static constexpr TextAttribute InvalidTextAttribute{ INVALID_COLOR, INVALID_COLOR, INVALID_COLOR };
|
static constexpr TextAttribute InvalidTextAttribute{ INVALID_COLOR, INVALID_COLOR, INVALID_COLOR };
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - This is a fill-mode iterator for one particular wchar. It will repeat forever if fillLimit is 0.
|
|
||||||
// Arguments:
|
|
||||||
// - wch - The character to use for filling
|
|
||||||
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
|
|
||||||
OutputCellIterator::OutputCellIterator(const wchar_t& wch, const size_t fillLimit) noexcept :
|
|
||||||
_mode(Mode::Fill),
|
|
||||||
_currentView(s_GenerateView(wch)),
|
|
||||||
_run(),
|
|
||||||
_attr(InvalidTextAttribute),
|
|
||||||
_pos(0),
|
|
||||||
_distance(0),
|
|
||||||
_fillLimit(fillLimit)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - This is a fill-mode iterator for one particular color. It will repeat forever if fillLimit is 0.
|
// - This is a fill-mode iterator for one particular color. It will repeat forever if fillLimit is 0.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
@@ -45,70 +28,6 @@ OutputCellIterator::OutputCellIterator(const TextAttribute& attr, const size_t f
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - This is a fill-mode iterator for one particular character and color. It will repeat forever if fillLimit is 0.
|
|
||||||
// Arguments:
|
|
||||||
// - wch - The character to use for filling
|
|
||||||
// - attr - The color attribute to use for filling
|
|
||||||
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
|
|
||||||
OutputCellIterator::OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit) noexcept :
|
|
||||||
_mode(Mode::Fill),
|
|
||||||
_currentView(s_GenerateView(wch, attr)),
|
|
||||||
_run(),
|
|
||||||
_attr(InvalidTextAttribute),
|
|
||||||
_pos(0),
|
|
||||||
_distance(0),
|
|
||||||
_fillLimit(fillLimit)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - This is a fill-mode iterator for one particular CHAR_INFO. It will repeat forever if fillLimit is 0.
|
|
||||||
// Arguments:
|
|
||||||
// - charInfo - The legacy character and color data to use for filling (uses Unicode portion of text data)
|
|
||||||
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
|
|
||||||
OutputCellIterator::OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit) noexcept :
|
|
||||||
_mode(Mode::Fill),
|
|
||||||
_currentView(s_GenerateView(charInfo)),
|
|
||||||
_run(),
|
|
||||||
_attr(InvalidTextAttribute),
|
|
||||||
_pos(0),
|
|
||||||
_distance(0),
|
|
||||||
_fillLimit(fillLimit)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - This is an iterator over a range of text only. No color data will be modified as the text is inserted.
|
|
||||||
// Arguments:
|
|
||||||
// - utf16Text - UTF-16 text range
|
|
||||||
OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text) noexcept :
|
|
||||||
_mode(Mode::LooseTextOnly),
|
|
||||||
_currentView(s_GenerateView(utf16Text)),
|
|
||||||
_run(utf16Text),
|
|
||||||
_attr(InvalidTextAttribute),
|
|
||||||
_pos(0),
|
|
||||||
_distance(0),
|
|
||||||
_fillLimit(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - This is an iterator over a range text that will apply the same color to every position.
|
|
||||||
// Arguments:
|
|
||||||
// - utf16Text - UTF-16 text range
|
|
||||||
// - attribute - Color to apply over the entire range
|
|
||||||
OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text, const TextAttribute& attribute, const size_t fillLimit) noexcept :
|
|
||||||
_mode(Mode::Loose),
|
|
||||||
_currentView(s_GenerateView(utf16Text, attribute)),
|
|
||||||
_run(utf16Text),
|
|
||||||
_attr(attribute),
|
|
||||||
_distance(0),
|
|
||||||
_pos(0),
|
|
||||||
_fillLimit(fillLimit)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - This is an iterator over legacy colors only. The text is not modified.
|
// - This is an iterator over legacy colors only. The text is not modified.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
@@ -124,21 +43,6 @@ OutputCellIterator::OutputCellIterator(const std::span<const WORD> legacyAttrs)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - This is an iterator over legacy cell data. We will use the unicode text and the legacy color attribute.
|
|
||||||
// Arguments:
|
|
||||||
// - charInfos - Multiple cell with unicode text and legacy color data.
|
|
||||||
OutputCellIterator::OutputCellIterator(const std::span<const CHAR_INFO> charInfos) noexcept :
|
|
||||||
_mode(Mode::CharInfo),
|
|
||||||
_currentView(s_GenerateView(til::at(charInfos, 0))),
|
|
||||||
_run(charInfos),
|
|
||||||
_attr(InvalidTextAttribute),
|
|
||||||
_distance(0),
|
|
||||||
_pos(0),
|
|
||||||
_fillLimit(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - This is an iterator over existing OutputCells with full text and color data.
|
// - This is an iterator over existing OutputCells with full text and color data.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
@@ -164,13 +68,6 @@ OutputCellIterator::operator bool() const noexcept
|
|||||||
{
|
{
|
||||||
switch (_mode)
|
switch (_mode)
|
||||||
{
|
{
|
||||||
case Mode::Loose:
|
|
||||||
case Mode::LooseTextOnly:
|
|
||||||
{
|
|
||||||
// In lieu of using start and end, this custom iterator type simply becomes bool false
|
|
||||||
// when we run out of items to iterate over.
|
|
||||||
return _pos < std::get<std::wstring_view>(_run).length();
|
|
||||||
}
|
|
||||||
case Mode::Fill:
|
case Mode::Fill:
|
||||||
{
|
{
|
||||||
if (_fillLimit > 0)
|
if (_fillLimit > 0)
|
||||||
@@ -183,10 +80,6 @@ OutputCellIterator::operator bool() const noexcept
|
|||||||
{
|
{
|
||||||
return _pos < std::get<std::span<const OutputCell>>(_run).size();
|
return _pos < std::get<std::span<const OutputCell>>(_run).size();
|
||||||
}
|
}
|
||||||
case Mode::CharInfo:
|
|
||||||
{
|
|
||||||
return _pos < std::get<std::span<const CHAR_INFO>>(_run).size();
|
|
||||||
}
|
|
||||||
case Mode::LegacyAttr:
|
case Mode::LegacyAttr:
|
||||||
{
|
{
|
||||||
return _pos < std::get<std::span<const WORD>>(_run).size();
|
return _pos < std::get<std::span<const WORD>>(_run).size();
|
||||||
@@ -214,34 +107,6 @@ OutputCellIterator& OutputCellIterator::operator++()
|
|||||||
|
|
||||||
switch (_mode)
|
switch (_mode)
|
||||||
{
|
{
|
||||||
case Mode::Loose:
|
|
||||||
{
|
|
||||||
if (!_TryMoveTrailing())
|
|
||||||
{
|
|
||||||
// When walking through a text sequence, we need to move forward by the number of wchar_ts consumed in the previous view
|
|
||||||
// in case we had a surrogate pair (or wider complex sequence) in the previous view.
|
|
||||||
_pos += _currentView.Chars().size();
|
|
||||||
if (operator bool())
|
|
||||||
{
|
|
||||||
_currentView = s_GenerateView(std::get<std::wstring_view>(_run).substr(_pos), _attr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Mode::LooseTextOnly:
|
|
||||||
{
|
|
||||||
if (!_TryMoveTrailing())
|
|
||||||
{
|
|
||||||
// When walking through a text sequence, we need to move forward by the number of wchar_ts consumed in the previous view
|
|
||||||
// in case we had a surrogate pair (or wider complex sequence) in the previous view.
|
|
||||||
_pos += _currentView.Chars().size();
|
|
||||||
if (operator bool())
|
|
||||||
{
|
|
||||||
_currentView = s_GenerateView(std::get<std::wstring_view>(_run).substr(_pos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Mode::Fill:
|
case Mode::Fill:
|
||||||
{
|
{
|
||||||
if (!_TryMoveTrailing())
|
if (!_TryMoveTrailing())
|
||||||
@@ -272,16 +137,6 @@ OutputCellIterator& OutputCellIterator::operator++()
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Mode::CharInfo:
|
|
||||||
{
|
|
||||||
// Walk forward by one because charinfos are just the legacy version of cells and prealigned to columns
|
|
||||||
_pos++;
|
|
||||||
if (operator bool())
|
|
||||||
{
|
|
||||||
_currentView = s_GenerateView(til::at(std::get<std::span<const CHAR_INFO>>(_run), _pos));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Mode::LegacyAttr:
|
case Mode::LegacyAttr:
|
||||||
{
|
{
|
||||||
// Walk forward by one because color attributes apply cell by cell (no complex text information)
|
// Walk forward by one because color attributes apply cell by cell (no complex text information)
|
||||||
@@ -353,69 +208,6 @@ bool OutputCellIterator::_TryMoveTrailing() noexcept
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - Static function to create a view.
|
|
||||||
// - It's pulled out statically so it can be used during construction with just the given
|
|
||||||
// variables (so OutputCellView doesn't need an empty default constructor)
|
|
||||||
// - This will infer the width of the glyph and specify that the attributes shouldn't be changed.
|
|
||||||
// Arguments:
|
|
||||||
// - view - View representing characters corresponding to a single glyph
|
|
||||||
// Return Value:
|
|
||||||
// - Object representing the view into this cell
|
|
||||||
OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view) noexcept
|
|
||||||
{
|
|
||||||
return s_GenerateView(view, InvalidTextAttribute, TextAttributeBehavior::Current);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - Static function to create a view.
|
|
||||||
// - It's pulled out statically so it can be used during construction with just the given
|
|
||||||
// variables (so OutputCellView doesn't need an empty default constructor)
|
|
||||||
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
|
|
||||||
// Arguments:
|
|
||||||
// - view - View representing characters corresponding to a single glyph
|
|
||||||
// - attr - Color attributes to apply to the text
|
|
||||||
// Return Value:
|
|
||||||
// - Object representing the view into this cell
|
|
||||||
OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view, const TextAttribute attr) noexcept
|
|
||||||
{
|
|
||||||
return s_GenerateView(view, attr, TextAttributeBehavior::Stored);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - Static function to create a view.
|
|
||||||
// - It's pulled out statically so it can be used during construction with just the given
|
|
||||||
// variables (so OutputCellView doesn't need an empty default constructor)
|
|
||||||
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
|
|
||||||
// Arguments:
|
|
||||||
// - view - View representing characters corresponding to a single glyph
|
|
||||||
// - attr - Color attributes to apply to the text
|
|
||||||
// - behavior - Behavior of the given text attribute (used when writing)
|
|
||||||
// Return Value:
|
|
||||||
// - Object representing the view into this cell
|
|
||||||
OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view, const TextAttribute attr, const TextAttributeBehavior behavior) noexcept
|
|
||||||
{
|
|
||||||
const auto glyph = til::utf16_next(view);
|
|
||||||
const auto dbcsAttr = IsGlyphFullWidth(glyph) ? DbcsAttribute::Leading : DbcsAttribute::Single;
|
|
||||||
return OutputCellView(glyph, dbcsAttr, attr, behavior);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - Static function to create a view.
|
|
||||||
// - It's pulled out statically so it can be used during construction with just the given
|
|
||||||
// variables (so OutputCellView doesn't need an empty default constructor)
|
|
||||||
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
|
|
||||||
// Arguments:
|
|
||||||
// - wch - View representing a single UTF-16 character (that can be represented without surrogates)
|
|
||||||
// Return Value:
|
|
||||||
// - Object representing the view into this cell
|
|
||||||
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch) noexcept
|
|
||||||
{
|
|
||||||
const auto glyph = std::wstring_view(&wch, 1);
|
|
||||||
const auto dbcsAttr = IsGlyphFullWidth(wch) ? DbcsAttribute::Leading : DbcsAttribute::Single;
|
|
||||||
return OutputCellView(glyph, dbcsAttr, InvalidTextAttribute, TextAttributeBehavior::Current);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Static function to create a view.
|
// - Static function to create a view.
|
||||||
// - It's pulled out statically so it can be used during construction with just the given
|
// - It's pulled out statically so it can be used during construction with just the given
|
||||||
@@ -430,23 +222,6 @@ OutputCellView OutputCellIterator::s_GenerateView(const TextAttribute& attr) noe
|
|||||||
return OutputCellView({}, {}, attr, TextAttributeBehavior::StoredOnly);
|
return OutputCellView({}, {}, attr, TextAttributeBehavior::StoredOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - Static function to create a view.
|
|
||||||
// - It's pulled out statically so it can be used during construction with just the given
|
|
||||||
// variables (so OutputCellView doesn't need an empty default constructor)
|
|
||||||
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
|
|
||||||
// Arguments:
|
|
||||||
// - wch - View representing a single UTF-16 character (that can be represented without surrogates)
|
|
||||||
// - attr - View representing a single color
|
|
||||||
// Return Value:
|
|
||||||
// - Object representing the view into this cell
|
|
||||||
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch, const TextAttribute& attr) noexcept
|
|
||||||
{
|
|
||||||
const auto glyph = std::wstring_view(&wch, 1);
|
|
||||||
const auto dbcsAttr = IsGlyphFullWidth(wch) ? DbcsAttribute::Leading : DbcsAttribute::Single;
|
|
||||||
return OutputCellView(glyph, dbcsAttr, attr, TextAttributeBehavior::Stored);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Static function to create a view.
|
// - Static function to create a view.
|
||||||
// - It's pulled out statically so it can be used during construction with just the given
|
// - It's pulled out statically so it can be used during construction with just the given
|
||||||
@@ -465,35 +240,6 @@ OutputCellView OutputCellIterator::s_GenerateViewLegacyAttr(const WORD& legacyAt
|
|||||||
return s_GenerateView(attr);
|
return s_GenerateView(attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - Static function to create a view.
|
|
||||||
// - It's pulled out statically so it can be used during construction with just the given
|
|
||||||
// variables (so OutputCellView doesn't need an empty default constructor)
|
|
||||||
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
|
|
||||||
// Arguments:
|
|
||||||
// - charInfo - character and attribute pair representing a single cell
|
|
||||||
// Return Value:
|
|
||||||
// - Object representing the view into this cell
|
|
||||||
OutputCellView OutputCellIterator::s_GenerateView(const CHAR_INFO& charInfo) noexcept
|
|
||||||
{
|
|
||||||
const auto glyph = std::wstring_view(&charInfo.Char.UnicodeChar, 1);
|
|
||||||
|
|
||||||
DbcsAttribute dbcsAttr = DbcsAttribute::Single;
|
|
||||||
if (WI_IsFlagSet(charInfo.Attributes, COMMON_LVB_LEADING_BYTE))
|
|
||||||
{
|
|
||||||
dbcsAttr = DbcsAttribute::Leading;
|
|
||||||
}
|
|
||||||
else if (WI_IsFlagSet(charInfo.Attributes, COMMON_LVB_TRAILING_BYTE))
|
|
||||||
{
|
|
||||||
dbcsAttr = DbcsAttribute::Trailing;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TextAttribute textAttr(charInfo.Attributes);
|
|
||||||
|
|
||||||
const auto behavior = TextAttributeBehavior::Stored;
|
|
||||||
return OutputCellView(glyph, dbcsAttr, textAttr, behavior);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Static function to create a view.
|
// - Static function to create a view.
|
||||||
// - It's pulled out statically so it can be used during construction with just the given
|
// - It's pulled out statically so it can be used during construction with just the given
|
||||||
|
|||||||
@@ -34,14 +34,8 @@ public:
|
|||||||
using reference = OutputCellView&;
|
using reference = OutputCellView&;
|
||||||
|
|
||||||
OutputCellIterator() = default;
|
OutputCellIterator() = default;
|
||||||
OutputCellIterator(const wchar_t& wch, const size_t fillLimit = 0) noexcept;
|
|
||||||
OutputCellIterator(const TextAttribute& attr, const size_t fillLimit = 0) noexcept;
|
OutputCellIterator(const TextAttribute& attr, const size_t fillLimit = 0) noexcept;
|
||||||
OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit = 0) noexcept;
|
|
||||||
OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit = 0) noexcept;
|
|
||||||
OutputCellIterator(const std::wstring_view utf16Text) noexcept;
|
|
||||||
OutputCellIterator(const std::wstring_view utf16Text, const TextAttribute& attribute, const size_t fillLimit = 0) noexcept;
|
|
||||||
OutputCellIterator(const std::span<const WORD> legacyAttributes) noexcept;
|
OutputCellIterator(const std::span<const WORD> legacyAttributes) noexcept;
|
||||||
OutputCellIterator(const std::span<const CHAR_INFO> charInfos) noexcept;
|
|
||||||
OutputCellIterator(const std::span<const OutputCell> cells);
|
OutputCellIterator(const std::span<const OutputCell> cells);
|
||||||
~OutputCellIterator() = default;
|
~OutputCellIterator() = default;
|
||||||
|
|
||||||
@@ -63,14 +57,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
enum class Mode
|
enum class Mode
|
||||||
{
|
{
|
||||||
// Loose mode is where we're given text and attributes in a raw sort of form
|
|
||||||
// like while data is being inserted from an API call.
|
|
||||||
Loose,
|
|
||||||
|
|
||||||
// Loose mode with only text is where we're given just text and we want
|
|
||||||
// to use the attribute already in the buffer when writing
|
|
||||||
LooseTextOnly,
|
|
||||||
|
|
||||||
// Fill mode is where we were given one thing and we just need to keep giving
|
// Fill mode is where we were given one thing and we just need to keep giving
|
||||||
// that back over and over for eternity.
|
// that back over and over for eternity.
|
||||||
Fill,
|
Fill,
|
||||||
@@ -78,10 +64,6 @@ private:
|
|||||||
// Given a run of legacy attributes, convert each of them and insert only attribute data.
|
// Given a run of legacy attributes, convert each of them and insert only attribute data.
|
||||||
LegacyAttr,
|
LegacyAttr,
|
||||||
|
|
||||||
// CharInfo mode is where we've been given a pair of text and attribute for each
|
|
||||||
// cell in the legacy format from an API call.
|
|
||||||
CharInfo,
|
|
||||||
|
|
||||||
// Cell mode is where we have an already fully structured cell data usually
|
// Cell mode is where we have an already fully structured cell data usually
|
||||||
// from accessing/copying data already put into the OutputBuffer.
|
// from accessing/copying data already put into the OutputBuffer.
|
||||||
Cell,
|
Cell,
|
||||||
@@ -91,9 +73,7 @@ private:
|
|||||||
std::span<const WORD> _legacyAttrs;
|
std::span<const WORD> _legacyAttrs;
|
||||||
|
|
||||||
std::variant<
|
std::variant<
|
||||||
std::wstring_view,
|
|
||||||
std::span<const WORD>,
|
std::span<const WORD>,
|
||||||
std::span<const CHAR_INFO>,
|
|
||||||
std::span<const OutputCell>,
|
std::span<const OutputCell>,
|
||||||
std::monostate>
|
std::monostate>
|
||||||
_run;
|
_run;
|
||||||
@@ -102,14 +82,10 @@ private:
|
|||||||
|
|
||||||
bool _TryMoveTrailing() noexcept;
|
bool _TryMoveTrailing() noexcept;
|
||||||
|
|
||||||
static OutputCellView s_GenerateView(const std::wstring_view view) noexcept;
|
|
||||||
static OutputCellView s_GenerateView(const std::wstring_view view, const TextAttribute attr) noexcept;
|
|
||||||
static OutputCellView s_GenerateView(const std::wstring_view view, const TextAttribute attr, const TextAttributeBehavior behavior) noexcept;
|
|
||||||
static OutputCellView s_GenerateView(const wchar_t& wch) noexcept;
|
static OutputCellView s_GenerateView(const wchar_t& wch) noexcept;
|
||||||
static OutputCellView s_GenerateViewLegacyAttr(const WORD& legacyAttr) noexcept;
|
static OutputCellView s_GenerateViewLegacyAttr(const WORD& legacyAttr) noexcept;
|
||||||
static OutputCellView s_GenerateView(const TextAttribute& attr) noexcept;
|
static OutputCellView s_GenerateView(const TextAttribute& attr) noexcept;
|
||||||
static OutputCellView s_GenerateView(const wchar_t& wch, const TextAttribute& attr) noexcept;
|
static OutputCellView s_GenerateView(const wchar_t& wch, const TextAttribute& attr) noexcept;
|
||||||
static OutputCellView s_GenerateView(const CHAR_INFO& charInfo) noexcept;
|
|
||||||
|
|
||||||
static OutputCellView s_GenerateView(const OutputCell& cell);
|
static OutputCellView s_GenerateView(const OutputCell& cell);
|
||||||
|
|
||||||
|
|||||||
@@ -374,22 +374,30 @@ TextBufferCellIterator TextBuffer::GetCellDataAt(const til::point at, const View
|
|||||||
// Given the character offset `position` in the `chars` string, this function returns the starting position of the next grapheme.
|
// Given the character offset `position` in the `chars` string, this function returns the starting position of the next grapheme.
|
||||||
// For instance, given a `chars` of L"x\uD83D\uDE42y" and a `position` of 1 it'll return 3.
|
// For instance, given a `chars` of L"x\uD83D\uDE42y" and a `position` of 1 it'll return 3.
|
||||||
// GraphemePrev would do the exact inverse of this operation.
|
// GraphemePrev would do the exact inverse of this operation.
|
||||||
size_t TextBuffer::GraphemeNext(const std::wstring_view& chars, size_t position) noexcept
|
size_t TextBuffer::GraphemeNext(const std::wstring_view& chars, size_t position, til::CoordType* width) noexcept
|
||||||
{
|
{
|
||||||
auto& cwd = CodepointWidthDetector::Singleton();
|
auto& cwd = CodepointWidthDetector::Singleton();
|
||||||
#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
|
#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
|
||||||
GraphemeState state{ .beg = chars.data() + position };
|
GraphemeState state{ .beg = chars.data() + position };
|
||||||
cwd.GraphemeNext(state, chars);
|
cwd.GraphemeNext(state, chars);
|
||||||
|
if (width)
|
||||||
|
{
|
||||||
|
*width = state.width;
|
||||||
|
}
|
||||||
return position + state.len;
|
return position + state.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It's the counterpart to GraphemeNext. See GraphemeNext.
|
// It's the counterpart to GraphemeNext. See GraphemeNext.
|
||||||
size_t TextBuffer::GraphemePrev(const std::wstring_view& chars, size_t position) noexcept
|
size_t TextBuffer::GraphemePrev(const std::wstring_view& chars, size_t position, til::CoordType* width) noexcept
|
||||||
{
|
{
|
||||||
auto& cwd = CodepointWidthDetector::Singleton();
|
auto& cwd = CodepointWidthDetector::Singleton();
|
||||||
#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
|
#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
|
||||||
GraphemeState state{ .beg = chars.data() + position };
|
GraphemeState state{ .beg = chars.data() + position };
|
||||||
cwd.GraphemePrev(state, chars);
|
cwd.GraphemePrev(state, chars);
|
||||||
|
if (width)
|
||||||
|
{
|
||||||
|
*width = state.width;
|
||||||
|
}
|
||||||
return position - state.len;
|
return position - state.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ public:
|
|||||||
|
|
||||||
size_t GetCellDistance(const til::point from, const til::point to) const;
|
size_t GetCellDistance(const til::point from, const til::point to) const;
|
||||||
|
|
||||||
static size_t GraphemeNext(const std::wstring_view& chars, size_t position) noexcept;
|
static size_t GraphemeNext(const std::wstring_view& chars, size_t position, til::CoordType* width = nullptr) noexcept;
|
||||||
static size_t GraphemePrev(const std::wstring_view& chars, size_t position) noexcept;
|
static size_t GraphemePrev(const std::wstring_view& chars, size_t position, til::CoordType* width = nullptr) noexcept;
|
||||||
static size_t FitTextIntoColumns(const std::wstring_view& chars, til::CoordType columnLimit, til::CoordType& columns) noexcept;
|
static size_t FitTextIntoColumns(const std::wstring_view& chars, til::CoordType columnLimit, til::CoordType& columns) noexcept;
|
||||||
|
|
||||||
til::point NavigateCursor(til::point position, til::CoordType distance) const;
|
til::point NavigateCursor(til::point position, til::CoordType distance) const;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include "../textBuffer.hpp"
|
#include "../textBuffer.hpp"
|
||||||
#include "../../renderer/inc/DummyRenderer.hpp"
|
#include "../../renderer/inc/DummyRenderer.hpp"
|
||||||
#include "../../types/inc/GlyphWidth.hpp"
|
|
||||||
|
|
||||||
#include <IDataSource.h>
|
#include <IDataSource.h>
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include <unicode.hpp>
|
#include <unicode.hpp>
|
||||||
#include <Utils.h>
|
#include <Utils.h>
|
||||||
#include <LibraryResources.h>
|
#include <LibraryResources.h>
|
||||||
#include "../../types/inc/GlyphWidth.hpp"
|
|
||||||
#include "../../types/inc/Utils.hpp"
|
#include "../../types/inc/Utils.hpp"
|
||||||
#include "../../buffer/out/search.h"
|
#include "../../buffer/out/search.h"
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#include "../../terminal/parser/StateMachine.hpp"
|
#include "../../terminal/parser/StateMachine.hpp"
|
||||||
#include "../../terminal/input/terminalInput.hpp"
|
#include "../../terminal/input/terminalInput.hpp"
|
||||||
#include "../../types/inc/Viewport.hpp"
|
#include "../../types/inc/Viewport.hpp"
|
||||||
#include "../../types/inc/GlyphWidth.hpp"
|
|
||||||
#include "../../cascadia/terminalcore/ITerminalInput.hpp"
|
#include "../../cascadia/terminalcore/ITerminalInput.hpp"
|
||||||
|
|
||||||
#include <til/ticket_lock.h>
|
#include <til/ticket_lock.h>
|
||||||
|
|||||||
@@ -712,68 +712,3 @@ void VtIo::Writer::WriteAttributes(const TextAttribute& attributes) const
|
|||||||
{
|
{
|
||||||
FormatAttributes(_io->_back, attributes);
|
FormatAttributes(_io->_back, attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VtIo::Writer::WriteInfos(til::point target, std::span<const CHAR_INFO> infos) const
|
|
||||||
{
|
|
||||||
const auto beg = infos.begin();
|
|
||||||
const auto end = infos.end();
|
|
||||||
const auto last = end - 1;
|
|
||||||
WORD attributes = 0xffff;
|
|
||||||
|
|
||||||
WriteCUP(target);
|
|
||||||
|
|
||||||
for (auto it = beg; it != end; ++it)
|
|
||||||
{
|
|
||||||
const auto& ci = *it;
|
|
||||||
auto ch = ci.Char.UnicodeChar;
|
|
||||||
auto wide = WI_IsAnyFlagSet(ci.Attributes, COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
|
|
||||||
|
|
||||||
if (wide)
|
|
||||||
{
|
|
||||||
if (WI_IsAnyFlagSet(ci.Attributes, COMMON_LVB_LEADING_BYTE))
|
|
||||||
{
|
|
||||||
if (it == last)
|
|
||||||
{
|
|
||||||
// The leading half of a wide glyph won't fit into the last remaining column.
|
|
||||||
// --> Replace it with a space.
|
|
||||||
ch = L' ';
|
|
||||||
wide = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (it == beg)
|
|
||||||
{
|
|
||||||
// The trailing half of a wide glyph won't fit into the first column. It's incomplete.
|
|
||||||
// --> Replace it with a space.
|
|
||||||
ch = L' ';
|
|
||||||
wide = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Trailing halves of glyphs are ignored within the run. We only emit the leading half.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attributes != ci.Attributes)
|
|
||||||
{
|
|
||||||
attributes = ci.Attributes;
|
|
||||||
WriteAttributes(TextAttribute{ attributes });
|
|
||||||
}
|
|
||||||
|
|
||||||
int repeat = 1;
|
|
||||||
if (wide && (til::is_surrogate(ch) || IsControlCharacter(ch)))
|
|
||||||
{
|
|
||||||
// Control characters, U+FFFD, etc. are narrow characters, so if the caller
|
|
||||||
// asked for a wide glyph we need to repeat the replacement character twice.
|
|
||||||
repeat++;
|
|
||||||
}
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
WriteUCS2StripControlChars(ch);
|
|
||||||
} while (--repeat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ namespace Microsoft::Console::VirtualTerminal
|
|||||||
void WriteWindowVisibility(bool visible) const;
|
void WriteWindowVisibility(bool visible) const;
|
||||||
void WriteWindowTitle(std::wstring_view title) const;
|
void WriteWindowTitle(std::wstring_view title) const;
|
||||||
void WriteAttributes(const TextAttribute& attributes) const;
|
void WriteAttributes(const TextAttribute& attributes) const;
|
||||||
void WriteInfos(til::point target, std::span<const CHAR_INFO> infos) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VtIo* _io = nullptr;
|
VtIo* _io = nullptr;
|
||||||
|
|||||||
@@ -12,9 +12,10 @@
|
|||||||
|
|
||||||
#include "../interactivity/inc/ServiceLocator.hpp"
|
#include "../interactivity/inc/ServiceLocator.hpp"
|
||||||
#include "../types/inc/convert.hpp"
|
#include "../types/inc/convert.hpp"
|
||||||
#include "../types/inc/GlyphWidth.hpp"
|
|
||||||
#include "../types/inc/Viewport.hpp"
|
#include "../types/inc/Viewport.hpp"
|
||||||
|
|
||||||
|
#include <til/unicode.h>
|
||||||
|
|
||||||
using namespace Microsoft::Console::Types;
|
using namespace Microsoft::Console::Types;
|
||||||
using Microsoft::Console::Interactivity::ServiceLocator;
|
using Microsoft::Console::Interactivity::ServiceLocator;
|
||||||
|
|
||||||
@@ -76,171 +77,166 @@ static FillConsoleResult FillConsoleImpl(SCREEN_INFORMATION& screenInfo, FillCon
|
|||||||
|
|
||||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||||
auto& screenBuffer = screenInfo.GetActiveBuffer();
|
auto& screenBuffer = screenInfo.GetActiveBuffer();
|
||||||
|
auto& textBuffer = screenBuffer.GetTextBuffer();
|
||||||
const auto bufferSize = screenBuffer.GetBufferSize();
|
const auto bufferSize = screenBuffer.GetBufferSize();
|
||||||
FillConsoleResult result;
|
|
||||||
|
|
||||||
if (!bufferSize.IsInBounds(startingCoordinate))
|
if (!bufferSize.IsInBounds(startingCoordinate))
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto writer = gci.GetVtWriterForBuffer(&screenInfo))
|
auto writer = gci.GetVtWriterForBuffer(&screenInfo);
|
||||||
|
if (writer)
|
||||||
{
|
{
|
||||||
writer.BackupCursor();
|
writer.BackupCursor();
|
||||||
|
|
||||||
const auto h = bufferSize.Height();
|
|
||||||
const auto w = bufferSize.Width();
|
|
||||||
auto y = startingCoordinate.y;
|
|
||||||
auto input = static_cast<const uint16_t*>(data);
|
|
||||||
size_t inputPos = 0;
|
|
||||||
til::small_vector<CHAR_INFO, 1024> infoBuffer;
|
|
||||||
Viewport unused;
|
|
||||||
|
|
||||||
infoBuffer.resize(gsl::narrow_cast<size_t>(w));
|
|
||||||
|
|
||||||
while (y < h && inputPos < lengthToWrite)
|
|
||||||
{
|
|
||||||
const auto beg = y == startingCoordinate.y ? startingCoordinate.x : 0;
|
|
||||||
const auto columnsAvailable = w - beg;
|
|
||||||
til::CoordType columns = 0;
|
|
||||||
|
|
||||||
const auto readViewport = Viewport::FromInclusive({ beg, y, w - 1, y });
|
|
||||||
THROW_IF_FAILED(ReadConsoleOutputWImplHelper(screenInfo, infoBuffer, readViewport, unused));
|
|
||||||
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
case FillConsoleMode::WriteAttribute:
|
|
||||||
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++columns, ++inputPos)
|
|
||||||
{
|
|
||||||
infoBuffer[columns].Attributes = input[inputPos];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case FillConsoleMode::FillAttribute:
|
|
||||||
for (const auto attr = input[0]; columns < columnsAvailable && inputPos < lengthToWrite; ++columns, ++inputPos)
|
|
||||||
{
|
|
||||||
infoBuffer[columns].Attributes = attr;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case FillConsoleMode::WriteCharacter:
|
|
||||||
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++inputPos)
|
|
||||||
{
|
|
||||||
const auto ch = input[inputPos];
|
|
||||||
if (ch >= 0x80 && IsGlyphFullWidth(ch))
|
|
||||||
{
|
|
||||||
// If the wide glyph doesn't fit into the last column, pad it with whitespace.
|
|
||||||
if ((columns + 1) >= columnsAvailable)
|
|
||||||
{
|
|
||||||
auto& lead = infoBuffer[columns++];
|
|
||||||
lead.Char.UnicodeChar = L' ';
|
|
||||||
lead.Attributes = lead.Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& lead = infoBuffer[columns++];
|
|
||||||
lead.Char.UnicodeChar = ch;
|
|
||||||
lead.Attributes = lead.Attributes & ~COMMON_LVB_TRAILING_BYTE | COMMON_LVB_LEADING_BYTE;
|
|
||||||
|
|
||||||
auto& trail = infoBuffer[columns++];
|
|
||||||
trail.Char.UnicodeChar = ch;
|
|
||||||
trail.Attributes = trail.Attributes & ~COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto& lead = infoBuffer[columns++];
|
|
||||||
lead.Char.UnicodeChar = ch;
|
|
||||||
lead.Attributes = lead.Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case FillConsoleMode::FillCharacter:
|
|
||||||
// Identical to WriteCharacter above, but with the if() and for() swapped.
|
|
||||||
if (const auto ch = input[0]; ch >= 0x80 && IsGlyphFullWidth(ch))
|
|
||||||
{
|
|
||||||
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++inputPos)
|
|
||||||
{
|
|
||||||
// If the wide glyph doesn't fit into the last column, pad it with whitespace.
|
|
||||||
if ((columns + 1) >= columnsAvailable)
|
|
||||||
{
|
|
||||||
auto& lead = infoBuffer[columns++];
|
|
||||||
lead.Char.UnicodeChar = L' ';
|
|
||||||
lead.Attributes = lead.Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& lead = infoBuffer[columns++];
|
|
||||||
lead.Char.UnicodeChar = ch;
|
|
||||||
lead.Attributes = lead.Attributes & ~COMMON_LVB_TRAILING_BYTE | COMMON_LVB_LEADING_BYTE;
|
|
||||||
|
|
||||||
auto& trail = infoBuffer[columns++];
|
|
||||||
trail.Char.UnicodeChar = ch;
|
|
||||||
trail.Attributes = trail.Attributes & ~COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++inputPos)
|
|
||||||
{
|
|
||||||
auto& lead = infoBuffer[columns++];
|
|
||||||
lead.Char.UnicodeChar = ch;
|
|
||||||
lead.Attributes = lead.Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto writeViewport = Viewport::FromInclusive({ beg, y, beg + columns - 1, y });
|
|
||||||
THROW_IF_FAILED(WriteConsoleOutputWImplHelper(screenInfo, infoBuffer, w, writeViewport, unused));
|
|
||||||
|
|
||||||
y += 1;
|
|
||||||
result.cellsModified += columns;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.lengthRead = inputPos;
|
|
||||||
|
|
||||||
writer.Submit();
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// Technically we could always pass `data` as `uint16_t*`, because `wchar_t` is guaranteed to be 16 bits large.
|
|
||||||
// However, OutputCellIterator is terrifyingly unsafe code and so we don't do that.
|
|
||||||
//
|
|
||||||
// Constructing an OutputCellIterator with a `wchar_t` takes the `wchar_t` by reference, so that it can reference
|
|
||||||
// it in a `wstring_view` forever. That's of course really bad because passing a `const uint16_t&` to a
|
|
||||||
// `const wchar_t&` argument implicitly converts the types. To do so, the implicit conversion allocates a
|
|
||||||
// `wchar_t` value on the stack. The lifetime of that copy DOES NOT get extended beyond the constructor call.
|
|
||||||
// The result is that OutputCellIterator would read random data from the stack.
|
|
||||||
//
|
|
||||||
// Don't ever assume the lifetime of implicitly convertible types given by reference.
|
|
||||||
// Ironically that's a bug that cannot happen with C pointers. To no ones surprise, C keeps on winning.
|
|
||||||
auto attrs = static_cast<const uint16_t*>(data);
|
|
||||||
auto chars = static_cast<const wchar_t*>(data);
|
|
||||||
|
|
||||||
OutputCellIterator it;
|
FillConsoleResult result;
|
||||||
|
const auto h = bufferSize.Height();
|
||||||
|
const auto w = bufferSize.Width();
|
||||||
|
auto y = startingCoordinate.y;
|
||||||
|
auto input = static_cast<const uint16_t*>(data);
|
||||||
|
const std::wstring_view inputText{ (const wchar_t*)input, lengthToWrite };
|
||||||
|
size_t inputPos = 0;
|
||||||
|
til::small_vector<CHAR_INFO, 1024> infoBuffer;
|
||||||
|
Viewport unused;
|
||||||
|
|
||||||
|
infoBuffer.resize(gsl::narrow_cast<size_t>(w));
|
||||||
|
|
||||||
|
auto fillCharacter = UNICODE_REPLACEMENT;
|
||||||
|
til::CoordType fillCharacterWidth = 1;
|
||||||
|
if (mode == FillConsoleMode::FillCharacter)
|
||||||
|
{
|
||||||
|
wchar_t chars[3] = { L'a', inputText[0], L'a' };
|
||||||
|
std::wstring_view foobar{ &chars[0], 3 };
|
||||||
|
til::CoordType width = 1;
|
||||||
|
const auto stop1 = textBuffer.GraphemeNext(foobar, 0, nullptr);
|
||||||
|
const auto stop2 = textBuffer.GraphemeNext(foobar, stop1, &width);
|
||||||
|
if (stop1 == 1 && stop2 == 2 && !til::is_surrogate(foobar[1]))
|
||||||
|
{
|
||||||
|
fillCharacter = foobar[1];
|
||||||
|
fillCharacterWidth = width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (y < h && inputPos < lengthToWrite)
|
||||||
|
{
|
||||||
|
const auto beg = y == startingCoordinate.y ? startingCoordinate.x : 0;
|
||||||
|
const auto columnsAvailable = w - beg;
|
||||||
|
til::CoordType columns = 0;
|
||||||
|
|
||||||
|
const auto readViewport = Viewport::FromInclusive({ beg, y, w - 1, y });
|
||||||
|
THROW_IF_FAILED(ReadConsoleOutputWImplHelper(screenInfo, infoBuffer, readViewport, unused));
|
||||||
|
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
case FillConsoleMode::WriteAttribute:
|
case FillConsoleMode::WriteAttribute:
|
||||||
it = OutputCellIterator({ attrs, lengthToWrite });
|
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++columns, ++inputPos)
|
||||||
break;
|
{
|
||||||
case FillConsoleMode::WriteCharacter:
|
infoBuffer[columns].Attributes = input[inputPos];
|
||||||
it = OutputCellIterator({ chars, lengthToWrite });
|
}
|
||||||
break;
|
break;
|
||||||
case FillConsoleMode::FillAttribute:
|
case FillConsoleMode::FillAttribute:
|
||||||
it = OutputCellIterator(TextAttribute(*attrs), lengthToWrite);
|
for (const auto attr = input[0]; columns < columnsAvailable && inputPos < lengthToWrite; ++columns, ++inputPos)
|
||||||
|
{
|
||||||
|
infoBuffer[columns].Attributes = attr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FillConsoleMode::WriteCharacter:
|
||||||
|
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++inputPos)
|
||||||
|
{
|
||||||
|
auto ch = input[inputPos];
|
||||||
|
auto next = inputPos + 1;
|
||||||
|
til::CoordType width = 1;
|
||||||
|
|
||||||
|
if (ch >= 0x80)
|
||||||
|
{
|
||||||
|
next = textBuffer.GraphemeNext(inputText, inputPos, &width);
|
||||||
|
if (next - inputPos != 1 || til::is_surrogate(ch))
|
||||||
|
{
|
||||||
|
ch = UNICODE_REPLACEMENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width > 1)
|
||||||
|
{
|
||||||
|
const auto columnEnd = columns + width;
|
||||||
|
|
||||||
|
// If the wide glyph doesn't fit into the last column, pad it with whitespace.
|
||||||
|
if (columnEnd > columnsAvailable)
|
||||||
|
{
|
||||||
|
auto& lead = infoBuffer[columns++];
|
||||||
|
lead.Char.UnicodeChar = L' ';
|
||||||
|
lead.Attributes = lead.Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& lead = infoBuffer[columns++];
|
||||||
|
lead.Char.UnicodeChar = ch;
|
||||||
|
lead.Attributes = lead.Attributes & ~COMMON_LVB_TRAILING_BYTE | COMMON_LVB_LEADING_BYTE;
|
||||||
|
|
||||||
|
auto& trail = infoBuffer[columns++];
|
||||||
|
trail.Char.UnicodeChar = ch;
|
||||||
|
trail.Attributes = trail.Attributes & ~COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto& lead = infoBuffer[columns++];
|
||||||
|
lead.Char.UnicodeChar = ch;
|
||||||
|
lead.Attributes = lead.Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case FillConsoleMode::FillCharacter:
|
case FillConsoleMode::FillCharacter:
|
||||||
it = OutputCellIterator(*chars, lengthToWrite);
|
{
|
||||||
|
// Identical to WriteCharacter above, but with the if() and for() swapped.
|
||||||
|
if (fillCharacterWidth > 1)
|
||||||
|
{
|
||||||
|
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++inputPos)
|
||||||
|
{
|
||||||
|
// If the wide glyph doesn't fit into the last column, pad it with whitespace.
|
||||||
|
if ((columns + fillCharacterWidth) > columnsAvailable)
|
||||||
|
{
|
||||||
|
auto& lead = infoBuffer[columns++];
|
||||||
|
lead.Char.UnicodeChar = L' ';
|
||||||
|
lead.Attributes = lead.Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& lead = infoBuffer[columns++];
|
||||||
|
lead.Char.UnicodeChar = fillCharacter;
|
||||||
|
lead.Attributes = lead.Attributes & ~COMMON_LVB_TRAILING_BYTE | COMMON_LVB_LEADING_BYTE;
|
||||||
|
|
||||||
|
auto& trail = infoBuffer[columns++];
|
||||||
|
trail.Char.UnicodeChar = fillCharacter;
|
||||||
|
trail.Attributes = trail.Attributes & ~COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++inputPos)
|
||||||
|
{
|
||||||
|
auto& lead = infoBuffer[columns++];
|
||||||
|
lead.Char.UnicodeChar = fillCharacter;
|
||||||
|
lead.Attributes = lead.Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
__assume(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto done = screenBuffer.Write(it, startingCoordinate, false);
|
const auto writeViewport = Viewport::FromInclusive({ beg, y, beg + columns - 1, y });
|
||||||
result.lengthRead = done.GetInputDistance(it);
|
THROW_IF_FAILED(WriteConsoleOutputWImplHelper(screenInfo, infoBuffer, w, writeViewport, unused));
|
||||||
result.cellsModified = done.GetCellDistance(it);
|
|
||||||
|
|
||||||
// If we've overwritten image content, it needs to be erased.
|
y += 1;
|
||||||
ImageSlice::EraseCells(screenInfo.GetTextBuffer(), startingCoordinate, result.cellsModified);
|
result.cellsModified += columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.lengthRead = inputPos;
|
||||||
|
|
||||||
|
if (writer)
|
||||||
|
{
|
||||||
|
writer.Submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (screenBuffer.HasAccessibilityEventing())
|
if (screenBuffer.HasAccessibilityEventing())
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
#include "VtIo.hpp"
|
#include "VtIo.hpp"
|
||||||
|
|
||||||
#include "../types/inc/convert.hpp"
|
#include "../types/inc/convert.hpp"
|
||||||
#include "../types/inc/GlyphWidth.hpp"
|
|
||||||
#include "../types/inc/Viewport.hpp"
|
#include "../types/inc/Viewport.hpp"
|
||||||
|
|
||||||
#include "../interactivity/inc/ServiceLocator.hpp"
|
#include "../interactivity/inc/ServiceLocator.hpp"
|
||||||
|
|||||||
@@ -349,24 +349,3 @@ MidiAudio& CONSOLE_INFORMATION::GetMidiAudio()
|
|||||||
{
|
{
|
||||||
return _midiAudio;
|
return _midiAudio;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method Description:
|
|
||||||
// - Generates a CHAR_INFO for this output cell, using the TextAttribute
|
|
||||||
// GetLegacyAttributes method to generate the legacy style attributes.
|
|
||||||
// Arguments:
|
|
||||||
// - cell: The cell to get the CHAR_INFO from
|
|
||||||
// Return Value:
|
|
||||||
// - a CHAR_INFO containing legacy information about the cell
|
|
||||||
CHAR_INFO CONSOLE_INFORMATION::AsCharInfo(const OutputCellView& cell) const noexcept
|
|
||||||
{
|
|
||||||
CHAR_INFO ci{ 0 };
|
|
||||||
ci.Char.UnicodeChar = Utf16ToUcs2(cell.Chars());
|
|
||||||
|
|
||||||
// If the current text attributes aren't legacy attributes, then
|
|
||||||
// use gci to look up the correct legacy attributes to use
|
|
||||||
// (for mapping RGB values to the nearest table value)
|
|
||||||
const auto& attr = cell.TextAttr();
|
|
||||||
ci.Attributes = attr.GetLegacyAttributes();
|
|
||||||
ci.Attributes |= GeneratePublicApiAttributeFormat(cell.DbcsAttr());
|
|
||||||
return ci;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
#include "ApiRoutines.h"
|
#include "ApiRoutines.h"
|
||||||
|
|
||||||
#include "../types/inc/convert.hpp"
|
#include "../types/inc/convert.hpp"
|
||||||
#include "../types/inc/GlyphWidth.hpp"
|
|
||||||
#include "../types/inc/viewport.hpp"
|
#include "../types/inc/viewport.hpp"
|
||||||
|
|
||||||
#include "../interactivity/inc/ServiceLocator.hpp"
|
#include "../interactivity/inc/ServiceLocator.hpp"
|
||||||
@@ -413,56 +412,6 @@ CATCH_RETURN();
|
|||||||
CATCH_RETURN();
|
CATCH_RETURN();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] static std::vector<CHAR_INFO> _ConvertCellsToMungedW(std::span<CHAR_INFO> buffer, const Viewport& rectangle)
|
|
||||||
{
|
|
||||||
std::vector<CHAR_INFO> result;
|
|
||||||
result.reserve(buffer.size());
|
|
||||||
|
|
||||||
const auto size = rectangle.Dimensions();
|
|
||||||
auto bufferIter = buffer.begin();
|
|
||||||
|
|
||||||
for (til::CoordType i = 0; i < size.height; i++)
|
|
||||||
{
|
|
||||||
for (til::CoordType j = 0; j < size.width; j++)
|
|
||||||
{
|
|
||||||
// Prepare a candidate charinfo on the output side copying the colors but not the lead/trail information.
|
|
||||||
auto candidate = *bufferIter;
|
|
||||||
WI_ClearAllFlags(candidate.Attributes, COMMON_LVB_SBCSDBCS);
|
|
||||||
|
|
||||||
// If the glyph we're given is full width, it needs to take two cells.
|
|
||||||
if (IsGlyphFullWidth(candidate.Char.UnicodeChar))
|
|
||||||
{
|
|
||||||
// If we're not on the final cell of the row...
|
|
||||||
if (j < size.width - 1)
|
|
||||||
{
|
|
||||||
// Mark that we're consuming two cells.
|
|
||||||
j++;
|
|
||||||
|
|
||||||
// Fill one cell with a copy of the color and character marked leading
|
|
||||||
WI_SetFlag(candidate.Attributes, COMMON_LVB_LEADING_BYTE);
|
|
||||||
result.push_back(candidate);
|
|
||||||
|
|
||||||
// Fill a second cell with a copy of the color marked trailing and a padding character.
|
|
||||||
WI_ClearFlag(candidate.Attributes, COMMON_LVB_LEADING_BYTE);
|
|
||||||
WI_SetFlag(candidate.Attributes, COMMON_LVB_TRAILING_BYTE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If we're on the final cell, this won't fit. Replace with a space.
|
|
||||||
candidate.Char.UnicodeChar = UNICODE_SPACE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push our candidate in.
|
|
||||||
result.push_back(candidate);
|
|
||||||
|
|
||||||
// Advance to read the next item.
|
|
||||||
++bufferIter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] HRESULT ReadConsoleOutputWImplHelper(const SCREEN_INFORMATION& context,
|
[[nodiscard]] HRESULT ReadConsoleOutputWImplHelper(const SCREEN_INFORMATION& context,
|
||||||
std::span<CHAR_INFO> targetBuffer,
|
std::span<CHAR_INFO> targetBuffer,
|
||||||
const Viewport& requestRectangle,
|
const Viewport& requestRectangle,
|
||||||
@@ -470,8 +419,8 @@ CATCH_RETURN();
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
const auto& storageBuffer = context.GetActiveBuffer();
|
||||||
auto& storageBuffer = context.GetActiveBuffer();
|
const auto& textBuffer = storageBuffer.GetTextBuffer();
|
||||||
const auto storageRectangle = storageBuffer.GetBufferSize();
|
const auto storageRectangle = storageBuffer.GetBufferSize();
|
||||||
const auto clippedRectangle = storageRectangle.Clamp(requestRectangle);
|
const auto clippedRectangle = storageRectangle.Clamp(requestRectangle);
|
||||||
|
|
||||||
@@ -482,7 +431,6 @@ CATCH_RETURN();
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto bufferStride = gsl::narrow_cast<size_t>(std::max(0, requestRectangle.Width()));
|
const auto bufferStride = gsl::narrow_cast<size_t>(std::max(0, requestRectangle.Width()));
|
||||||
const auto width = gsl::narrow_cast<size_t>(clippedRectangle.Width());
|
|
||||||
const auto offsetY = clippedRectangle.Top() - requestRectangle.Top();
|
const auto offsetY = clippedRectangle.Top() - requestRectangle.Top();
|
||||||
const auto offsetX = clippedRectangle.Left() - requestRectangle.Left();
|
const auto offsetX = clippedRectangle.Left() - requestRectangle.Left();
|
||||||
// We always write the intersection between the valid `storageRectangle` and the given `requestRectangle`.
|
// We always write the intersection between the valid `storageRectangle` and the given `requestRectangle`.
|
||||||
@@ -498,12 +446,53 @@ CATCH_RETURN();
|
|||||||
|
|
||||||
for (til::CoordType y = clippedRectangle.Top(); y <= clippedRectangle.BottomInclusive(); y++)
|
for (til::CoordType y = clippedRectangle.Top(); y <= clippedRectangle.BottomInclusive(); y++)
|
||||||
{
|
{
|
||||||
auto it = storageBuffer.GetCellDataAt({ clippedRectangle.Left(), y });
|
const auto& row = textBuffer.GetRowByOffset(y);
|
||||||
|
const auto& attributes = row.Attributes();
|
||||||
|
const auto left = row.AdjustToGlyphStart(clippedRectangle.Left());
|
||||||
|
const auto right = clippedRectangle.RightExclusive();
|
||||||
|
|
||||||
for (size_t i = 0; i < width; i++)
|
|
||||||
{
|
{
|
||||||
targetBuffer[totalOffset + i] = gci.AsCharInfo(*it);
|
const auto endOff = std::min<til::CoordType>(right, attributes.size());
|
||||||
++it;
|
const auto begOff = std::min<til::CoordType>(left, endOff);
|
||||||
|
const auto end = attributes.begin() + endOff;
|
||||||
|
auto it = attributes.begin() + begOff;
|
||||||
|
auto off = totalOffset;
|
||||||
|
|
||||||
|
for (; it != end; ++it, ++off)
|
||||||
|
{
|
||||||
|
targetBuffer[off].Attributes = it->GetLegacyAttributes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto x = left;
|
||||||
|
auto off = totalOffset;
|
||||||
|
|
||||||
|
while (x < right)
|
||||||
|
{
|
||||||
|
const auto next = row.NavigateToNext(x);
|
||||||
|
const auto text = row.GetText(x, next);
|
||||||
|
const auto wch = text.size() == 1 ? text.front() : UNICODE_REPLACEMENT;
|
||||||
|
auto ci = &targetBuffer[off];
|
||||||
|
|
||||||
|
ci->Char.UnicodeChar = wch;
|
||||||
|
off += 1;
|
||||||
|
x += 1;
|
||||||
|
|
||||||
|
if (x < next)
|
||||||
|
{
|
||||||
|
ci->Attributes |= COMMON_LVB_LEADING_BYTE;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ci = &targetBuffer[off];
|
||||||
|
ci->Char.UnicodeChar = wch;
|
||||||
|
ci->Attributes |= COMMON_LVB_TRAILING_BYTE;
|
||||||
|
off += 1;
|
||||||
|
x += 1;
|
||||||
|
} while (x < next);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
totalOffset += bufferStride;
|
totalOffset += bufferStride;
|
||||||
@@ -575,6 +564,7 @@ CATCH_RETURN();
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto& storageBuffer = context.GetActiveBuffer();
|
auto& storageBuffer = context.GetActiveBuffer();
|
||||||
|
auto& textBuffer = storageBuffer.GetTextBuffer();
|
||||||
const auto storageRectangle = storageBuffer.GetBufferSize();
|
const auto storageRectangle = storageBuffer.GetBufferSize();
|
||||||
const auto clippedRectangle = storageRectangle.Clamp(requestRectangle);
|
const auto clippedRectangle = storageRectangle.Clamp(requestRectangle);
|
||||||
|
|
||||||
@@ -600,18 +590,83 @@ CATCH_RETURN();
|
|||||||
|
|
||||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||||
auto writer = gci.GetVtWriterForBuffer(&context);
|
auto writer = gci.GetVtWriterForBuffer(&context);
|
||||||
|
std::wstring stringBuffer;
|
||||||
|
|
||||||
for (til::CoordType y = clippedRectangle.Top(); y <= clippedRectangle.BottomInclusive(); y++)
|
for (til::CoordType y = clippedRectangle.Top(); y <= clippedRectangle.BottomInclusive(); y++)
|
||||||
{
|
{
|
||||||
const auto charInfos = buffer.subspan(totalOffset, width);
|
const auto charInfos = til::safe_slice_len(buffer, totalOffset, width);
|
||||||
const til::point target{ clippedRectangle.Left(), y };
|
if (charInfos.empty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Make the iterator and write to the target position.
|
auto left = clippedRectangle.Left();
|
||||||
storageBuffer.Write(OutputCellIterator(charInfos), target);
|
|
||||||
|
|
||||||
if (writer)
|
if (writer)
|
||||||
{
|
{
|
||||||
writer.WriteInfos(target, charInfos);
|
writer.WriteCUP({ left, y });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t end = 0; end < charInfos.size();)
|
||||||
|
{
|
||||||
|
const auto attr = charInfos[end].Attributes;
|
||||||
|
const auto beg = end;
|
||||||
|
|
||||||
|
// Find the next attribute change in the given CHAR_INFOs.
|
||||||
|
while (++end < charInfos.size() && charInfos[end].Attributes == attr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto infoRun = til::safe_slice_abs(charInfos, beg, end);
|
||||||
|
THROW_HR_IF(E_UNEXPECTED, infoRun.empty());
|
||||||
|
|
||||||
|
// Accumulate this run of CHAR_INFOs with identical attributes into a single string.
|
||||||
|
{
|
||||||
|
stringBuffer.clear();
|
||||||
|
stringBuffer.reserve(infoRun.size() * 2);
|
||||||
|
|
||||||
|
if (WI_IsAnyFlagSet(infoRun.front().Attributes, COMMON_LVB_TRAILING_BYTE))
|
||||||
|
{
|
||||||
|
stringBuffer.push_back(L' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& ci : infoRun)
|
||||||
|
{
|
||||||
|
if (WI_IsAnyFlagClear(ci.Attributes, COMMON_LVB_TRAILING_BYTE))
|
||||||
|
{
|
||||||
|
stringBuffer.push_back(Microsoft::Console::VirtualTerminal::VtIo::SanitizeUCS2(ci.Char.UnicodeChar));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WI_IsAnyFlagSet(infoRun.back().Attributes, COMMON_LVB_LEADING_BYTE))
|
||||||
|
{
|
||||||
|
stringBuffer.back() = L' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the string into to the text buffer.
|
||||||
|
// If the character widths assigned by the application via COMMON_LVB_LEADING_BYTE/COMMON_LVB_TRAILING_BYTE
|
||||||
|
// don't match our idea of the character widths, we'll either pad it with whitespace or truncate it.
|
||||||
|
stringBuffer.append(infoRun.size(), L' ');
|
||||||
|
|
||||||
|
const auto right = gsl::narrow<til::CoordType>(left + infoRun.size());
|
||||||
|
const TextAttribute textAttr{ attr };
|
||||||
|
std::wstring_view text{ stringBuffer };
|
||||||
|
RowWriteState state{
|
||||||
|
.text = text,
|
||||||
|
.columnBegin = left,
|
||||||
|
.columnLimit = right,
|
||||||
|
};
|
||||||
|
textBuffer.Replace(y, textAttr, state);
|
||||||
|
|
||||||
|
if (writer)
|
||||||
|
{
|
||||||
|
text.remove_suffix(state.text.size());
|
||||||
|
writer.WriteAttributes(textAttr);
|
||||||
|
writer.WriteUTF16(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
left = right;
|
||||||
}
|
}
|
||||||
|
|
||||||
totalOffset += bufferStride;
|
totalOffset += bufferStride;
|
||||||
@@ -684,17 +739,7 @@ CATCH_RETURN();
|
|||||||
writer.BackupCursor();
|
writer.BackupCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!context.GetActiveBuffer().GetCurrentFont().IsTrueTypeFont())
|
RETURN_IF_FAILED(WriteConsoleOutputWImplHelper(context, buffer, requestRectangle.Width(), requestRectangle, writtenRectangle));
|
||||||
{
|
|
||||||
// For compatibility reasons, we must maintain the behavior that munges the data if we are writing while a raster font is enabled.
|
|
||||||
// This can be removed when raster font support is removed.
|
|
||||||
auto translated = _ConvertCellsToMungedW(buffer, requestRectangle);
|
|
||||||
RETURN_IF_FAILED(WriteConsoleOutputWImplHelper(context, translated, requestRectangle.Width(), requestRectangle, writtenRectangle));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RETURN_IF_FAILED(WriteConsoleOutputWImplHelper(context, buffer, requestRectangle.Width(), requestRectangle, writtenRectangle));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (writer)
|
if (writer)
|
||||||
{
|
{
|
||||||
@@ -718,10 +763,38 @@ CATCH_RETURN();
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const auto attrs = ReadOutputAttributes(context.GetActiveBuffer(), origin, buffer.size());
|
const auto width = context.GetBufferSize().Width();
|
||||||
std::copy(attrs.cbegin(), attrs.cend(), buffer.begin());
|
auto y = origin.y;
|
||||||
written = attrs.size();
|
size_t read = 0;
|
||||||
|
|
||||||
|
til::small_vector<CHAR_INFO, 1024> infoBuffer{ gsl::narrow_cast<size_t>(width) };
|
||||||
|
|
||||||
|
while (read < buffer.size())
|
||||||
|
{
|
||||||
|
const auto beg = y == origin.y ? origin.x : 0;
|
||||||
|
const auto bufferRemaining = buffer.size() - read;
|
||||||
|
const auto columnsRemaining = gsl::narrow_cast<size_t>(width - beg);
|
||||||
|
const auto actualRemaining = gsl::narrow_cast<til::CoordType>(std::min(bufferRemaining, columnsRemaining));
|
||||||
|
const auto end = beg + actualRemaining;
|
||||||
|
|
||||||
|
const auto wantViewport = Viewport::FromInclusive({ beg, y, end, y });
|
||||||
|
Viewport actualViewport;
|
||||||
|
RETURN_IF_FAILED(ReadConsoleOutputWImplHelper(context, infoBuffer, wantViewport, actualViewport));
|
||||||
|
|
||||||
|
if (wantViewport != actualViewport)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (til::CoordType i = 0; i < actualRemaining; ++i)
|
||||||
|
{
|
||||||
|
buffer[read++] = infoBuffer[i].Attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
y += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
written = read;
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
CATCH_RETURN();
|
CATCH_RETURN();
|
||||||
|
|||||||
@@ -503,16 +503,15 @@ void ApiRoutines::SetConsoleActiveScreenBufferImpl(SCREEN_INFORMATION& newContex
|
|||||||
alt.SetViewportSize(&size);
|
alt.SetViewportSize(&size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Viewport read;
|
|
||||||
til::small_vector<CHAR_INFO, 1024> infos;
|
til::small_vector<CHAR_INFO, 1024> infos;
|
||||||
infos.resize(area, CHAR_INFO{ L' ', FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED });
|
infos.resize(area, CHAR_INFO{ L' ', FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED });
|
||||||
|
|
||||||
const auto dumpScreenInfo = [&](SCREEN_INFORMATION& screenInfo) {
|
const auto dumpScreenInfo = [&](SCREEN_INFORMATION& screenInfo) {
|
||||||
|
Viewport read;
|
||||||
THROW_IF_FAILED(ReadConsoleOutputWImpl(screenInfo, infos, viewport, read));
|
THROW_IF_FAILED(ReadConsoleOutputWImpl(screenInfo, infos, viewport, read));
|
||||||
for (til::CoordType i = 0; i < size.height; i++)
|
|
||||||
{
|
Viewport write;
|
||||||
writer.WriteInfos({ 0, i }, { infos.begin() + i * size.width, static_cast<size_t>(size.width) });
|
THROW_IF_FAILED(WriteConsoleOutputWImpl(screenInfo, infos, read, write));
|
||||||
}
|
|
||||||
|
|
||||||
writer.WriteCUP(screenInfo.GetTextBuffer().GetCursor().GetPosition());
|
writer.WriteCUP(screenInfo.GetTextBuffer().GetCursor().GetPosition());
|
||||||
writer.WriteAttributes(screenInfo.GetAttributes());
|
writer.WriteAttributes(screenInfo.GetAttributes());
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include <til/unicode.h>
|
#include <til/unicode.h>
|
||||||
|
|
||||||
#include "dbcs.h"
|
#include "dbcs.h"
|
||||||
#include "../types/inc/GlyphWidth.hpp"
|
|
||||||
#include "../interactivity/inc/ServiceLocator.hpp"
|
#include "../interactivity/inc/ServiceLocator.hpp"
|
||||||
|
|
||||||
#pragma hdrstop
|
#pragma hdrstop
|
||||||
|
|||||||
@@ -119,63 +119,6 @@ static void _CopyRectangle(SCREEN_INFORMATION& screenInfo,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - This routine reads a sequence of attributes from the screen buffer.
|
|
||||||
// Arguments:
|
|
||||||
// - screenInfo - reference to screen buffer information.
|
|
||||||
// - coordRead - Screen buffer coordinate to begin reading from.
|
|
||||||
// - amountToRead - the number of elements to read
|
|
||||||
// Return Value:
|
|
||||||
// - vector of attribute data
|
|
||||||
std::vector<WORD> ReadOutputAttributes(const SCREEN_INFORMATION& screenInfo,
|
|
||||||
const til::point coordRead,
|
|
||||||
const size_t amountToRead)
|
|
||||||
{
|
|
||||||
// Short circuit. If nothing to read, leave early.
|
|
||||||
if (amountToRead == 0)
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Short circuit, if reading out of bounds, leave early.
|
|
||||||
if (!screenInfo.GetBufferSize().IsInBounds(coordRead))
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get iterator to the position we should start reading at.
|
|
||||||
auto it = screenInfo.GetCellDataAt(coordRead);
|
|
||||||
// Count up the number of cells we've attempted to read.
|
|
||||||
ULONG amountRead = 0;
|
|
||||||
// Prepare the return value string.
|
|
||||||
std::vector<WORD> retVal;
|
|
||||||
// Reserve the number of cells. If we have >U+FFFF, it will auto-grow later and that's OK.
|
|
||||||
retVal.reserve(amountToRead);
|
|
||||||
|
|
||||||
// While we haven't read enough cells yet and the iterator is still valid (hasn't reached end of buffer)
|
|
||||||
while (amountRead < amountToRead && it)
|
|
||||||
{
|
|
||||||
const auto legacyAttributes = it->TextAttr().GetLegacyAttributes();
|
|
||||||
|
|
||||||
// If the first thing we read is trailing, pad with a space.
|
|
||||||
// OR If the last thing we read is leading, pad with a space.
|
|
||||||
if ((amountRead == 0 && it->DbcsAttr() == DbcsAttribute::Trailing) ||
|
|
||||||
(amountRead == (amountToRead - 1) && it->DbcsAttr() == DbcsAttribute::Leading))
|
|
||||||
{
|
|
||||||
retVal.push_back(legacyAttributes);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
retVal.push_back(legacyAttributes | GeneratePublicApiAttributeFormat(it->DbcsAttr()));
|
|
||||||
}
|
|
||||||
|
|
||||||
amountRead++;
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - This routine reads a sequence of unicode characters from the screen buffer
|
// - This routine reads a sequence of unicode characters from the screen buffer
|
||||||
// Arguments:
|
// Arguments:
|
||||||
|
|||||||
@@ -26,10 +26,6 @@ void ScreenBufferSizeChange(const til::size coordNewSize);
|
|||||||
|
|
||||||
[[nodiscard]] NTSTATUS DoCreateScreenBuffer();
|
[[nodiscard]] NTSTATUS DoCreateScreenBuffer();
|
||||||
|
|
||||||
std::vector<WORD> ReadOutputAttributes(const SCREEN_INFORMATION& screenInfo,
|
|
||||||
const til::point coordRead,
|
|
||||||
const size_t amountToRead);
|
|
||||||
|
|
||||||
std::wstring ReadOutputStringW(const SCREEN_INFORMATION& screenInfo,
|
std::wstring ReadOutputStringW(const SCREEN_INFORMATION& screenInfo,
|
||||||
const til::point coordRead,
|
const til::point coordRead,
|
||||||
const size_t amountToRead);
|
const size_t amountToRead);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include "readDataRaw.hpp"
|
#include "readDataRaw.hpp"
|
||||||
#include "dbcs.h"
|
#include "dbcs.h"
|
||||||
#include "stream.h"
|
#include "stream.h"
|
||||||
#include "../types/inc/GlyphWidth.hpp"
|
|
||||||
|
|
||||||
#include "../interactivity/inc/ServiceLocator.hpp"
|
#include "../interactivity/inc/ServiceLocator.hpp"
|
||||||
|
|
||||||
|
|||||||
@@ -142,8 +142,6 @@ public:
|
|||||||
|
|
||||||
MidiAudio& GetMidiAudio();
|
MidiAudio& GetMidiAudio();
|
||||||
|
|
||||||
CHAR_INFO AsCharInfo(const OutputCellView& cell) const noexcept;
|
|
||||||
|
|
||||||
RenderData renderData;
|
RenderData renderData;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
#include "ApiRoutines.h"
|
#include "ApiRoutines.h"
|
||||||
|
|
||||||
#include "../types/inc/GlyphWidth.hpp"
|
|
||||||
|
|
||||||
#include "../interactivity/inc/ServiceLocator.hpp"
|
#include "../interactivity/inc/ServiceLocator.hpp"
|
||||||
|
|
||||||
|
|||||||
@@ -305,8 +305,6 @@ class TextBufferIteratorTests
|
|||||||
TEST_METHOD(DifferenceOperatorText);
|
TEST_METHOD(DifferenceOperatorText);
|
||||||
TEST_METHOD(DifferenceOperatorCell);
|
TEST_METHOD(DifferenceOperatorCell);
|
||||||
|
|
||||||
TEST_METHOD(AsCharInfoCell);
|
|
||||||
|
|
||||||
TEST_METHOD(DereferenceOperatorText);
|
TEST_METHOD(DereferenceOperatorText);
|
||||||
TEST_METHOD(DereferenceOperatorCell);
|
TEST_METHOD(DereferenceOperatorCell);
|
||||||
|
|
||||||
@@ -442,27 +440,6 @@ void TextBufferIteratorTests::DifferenceOperatorCell()
|
|||||||
DifferenceOperatorTestHelper<TextBufferCellIterator>();
|
DifferenceOperatorTestHelper<TextBufferCellIterator>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextBufferIteratorTests::AsCharInfoCell()
|
|
||||||
{
|
|
||||||
m_state->FillTextBuffer();
|
|
||||||
const auto it = GetIterator<TextBufferCellIterator>();
|
|
||||||
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
|
||||||
|
|
||||||
const auto& outputBuffer = gci.GetActiveOutputBuffer();
|
|
||||||
|
|
||||||
const auto& row = outputBuffer._textBuffer->GetRowByOffset(it._pos.y);
|
|
||||||
|
|
||||||
const auto wcharExpected = *row.GlyphAt(it._pos.x).begin();
|
|
||||||
const auto attrExpected = row.GetAttrByColumn(it._pos.x);
|
|
||||||
|
|
||||||
const auto cellActual = gci.AsCharInfo(*it);
|
|
||||||
const auto wcharActual = cellActual.Char.UnicodeChar;
|
|
||||||
const auto attrActual = it->TextAttr();
|
|
||||||
|
|
||||||
VERIFY_ARE_EQUAL(wcharExpected, wcharActual);
|
|
||||||
VERIFY_ARE_EQUAL(attrExpected, attrActual);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextBufferIteratorTests::DereferenceOperatorText()
|
void TextBufferIteratorTests::DereferenceOperatorText()
|
||||||
{
|
{
|
||||||
m_state->FillTextBuffer();
|
m_state->FillTextBuffer();
|
||||||
|
|||||||
Reference in New Issue
Block a user