mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-06 22:29:43 +00:00
Compare commits
7 Commits
release-1.
...
dev/miniks
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d95d59a9ab | ||
|
|
3c7ff8bbf1 | ||
|
|
49b14f6f07 | ||
|
|
c51542ba2b | ||
|
|
d1551fd2fb | ||
|
|
72aac0b87c | ||
|
|
a0710e8025 |
@@ -8,7 +8,7 @@
|
||||
<!--<add key="Static Package Dependencies" value="dep\packages" />-->
|
||||
|
||||
<!-- Use our own NuGet Feed -->
|
||||
<add key="TerminalDependencies" value="https://pkgs.dev.azure.com/ms/terminal/_packaging/TerminalDependencies/nuget/v3/index.json" />
|
||||
<add key="Windows Terminal NuGet Feed" value="https://terminalnuget.blob.core.windows.net/feed/index.json" />
|
||||
|
||||
<!-- Internal NuGet feeds that may not be accessible outside Microsoft corporate network -->
|
||||
<!--<add key="TAEF - internal" value="https://microsoft.pkgs.visualstudio.com/DefaultCollection/_packaging/Taef/nuget/v3/index.json" />
|
||||
|
||||
@@ -34,7 +34,7 @@ steps:
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: "${{ parameters.additionalBuildArguments }}"
|
||||
clean: true
|
||||
maximumCpuCount: false
|
||||
maximumCpuCount: true
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Check MSIX for common regressions'
|
||||
|
||||
@@ -158,7 +158,7 @@ For commands with arguments:
|
||||
| ---- | ---- |
|
||||
| Function and Alphanumeric Keys | `f1-f24`, `a-z`, `0-9` |
|
||||
| Symbols | ``` ` ```, `-`, `=`, `[`, `]`, `\`, `;`, `'`, `,`, `.`, `/` |
|
||||
| Arrow Keys | `down`, `left`, `right`, `up`, `pagedown`, `pageup`, `pgdn`, `pgup`, `end`, `home`, `plus`, `app`, `menu` |
|
||||
| Arrow Keys | `down`, `left`, `right`, `up`, `pagedown`, `pageup`, `pgdn`, `pgup`, `end`, `home`, `plus` |
|
||||
| Action Keys | `tab`, `enter`, `esc`, `escape`, `space`, `backspace`, `delete`, `insert` |
|
||||
| Numpad Keys | `numpad_0-numpad_9`, `numpad0-numpad9`, `numpad_add`, `numpad_plus`, `numpad_decimal`, `numpad_period`, `numpad_divide`, `numpad_minus`, `numpad_subtract`, `numpad_multiply` |
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
"title": "Microsoft's Windows Terminal Settings Profile Schema",
|
||||
"definitions": {
|
||||
"KeyChordSegment": {
|
||||
"pattern": "^(?<modifier>(ctrl|alt|shift)(?:\\+(ctrl|alt|shift)(?<!\\2))?(?:\\+(ctrl|alt|shift)(?<!\\2|\\3))?\\+)?(?<key>[^\\s+]|app|menu|backspace|tab|enter|esc|escape|space|pgup|pageup|pgdn|pagedown|end|home|left|up|right|down|insert|delete|(?<!shift.+)(?:numpad_?[0-9]|numpad_(?:period|decimal))|numpad_(?:multiply|plus|add|minus|subtract|divide)|f[1-9]|f1[0-9]|f2[0-4]|plus)$",
|
||||
"pattern": "^(?<modifier>(ctrl|alt|shift)(?:\\+(ctrl|alt|shift)(?<!\\2))?(?:\\+(ctrl|alt|shift)(?<!\\2|\\3))?\\+)?(?<key>[^\\s+]|backspace|tab|enter|esc|escape|space|pgup|pageup|pgdn|pagedown|end|home|left|up|right|down|insert|delete|(?<!shift.+)(?:numpad_?[0-9]|numpad_(?:period|decimal))|numpad_(?:multiply|plus|add|minus|subtract|divide)|f[1-9]|f1[0-9]|f2[0-4]|plus)$",
|
||||
"type": "string",
|
||||
"description": "The string should fit the format \"[ctrl+][alt+][shift+]<keyName>\", where each modifier is optional, separated by + symbols, and keyName is either one of the names listed in the table below, or any single key character. The string should be written in full lowercase.\napp, menu\tMENU key\nbackspace\tBACKSPACE key\ntab\tTAB key\nenter\tENTER key\nesc, escape\tESC key\nspace\tSPACEBAR\npgup, pageup\tPAGE UP key\npgdn, pagedown\tPAGE DOWN key\nend\tEND key\nhome\tHOME key\nleft\tLEFT ARROW key\nup\tUP ARROW key\nright\tRIGHT ARROW key\ndown\tDOWN ARROW key\ninsert\tINS key\ndelete\tDEL key\nnumpad_0-numpad_9, numpad0-numpad9\tNumeric keypad keys 0 to 9. Can't be combined with the shift modifier.\nnumpad_multiply\tNumeric keypad MULTIPLY key (*)\nnumpad_plus, numpad_add\tNumeric keypad ADD key (+)\nnumpad_minus, numpad_subtract\tNumeric keypad SUBTRACT key (-)\nnumpad_period, numpad_decimal\tNumeric keypad DECIMAL key (.). Can't be combined with the shift modifier.\nnumpad_divide\tNumeric keypad DIVIDE key (/)\nf1-f24\tF1 to F24 function keys\nplus\tADD key (+)"
|
||||
"description": "The string should fit the format \"[ctrl+][alt+][shift+]<keyName>\", where each modifier is optional, separated by + symbols, and keyName is either one of the names listed in the table below, or any single key character. The string should be written in full lowercase.\nbackspace\tBACKSPACE key\ntab\tTAB key\nenter\tENTER key\nesc, escape\tESC key\nspace\tSPACEBAR\npgup, pageup\tPAGE UP key\npgdn, pagedown\tPAGE DOWN key\nend\tEND key\nhome\tHOME key\nleft\tLEFT ARROW key\nup\tUP ARROW key\nright\tRIGHT ARROW key\ndown\tDOWN ARROW key\ninsert\tINS key\ndelete\tDEL key\nnumpad_0-numpad_9, numpad0-numpad9\tNumeric keypad keys 0 to 9. Can't be combined with the shift modifier.\nnumpad_multiply\tNumeric keypad MULTIPLY key (*)\nnumpad_plus, numpad_add\tNumeric keypad ADD key (+)\nnumpad_minus, numpad_subtract\tNumeric keypad SUBTRACT key (-)\nnumpad_period, numpad_decimal\tNumeric keypad DECIMAL key (.). Can't be combined with the shift modifier.\nnumpad_divide\tNumeric keypad DIVIDE key (/)\nf1-f24\tF1 to F24 function keys\nplus\tADD key (+)"
|
||||
},
|
||||
"Color": {
|
||||
"default": "#",
|
||||
@@ -392,16 +392,10 @@
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"startOnUserLogin": {
|
||||
"default": false,
|
||||
"description": "When set to true, this enables the launch of Windows Terminal at startup. Setting this to false will disable the startup task entry. If the Windows Terminal startup task entry is disabled either by org policy or by user action this setting will have no effect.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"launchMode": {
|
||||
"default": "default",
|
||||
"description": "Defines whether the terminal will launch as maximized, full screen, or in a window.",
|
||||
"description": "Defines whether the Terminal will launch as maximized or not.",
|
||||
"enum": [
|
||||
"fullscreen",
|
||||
"maximized",
|
||||
"default"
|
||||
],
|
||||
|
||||
BIN
res/Cascadia.ttf
BIN
res/Cascadia.ttf
Binary file not shown.
Binary file not shown.
@@ -17,5 +17,5 @@ Please consult the [license](https://raw.githubusercontent.com/microsoft/cascadi
|
||||
|
||||
### Fonts Included
|
||||
|
||||
* Cascadia Code, Cascadia Mono (2008.25)
|
||||
* from microsoft/cascadia-code@678eea921b0c8b921b9fb009bb16d3d2ca5b8112
|
||||
* Cascadia Code, Cascadia Mono (2007.01)
|
||||
* from microsoft/cascadia-code@311cc603f30635da704b6a7d13050e245e61667b
|
||||
|
||||
@@ -223,7 +223,7 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
|
||||
// Return Value:
|
||||
// - STATUS_NO_MEMORY if there wasn't enough memory to insert the runs
|
||||
// otherwise STATUS_SUCCESS if we were successful.
|
||||
[[nodiscard]] HRESULT ATTR_ROW::InsertAttrRuns(const gsl::span<const TextAttributeRun> newAttrs,
|
||||
[[nodiscard]] HRESULT ATTR_ROW::InsertAttrRuns(const std::basic_string_view<TextAttributeRun> newAttrs,
|
||||
const size_t iStart,
|
||||
const size_t iEnd,
|
||||
const size_t cBufferWidth)
|
||||
@@ -250,11 +250,11 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
|
||||
if (newAttrs.size() == 1)
|
||||
{
|
||||
// Get the new color attribute we're trying to apply
|
||||
const TextAttribute NewAttr = til::at(newAttrs, 0).GetAttributes();
|
||||
const TextAttribute NewAttr = newAttrs[0].GetAttributes();
|
||||
|
||||
// If the existing run was only 1 element...
|
||||
// ...and the new color is the same as the old, we don't have to do anything and can exit quick.
|
||||
if (_list.size() == 1 && _list.at(0).GetAttributes() == NewAttr)
|
||||
if (_list.size() == 1 && _list[0].GetAttributes() == NewAttr)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
@@ -372,7 +372,7 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
|
||||
if (iStart == 0 && iEnd == iLastBufferCol)
|
||||
{
|
||||
// Just dump what we're given over what we have and call it a day.
|
||||
_list.assign(newAttrs.begin(), newAttrs.end());
|
||||
_list.assign(newAttrs.cbegin(), newAttrs.cend());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
|
||||
void Resize(const size_t newWidth);
|
||||
|
||||
[[nodiscard]] HRESULT InsertAttrRuns(const gsl::span<const TextAttributeRun> newAttrs,
|
||||
[[nodiscard]] HRESULT InsertAttrRuns(const std::basic_string_view<TextAttributeRun> newAttrs,
|
||||
const size_t iStart,
|
||||
const size_t iEnd,
|
||||
const size_t cBufferWidth);
|
||||
|
||||
@@ -233,7 +233,9 @@ void CharRow::ClearGlyph(const size_t column)
|
||||
// - Note: will throw exception if column is out of bounds
|
||||
const CharRow::reference CharRow::GlyphAt(const size_t column) const
|
||||
{
|
||||
#ifdef DBG
|
||||
THROW_HR_IF(E_INVALIDARG, column >= _data.size());
|
||||
#endif
|
||||
return { const_cast<CharRow&>(*this), column };
|
||||
}
|
||||
|
||||
@@ -246,7 +248,9 @@ const CharRow::reference CharRow::GlyphAt(const size_t column) const
|
||||
// - Note: will throw exception if column is out of bounds
|
||||
CharRow::reference CharRow::GlyphAt(const size_t column)
|
||||
{
|
||||
#ifdef DBG
|
||||
THROW_HR_IF(E_INVALIDARG, column >= _data.size());
|
||||
#endif
|
||||
return { *this, column };
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ CharRowCellReference::operator std::wstring_view() const
|
||||
// - ref to the CharRowCell
|
||||
CharRowCell& CharRowCellReference::_cellData()
|
||||
{
|
||||
return _parent._data.at(_index);
|
||||
return _parent._data[_index];
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -50,7 +50,7 @@ CharRowCell& CharRowCellReference::_cellData()
|
||||
// - ref to the CharRowCell
|
||||
const CharRowCell& CharRowCellReference::_cellData() const
|
||||
{
|
||||
return _parent._data.at(_index);
|
||||
return _parent._data[_index];
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
||||
@@ -112,9 +112,9 @@ OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text, const
|
||||
// - This is an iterator over legacy colors only. The text is not modified.
|
||||
// Arguments:
|
||||
// - legacyAttrs - One legacy color item per cell
|
||||
OutputCellIterator::OutputCellIterator(const gsl::span<const WORD> legacyAttrs) noexcept :
|
||||
OutputCellIterator::OutputCellIterator(const std::basic_string_view<WORD> legacyAttrs) noexcept :
|
||||
_mode(Mode::LegacyAttr),
|
||||
_currentView(s_GenerateViewLegacyAttr(til::at(legacyAttrs, 0))),
|
||||
_currentView(s_GenerateViewLegacyAttr(legacyAttrs.at(0))),
|
||||
_run(legacyAttrs),
|
||||
_attr(InvalidTextAttribute),
|
||||
_distance(0),
|
||||
@@ -127,9 +127,9 @@ OutputCellIterator::OutputCellIterator(const gsl::span<const WORD> legacyAttrs)
|
||||
// - 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 gsl::span<const CHAR_INFO> charInfos) noexcept :
|
||||
OutputCellIterator::OutputCellIterator(const std::basic_string_view<CHAR_INFO> charInfos) noexcept :
|
||||
_mode(Mode::CharInfo),
|
||||
_currentView(s_GenerateView(til::at(charInfos, 0))),
|
||||
_currentView(s_GenerateView(charInfos.at(0))),
|
||||
_run(charInfos),
|
||||
_attr(InvalidTextAttribute),
|
||||
_distance(0),
|
||||
@@ -142,9 +142,9 @@ OutputCellIterator::OutputCellIterator(const gsl::span<const CHAR_INFO> charInfo
|
||||
// - This is an iterator over existing OutputCells with full text and color data.
|
||||
// Arguments:
|
||||
// - cells - Multiple cells in a run
|
||||
OutputCellIterator::OutputCellIterator(const gsl::span<const OutputCell> cells) :
|
||||
OutputCellIterator::OutputCellIterator(const std::basic_string_view<OutputCell> cells) :
|
||||
_mode(Mode::Cell),
|
||||
_currentView(s_GenerateView(til::at(cells, 0))),
|
||||
_currentView(s_GenerateView(cells.at(0))),
|
||||
_run(cells),
|
||||
_attr(InvalidTextAttribute),
|
||||
_distance(0),
|
||||
@@ -159,42 +159,33 @@ OutputCellIterator::OutputCellIterator(const gsl::span<const OutputCell> cells)
|
||||
// - True if the views on dereference are valid. False if it shouldn't be dereferenced.
|
||||
OutputCellIterator::operator bool() const noexcept
|
||||
{
|
||||
try
|
||||
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:
|
||||
{
|
||||
if (_fillLimit > 0)
|
||||
{
|
||||
return _pos < _fillLimit;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Mode::Cell:
|
||||
{
|
||||
return _pos < std::get<gsl::span<const OutputCell>>(_run).size();
|
||||
}
|
||||
case Mode::CharInfo:
|
||||
{
|
||||
return _pos < std::get<gsl::span<const CHAR_INFO>>(_run).size();
|
||||
}
|
||||
case Mode::LegacyAttr:
|
||||
{
|
||||
return _pos < std::get<gsl::span<const WORD>>(_run).size();
|
||||
}
|
||||
default:
|
||||
FAIL_FAST_HR(E_NOTIMPL);
|
||||
}
|
||||
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: {
|
||||
if (_fillLimit > 0)
|
||||
{
|
||||
return _pos < _fillLimit;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Mode::Cell: {
|
||||
return _pos < std::get<std::basic_string_view<OutputCell>>(_run).length();
|
||||
}
|
||||
case Mode::CharInfo: {
|
||||
return _pos < std::get<std::basic_string_view<CHAR_INFO>>(_run).length();
|
||||
}
|
||||
case Mode::LegacyAttr: {
|
||||
return _pos < std::get<std::wstring_view>(_run).length();
|
||||
}
|
||||
default:
|
||||
FAIL_FAST_HR(E_NOTIMPL);
|
||||
}
|
||||
CATCH_FAIL_FAST();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -208,8 +199,7 @@ OutputCellIterator& OutputCellIterator::operator++()
|
||||
|
||||
switch (_mode)
|
||||
{
|
||||
case Mode::Loose:
|
||||
{
|
||||
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
|
||||
@@ -222,8 +212,7 @@ OutputCellIterator& OutputCellIterator::operator++()
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Mode::LooseTextOnly:
|
||||
{
|
||||
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
|
||||
@@ -236,8 +225,7 @@ OutputCellIterator& OutputCellIterator::operator++()
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Mode::Fill:
|
||||
{
|
||||
case Mode::Fill: {
|
||||
if (!_TryMoveTrailing())
|
||||
{
|
||||
if (_currentView.DbcsAttr().IsTrailing())
|
||||
@@ -259,33 +247,30 @@ OutputCellIterator& OutputCellIterator::operator++()
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Mode::Cell:
|
||||
{
|
||||
case Mode::Cell: {
|
||||
// Walk forward by one because cells are assumed to be in the form they needed to be
|
||||
_pos++;
|
||||
if (operator bool())
|
||||
{
|
||||
_currentView = s_GenerateView(til::at(std::get<gsl::span<const OutputCell>>(_run), _pos));
|
||||
_currentView = s_GenerateView(std::get<std::basic_string_view<OutputCell>>(_run).at(_pos));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Mode::CharInfo:
|
||||
{
|
||||
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<gsl::span<const CHAR_INFO>>(_run), _pos));
|
||||
_currentView = s_GenerateView(std::get<std::basic_string_view<CHAR_INFO>>(_run).at(_pos));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Mode::LegacyAttr:
|
||||
{
|
||||
case Mode::LegacyAttr: {
|
||||
// Walk forward by one because color attributes apply cell by cell (no complex text information)
|
||||
_pos++;
|
||||
if (operator bool())
|
||||
{
|
||||
_currentView = s_GenerateViewLegacyAttr(til::at(std::get<gsl::span<const WORD>>(_run), _pos));
|
||||
_currentView = s_GenerateViewLegacyAttr(std::get<std::basic_string_view<WORD>>(_run).at(_pos));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -39,9 +39,9 @@ public:
|
||||
OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit = 0) noexcept;
|
||||
OutputCellIterator(const std::wstring_view utf16Text);
|
||||
OutputCellIterator(const std::wstring_view utf16Text, const TextAttribute attribute);
|
||||
OutputCellIterator(const gsl::span<const WORD> legacyAttributes) noexcept;
|
||||
OutputCellIterator(const gsl::span<const CHAR_INFO> charInfos) noexcept;
|
||||
OutputCellIterator(const gsl::span<const OutputCell> cells);
|
||||
OutputCellIterator(const std::basic_string_view<WORD> legacyAttributes) noexcept;
|
||||
OutputCellIterator(const std::basic_string_view<CHAR_INFO> charInfos) noexcept;
|
||||
OutputCellIterator(const std::basic_string_view<OutputCell> cells);
|
||||
~OutputCellIterator() = default;
|
||||
|
||||
OutputCellIterator& operator=(const OutputCellIterator& it) = default;
|
||||
@@ -86,13 +86,13 @@ private:
|
||||
};
|
||||
Mode _mode;
|
||||
|
||||
gsl::span<const WORD> _legacyAttrs;
|
||||
std::basic_string_view<WORD> _legacyAttrs;
|
||||
|
||||
std::variant<
|
||||
std::wstring_view,
|
||||
gsl::span<const WORD>,
|
||||
gsl::span<const CHAR_INFO>,
|
||||
gsl::span<const OutputCell>,
|
||||
std::basic_string_view<WORD>,
|
||||
std::basic_string_view<CHAR_INFO>,
|
||||
std::basic_string_view<OutputCell>,
|
||||
std::monostate>
|
||||
_run;
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ gsl::span<OutputCell> OutputCellRect::GetRow(const size_t row)
|
||||
// - Read-only iterator of OutputCells
|
||||
OutputCellIterator OutputCellRect::GetRowIter(const size_t row) const
|
||||
{
|
||||
const gsl::span<const OutputCell> view(_FindRowOffset(row), _cols);
|
||||
const std::basic_string_view<OutputCell> view(_FindRowOffset(row), _cols);
|
||||
|
||||
return OutputCellIterator(view);
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ bool TextAttribute::IsLegacy() const noexcept
|
||||
// - reverseScreenMode: true if the screen mode is reversed.
|
||||
// Return Value:
|
||||
// - the foreground and background colors that should be displayed.
|
||||
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const gsl::span<const COLORREF> colorTable,
|
||||
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const std::basic_string_view<COLORREF> colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
const bool reverseScreenMode) const noexcept
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
static TextAttribute StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) noexcept;
|
||||
WORD GetLegacyAttributes() const noexcept;
|
||||
|
||||
std::pair<COLORREF, COLORREF> CalculateRgbColors(const gsl::span<const COLORREF> colorTable,
|
||||
std::pair<COLORREF, COLORREF> CalculateRgbColors(const std::basic_string_view<COLORREF> colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
const bool reverseScreenMode = false) const noexcept;
|
||||
|
||||
@@ -138,7 +138,7 @@ void TextColor::SetDefault() noexcept
|
||||
// - brighten: if true, we'll brighten a dark color table index.
|
||||
// Return Value:
|
||||
// - a COLORREF containing the real value of this TextColor.
|
||||
COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
|
||||
COLORREF TextColor::GetColor(std::basic_string_view<COLORREF> colorTable,
|
||||
const COLORREF defaultColor,
|
||||
bool brighten) const noexcept
|
||||
{
|
||||
@@ -158,9 +158,9 @@ COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
|
||||
// If we find a match, return instead the bright version of this color
|
||||
for (size_t i = 0; i < 8; i++)
|
||||
{
|
||||
if (til::at(colorTable, i) == defaultColor)
|
||||
if (colorTable.at(i) == defaultColor)
|
||||
{
|
||||
return til::at(colorTable, i + 8);
|
||||
return colorTable.at(i + 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,11 +173,11 @@ COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
|
||||
}
|
||||
else if (IsIndex16() && brighten)
|
||||
{
|
||||
return til::at(colorTable, _index | 8);
|
||||
return colorTable.at(_index | 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
return til::at(colorTable, _index);
|
||||
return colorTable.at(_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
void SetIndex(const BYTE index, const bool isIndex256) noexcept;
|
||||
void SetDefault() noexcept;
|
||||
|
||||
COLORREF GetColor(gsl::span<const COLORREF> colorTable,
|
||||
COLORREF GetColor(std::basic_string_view<COLORREF> colorTable,
|
||||
const COLORREF defaultColor,
|
||||
const bool brighten = false) const noexcept;
|
||||
|
||||
|
||||
@@ -35,11 +35,6 @@ void UnicodeStorage::StoreGlyph(const key_type key, const mapped_type& glyph)
|
||||
// - erases key and its associated data from the storage
|
||||
// Arguments:
|
||||
// - key - the key to remove
|
||||
// ~~~~~~~~~~~~~~
|
||||
// NOTE: VS 16.7 changes std::map::erase to noexcept, but the build agents are still 16.6.5.
|
||||
// Ignore this audit warning on your dev box until the build starts failing. Then fix it
|
||||
// and remove this comment.
|
||||
// ~~~~~~~~~~~~~
|
||||
void UnicodeStorage::Erase(const key_type key)
|
||||
{
|
||||
_map.erase(key);
|
||||
|
||||
@@ -81,7 +81,7 @@ const ROW& TextBuffer::GetRowByOffset(const size_t index) const
|
||||
|
||||
// Rows are stored circularly, so the index you ask for is offset by the start position and mod the total of rows.
|
||||
const size_t offsetIndex = (_firstRow + index) % totalRows;
|
||||
return _storage.at(offsetIndex);
|
||||
return _storage[offsetIndex];
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -97,7 +97,7 @@ ROW& TextBuffer::GetRowByOffset(const size_t index)
|
||||
|
||||
// Rows are stored circularly, so the index you ask for is offset by the start position and mod the total of rows.
|
||||
const size_t offsetIndex = (_firstRow + index) % totalRows;
|
||||
return _storage.at(offsetIndex);
|
||||
return _storage[offsetIndex];
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
||||
@@ -27,7 +27,7 @@ class TextAttributeTests
|
||||
COLORREF _colorTable[COLOR_TABLE_SIZE];
|
||||
COLORREF _defaultFg = RGB(1, 2, 3);
|
||||
COLORREF _defaultBg = RGB(4, 5, 6);
|
||||
gsl::span<const COLORREF> _GetTableView();
|
||||
std::basic_string_view<COLORREF> _GetTableView();
|
||||
};
|
||||
|
||||
bool TextAttributeTests::ClassSetup()
|
||||
@@ -51,9 +51,9 @@ bool TextAttributeTests::ClassSetup()
|
||||
return true;
|
||||
}
|
||||
|
||||
gsl::span<const COLORREF> TextAttributeTests::_GetTableView()
|
||||
std::basic_string_view<COLORREF> TextAttributeTests::_GetTableView()
|
||||
{
|
||||
return gsl::span<const COLORREF>(&_colorTable[0], COLOR_TABLE_SIZE);
|
||||
return std::basic_string_view<COLORREF>(&_colorTable[0], COLOR_TABLE_SIZE);
|
||||
}
|
||||
|
||||
void TextAttributeTests::TestRoundtripLegacy()
|
||||
|
||||
@@ -27,7 +27,7 @@ class TextColorTests
|
||||
COLORREF _colorTable[COLOR_TABLE_SIZE];
|
||||
COLORREF _defaultFg = RGB(1, 2, 3);
|
||||
COLORREF _defaultBg = RGB(4, 5, 6);
|
||||
gsl::span<const COLORREF> _GetTableView();
|
||||
std::basic_string_view<COLORREF> _GetTableView();
|
||||
};
|
||||
|
||||
bool TextColorTests::ClassSetup()
|
||||
@@ -51,9 +51,9 @@ bool TextColorTests::ClassSetup()
|
||||
return true;
|
||||
}
|
||||
|
||||
gsl::span<const COLORREF> TextColorTests::_GetTableView()
|
||||
std::basic_string_view<COLORREF> TextColorTests::_GetTableView()
|
||||
{
|
||||
return gsl::span<const COLORREF>(&_colorTable[0], COLOR_TABLE_SIZE);
|
||||
return std::basic_string_view<COLORREF>(&_colorTable[0], COLOR_TABLE_SIZE);
|
||||
}
|
||||
|
||||
void TextColorTests::TestDefaultColor()
|
||||
|
||||
@@ -2354,7 +2354,7 @@ namespace TerminalAppLocalTests
|
||||
"commandline": "wsl.exe"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
"bindings": [
|
||||
{ "keys": "ctrl+a", "command": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "name": "ctrl+b", "command": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "keys": "ctrl+c", "name": "ctrl+c", "command": { "action": "splitPane", "split": "vertical" } },
|
||||
@@ -2528,4 +2528,5 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,35 +16,12 @@ using namespace ::Microsoft::Terminal::Core;
|
||||
|
||||
static LPCWSTR term_window_class = L"HwndTerminalClass";
|
||||
|
||||
// This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx
|
||||
// "If the high-order bit is 1, the key is down; otherwise, it is up."
|
||||
static constexpr short KeyPressed{ gsl::narrow_cast<short>(0x8000) };
|
||||
|
||||
static constexpr bool _IsMouseMessage(UINT uMsg)
|
||||
{
|
||||
return uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP || uMsg == WM_LBUTTONDBLCLK ||
|
||||
uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP || uMsg == WM_MBUTTONDBLCLK ||
|
||||
uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP || uMsg == WM_RBUTTONDBLCLK ||
|
||||
uMsg == WM_MOUSEMOVE || uMsg == WM_MOUSEWHEEL || uMsg == WM_MOUSEHWHEEL;
|
||||
}
|
||||
|
||||
// Helper static function to ensure that all ambiguous-width glyphs are reported as narrow.
|
||||
// See microsoft/terminal#2066 for more info.
|
||||
static bool _IsGlyphWideForceNarrowFallback(const std::wstring_view /* glyph */) noexcept
|
||||
{
|
||||
return false; // glyph is not wide.
|
||||
}
|
||||
|
||||
static bool _EnsureStaticInitialization()
|
||||
{
|
||||
// use C++11 magic statics to make sure we only do this once.
|
||||
static bool initialized = []() {
|
||||
// *** THIS IS A SINGLETON ***
|
||||
SetGlyphWidthFallback(_IsGlyphWideForceNarrowFallback);
|
||||
|
||||
return true;
|
||||
}();
|
||||
return initialized;
|
||||
uMsg == WM_MOUSEMOVE || uMsg == WM_MOUSEWHEEL;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK HwndTerminal::HwndTerminalWndProc(
|
||||
@@ -59,31 +36,10 @@ try
|
||||
|
||||
if (terminal)
|
||||
{
|
||||
if (_IsMouseMessage(uMsg))
|
||||
if (_IsMouseMessage(uMsg) && terminal->_CanSendVTMouseInput())
|
||||
{
|
||||
if (terminal->_CanSendVTMouseInput() && terminal->_SendMouseEvent(uMsg, wParam, lParam))
|
||||
if (terminal->_SendMouseEvent(uMsg, wParam, lParam))
|
||||
{
|
||||
// GH#6401: Capturing the mouse ensures that we get drag/release events
|
||||
// even if the user moves outside the window.
|
||||
// _SendMouseEvent returns false if the terminal's not in VT mode, so we'll
|
||||
// fall through to release the capture.
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
SetCapture(hwnd);
|
||||
break;
|
||||
case WM_LBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
ReleaseCapture();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Suppress all mouse events that made it into the terminal.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -101,10 +57,6 @@ try
|
||||
return 0;
|
||||
case WM_LBUTTONUP:
|
||||
terminal->_singleClickTouchdownPos = std::nullopt;
|
||||
[[fallthrough]];
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
ReleaseCapture();
|
||||
break;
|
||||
case WM_MOUSEMOVE:
|
||||
if (WI_IsFlagSet(wParam, MK_LBUTTON))
|
||||
@@ -120,7 +72,7 @@ try
|
||||
{
|
||||
const auto bufferData = terminal->_terminal->RetrieveSelectedTextFromBuffer(false);
|
||||
LOG_IF_FAILED(terminal->_CopyTextToSystemClipboard(bufferData, true));
|
||||
TerminalClearSelection(terminal);
|
||||
terminal->_terminal->ClearSelection();
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
@@ -129,13 +81,6 @@ try
|
||||
terminal->_PasteTextFromClipboard();
|
||||
}
|
||||
return 0;
|
||||
case WM_DESTROY:
|
||||
// Release Terminal's hwnd so Teardown doesn't try to destroy it again
|
||||
terminal->_hwnd.release();
|
||||
terminal->Teardown();
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
@@ -169,16 +114,14 @@ static bool RegisterTermClass(HINSTANCE hInstance) noexcept
|
||||
}
|
||||
|
||||
HwndTerminal::HwndTerminal(HWND parentHwnd) :
|
||||
_desiredFont{ L"Consolas", 0, DEFAULT_FONT_WEIGHT, { 0, 14 }, CP_UTF8 },
|
||||
_actualFont{ L"Consolas", 0, DEFAULT_FONT_WEIGHT, { 0, 14 }, CP_UTF8, false },
|
||||
_desiredFont{ L"Consolas", 0, 10, { 0, 14 }, CP_UTF8 },
|
||||
_actualFont{ L"Consolas", 0, 10, { 0, 14 }, CP_UTF8, false },
|
||||
_uiaProvider{ nullptr },
|
||||
_uiaProviderInitialized{ false },
|
||||
_currentDpi{ USER_DEFAULT_SCREEN_DPI },
|
||||
_pfnWriteCallback{ nullptr },
|
||||
_multiClickTime{ 500 } // this will be overwritten by the windows system double-click time
|
||||
{
|
||||
_EnsureStaticInitialization();
|
||||
|
||||
HINSTANCE hInstance = wil::GetModuleInstanceHandle();
|
||||
|
||||
if (RegisterTermClass(hInstance))
|
||||
@@ -205,11 +148,6 @@ HwndTerminal::HwndTerminal(HWND parentHwnd) :
|
||||
}
|
||||
}
|
||||
|
||||
HwndTerminal::~HwndTerminal()
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
|
||||
HRESULT HwndTerminal::Initialize()
|
||||
{
|
||||
_terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
|
||||
@@ -224,6 +162,9 @@ HRESULT HwndTerminal::Initialize()
|
||||
RETURN_IF_FAILED(dxEngine->Enable());
|
||||
_renderer->AddRenderEngine(dxEngine.get());
|
||||
|
||||
const auto pfn = std::bind(&::Microsoft::Console::Render::Renderer::IsGlyphWideByFont, _renderer.get(), std::placeholders::_1);
|
||||
SetGlyphWidthFallback(pfn);
|
||||
|
||||
_UpdateFont(USER_DEFAULT_SCREEN_DPI);
|
||||
RECT windowRect;
|
||||
GetWindowRect(_hwnd.get(), &windowRect);
|
||||
@@ -240,8 +181,8 @@ HRESULT HwndTerminal::Initialize()
|
||||
_terminal->SetBackgroundCallback([](auto) {});
|
||||
|
||||
_terminal->Create(COORD{ 80, 25 }, 1000, *_renderer);
|
||||
_terminal->SetDefaultBackground(RGB(12, 12, 12));
|
||||
_terminal->SetDefaultForeground(RGB(204, 204, 204));
|
||||
_terminal->SetDefaultBackground(RGB(5, 27, 80));
|
||||
_terminal->SetDefaultForeground(RGB(255, 255, 255));
|
||||
_terminal->SetWriteInputCallback([=](std::wstring & input) noexcept { _WriteTextToConnection(input); });
|
||||
localPointerToThread->EnablePainting();
|
||||
|
||||
@@ -250,33 +191,6 @@ HRESULT HwndTerminal::Initialize()
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void HwndTerminal::Teardown() noexcept
|
||||
try
|
||||
{
|
||||
// As a rule, detach resources from the Terminal before shutting them down.
|
||||
// This ensures that teardown is reentrant.
|
||||
|
||||
// Shut down the renderer (and therefore the thread) before we implode
|
||||
if (auto localRenderEngine{ std::exchange(_renderEngine, nullptr) })
|
||||
{
|
||||
if (auto localRenderer{ std::exchange(_renderer, nullptr) })
|
||||
{
|
||||
localRenderer->TriggerTeardown();
|
||||
// renderer is destroyed
|
||||
}
|
||||
// renderEngine is destroyed
|
||||
}
|
||||
|
||||
if (auto localHwnd{ _hwnd.release() })
|
||||
{
|
||||
// If we're being called through WM_DESTROY, we won't get here (hwnd is already released)
|
||||
// If we're not, we may end up in Teardown _again_... but by the time we do, all other
|
||||
// resources have been released and will not be released again.
|
||||
DestroyWindow(localHwnd);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void HwndTerminal::RegisterScrollCallback(std::function<void(int, int, int)> callback)
|
||||
{
|
||||
_terminal->SetScrollPositionChangedCallback(callback);
|
||||
@@ -553,21 +467,11 @@ try
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
void HwndTerminal::_ClearSelection() noexcept
|
||||
try
|
||||
{
|
||||
auto lock{ _terminal->LockForWriting() };
|
||||
_terminal->ClearSelection();
|
||||
_renderer->TriggerSelection();
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void _stdcall TerminalClearSelection(void* terminal)
|
||||
{
|
||||
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
publicTerminal->_ClearSelection();
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
publicTerminal->_terminal->ClearSelection();
|
||||
}
|
||||
|
||||
bool _stdcall TerminalIsSelectionActive(void* terminal)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
@@ -578,10 +482,9 @@ bool _stdcall TerminalIsSelectionActive(void* terminal)
|
||||
// Returns the selected text in the terminal.
|
||||
const wchar_t* _stdcall TerminalGetSelection(void* terminal)
|
||||
{
|
||||
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
|
||||
const auto bufferData = publicTerminal->_terminal->RetrieveSelectedTextFromBuffer(false);
|
||||
publicTerminal->_ClearSelection();
|
||||
|
||||
// convert text: vector<string> --> string
|
||||
std::wstring selectedText;
|
||||
@@ -591,6 +494,8 @@ const wchar_t* _stdcall TerminalGetSelection(void* terminal)
|
||||
}
|
||||
|
||||
auto returnText = wil::make_cotaskmem_string_nothrow(selectedText.c_str());
|
||||
TerminalClearSelection(terminal);
|
||||
|
||||
return returnText.release();
|
||||
}
|
||||
|
||||
@@ -636,30 +541,19 @@ bool HwndTerminal::_CanSendVTMouseInput() const noexcept
|
||||
bool HwndTerminal::_SendMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept
|
||||
try
|
||||
{
|
||||
til::point cursorPosition{
|
||||
const til::point cursorPosition{
|
||||
GET_X_LPARAM(lParam),
|
||||
GET_Y_LPARAM(lParam),
|
||||
};
|
||||
|
||||
const til::size fontSize{ this->_actualFont.GetSize() };
|
||||
short wheelDelta{ 0 };
|
||||
if (uMsg == WM_MOUSEWHEEL || uMsg == WM_MOUSEHWHEEL)
|
||||
if (uMsg == WM_MOUSEWHEEL)
|
||||
{
|
||||
wheelDelta = HIWORD(wParam);
|
||||
|
||||
// If it's a *WHEEL event, it's in screen coordinates, not window (?!)
|
||||
POINT coordsToTransform = cursorPosition;
|
||||
ScreenToClient(_hwnd.get(), &coordsToTransform);
|
||||
cursorPosition = coordsToTransform;
|
||||
}
|
||||
|
||||
const TerminalInput::MouseButtonState state{
|
||||
WI_IsFlagSet(GetKeyState(VK_LBUTTON), KeyPressed),
|
||||
WI_IsFlagSet(GetKeyState(VK_MBUTTON), KeyPressed),
|
||||
WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed)
|
||||
};
|
||||
|
||||
return _terminal->SendMouseEvent(cursorPosition / fontSize, uMsg, getControlKeyState(), wheelDelta, state);
|
||||
return _terminal->SendMouseEvent(cursorPosition / fontSize, uMsg, getControlKeyState(), wheelDelta);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -667,24 +561,20 @@ catch (...)
|
||||
return false;
|
||||
}
|
||||
|
||||
void HwndTerminal::_SendKeyEvent(WORD vkey, WORD scanCode, WORD flags, bool keyDown) noexcept
|
||||
void HwndTerminal::_SendKeyEvent(WORD vkey, WORD scanCode, bool keyDown) noexcept
|
||||
try
|
||||
{
|
||||
auto modifiers = getControlKeyState();
|
||||
if (WI_IsFlagSet(flags, ENHANCED_KEY))
|
||||
{
|
||||
modifiers |= ControlKeyStates::EnhancedKey;
|
||||
}
|
||||
_terminal->SendKeyEvent(vkey, scanCode, modifiers, keyDown);
|
||||
const auto flags = getControlKeyState();
|
||||
_terminal->SendKeyEvent(vkey, scanCode, flags, keyDown);
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void HwndTerminal::_SendCharEvent(wchar_t ch, WORD scanCode, WORD flags) noexcept
|
||||
void HwndTerminal::_SendCharEvent(wchar_t ch, WORD scanCode) noexcept
|
||||
try
|
||||
{
|
||||
if (_terminal->IsSelectionActive())
|
||||
{
|
||||
_ClearSelection();
|
||||
_terminal->ClearSelection();
|
||||
if (ch == UNICODE_ESC)
|
||||
{
|
||||
// ESC should clear any selection before it triggers input.
|
||||
@@ -699,25 +589,21 @@ try
|
||||
return;
|
||||
}
|
||||
|
||||
auto modifiers = getControlKeyState();
|
||||
if (WI_IsFlagSet(flags, ENHANCED_KEY))
|
||||
{
|
||||
modifiers |= ControlKeyStates::EnhancedKey;
|
||||
}
|
||||
_terminal->SendCharEvent(ch, scanCode, modifiers);
|
||||
const auto flags = getControlKeyState();
|
||||
_terminal->SendCharEvent(ch, scanCode, flags);
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, WORD flags, bool keyDown)
|
||||
void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, bool keyDown)
|
||||
{
|
||||
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
publicTerminal->_SendKeyEvent(vkey, scanCode, flags, keyDown);
|
||||
publicTerminal->_SendKeyEvent(vkey, scanCode, keyDown);
|
||||
}
|
||||
|
||||
void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode, WORD flags)
|
||||
void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode)
|
||||
{
|
||||
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
publicTerminal->_SendCharEvent(ch, scanCode, flags);
|
||||
publicTerminal->_SendCharEvent(ch, scanCode);
|
||||
}
|
||||
|
||||
void _stdcall DestroyTerminal(void* terminal)
|
||||
@@ -735,7 +621,6 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font
|
||||
|
||||
publicTerminal->_terminal->SetDefaultForeground(theme.DefaultForeground);
|
||||
publicTerminal->_terminal->SetDefaultBackground(theme.DefaultBackground);
|
||||
publicTerminal->_renderEngine->SetSelectionBackground(theme.DefaultSelectionBackground, theme.SelectionBackgroundAlpha);
|
||||
|
||||
// Set the font colors
|
||||
for (size_t tableIndex = 0; tableIndex < 16; tableIndex++)
|
||||
@@ -747,7 +632,7 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font
|
||||
|
||||
publicTerminal->_terminal->SetCursorStyle(theme.CursorStyle);
|
||||
|
||||
publicTerminal->_desiredFont = { fontFamily, 0, DEFAULT_FONT_WEIGHT, { 0, fontSize }, CP_UTF8 };
|
||||
publicTerminal->_desiredFont = { fontFamily, 0, 10, { 0, fontSize }, CP_UTF8 };
|
||||
publicTerminal->_UpdateFont(newDpi);
|
||||
|
||||
// When the font changes the terminal dimensions need to be recalculated since the available row and column
|
||||
|
||||
@@ -12,13 +12,10 @@
|
||||
|
||||
using namespace Microsoft::Console::VirtualTerminal;
|
||||
|
||||
// Keep in sync with TerminalTheme.cs
|
||||
typedef struct _TerminalTheme
|
||||
{
|
||||
COLORREF DefaultBackground;
|
||||
COLORREF DefaultForeground;
|
||||
COLORREF DefaultSelectionBackground;
|
||||
float SelectionBackgroundAlpha;
|
||||
DispatchTypes::CursorStyle CursorStyle;
|
||||
COLORREF ColorTable[16];
|
||||
} TerminalTheme, *LPTerminalTheme;
|
||||
@@ -37,8 +34,8 @@ __declspec(dllexport) bool _stdcall TerminalIsSelectionActive(void* terminal);
|
||||
__declspec(dllexport) void _stdcall DestroyTerminal(void* terminal);
|
||||
__declspec(dllexport) void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
|
||||
__declspec(dllexport) void _stdcall TerminalRegisterWriteCallback(void* terminal, const void __stdcall callback(wchar_t*));
|
||||
__declspec(dllexport) void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, WORD flags, bool keyDown);
|
||||
__declspec(dllexport) void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD flags, WORD scanCode);
|
||||
__declspec(dllexport) void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, bool keyDown);
|
||||
__declspec(dllexport) void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode);
|
||||
__declspec(dllexport) void _stdcall TerminalBlinkCursor(void* terminal);
|
||||
__declspec(dllexport) void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
|
||||
__declspec(dllexport) void _stdcall TerminalSetFocus(void* terminal);
|
||||
@@ -54,10 +51,9 @@ public:
|
||||
HwndTerminal(HwndTerminal&&) = default;
|
||||
HwndTerminal& operator=(const HwndTerminal&) = default;
|
||||
HwndTerminal& operator=(HwndTerminal&&) = default;
|
||||
~HwndTerminal();
|
||||
~HwndTerminal() = default;
|
||||
|
||||
HRESULT Initialize();
|
||||
void Teardown() noexcept;
|
||||
void SendOutput(std::wstring_view data);
|
||||
HRESULT Refresh(const SIZE windowSize, _Out_ COORD* dimensions);
|
||||
void RegisterScrollCallback(std::function<void(int, int, int)> callback);
|
||||
@@ -96,8 +92,8 @@ private:
|
||||
friend void _stdcall TerminalClearSelection(void* terminal);
|
||||
friend const wchar_t* _stdcall TerminalGetSelection(void* terminal);
|
||||
friend bool _stdcall TerminalIsSelectionActive(void* terminal);
|
||||
friend void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, WORD flags, bool keyDown);
|
||||
friend void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode, WORD flags);
|
||||
friend void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, bool keyDown);
|
||||
friend void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode);
|
||||
friend void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
|
||||
friend void _stdcall TerminalBlinkCursor(void* terminal);
|
||||
friend void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
|
||||
@@ -116,13 +112,11 @@ private:
|
||||
HRESULT _MoveSelection(LPARAM lParam) noexcept;
|
||||
IRawElementProviderSimple* _GetUiaProvider() noexcept;
|
||||
|
||||
void _ClearSelection() noexcept;
|
||||
|
||||
bool _CanSendVTMouseInput() const noexcept;
|
||||
bool _SendMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept;
|
||||
|
||||
void _SendKeyEvent(WORD vkey, WORD scanCode, WORD flags, bool keyDown) noexcept;
|
||||
void _SendCharEvent(wchar_t ch, WORD scanCode, WORD flags) noexcept;
|
||||
void _SendKeyEvent(WORD vkey, WORD scanCode, bool keyDown) noexcept;
|
||||
void _SendCharEvent(wchar_t ch, WORD scanCode) noexcept;
|
||||
|
||||
// Inherited via IControlAccessibilityInfo
|
||||
COORD GetFontSize() const override;
|
||||
|
||||
@@ -911,7 +911,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - Implements the Alt handler (per GH#6421)
|
||||
// Return value:
|
||||
// - whether the key was handled
|
||||
bool AppLogic::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down)
|
||||
bool AppLogic::OnDirectKeyEvent(const uint32_t vkey, const bool down)
|
||||
{
|
||||
if (_root)
|
||||
{
|
||||
@@ -922,7 +922,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (auto keyListener{ focusedObject.try_as<IDirectKeyListener>() })
|
||||
{
|
||||
if (keyListener.OnDirectKeyEvent(vkey, scanCode, down))
|
||||
if (keyListener.OnDirectKeyEvent(vkey, down))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
hstring Title();
|
||||
void TitlebarClicked();
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const bool down);
|
||||
|
||||
void WindowCloseButtonClicked();
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ const Profile* CascadiaSettings::FindProfile(GUID profileGuid) const noexcept
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - an iterable collection of all of our Profiles.
|
||||
gsl::span<const Profile> CascadiaSettings::GetProfiles() const noexcept
|
||||
std::basic_string_view<Profile> CascadiaSettings::GetProfiles() const noexcept
|
||||
{
|
||||
return { &_profiles[0], _profiles.size() };
|
||||
}
|
||||
@@ -226,12 +226,9 @@ void CascadiaSettings::_ValidateProfilesHaveGuid()
|
||||
void CascadiaSettings::_ResolveDefaultProfile()
|
||||
{
|
||||
const auto unparsedDefaultProfile{ GlobalSettings().UnparsedDefaultProfile() };
|
||||
if (unparsedDefaultProfile)
|
||||
{
|
||||
auto maybeParsedDefaultProfile{ _GetProfileGuidByName(*unparsedDefaultProfile) };
|
||||
auto defaultProfileGuid{ Utils::CoalesceOptionals(maybeParsedDefaultProfile, GUID{}) };
|
||||
GlobalSettings().DefaultProfile(defaultProfileGuid);
|
||||
}
|
||||
auto maybeParsedDefaultProfile{ _GetProfileGuidByName(unparsedDefaultProfile) };
|
||||
auto defaultProfileGuid{ Utils::CoalesceOptionals(maybeParsedDefaultProfile, GUID{}) };
|
||||
GlobalSettings().DefaultProfile(defaultProfileGuid);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
|
||||
GlobalAppSettings& GlobalSettings();
|
||||
|
||||
gsl::span<const Profile> GetProfiles() const noexcept;
|
||||
std::basic_string_view<Profile> GetProfiles() const noexcept;
|
||||
|
||||
winrt::TerminalApp::AppKeyBindings GetKeybindings() const noexcept;
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadAll()
|
||||
// GH 3588, we need this below to know if the user chose something that wasn't our default.
|
||||
// Collect it up here in case it gets modified by any of the other layers between now and when
|
||||
// the user's preferences are loaded and layered.
|
||||
const auto hardcodedDefaultGuid = resultPtr->GlobalSettings().DefaultProfile();
|
||||
const auto hardcodedDefaultGuid = resultPtr->GlobalSettings().UnparsedDefaultProfile();
|
||||
|
||||
std::optional<std::string> fileData = _ReadUserSettings();
|
||||
const bool foundFile = fileData.has_value();
|
||||
@@ -141,11 +141,12 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadAll()
|
||||
// is a lot of computation we can skip if no one cares.
|
||||
if (TraceLoggingProviderEnabled(g_hTerminalAppProvider, 0, MICROSOFT_KEYWORD_MEASURES))
|
||||
{
|
||||
const auto hardcodedDefaultGuidAsGuid = Utils::GuidFromString(hardcodedDefaultGuid);
|
||||
const auto guid = resultPtr->GlobalSettings().DefaultProfile();
|
||||
|
||||
// Compare to the defaults.json one that we set on install.
|
||||
// If it's different, log what the user chose.
|
||||
if (hardcodedDefaultGuid != guid)
|
||||
if (hardcodedDefaultGuidAsGuid != guid)
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
|
||||
@@ -228,7 +229,6 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadDefaults()
|
||||
// them from a file (and the potential that could fail)
|
||||
resultPtr->_ParseJsonString(DefaultJson, true);
|
||||
resultPtr->LayerJson(resultPtr->_defaultSettings);
|
||||
resultPtr->_ResolveDefaultProfile();
|
||||
|
||||
return resultPtr;
|
||||
}
|
||||
|
||||
@@ -114,11 +114,11 @@
|
||||
</StackPanel.Resources>
|
||||
<Button Padding="5"
|
||||
Click="ClearColorButton_Click"
|
||||
x:Name="ClearColorButton" x:Uid="TabColorClearButton" CornerRadius="2" Content="Reset">
|
||||
x:Name="ClearColorButton" x:Uid="TabColorClearButton" Content="Reset">
|
||||
</Button>
|
||||
<Button Padding="5"
|
||||
Click="ShowColorPickerButton_Click"
|
||||
x:Name="CustomColorButton" x:Uid="TabColorCustomButton" CornerRadius="2" Content="Custom...">
|
||||
x:Name="CustomColorButton" x:Uid="TabColorCustomButton" Content="Custom...">
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
@@ -187,16 +187,14 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
const auto actionAndArgs = command.Action();
|
||||
_dispatch.DoAction(actionAndArgs);
|
||||
_close();
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
|
||||
"CommandPaletteDispatchedAction",
|
||||
TraceLoggingDescription("Event emitted when the user selects an action in the Command Palette"),
|
||||
TraceLoggingUInt32(_searchBox().Text().size(), "SearchTextLength", "Number of characters in the search string"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
|
||||
_close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,17 +271,16 @@ namespace winrt::TerminalApp::implementation
|
||||
};
|
||||
|
||||
// Method Description:
|
||||
// - Produce a list of filtered actions to reflect the current contents of
|
||||
// - Update our list of filtered actions to reflect the current contents of
|
||||
// the input box. For more details on which commands will be displayed,
|
||||
// see `_getWeight`.
|
||||
// Arguments:
|
||||
// - A collection that will receive the filtered actions
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
std::vector<winrt::TerminalApp::Command> CommandPalette::_collectFilteredActions()
|
||||
void CommandPalette::_updateFilteredActions()
|
||||
{
|
||||
std::vector<winrt::TerminalApp::Command> actions;
|
||||
|
||||
_filteredActions.Clear();
|
||||
auto searchText = _searchBox().Text();
|
||||
const bool addAll = searchText.empty();
|
||||
|
||||
@@ -306,10 +303,10 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
for (auto action : sortedCommands)
|
||||
{
|
||||
actions.push_back(action);
|
||||
_filteredActions.Append(action);
|
||||
}
|
||||
|
||||
return actions;
|
||||
return;
|
||||
}
|
||||
|
||||
// Here, there was some filter text.
|
||||
@@ -346,56 +343,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
auto top = heap.top();
|
||||
heap.pop();
|
||||
actions.push_back(top.command);
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update our list of filtered actions to reflect the current contents of
|
||||
// the input box. For more details on which commands will be displayed,
|
||||
// see `_getWeight`.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void CommandPalette::_updateFilteredActions()
|
||||
{
|
||||
auto actions = _collectFilteredActions();
|
||||
|
||||
// Make _filteredActions look identical to actions, using only Insert and Remove.
|
||||
// This allows WinUI to nicely animate the ListView as it changes.
|
||||
for (uint32_t i = 0; i < _filteredActions.Size() && i < actions.size(); i++)
|
||||
{
|
||||
for (uint32_t j = i; j < _filteredActions.Size(); j++)
|
||||
{
|
||||
if (_filteredActions.GetAt(j) == actions[i])
|
||||
{
|
||||
for (uint32_t k = i; k < j; k++)
|
||||
{
|
||||
_filteredActions.RemoveAt(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_filteredActions.GetAt(i) != actions[i])
|
||||
{
|
||||
_filteredActions.InsertAt(i, actions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any extra trailing items from the destination
|
||||
while (_filteredActions.Size() > actions.size())
|
||||
{
|
||||
_filteredActions.RemoveAtEnd();
|
||||
}
|
||||
|
||||
// Add any extra trailing items from the source
|
||||
while (_filteredActions.Size() < actions.size())
|
||||
{
|
||||
_filteredActions.Append(actions[_filteredActions.Size()]);
|
||||
_filteredActions.Append(top.command);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ namespace winrt::TerminalApp::implementation
|
||||
void _selectNextItem(const bool moveDown);
|
||||
|
||||
void _updateFilteredActions();
|
||||
std::vector<winrt::TerminalApp::Command> _collectFilteredActions();
|
||||
static int _getWeight(const winrt::hstring& searchText, const winrt::hstring& name);
|
||||
void _close();
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ using namespace ::Microsoft::Console;
|
||||
using namespace winrt::Microsoft::UI::Xaml::Controls;
|
||||
|
||||
static constexpr std::string_view LegacyKeybindingsKey{ "keybindings" };
|
||||
static constexpr std::string_view ActionsKey{ "actions" };
|
||||
static constexpr std::string_view BindingsKey{ "bindings" };
|
||||
static constexpr std::string_view DefaultProfileKey{ "defaultProfile" };
|
||||
static constexpr std::string_view AlwaysShowTabsKey{ "alwaysShowTabs" };
|
||||
static constexpr std::string_view InitialRowsKey{ "initialRows" };
|
||||
@@ -105,9 +105,9 @@ GUID GlobalAppSettings::DefaultProfile() const
|
||||
return _defaultProfile;
|
||||
}
|
||||
|
||||
std::optional<std::wstring> GlobalAppSettings::UnparsedDefaultProfile() const
|
||||
std::wstring GlobalAppSettings::UnparsedDefaultProfile() const
|
||||
{
|
||||
return _unparsedDefaultProfile;
|
||||
return _unparsedDefaultProfile.value();
|
||||
}
|
||||
|
||||
AppKeyBindings GlobalAppSettings::GetKeybindings() const noexcept
|
||||
@@ -233,7 +233,7 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
|
||||
}
|
||||
};
|
||||
parseBindings(LegacyKeybindingsKey);
|
||||
parseBindings(ActionsKey);
|
||||
parseBindings(BindingsKey);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
// by higher layers in the app.
|
||||
void DefaultProfile(const GUID defaultProfile) noexcept;
|
||||
GUID DefaultProfile() const;
|
||||
std::optional<std::wstring> UnparsedDefaultProfile() const;
|
||||
std::wstring UnparsedDefaultProfile() const;
|
||||
|
||||
GETSET_PROPERTY(int32_t, InitialRows); // default value set in constructor
|
||||
GETSET_PROPERTY(int32_t, InitialCols); // default value set in constructor
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace TerminalApp
|
||||
// If you update this one, please update the one in TerminalControl\TermControl.idl
|
||||
// If you change this interface, please update the guid.
|
||||
// If you press F7 or Alt and get a runtime error, go make sure both copies are the same.
|
||||
[uuid("0ddf4edc-3fda-4dee-97ca-a417ee3dd510")] interface IDirectKeyListener {
|
||||
Boolean OnDirectKeyEvent(UInt32 vkey, UInt8 scanCode, Boolean down);
|
||||
[uuid("339e1a87-5315-4da6-96f0-565549b6472b")] interface IDirectKeyListener {
|
||||
Boolean OnDirectKeyEvent(UInt32 vkey, Boolean down);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,11 @@ static constexpr int MAX_CHORD_PARTS = 4;
|
||||
|
||||
// clang-format off
|
||||
static const std::unordered_map<std::wstring_view, int32_t> vkeyNamePairs {
|
||||
{ L"app" , VK_APPS },
|
||||
{ L"backspace" , VK_BACK },
|
||||
{ L"tab" , VK_TAB },
|
||||
{ L"enter" , VK_RETURN },
|
||||
{ L"esc" , VK_ESCAPE },
|
||||
{ L"escape" , VK_ESCAPE },
|
||||
{ L"menu" , VK_APPS },
|
||||
{ L"space" , VK_SPACE },
|
||||
{ L"pgup" , VK_PRIOR },
|
||||
{ L"pageup" , VK_PRIOR },
|
||||
|
||||
@@ -166,28 +166,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
// if alt is pressed, open a pane
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
|
||||
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
|
||||
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
// Check for DebugTap
|
||||
bool debugTap = page->_settings->GlobalSettings().DebugFeaturesEnabled() &&
|
||||
WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
if (altPressed && !debugTap)
|
||||
{
|
||||
page->_SplitPane(TerminalApp::SplitState::Automatic,
|
||||
TerminalApp::SplitType::Manual,
|
||||
nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
page->_OpenNewTab(nullptr);
|
||||
}
|
||||
page->_OpenNewTab(nullptr);
|
||||
}
|
||||
});
|
||||
_tabView.SelectionChanged({ this, &TerminalPage::_OnTabSelectionChanged });
|
||||
@@ -405,22 +384,25 @@ namespace winrt::TerminalApp::implementation
|
||||
const auto& profile = _settings->GetProfiles()[profileIndex];
|
||||
auto profileMenuItem = WUX::Controls::MenuFlyoutItem{};
|
||||
|
||||
// Add the keyboard shortcuts based on the number of profiles defined
|
||||
// Look for a keychord that is bound to the equivalent
|
||||
// NewTab(ProfileIndex=N) action
|
||||
auto actionAndArgs = winrt::make_self<winrt::TerminalApp::implementation::ActionAndArgs>();
|
||||
actionAndArgs->Action(ShortcutAction::NewTab);
|
||||
auto newTabArgs = winrt::make_self<winrt::TerminalApp::implementation::NewTabArgs>();
|
||||
auto newTerminalArgs = winrt::make_self<winrt::TerminalApp::implementation::NewTerminalArgs>();
|
||||
newTerminalArgs->ProfileIndex(profileIndex);
|
||||
newTabArgs->TerminalArgs(*newTerminalArgs);
|
||||
actionAndArgs->Args(*newTabArgs);
|
||||
auto profileKeyChord{ keyBindings.GetKeyBindingForActionWithArgs(*actionAndArgs) };
|
||||
|
||||
// make sure we find one to display
|
||||
if (profileKeyChord)
|
||||
// add the keyboard shortcuts for the first 9 profiles
|
||||
if (profileIndex < 9)
|
||||
{
|
||||
_SetAcceleratorForMenuItem(profileMenuItem, profileKeyChord);
|
||||
// Look for a keychord that is bound to the equivalent
|
||||
// NewTab(ProfileIndex=N) action
|
||||
auto actionAndArgs = winrt::make_self<winrt::TerminalApp::implementation::ActionAndArgs>();
|
||||
actionAndArgs->Action(ShortcutAction::NewTab);
|
||||
auto newTabArgs = winrt::make_self<winrt::TerminalApp::implementation::NewTabArgs>();
|
||||
auto newTerminalArgs = winrt::make_self<winrt::TerminalApp::implementation::NewTerminalArgs>();
|
||||
newTerminalArgs->ProfileIndex(profileIndex);
|
||||
newTabArgs->TerminalArgs(*newTerminalArgs);
|
||||
actionAndArgs->Args(*newTabArgs);
|
||||
auto profileKeyChord{ keyBindings.GetKeyBindingForActionWithArgs(*actionAndArgs) };
|
||||
|
||||
// make sure we find one to display
|
||||
if (profileKeyChord)
|
||||
{
|
||||
_SetAcceleratorForMenuItem(profileMenuItem, profileKeyChord);
|
||||
}
|
||||
}
|
||||
|
||||
auto profileName = profile.GetName();
|
||||
@@ -1796,7 +1778,7 @@ namespace winrt::TerminalApp::implementation
|
||||
tab->SetFocused(true);
|
||||
|
||||
// Raise an event that our title changed
|
||||
_titleChangeHandlers(*this, tab->GetActiveTitle());
|
||||
_titleChangeHandlers(*this, Title());
|
||||
|
||||
// Raise an event that our titlebar color changed
|
||||
std::optional<Windows::UI::Color> color = tab->GetTabColor();
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
"brightWhite": "#FFFFFF"
|
||||
}
|
||||
],
|
||||
"actions":
|
||||
"keybindings":
|
||||
[
|
||||
// Application-level Keys
|
||||
{ "command": "closeWindow", "keys": "alt+f4" },
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
"foreground": "#839496",
|
||||
"background": "#002B36",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"black": "#002B36",
|
||||
"black": "#073642",
|
||||
"red": "#DC322F",
|
||||
"green": "#859900",
|
||||
"yellow": "#B58900",
|
||||
@@ -195,7 +195,7 @@
|
||||
"purple": "#D33682",
|
||||
"cyan": "#2AA198",
|
||||
"white": "#EEE8D5",
|
||||
"brightBlack": "#073642",
|
||||
"brightBlack": "#002B36",
|
||||
"brightRed": "#CB4B16",
|
||||
"brightGreen": "#586E75",
|
||||
"brightYellow": "#657B83",
|
||||
@@ -209,7 +209,7 @@
|
||||
"foreground": "#657B83",
|
||||
"background": "#FDF6E3",
|
||||
"cursorColor": "#002B36",
|
||||
"black": "#002B36",
|
||||
"black": "#073642",
|
||||
"red": "#DC322F",
|
||||
"green": "#859900",
|
||||
"yellow": "#B58900",
|
||||
@@ -217,7 +217,7 @@
|
||||
"purple": "#D33682",
|
||||
"cyan": "#2AA198",
|
||||
"white": "#EEE8D5",
|
||||
"brightBlack": "#073642",
|
||||
"brightBlack": "#002B36",
|
||||
"brightRed": "#CB4B16",
|
||||
"brightGreen": "#586E75",
|
||||
"brightYellow": "#657B83",
|
||||
@@ -271,7 +271,7 @@
|
||||
"brightWhite": "#EEEEEC"
|
||||
}
|
||||
],
|
||||
"actions":
|
||||
"bindings":
|
||||
[
|
||||
// Application-level Keys
|
||||
{ "command": "closeWindow", "keys": "alt+f4" },
|
||||
|
||||
@@ -28,8 +28,6 @@ BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID /*reserved*/)
|
||||
TraceLoggingUnregister(g_hTerminalConnectionProvider);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
@@ -11,6 +11,6 @@ namespace Microsoft.Terminal.TerminalControl
|
||||
[uuid("65b8b8c5-988f-43ff-aba9-e89368da1598")]
|
||||
interface IMouseWheelListener
|
||||
{
|
||||
Boolean OnMouseWheel(Windows.Foundation.Point coord, Int32 delta, Boolean leftButtonDown, Boolean midButtonDown, Boolean rightButtonDown);
|
||||
Boolean OnMouseWheel(Windows.Foundation.Point coord, Int32 delta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "TermControlAutomationPeer.h"
|
||||
|
||||
using namespace ::Microsoft::Console::Types;
|
||||
using namespace ::Microsoft::Console::VirtualTerminal;
|
||||
using namespace ::Microsoft::Terminal::Core;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Input;
|
||||
@@ -738,11 +737,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
const auto ch = e.Character();
|
||||
const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode);
|
||||
auto modifiers = _GetPressedModifierKeys();
|
||||
if (e.KeyStatus().IsExtendedKey)
|
||||
{
|
||||
modifiers |= ControlKeyStates::EnhancedKey;
|
||||
}
|
||||
const auto modifiers = _GetPressedModifierKeys();
|
||||
const bool handled = _terminal->SendCharEvent(ch, scanCode, modifiers);
|
||||
e.Handled(handled);
|
||||
}
|
||||
@@ -752,7 +747,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// normally. Namely, the keys we're concerned with are F7 down and Alt up.
|
||||
// Return value:
|
||||
// - Whether the key was handled.
|
||||
bool TermControl::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down)
|
||||
bool TermControl::OnDirectKeyEvent(const uint32_t vkey, const bool down)
|
||||
{
|
||||
const auto modifiers{ _GetPressedModifierKeys() };
|
||||
auto handled = false;
|
||||
@@ -760,7 +755,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
{
|
||||
// Manually generate an Alt KeyUp event into the key bindings or terminal.
|
||||
// This is required as part of GH#6421.
|
||||
(void)_TrySendKeyEvent(VK_MENU, scanCode, modifiers, false);
|
||||
// GH#6513 - make sure to set the scancode too, otherwise conpty
|
||||
// will think this is a NUL
|
||||
(void)_TrySendKeyEvent(VK_MENU, LOWORD(MapVirtualKeyW(VK_MENU, MAPVK_VK_TO_VSC)), modifiers, false);
|
||||
handled = true;
|
||||
}
|
||||
else if (vkey == VK_F7 && down)
|
||||
@@ -782,7 +779,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
if (!handled)
|
||||
{
|
||||
// _TrySendKeyEvent pretends it didn't handle F7 for some unknown reason.
|
||||
(void)_TrySendKeyEvent(VK_F7, scanCode, modifiers, true);
|
||||
(void)_TrySendKeyEvent(VK_F7, 0, modifiers, true);
|
||||
// GH#6438: Note that we're _not_ sending the key up here - that'll
|
||||
// get passed through XAML to our KeyUp handler normally.
|
||||
handled = true;
|
||||
@@ -829,13 +826,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
return;
|
||||
}
|
||||
|
||||
auto modifiers = _GetPressedModifierKeys();
|
||||
const auto modifiers = _GetPressedModifierKeys();
|
||||
const auto vkey = gsl::narrow_cast<WORD>(e.OriginalKey());
|
||||
const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode);
|
||||
if (e.KeyStatus().IsExtendedKey)
|
||||
{
|
||||
modifiers |= ControlKeyStates::EnhancedKey;
|
||||
}
|
||||
|
||||
// Alt-Numpad# input will send us a character once the user releases
|
||||
// Alt, so we should be ignoring the individual keydowns. The character
|
||||
@@ -1008,8 +1001,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
|
||||
const auto modifiers = _GetPressedModifierKeys();
|
||||
const TerminalInput::MouseButtonState state{ props.IsLeftButtonPressed(), props.IsMiddleButtonPressed(), props.IsRightButtonPressed() };
|
||||
return _terminal->SendMouseEvent(terminalPosition, uiButton, modifiers, sWheelDelta, state);
|
||||
return _terminal->SendMouseEvent(terminalPosition, uiButton, modifiers, sWheelDelta);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1333,12 +1325,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
|
||||
const auto point = args.GetCurrentPoint(*this);
|
||||
const auto props = point.Properties();
|
||||
const TerminalInput::MouseButtonState state{ props.IsLeftButtonPressed(), props.IsMiddleButtonPressed(), props.IsRightButtonPressed() };
|
||||
auto result = _DoMouseWheel(point.Position(),
|
||||
ControlKeyStates{ args.KeyModifiers() },
|
||||
point.Properties().MouseWheelDelta(),
|
||||
state);
|
||||
point.Properties().IsLeftButtonPressed());
|
||||
if (result)
|
||||
{
|
||||
args.Handled(true);
|
||||
@@ -1361,7 +1351,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
bool TermControl::_DoMouseWheel(const Windows::Foundation::Point point,
|
||||
const ControlKeyStates modifiers,
|
||||
const int32_t delta,
|
||||
const TerminalInput::MouseButtonState state)
|
||||
const bool isLeftButtonPressed)
|
||||
{
|
||||
if (_CanSendVTMouseInput())
|
||||
{
|
||||
@@ -1373,8 +1363,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
return _terminal->SendMouseEvent(_GetTerminalPosition(point),
|
||||
WM_MOUSEWHEEL,
|
||||
_GetPressedModifierKeys(),
|
||||
::base::saturated_cast<short>(delta),
|
||||
state);
|
||||
::base::saturated_cast<short>(delta));
|
||||
}
|
||||
|
||||
const auto ctrlPressed = modifiers.IsCtrlPressed();
|
||||
@@ -1390,7 +1379,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
_MouseScrollHandler(delta, point, state.isLeftButtonDown);
|
||||
_MouseScrollHandler(delta, point, isLeftButtonPressed);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1404,16 +1393,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// - location: the location of the mouse during this event. This location is
|
||||
// relative to the origin of the control
|
||||
// - delta: the mouse wheel delta that triggered this event.
|
||||
// - state: the state for each of the mouse buttons individually (pressed/unpressed)
|
||||
bool TermControl::OnMouseWheel(const Windows::Foundation::Point location,
|
||||
const int32_t delta,
|
||||
const bool leftButtonDown,
|
||||
const bool midButtonDown,
|
||||
const bool rightButtonDown)
|
||||
const int32_t delta)
|
||||
{
|
||||
const auto modifiers = _GetPressedModifierKeys();
|
||||
TerminalInput::MouseButtonState state{ leftButtonDown, midButtonDown, rightButtonDown };
|
||||
return _DoMouseWheel(location, modifiers, delta, state);
|
||||
return _DoMouseWheel(location, modifiers, delta, false);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -17,11 +17,6 @@
|
||||
#include "SearchBoxControl.h"
|
||||
#include "ThrottledFunc.h"
|
||||
|
||||
namespace Microsoft::Console::VirtualTerminal
|
||||
{
|
||||
struct MouseButtonState;
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
{
|
||||
struct CopyToClipboardEventArgs :
|
||||
@@ -92,9 +87,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
void CreateSearchBoxControl();
|
||||
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const bool down);
|
||||
|
||||
bool OnMouseWheel(const Windows::Foundation::Point location, const int32_t delta, const bool leftButtonDown, const bool midButtonDown, const bool rightButtonDown);
|
||||
bool OnMouseWheel(const Windows::Foundation::Point location, const int32_t delta);
|
||||
|
||||
~TermControl();
|
||||
|
||||
@@ -226,7 +221,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
void _MouseScrollHandler(const double mouseDelta, const Windows::Foundation::Point point, const bool isLeftButtonPressed);
|
||||
void _MouseZoomHandler(const double delta);
|
||||
void _MouseTransparencyHandler(const double delta);
|
||||
bool _DoMouseWheel(const Windows::Foundation::Point point, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const int32_t delta, const ::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state);
|
||||
bool _DoMouseWheel(const Windows::Foundation::Point point, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const int32_t delta, const bool isLeftButtonPressed);
|
||||
|
||||
bool _CapturePointer(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
bool _ReleasePointerCapture(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
|
||||
@@ -14,8 +14,8 @@ namespace Microsoft.Terminal.TerminalControl
|
||||
// If you update this one, please update TerminalApp\IDirectKeyListener.idl.
|
||||
// If you change this interface, please update the guid.
|
||||
// If you press F7 or Alt and get a runtime error, go make sure both copies are the same.
|
||||
[uuid("0ddf4edc-3fda-4dee-97ca-a417ee3dd510")] interface IDirectKeyListener {
|
||||
Boolean OnDirectKeyEvent(UInt32 vkey, UInt8 scanCode, Boolean down);
|
||||
[uuid("339e1a87-5315-4da6-96f0-565549b6472b")] interface IDirectKeyListener {
|
||||
Boolean OnDirectKeyEvent(UInt32 vkey, Boolean down);
|
||||
}
|
||||
|
||||
runtimeclass CopyToClipboardEventArgs
|
||||
|
||||
@@ -39,7 +39,6 @@ namespace Microsoft::Terminal::Core
|
||||
virtual bool SetColorTableEntry(const size_t tableIndex, const DWORD color) noexcept = 0;
|
||||
|
||||
virtual bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) noexcept = 0;
|
||||
virtual bool SetCursorColor(const DWORD color) noexcept = 0;
|
||||
|
||||
virtual bool SetDefaultForeground(const DWORD color) noexcept = 0;
|
||||
virtual bool SetDefaultBackground(const DWORD color) noexcept = 0;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Microsoft::Terminal::Core
|
||||
ITerminalInput& operator=(ITerminalInput&&) = default;
|
||||
|
||||
virtual bool SendKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates states, const bool keyDown) = 0;
|
||||
virtual bool SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state) = 0;
|
||||
virtual bool SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta) = 0;
|
||||
virtual bool SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) = 0;
|
||||
|
||||
// void SendMouseEvent(uint row, uint col, KeyModifiers modifiers);
|
||||
|
||||
@@ -430,20 +430,8 @@ bool Terminal::SendKeyEvent(const WORD vkey,
|
||||
|
||||
_StoreKeyEvent(vkey, scanCode);
|
||||
|
||||
// As a Terminal we're mostly interested in getting key events from physical hardware (mouse & keyboard).
|
||||
// We're thus ignoring events whose values are outside the valid range and unlikely to be generated by the current keyboard.
|
||||
// It's very likely that a proper followup character event will be sent to us.
|
||||
// This prominently happens using AutoHotKey's keyboard remapping feature,
|
||||
// which sends input events whose vkey is 0xff and scanCode is 0.
|
||||
// We need to check for this early, as _CharacterFromKeyEvent() always returns 0 for such invalid values,
|
||||
// making us believe that this is an actual non-character input, while it usually isn't.
|
||||
// GH#7064
|
||||
if (vkey == 0 || vkey >= 0xff || scanCode == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto isAltOnlyPressed = states.IsAltPressed() && !states.IsCtrlPressed();
|
||||
const auto isSuppressedAltGrAlias = !_altGrAliasing && states.IsAltPressed() && states.IsCtrlPressed();
|
||||
|
||||
// DON'T manually handle Alt+Space - the system will use this to bring up
|
||||
// the system menu for restore, min/maximize, size, move, close.
|
||||
@@ -463,7 +451,6 @@ bool Terminal::SendKeyEvent(const WORD vkey,
|
||||
// as TerminalInput::HandleKey will then fall back to using the vkey which
|
||||
// is the underlying ASCII character (e.g. A-Z) on the keyboard in our case.
|
||||
// See GH#5525/GH#6211 for more details
|
||||
const auto isSuppressedAltGrAlias = !_altGrAliasing && states.IsAltPressed() && states.IsCtrlPressed() && !states.IsAltGrPressed();
|
||||
const auto ch = isSuppressedAltGrAlias ? UNICODE_NULL : _CharacterFromKeyEvent(vkey, scanCode, states);
|
||||
|
||||
// Delegate it to the character event handler if this key event can be
|
||||
@@ -497,16 +484,16 @@ bool Terminal::SendKeyEvent(const WORD vkey,
|
||||
// Return Value:
|
||||
// - true if we translated the key event, and it should not be processed any further.
|
||||
// - false if we did not translate the key, and it should be processed into a character.
|
||||
bool Terminal::SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const TerminalInput::MouseButtonState state)
|
||||
bool Terminal::SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta)
|
||||
{
|
||||
// GH#6401: VT applications should be able to receive mouse events from outside the
|
||||
// terminal buffer. This is likely to happen when the user drags the cursor offscreen.
|
||||
// We shouldn't throw away perfectly good events when they're offscreen, so we just
|
||||
// clamp them to be within the range [(0, 0), (W, H)].
|
||||
#pragma warning(suppress : 26496) // analysis can't tell we're assigning through a reference below
|
||||
auto clampedPos{ viewportPos };
|
||||
_mutableViewport.ToOrigin().Clamp(clampedPos);
|
||||
return _terminalInput->HandleMouse(clampedPos, uiButton, GET_KEYSTATE_WPARAM(states.Value()), wheelDelta, state);
|
||||
// viewportPos must be within the dimensions of the viewport
|
||||
const auto viewportDimensions = _mutableViewport.Dimensions();
|
||||
if (viewportPos.X < 0 || viewportPos.X >= viewportDimensions.X || viewportPos.Y < 0 || viewportPos.Y >= viewportDimensions.Y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _terminalInput->HandleMouse(viewportPos, uiButton, GET_KEYSTATE_WPARAM(states.Value()), wheelDelta);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -830,12 +817,8 @@ void Terminal::_AdjustCursorPosition(const COORD proposedPosition)
|
||||
}
|
||||
}
|
||||
|
||||
// If the viewport moved, or we circled the buffer, we might need to update
|
||||
// our _scrollOffset
|
||||
if (updatedViewport || newRows != 0)
|
||||
if (updatedViewport)
|
||||
{
|
||||
const auto oldScrollOffset = _scrollOffset;
|
||||
|
||||
// scroll if...
|
||||
// - no selection is active
|
||||
// - viewport is already at the bottom
|
||||
@@ -843,18 +826,6 @@ void Terminal::_AdjustCursorPosition(const COORD proposedPosition)
|
||||
|
||||
_scrollOffset = scrollToOutput ? 0 : _scrollOffset + scrollAmount + newRows;
|
||||
|
||||
// Clamp the range to make sure that we don't scroll way off the top of the buffer
|
||||
_scrollOffset = std::clamp(_scrollOffset,
|
||||
0,
|
||||
_buffer->GetSize().Height() - _mutableViewport.Height());
|
||||
|
||||
// If the new scroll offset is different, then we'll still want to raise a scroll event
|
||||
updatedViewport = updatedViewport || (oldScrollOffset != _scrollOffset);
|
||||
}
|
||||
|
||||
// If the viewport moved, then send a scrolling notification.
|
||||
if (updatedViewport)
|
||||
{
|
||||
_NotifyScrollEvent();
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,6 @@ public:
|
||||
bool SetWindowTitle(std::wstring_view title) noexcept override;
|
||||
bool SetColorTableEntry(const size_t tableIndex, const COLORREF color) noexcept override;
|
||||
bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) noexcept override;
|
||||
bool SetCursorColor(const COLORREF color) noexcept override;
|
||||
bool SetDefaultForeground(const COLORREF color) noexcept override;
|
||||
bool SetDefaultBackground(const COLORREF color) noexcept override;
|
||||
|
||||
@@ -116,7 +115,7 @@ public:
|
||||
#pragma region ITerminalInput
|
||||
// These methods are defined in Terminal.cpp
|
||||
bool SendKeyEvent(const WORD vkey, const WORD scanCode, const Microsoft::Terminal::Core::ControlKeyStates states, const bool keyDown) override;
|
||||
bool SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state) override;
|
||||
bool SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta) override;
|
||||
bool SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) override;
|
||||
|
||||
[[nodiscard]] HRESULT UserResize(const COORD viewportSize) noexcept override;
|
||||
|
||||
@@ -64,14 +64,6 @@ COORD Terminal::GetCursorPosition() noexcept
|
||||
return newPos;
|
||||
}
|
||||
|
||||
bool Terminal::SetCursorColor(const COLORREF color) noexcept
|
||||
try
|
||||
{
|
||||
_buffer->GetCursor().SetColor(color);
|
||||
return true;
|
||||
}
|
||||
CATCH_LOG_RETURN_FALSE()
|
||||
|
||||
// Method Description:
|
||||
// - Moves the cursor down one line, and possibly also to the leftmost column.
|
||||
// Arguments:
|
||||
|
||||
@@ -146,13 +146,6 @@ try
|
||||
}
|
||||
CATCH_LOG_RETURN_FALSE()
|
||||
|
||||
bool TerminalDispatch::SetCursorColor(const DWORD color) noexcept
|
||||
try
|
||||
{
|
||||
return _terminalApi.SetCursorColor(color);
|
||||
}
|
||||
CATCH_LOG_RETURN_FALSE()
|
||||
|
||||
bool TerminalDispatch::SetClipboard(std::wstring_view content) noexcept
|
||||
try
|
||||
{
|
||||
@@ -361,12 +354,12 @@ bool TerminalDispatch::EnableAlternateScroll(const bool enabled) noexcept
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TerminalDispatch::SetPrivateModes(const gsl::span<const DispatchTypes::PrivateModeParams> params) noexcept
|
||||
bool TerminalDispatch::SetPrivateModes(const std::basic_string_view<DispatchTypes::PrivateModeParams> params) noexcept
|
||||
{
|
||||
return _SetResetPrivateModes(params, true);
|
||||
}
|
||||
|
||||
bool TerminalDispatch::ResetPrivateModes(const gsl::span<const DispatchTypes::PrivateModeParams> params) noexcept
|
||||
bool TerminalDispatch::ResetPrivateModes(const std::basic_string_view<DispatchTypes::PrivateModeParams> params) noexcept
|
||||
{
|
||||
return _SetResetPrivateModes(params, false);
|
||||
}
|
||||
@@ -381,7 +374,7 @@ bool TerminalDispatch::ResetPrivateModes(const gsl::span<const DispatchTypes::Pr
|
||||
// - enable - True for set, false for unset.
|
||||
// Return Value:
|
||||
// - True if ALL params were handled successfully. False otherwise.
|
||||
bool TerminalDispatch::_SetResetPrivateModes(const gsl::span<const DispatchTypes::PrivateModeParams> params, const bool enable) noexcept
|
||||
bool TerminalDispatch::_SetResetPrivateModes(const std::basic_string_view<DispatchTypes::PrivateModeParams> params, const bool enable) noexcept
|
||||
{
|
||||
// because the user might chain together params we don't support with params we DO support, execute all
|
||||
// params in the sequence, and only return failure if we failed at least one of them
|
||||
|
||||
@@ -13,7 +13,7 @@ public:
|
||||
void Print(const wchar_t wchPrintable) noexcept override;
|
||||
void PrintString(const std::wstring_view string) noexcept override;
|
||||
|
||||
bool SetGraphicsRendition(const gsl::span<const ::Microsoft::Console::VirtualTerminal::DispatchTypes::GraphicsOptions> options) noexcept override;
|
||||
bool SetGraphicsRendition(const std::basic_string_view<::Microsoft::Console::VirtualTerminal::DispatchTypes::GraphicsOptions> options) noexcept override;
|
||||
|
||||
bool CursorPosition(const size_t line,
|
||||
const size_t column) noexcept override; // CUP
|
||||
@@ -35,7 +35,6 @@ public:
|
||||
|
||||
bool SetColorTableEntry(const size_t tableIndex, const DWORD color) noexcept override;
|
||||
bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) noexcept override;
|
||||
bool SetCursorColor(const DWORD color) noexcept override;
|
||||
|
||||
bool SetClipboard(std::wstring_view content) noexcept override;
|
||||
|
||||
@@ -60,16 +59,16 @@ public:
|
||||
bool EnableAnyEventMouseMode(const bool enabled) noexcept override; // ?1003
|
||||
bool EnableAlternateScroll(const bool enabled) noexcept override; // ?1007
|
||||
|
||||
bool SetPrivateModes(const gsl::span<const ::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams> /*params*/) noexcept override; // DECSET
|
||||
bool ResetPrivateModes(const gsl::span<const ::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams> /*params*/) noexcept override; // DECRST
|
||||
bool SetPrivateModes(const std::basic_string_view<::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams> /*params*/) noexcept override; // DECSET
|
||||
bool ResetPrivateModes(const std::basic_string_view<::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams> /*params*/) noexcept override; // DECRST
|
||||
|
||||
private:
|
||||
::Microsoft::Terminal::Core::ITerminalApi& _terminalApi;
|
||||
|
||||
size_t _SetRgbColorsHelper(const gsl::span<const ::Microsoft::Console::VirtualTerminal::DispatchTypes::GraphicsOptions> options,
|
||||
size_t _SetRgbColorsHelper(const std::basic_string_view<::Microsoft::Console::VirtualTerminal::DispatchTypes::GraphicsOptions> options,
|
||||
TextAttribute& attr,
|
||||
const bool isForeground) noexcept;
|
||||
|
||||
bool _SetResetPrivateModes(const gsl::span<const ::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams> params, const bool enable) noexcept;
|
||||
bool _SetResetPrivateModes(const std::basic_string_view<::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams> params, const bool enable) noexcept;
|
||||
bool _PrivateModeParamsHelper(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams param, const bool enable) noexcept;
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ const BYTE BRIGHT_WHITE = BRIGHT_ATTR | RED_ATTR | GREEN_ATTR | BLUE_ATTR;
|
||||
// - isForeground - Whether or not the parsed color is for the foreground.
|
||||
// Return Value:
|
||||
// - The number of options consumed, not including the initial 38/48.
|
||||
size_t TerminalDispatch::_SetRgbColorsHelper(const gsl::span<const DispatchTypes::GraphicsOptions> options,
|
||||
size_t TerminalDispatch::_SetRgbColorsHelper(const std::basic_string_view<DispatchTypes::GraphicsOptions> options,
|
||||
TextAttribute& attr,
|
||||
const bool isForeground) noexcept
|
||||
{
|
||||
@@ -94,7 +94,7 @@ size_t TerminalDispatch::_SetRgbColorsHelper(const gsl::span<const DispatchTypes
|
||||
// one at a time by setting or removing flags in the font style properties.
|
||||
// Return Value:
|
||||
// - True if handled successfully. False otherwise.
|
||||
bool TerminalDispatch::SetGraphicsRendition(const gsl::span<const DispatchTypes::GraphicsOptions> options) noexcept
|
||||
bool TerminalDispatch::SetGraphicsRendition(const std::basic_string_view<DispatchTypes::GraphicsOptions> options) noexcept
|
||||
{
|
||||
TextAttribute attr = _terminalApi.GetTextAttributes();
|
||||
|
||||
@@ -264,10 +264,10 @@ bool TerminalDispatch::SetGraphicsRendition(const gsl::span<const DispatchTypes:
|
||||
attr.SetIndexedBackground(BRIGHT_WHITE);
|
||||
break;
|
||||
case ForegroundExtended:
|
||||
i += _SetRgbColorsHelper(options.subspan(i + 1), attr, true);
|
||||
i += _SetRgbColorsHelper(options.substr(i + 1), attr, true);
|
||||
break;
|
||||
case BackgroundExtended:
|
||||
i += _SetRgbColorsHelper(options.subspan(i + 1), attr, false);
|
||||
i += _SetRgbColorsHelper(options.substr(i + 1), attr, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ namespace TerminalCoreUnitTests
|
||||
|
||||
TEST_METHOD(AltShiftKey);
|
||||
TEST_METHOD(AltSpace);
|
||||
TEST_METHOD(InvalidKeyEvent);
|
||||
|
||||
void _VerifyExpectedInput(std::wstring& actualInput)
|
||||
{
|
||||
@@ -66,14 +65,4 @@ namespace TerminalCoreUnitTests
|
||||
VERIFY_IS_FALSE(term.SendKeyEvent(L' ', 0, ControlKeyStates::LeftAltPressed, false));
|
||||
VERIFY_IS_FALSE(term.SendCharEvent(L' ', 0, ControlKeyStates::LeftAltPressed));
|
||||
}
|
||||
|
||||
void InputTest::InvalidKeyEvent()
|
||||
{
|
||||
// Certain applications like AutoHotKey and its keyboard remapping feature,
|
||||
// send us key events using SendInput() whose values are outside of the valid range.
|
||||
// We don't want to handle those events as we're probably going to get a proper followup character event.
|
||||
VERIFY_IS_FALSE(term.SendKeyEvent(0, 123, {}, true));
|
||||
VERIFY_IS_FALSE(term.SendKeyEvent(255, 123, {}, true));
|
||||
VERIFY_IS_FALSE(term.SendKeyEvent(123, 0, {}, true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,6 @@ class TerminalCoreUnitTests::TerminalBufferTests final
|
||||
TEST_METHOD(TestWrappingCharByChar);
|
||||
TEST_METHOD(TestWrappingALongString);
|
||||
|
||||
TEST_METHOD(DontSnapToOutputTest);
|
||||
|
||||
TEST_METHOD_SETUP(MethodSetup)
|
||||
{
|
||||
// STEP 1: Set up the Terminal
|
||||
@@ -151,85 +149,3 @@ void TerminalBufferTests::TestWrappingALongString()
|
||||
|
||||
TestUtils::VerifyExpectedString(termTb, TestUtils::Test100CharsString, { 0, 0 });
|
||||
}
|
||||
|
||||
void TerminalBufferTests::DontSnapToOutputTest()
|
||||
{
|
||||
auto& termTb = *term->_buffer;
|
||||
auto& termSm = *term->_stateMachine;
|
||||
const auto initialView = term->GetViewport();
|
||||
|
||||
VERIFY_ARE_EQUAL(0, initialView.Top());
|
||||
VERIFY_ARE_EQUAL(TerminalViewHeight, initialView.BottomExclusive());
|
||||
VERIFY_ARE_EQUAL(0, term->_scrollOffset);
|
||||
|
||||
// -1 so that we don't print the last \n
|
||||
for (int i = 0; i < TerminalViewHeight + 8 - 1; i++)
|
||||
{
|
||||
termSm.ProcessString(L"x\n");
|
||||
}
|
||||
|
||||
const auto secondView = term->GetViewport();
|
||||
|
||||
VERIFY_ARE_EQUAL(8, secondView.Top());
|
||||
VERIFY_ARE_EQUAL(TerminalViewHeight + 8, secondView.BottomExclusive());
|
||||
VERIFY_ARE_EQUAL(0, term->_scrollOffset);
|
||||
|
||||
Log::Comment(L"Scroll up one line");
|
||||
term->_scrollOffset = 1;
|
||||
|
||||
const auto thirdView = term->GetViewport();
|
||||
VERIFY_ARE_EQUAL(7, thirdView.Top());
|
||||
VERIFY_ARE_EQUAL(TerminalViewHeight + 7, thirdView.BottomExclusive());
|
||||
VERIFY_ARE_EQUAL(1, term->_scrollOffset);
|
||||
|
||||
Log::Comment(L"Print a few lines, to see that the viewport stays where it was");
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
termSm.ProcessString(L"x\n");
|
||||
}
|
||||
|
||||
const auto fourthView = term->GetViewport();
|
||||
VERIFY_ARE_EQUAL(7, fourthView.Top());
|
||||
VERIFY_ARE_EQUAL(TerminalViewHeight + 7, fourthView.BottomExclusive());
|
||||
VERIFY_ARE_EQUAL(1 + 8, term->_scrollOffset);
|
||||
|
||||
Log::Comment(L"Print enough lines to get the buffer just about ready to "
|
||||
L"circle (on the next newline)");
|
||||
auto viewBottom = term->_mutableViewport.BottomInclusive();
|
||||
do
|
||||
{
|
||||
termSm.ProcessString(L"x\n");
|
||||
viewBottom = term->_mutableViewport.BottomInclusive();
|
||||
} while (viewBottom < termTb.GetSize().BottomInclusive());
|
||||
|
||||
const auto fifthView = term->GetViewport();
|
||||
VERIFY_ARE_EQUAL(7, fifthView.Top());
|
||||
VERIFY_ARE_EQUAL(TerminalViewHeight + 7, fifthView.BottomExclusive());
|
||||
VERIFY_ARE_EQUAL(TerminalHistoryLength - 7, term->_scrollOffset);
|
||||
|
||||
Log::Comment(L"Print 3 more lines, and see that we stick to where the old "
|
||||
L"rows now are in the buffer (after circling)");
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
termSm.ProcessString(L"x\n");
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"_scrollOffset: %d", term->_scrollOffset));
|
||||
}
|
||||
const auto sixthView = term->GetViewport();
|
||||
VERIFY_ARE_EQUAL(4, sixthView.Top());
|
||||
VERIFY_ARE_EQUAL(TerminalViewHeight + 4, sixthView.BottomExclusive());
|
||||
VERIFY_ARE_EQUAL(TerminalHistoryLength - 4, term->_scrollOffset);
|
||||
|
||||
Log::Comment(L"Print 8 more lines, and see that we're now just stuck at the"
|
||||
L"top of the buffer");
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
termSm.ProcessString(L"x\n");
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"_scrollOffset: %d", term->_scrollOffset));
|
||||
}
|
||||
const auto seventhView = term->GetViewport();
|
||||
VERIFY_ARE_EQUAL(0, seventhView.Top());
|
||||
VERIFY_ARE_EQUAL(TerminalViewHeight, seventhView.BottomExclusive());
|
||||
VERIFY_ARE_EQUAL(TerminalHistoryLength, term->_scrollOffset);
|
||||
}
|
||||
|
||||
@@ -17,10 +17,6 @@ using namespace winrt::Windows::Foundation::Numerics;
|
||||
using namespace ::Microsoft::Console;
|
||||
using namespace ::Microsoft::Console::Types;
|
||||
|
||||
// This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx
|
||||
// "If the high-order bit is 1, the key is down; otherwise, it is up."
|
||||
static constexpr short KeyPressed{ gsl::narrow_cast<short>(0x8000) };
|
||||
|
||||
AppHost::AppHost() noexcept :
|
||||
_app{},
|
||||
_logic{ nullptr }, // don't make one, we're going to take a ref on app's
|
||||
@@ -68,11 +64,11 @@ AppHost::~AppHost()
|
||||
_app = nullptr;
|
||||
}
|
||||
|
||||
bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down)
|
||||
bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const bool down)
|
||||
{
|
||||
if (_logic)
|
||||
{
|
||||
return _logic.OnDirectKeyEvent(vkey, scanCode, down);
|
||||
return _logic.OnDirectKeyEvent(vkey, down);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -409,11 +405,7 @@ void AppHost::_WindowMouseWheeled(const til::point coord, const int32_t delta)
|
||||
|
||||
const til::point offsetPoint = coord - controlOrigin;
|
||||
|
||||
const auto lButtonDown = WI_IsFlagSet(GetKeyState(VK_LBUTTON), KeyPressed);
|
||||
const auto mButtonDown = WI_IsFlagSet(GetKeyState(VK_MBUTTON), KeyPressed);
|
||||
const auto rButtonDown = WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed);
|
||||
|
||||
if (control.OnMouseWheel(offsetPoint, delta, lButtonDown, mButtonDown, rButtonDown))
|
||||
if (control.OnMouseWheel(offsetPoint, delta))
|
||||
{
|
||||
// If the element handled the mouse wheel event, don't
|
||||
// continue to iterate over the remaining controls.
|
||||
|
||||
@@ -17,7 +17,7 @@ public:
|
||||
void AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle);
|
||||
void LastTabClosed(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::LastTabClosedEventArgs& args);
|
||||
void Initialize();
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const bool down);
|
||||
|
||||
private:
|
||||
bool _useNonClientArea;
|
||||
|
||||
@@ -141,7 +141,7 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
|
||||
// been handled we can discard the message before we even translate it.
|
||||
if (_messageIsF7Keypress(message))
|
||||
{
|
||||
if (host.OnDirectKeyEvent(VK_F7, LOBYTE(HIWORD(message.lParam)), true))
|
||||
if (host.OnDirectKeyEvent(VK_F7, true))
|
||||
{
|
||||
// The application consumed the F7. Don't let Xaml get it.
|
||||
continue;
|
||||
@@ -154,7 +154,7 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
|
||||
if (_messageIsAltKeyup(message))
|
||||
{
|
||||
// Let's pass <Alt> to the application
|
||||
if (host.OnDirectKeyEvent(VK_MENU, LOBYTE(HIWORD(message.lParam)), false))
|
||||
if (host.OnDirectKeyEvent(VK_MENU, false))
|
||||
{
|
||||
// The application consumed the Alt. Don't let Xaml get it.
|
||||
continue;
|
||||
|
||||
@@ -62,16 +62,6 @@ namespace Microsoft.Terminal.Wpf
|
||||
/// </summary>
|
||||
WM_CHAR = 0x0102,
|
||||
|
||||
/// <summary>
|
||||
/// The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when a system key is pressed. A system key is F10 or Alt+Something.
|
||||
/// </summary>
|
||||
WM_SYSKEYDOWN = 0x0104,
|
||||
|
||||
/// <summary>
|
||||
/// The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when a system key is released. A system key is F10 or Alt+Something.
|
||||
/// </summary>
|
||||
WM_SYSKEYUP = 0x0105,
|
||||
|
||||
/// <summary>
|
||||
/// The WM_MOUSEMOVE message is posted to a window when the cursor moves. If the mouse is not captured, the message is posted to the window that contains the cursor. Otherwise, the message is posted to the window that has captured the mouse.
|
||||
/// </summary>
|
||||
@@ -225,10 +215,10 @@ namespace Microsoft.Terminal.Wpf
|
||||
public static extern void DestroyTerminal(IntPtr terminal);
|
||||
|
||||
[DllImport("PublicTerminalCore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
|
||||
public static extern void TerminalSendKeyEvent(IntPtr terminal, ushort vkey, ushort scanCode, ushort flags, bool keyDown);
|
||||
public static extern void TerminalSendKeyEvent(IntPtr terminal, ushort vkey, ushort scanCode, bool keyDown);
|
||||
|
||||
[DllImport("PublicTerminalCore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
|
||||
public static extern void TerminalSendCharEvent(IntPtr terminal, char ch, ushort scanCode, ushort flags);
|
||||
public static extern void TerminalSendCharEvent(IntPtr terminal, char ch, ushort scanCode);
|
||||
|
||||
[DllImport("PublicTerminalCore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
|
||||
public static extern void TerminalSetTheme(IntPtr terminal, [MarshalAs(UnmanagedType.Struct)] TerminalTheme theme, string fontFamily, short fontSize, int newDpi);
|
||||
|
||||
@@ -20,20 +20,6 @@ namespace Microsoft.Terminal.Wpf
|
||||
/// </remarks>
|
||||
public class TerminalContainer : HwndHost
|
||||
{
|
||||
private static void UnpackKeyMessage(IntPtr wParam, IntPtr lParam, out ushort vkey, out ushort scanCode, out ushort flags)
|
||||
{
|
||||
ulong scanCodeAndFlags = (((ulong)lParam) & 0xFFFF0000) >> 16;
|
||||
scanCode = (ushort)(scanCodeAndFlags & 0x00FFu);
|
||||
flags = (ushort)(scanCodeAndFlags & 0xFF00u);
|
||||
vkey = (ushort)wParam;
|
||||
}
|
||||
|
||||
private static void UnpackCharMessage(IntPtr wParam, IntPtr lParam, out char character, out ushort scanCode, out ushort flags)
|
||||
{
|
||||
UnpackKeyMessage(wParam, lParam, out ushort vKey, out scanCode, out flags);
|
||||
character = (char)vKey;
|
||||
}
|
||||
|
||||
private ITerminalConnection connection;
|
||||
private IntPtr hwnd;
|
||||
private IntPtr terminal;
|
||||
@@ -138,20 +124,6 @@ namespace Microsoft.Terminal.Wpf
|
||||
this.TriggerResize(this.RenderSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected text from the terminal renderer and clears the selection.
|
||||
/// </summary>
|
||||
/// <returns>The selected text, empty if no text is selected.</returns>
|
||||
internal string GetSelectedText()
|
||||
{
|
||||
if (NativeMethods.TerminalIsSelectionActive(this.terminal))
|
||||
{
|
||||
return NativeMethods.TerminalGetSelection(this.terminal);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers a refresh of the terminal with the given size.
|
||||
/// </summary>
|
||||
@@ -263,35 +235,29 @@ namespace Microsoft.Terminal.Wpf
|
||||
this.Focus();
|
||||
NativeMethods.SetFocus(this.hwnd);
|
||||
break;
|
||||
case NativeMethods.WindowMessage.WM_SYSKEYDOWN: // fallthrough
|
||||
case NativeMethods.WindowMessage.WM_KEYDOWN:
|
||||
{
|
||||
// WM_KEYDOWN lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-keydown
|
||||
NativeMethods.TerminalSetCursorVisible(this.terminal, true);
|
||||
ulong scanCode = (((ulong)lParam) & 0x00FF0000) >> 16;
|
||||
|
||||
UnpackKeyMessage(wParam, lParam, out ushort vkey, out ushort scanCode, out ushort flags);
|
||||
NativeMethods.TerminalSendKeyEvent(this.terminal, vkey, scanCode, flags, true);
|
||||
NativeMethods.TerminalSendKeyEvent(this.terminal, (ushort)wParam, (ushort)scanCode, true);
|
||||
this.blinkTimer?.Start();
|
||||
break;
|
||||
}
|
||||
|
||||
case NativeMethods.WindowMessage.WM_SYSKEYUP: // fallthrough
|
||||
case NativeMethods.WindowMessage.WM_KEYUP:
|
||||
{
|
||||
// WM_KEYUP lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-keyup
|
||||
UnpackKeyMessage(wParam, lParam, out ushort vkey, out ushort scanCode, out ushort flags);
|
||||
NativeMethods.TerminalSendKeyEvent(this.terminal, (ushort)wParam, scanCode, flags, false);
|
||||
ulong scanCode = (((ulong)lParam) & 0x00FF0000) >> 16;
|
||||
NativeMethods.TerminalSendKeyEvent(this.terminal, (ushort)wParam, (ushort)scanCode, false);
|
||||
break;
|
||||
}
|
||||
|
||||
case NativeMethods.WindowMessage.WM_CHAR:
|
||||
{
|
||||
// WM_CHAR lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-char
|
||||
UnpackCharMessage(wParam, lParam, out char character, out ushort scanCode, out ushort flags);
|
||||
NativeMethods.TerminalSendCharEvent(this.terminal, character, scanCode, flags);
|
||||
break;
|
||||
}
|
||||
|
||||
// WM_CHAR lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-char
|
||||
NativeMethods.TerminalSendCharEvent(this.terminal, (char)wParam, (ushort)((uint)lParam >> 16));
|
||||
break;
|
||||
case NativeMethods.WindowMessage.WM_WINDOWPOSCHANGED:
|
||||
var windowpos = (NativeMethods.WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(NativeMethods.WINDOWPOS));
|
||||
if (((NativeMethods.SetWindowPosFlags)windowpos.flags).HasFlag(NativeMethods.SetWindowPosFlags.SWP_NOSIZE))
|
||||
|
||||
@@ -70,15 +70,6 @@ namespace Microsoft.Terminal.Wpf
|
||||
this.termContainer.SetTheme(theme, fontFamily, fontSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected text in the terminal, clearing the selection. Otherwise returns an empty string.
|
||||
/// </summary>
|
||||
/// <returns>Selected text, empty string if no content is selected.</returns>
|
||||
public string GetSelectedText()
|
||||
{
|
||||
return this.termContainer.GetSelectedText();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes the terminal to the specified rows and columns.
|
||||
/// </summary>
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Microsoft.Terminal.Wpf
|
||||
/// <summary>
|
||||
/// Structure for color handling in the terminal.
|
||||
/// </summary>
|
||||
/// <remarks>Keep in sync with HwndTerminal.hpp.</remarks>
|
||||
/// <remarks>Pack = 1 removes the padding added by some compilers/processors for optimization purposes.</remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct TerminalTheme
|
||||
{
|
||||
@@ -66,16 +66,6 @@ namespace Microsoft.Terminal.Wpf
|
||||
/// </summary>
|
||||
public uint DefaultForeground;
|
||||
|
||||
/// <summary>
|
||||
/// The default selection background color of the terminal, represented in Win32 COLORREF format.
|
||||
/// </summary>
|
||||
public uint DefaultSelectionBackground;
|
||||
|
||||
/// <summary>
|
||||
/// The opacity alpha for the selection color of the terminal, must be between 1.0 and 0.0.
|
||||
/// </summary>
|
||||
public float SelectionBackgroundAlpha;
|
||||
|
||||
/// <summary>
|
||||
/// The style of cursor to use in the terminal.
|
||||
/// </summary>
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
<Target Name="CleanUpPrecompForSmallCIAgents"
|
||||
DependsOnTargets="_ComputePrecompToCleanUp"
|
||||
AfterTargets="AfterBuild"
|
||||
Condition="'$(AGENT_ID)' != '' and !$(ProjectName.Contains('TerminalApp'))">
|
||||
Condition="'$(AGENT_ID)' != ''">
|
||||
<!-- We just need to keep *TerminalApp*'s PCHs because they get rebuilt more often. -->
|
||||
<Delete Files="@(_PCHFileToCleanWithTimestamp)"/>
|
||||
<Touch Files="@(_PCHFileToCleanWithTimestamp)" Time="%(LastWriteTime)" AlwaysCreate="true" />
|
||||
|
||||
@@ -22,20 +22,12 @@
|
||||
at the winmd we build to generate type info.
|
||||
In general, cppwinrt projects all want this.
|
||||
-->
|
||||
<PropertyGroup Condition="'$(OpenConsoleUniversalApp)'=='true'">
|
||||
<PropertyGroup Condition="'$(OpenConsoleUniversalApp)'!='false'">
|
||||
<MinimalCoreWin>true</MinimalCoreWin>
|
||||
<AppContainerApplication>true</AppContainerApplication>
|
||||
<WindowsStoreApp>true</WindowsStoreApp>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(OpenConsoleUniversalApp)'!='true'">
|
||||
<!-- Some of our projects include the cppwinrt build options to
|
||||
just build cppwinrt things, but don't want the store bits
|
||||
in full swing. MinimalCoreWin != false means "don't let me
|
||||
use win32 APIs"
|
||||
-->
|
||||
<MinimalCoreWin>false</MinimalCoreWin>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- This is magic that tells msbuild to link against the Desktop platform
|
||||
instead of the App platform. This you definitely want, because we're not
|
||||
|
||||
@@ -208,12 +208,12 @@ public:
|
||||
size_t& written) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT WriteConsoleInputAImpl(InputBuffer& context,
|
||||
const gsl::span<const INPUT_RECORD> buffer,
|
||||
const std::basic_string_view<INPUT_RECORD> buffer,
|
||||
size_t& written,
|
||||
const bool append) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT WriteConsoleInputWImpl(InputBuffer& context,
|
||||
const gsl::span<const INPUT_RECORD> buffer,
|
||||
const std::basic_string_view<INPUT_RECORD> buffer,
|
||||
size_t& written,
|
||||
const bool append) noexcept override;
|
||||
|
||||
@@ -228,7 +228,7 @@ public:
|
||||
Microsoft::Console::Types::Viewport& writtenRectangle) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT WriteConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext,
|
||||
const gsl::span<const WORD> attrs,
|
||||
const std::basic_string_view<WORD> attrs,
|
||||
const COORD target,
|
||||
size_t& used) noexcept override;
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region)
|
||||
// Return Value:
|
||||
// - S_OK, E_INVALIDARG or similar HRESULT error.
|
||||
[[nodiscard]] HRESULT ApiRoutines::WriteConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext,
|
||||
const gsl::span<const WORD> attrs,
|
||||
const std::basic_string_view<WORD> attrs,
|
||||
const COORD target,
|
||||
size_t& used) noexcept
|
||||
{
|
||||
|
||||
@@ -149,7 +149,7 @@ std::unordered_map<std::wstring,
|
||||
|
||||
if (target.has_value() && target->size() > 0)
|
||||
{
|
||||
til::at(*target, 0) = UNICODE_NULL;
|
||||
gsl::at(*target, 0) = UNICODE_NULL;
|
||||
}
|
||||
|
||||
std::wstring exeNameString(exeName);
|
||||
@@ -211,7 +211,7 @@ std::unordered_map<std::wstring,
|
||||
{
|
||||
if (target.size() > 0)
|
||||
{
|
||||
til::at(target, 0) = ANSI_NULL;
|
||||
gsl::at(target, 0) = ANSI_NULL;
|
||||
}
|
||||
|
||||
LockConsole();
|
||||
@@ -451,7 +451,7 @@ void Alias::s_ClearCmdExeAliases()
|
||||
|
||||
if (aliasBuffer.has_value() && aliasBuffer->size() > 0)
|
||||
{
|
||||
til::at(*aliasBuffer, 0) = UNICODE_NULL;
|
||||
gsl::at(*aliasBuffer, 0) = UNICODE_NULL;
|
||||
}
|
||||
|
||||
std::wstring exeNameString(exeName);
|
||||
@@ -543,7 +543,7 @@ void Alias::s_ClearCmdExeAliases()
|
||||
{
|
||||
if (alias.size() > 0)
|
||||
{
|
||||
til::at(alias, 0) = '\0';
|
||||
gsl::at(alias, 0) = '\0';
|
||||
}
|
||||
|
||||
LockConsole();
|
||||
@@ -698,7 +698,7 @@ void Alias::s_ClearCmdExeAliases()
|
||||
writtenOrNeeded = 0;
|
||||
if (aliasExesBuffer.has_value() && aliasExesBuffer->size() > 0)
|
||||
{
|
||||
til::at(*aliasExesBuffer, 0) = UNICODE_NULL;
|
||||
gsl::at(*aliasExesBuffer, 0) = UNICODE_NULL;
|
||||
}
|
||||
|
||||
LPWSTR AliasExesBufferPtrW = aliasExesBuffer.has_value() ? aliasExesBuffer->data() : nullptr;
|
||||
@@ -761,7 +761,7 @@ void Alias::s_ClearCmdExeAliases()
|
||||
{
|
||||
if (aliasExes.size() > 0)
|
||||
{
|
||||
til::at(aliasExes, 0) = '\0';
|
||||
gsl::at(aliasExes, 0) = '\0';
|
||||
}
|
||||
|
||||
LockConsole();
|
||||
|
||||
@@ -112,7 +112,7 @@ void ConversionAreaInfo::SetAttributes(const TextAttribute& attr)
|
||||
void ConversionAreaInfo::WriteText(const std::vector<OutputCell>& text,
|
||||
const SHORT column)
|
||||
{
|
||||
gsl::span<const OutputCell> view(text.data(), text.size());
|
||||
std::basic_string_view<OutputCell> view(text.data(), text.size());
|
||||
_screenBuffer->Write(view, { column, 0 });
|
||||
}
|
||||
|
||||
|
||||
@@ -57,15 +57,15 @@ void ConsoleImeInfo::RedrawCompMessage()
|
||||
// - attributes - Encoded attributes including the cursor position and the color index (to the array)
|
||||
// - colorArray - An array of colors to use for the text
|
||||
void ConsoleImeInfo::WriteCompMessage(const std::wstring_view text,
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray)
|
||||
const std::basic_string_view<BYTE> attributes,
|
||||
const std::basic_string_view<WORD> colorArray)
|
||||
{
|
||||
ClearAllAreas();
|
||||
|
||||
// Save copies of the composition message in case we need to redraw it as things scroll/resize
|
||||
_text = text;
|
||||
_attributes.assign(attributes.begin(), attributes.end());
|
||||
_colorArray.assign(colorArray.begin(), colorArray.end());
|
||||
_attributes = attributes;
|
||||
_colorArray = colorArray;
|
||||
|
||||
_WriteUndeterminedChars(text, attributes, colorArray);
|
||||
}
|
||||
@@ -177,8 +177,8 @@ void ConsoleImeInfo::ClearAllAreas()
|
||||
// Return Value:
|
||||
// - TextAttribute object with color and cursor and line drawing data.
|
||||
TextAttribute ConsoleImeInfo::s_RetrieveAttributeAt(const size_t pos,
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray)
|
||||
const std::basic_string_view<BYTE> attributes,
|
||||
const std::basic_string_view<WORD> colorArray)
|
||||
{
|
||||
// Encoded attribute is the shorthand information passed from the IME
|
||||
// that contains a cursor position packed in along with which color in the
|
||||
@@ -214,8 +214,8 @@ TextAttribute ConsoleImeInfo::s_RetrieveAttributeAt(const size_t pos,
|
||||
// Return Value:
|
||||
// - Vector of OutputCells where each one represents one cell of the output buffer.
|
||||
std::vector<OutputCell> ConsoleImeInfo::s_ConvertToCells(const std::wstring_view text,
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray)
|
||||
const std::basic_string_view<BYTE> attributes,
|
||||
const std::basic_string_view<WORD> colorArray)
|
||||
{
|
||||
std::vector<OutputCell> cells;
|
||||
|
||||
@@ -389,8 +389,8 @@ std::vector<OutputCell>::const_iterator ConsoleImeInfo::_WriteConversionArea(con
|
||||
// each text character. This view must be the same size as the text view.
|
||||
// - colorArray - 8 colors to be used to format the text for display
|
||||
void ConsoleImeInfo::_WriteUndeterminedChars(const std::wstring_view text,
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray)
|
||||
const std::basic_string_view<BYTE> attributes,
|
||||
const std::basic_string_view<WORD> colorArray)
|
||||
{
|
||||
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
SCREEN_INFORMATION& screenInfo = gci.GetActiveOutputBuffer();
|
||||
|
||||
@@ -48,8 +48,8 @@ public:
|
||||
[[nodiscard]] HRESULT ResizeAllAreas(const COORD newSize);
|
||||
|
||||
void WriteCompMessage(const std::wstring_view text,
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray);
|
||||
const std::basic_string_view<BYTE> attributes,
|
||||
const std::basic_string_view<WORD> colorArray);
|
||||
|
||||
void WriteResultMessage(const std::wstring_view text);
|
||||
|
||||
@@ -64,18 +64,18 @@ private:
|
||||
void _ClearComposition();
|
||||
|
||||
void _WriteUndeterminedChars(const std::wstring_view text,
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray);
|
||||
const std::basic_string_view<BYTE> attributes,
|
||||
const std::basic_string_view<WORD> colorArray);
|
||||
|
||||
void _InsertConvertedString(const std::wstring_view text);
|
||||
|
||||
static TextAttribute s_RetrieveAttributeAt(const size_t pos,
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray);
|
||||
const std::basic_string_view<BYTE> attributes,
|
||||
const std::basic_string_view<WORD> colorArray);
|
||||
|
||||
static std::vector<OutputCell> s_ConvertToCells(const std::wstring_view text,
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray);
|
||||
const std::basic_string_view<BYTE> attributes,
|
||||
const std::basic_string_view<WORD> colorArray);
|
||||
|
||||
std::vector<OutputCell>::const_iterator _WriteConversionArea(const std::vector<OutputCell>::const_iterator begin,
|
||||
const std::vector<OutputCell>::const_iterator end,
|
||||
@@ -86,6 +86,6 @@ private:
|
||||
bool _isSavedCursorVisible;
|
||||
|
||||
std::wstring _text;
|
||||
std::vector<BYTE> _attributes;
|
||||
std::vector<WORD> _colorArray;
|
||||
std::basic_string<BYTE> _attributes;
|
||||
std::basic_string<WORD> _colorArray;
|
||||
};
|
||||
|
||||
@@ -125,8 +125,8 @@ void WriteConvRegionToScreen(const SCREEN_INFORMATION& ScreenInfo,
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT ImeComposeData(std::wstring_view text,
|
||||
gsl::span<const BYTE> attributes,
|
||||
gsl::span<const WORD> colorArray)
|
||||
std::basic_string_view<BYTE> attributes,
|
||||
std::basic_string_view<WORD> colorArray)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -67,10 +67,10 @@ DWORD UnicodeRasterFontCellMungeOnRead(const gsl::span<CHAR_INFO> buffer)
|
||||
for (DWORD iSrc = 0; iSrc < buffer.size(); iSrc++)
|
||||
{
|
||||
// If it's not a trailing byte, copy it straight over, stripping out the Leading/Trailing flags from the attributes field.
|
||||
auto& src{ til::at(buffer, iSrc) };
|
||||
auto& src{ gsl::at(buffer, iSrc) };
|
||||
if (!WI_IsFlagSet(src.Attributes, COMMON_LVB_TRAILING_BYTE))
|
||||
{
|
||||
auto& dst{ til::at(buffer, iDst) };
|
||||
auto& dst{ gsl::at(buffer, iDst) };
|
||||
dst = src;
|
||||
WI_ClearAllFlags(dst.Attributes, COMMON_LVB_SBCSDBCS);
|
||||
iDst++;
|
||||
|
||||
@@ -472,7 +472,7 @@ void EventsToUnicode(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& inEvents,
|
||||
// Return Value:
|
||||
// - HRESULT indicating success or failure
|
||||
[[nodiscard]] HRESULT ApiRoutines::WriteConsoleInputAImpl(InputBuffer& context,
|
||||
const gsl::span<const INPUT_RECORD> buffer,
|
||||
const std::basic_string_view<INPUT_RECORD> buffer,
|
||||
size_t& written,
|
||||
const bool append) noexcept
|
||||
{
|
||||
@@ -516,7 +516,7 @@ void EventsToUnicode(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& inEvents,
|
||||
// Return Value:
|
||||
// - HRESULT indicating success or failure
|
||||
[[nodiscard]] HRESULT ApiRoutines::WriteConsoleInputWImpl(InputBuffer& context,
|
||||
const gsl::span<const INPUT_RECORD> buffer,
|
||||
const std::basic_string_view<INPUT_RECORD> buffer,
|
||||
size_t& written,
|
||||
const bool append) noexcept
|
||||
{
|
||||
@@ -1042,7 +1042,7 @@ void EventsToUnicode(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& inEvents,
|
||||
const auto subspan = buffer.subspan(totalOffset, writeRectangle.Width());
|
||||
|
||||
// Convert to a CHAR_INFO view to fit into the iterator
|
||||
const auto charInfos = gsl::span<const CHAR_INFO>(subspan.data(), subspan.size());
|
||||
const auto charInfos = std::basic_string_view<CHAR_INFO>(subspan.data(), subspan.size());
|
||||
|
||||
// Make the iterator and write to the target position.
|
||||
OutputCellIterator it(charInfos);
|
||||
|
||||
@@ -1604,7 +1604,7 @@ void DoSrvPrivateRefreshWindow(_In_ const SCREEN_INFORMATION& screenInfo)
|
||||
|
||||
if (title.has_value() && title->size() > 0)
|
||||
{
|
||||
til::at(*title, 0) = ANSI_NULL;
|
||||
gsl::at(*title, 0) = ANSI_NULL;
|
||||
}
|
||||
|
||||
// Get the appropriate title and length depending on the mode.
|
||||
@@ -1667,7 +1667,7 @@ void DoSrvPrivateRefreshWindow(_In_ const SCREEN_INFORMATION& screenInfo)
|
||||
|
||||
if (title.size() > 0)
|
||||
{
|
||||
til::at(title, 0) = ANSI_NULL;
|
||||
gsl::at(title, 0) = ANSI_NULL;
|
||||
}
|
||||
|
||||
// Figure out how big our temporary Unicode buffer must be to get the title.
|
||||
@@ -1722,7 +1722,7 @@ void DoSrvPrivateRefreshWindow(_In_ const SCREEN_INFORMATION& screenInfo)
|
||||
// If we didn't copy anything back and there is space, null terminate the given buffer and return.
|
||||
if (title.size() > 0)
|
||||
{
|
||||
til::at(title, 0) = ANSI_NULL;
|
||||
gsl::at(title, 0) = ANSI_NULL;
|
||||
written = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -782,7 +782,7 @@ HRESULT GetConsoleCommandHistoryWImplHelper(const std::wstring_view exeName,
|
||||
writtenOrNeeded = 0;
|
||||
if (historyBuffer.size() > 0)
|
||||
{
|
||||
til::at(historyBuffer, 0) = UNICODE_NULL;
|
||||
gsl::at(historyBuffer, 0) = UNICODE_NULL;
|
||||
}
|
||||
|
||||
CommandHistory* const CommandHistory = CommandHistory::s_FindByExe(exeName);
|
||||
@@ -859,7 +859,7 @@ HRESULT ApiRoutines::GetConsoleCommandHistoryAImpl(const std::string_view exeNam
|
||||
{
|
||||
if (commandHistory.size() > 0)
|
||||
{
|
||||
til::at(commandHistory, 0) = ANSI_NULL;
|
||||
gsl::at(commandHistory, 0) = ANSI_NULL;
|
||||
}
|
||||
|
||||
LockConsole();
|
||||
|
||||
@@ -726,12 +726,12 @@ void Settings::SetHistoryNoDup(const bool bHistoryNoDup)
|
||||
_bHistoryNoDup = bHistoryNoDup;
|
||||
}
|
||||
|
||||
gsl::span<const COLORREF> Settings::Get16ColorTable() const
|
||||
std::basic_string_view<COLORREF> Settings::Get16ColorTable() const
|
||||
{
|
||||
return Get256ColorTable().subspan(0, 16);
|
||||
return Get256ColorTable().substr(0, 16);
|
||||
}
|
||||
|
||||
gsl::span<const COLORREF> Settings::Get256ColorTable() const
|
||||
std::basic_string_view<COLORREF> Settings::Get256ColorTable() const
|
||||
{
|
||||
return { _colorTable.data(), _colorTable.size() };
|
||||
}
|
||||
|
||||
@@ -159,8 +159,8 @@ public:
|
||||
bool GetHistoryNoDup() const;
|
||||
void SetHistoryNoDup(const bool fHistoryNoDup);
|
||||
|
||||
gsl::span<const COLORREF> Get16ColorTable() const;
|
||||
gsl::span<const COLORREF> Get256ColorTable() const;
|
||||
std::basic_string_view<COLORREF> Get16ColorTable() const;
|
||||
std::basic_string_view<COLORREF> Get256ColorTable() const;
|
||||
void SetColorTableEntry(const size_t index, const COLORREF ColorValue);
|
||||
COLORREF GetColorTableEntry(const size_t index) const;
|
||||
|
||||
|
||||
@@ -367,7 +367,7 @@ class OutputCellIteratorTests
|
||||
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
|
||||
|
||||
const std::vector<WORD> colors{ FOREGROUND_GREEN, FOREGROUND_RED | BACKGROUND_BLUE, FOREGROUND_BLUE | FOREGROUND_INTENSITY, BACKGROUND_GREEN };
|
||||
const gsl::span<const WORD> view{ colors.data(), colors.size() };
|
||||
const std::basic_string_view<WORD> view{ colors.data(), colors.size() };
|
||||
|
||||
OutputCellIterator it(view);
|
||||
|
||||
@@ -401,7 +401,7 @@ class OutputCellIteratorTests
|
||||
charInfos.push_back(ci);
|
||||
}
|
||||
|
||||
const gsl::span<const CHAR_INFO> view{ charInfos.data(), charInfos.size() };
|
||||
const std::basic_string_view<CHAR_INFO> view{ charInfos.data(), charInfos.size() };
|
||||
|
||||
OutputCellIterator it(view);
|
||||
|
||||
@@ -433,7 +433,7 @@ class OutputCellIteratorTests
|
||||
cells.push_back(cell);
|
||||
}
|
||||
|
||||
const gsl::span<const OutputCell> view{ cells.data(), cells.size() };
|
||||
const std::basic_string_view<OutputCell> view{ cells.data(), cells.size() };
|
||||
|
||||
OutputCellIterator it(view);
|
||||
|
||||
|
||||
@@ -254,145 +254,6 @@ void VtIoTests::DtorTestStackAllocMany()
|
||||
}
|
||||
}
|
||||
|
||||
class MockRenderData : public IRenderData, IUiaData
|
||||
{
|
||||
public:
|
||||
Microsoft::Console::Types::Viewport GetViewport() noexcept override
|
||||
{
|
||||
return Microsoft::Console::Types::Viewport{};
|
||||
}
|
||||
|
||||
COORD GetTextBufferEndPosition() const noexcept override
|
||||
{
|
||||
return COORD{};
|
||||
}
|
||||
|
||||
const TextBuffer& GetTextBuffer() noexcept override
|
||||
{
|
||||
FAIL_FAST_HR(E_NOTIMPL);
|
||||
}
|
||||
|
||||
const FontInfo& GetFontInfo() noexcept override
|
||||
{
|
||||
FAIL_FAST_HR(E_NOTIMPL);
|
||||
}
|
||||
|
||||
std::vector<Microsoft::Console::Types::Viewport> GetSelectionRects() noexcept override
|
||||
{
|
||||
return std::vector<Microsoft::Console::Types::Viewport>{};
|
||||
}
|
||||
|
||||
void LockConsole() noexcept override
|
||||
{
|
||||
}
|
||||
|
||||
void UnlockConsole() noexcept override
|
||||
{
|
||||
}
|
||||
|
||||
const TextAttribute GetDefaultBrushColors() noexcept override
|
||||
{
|
||||
return TextAttribute{};
|
||||
}
|
||||
|
||||
std::pair<COLORREF, COLORREF> GetAttributeColors(const TextAttribute& /*attr*/) const noexcept override
|
||||
{
|
||||
return std::make_pair(COLORREF{}, COLORREF{});
|
||||
}
|
||||
|
||||
COORD GetCursorPosition() const noexcept override
|
||||
{
|
||||
return COORD{};
|
||||
}
|
||||
|
||||
bool IsCursorVisible() const noexcept override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsCursorOn() const noexcept override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ULONG GetCursorHeight() const noexcept override
|
||||
{
|
||||
return 42ul;
|
||||
}
|
||||
|
||||
CursorType GetCursorStyle() const noexcept override
|
||||
{
|
||||
return CursorType::FullBox;
|
||||
}
|
||||
|
||||
ULONG GetCursorPixelWidth() const noexcept override
|
||||
{
|
||||
return 12ul;
|
||||
}
|
||||
|
||||
COLORREF GetCursorColor() const noexcept override
|
||||
{
|
||||
return COLORREF{};
|
||||
}
|
||||
|
||||
bool IsCursorDoubleWidth() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsScreenReversed() const noexcept override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::vector<RenderOverlay> GetOverlays() const noexcept override
|
||||
{
|
||||
return std::vector<RenderOverlay>{};
|
||||
}
|
||||
|
||||
const bool IsGridLineDrawingAllowed() noexcept override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::wstring GetConsoleTitle() const noexcept override
|
||||
{
|
||||
return std::wstring{};
|
||||
}
|
||||
|
||||
const bool IsSelectionActive() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool IsBlockSelection() const noexcept override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClearSelection() override
|
||||
{
|
||||
}
|
||||
|
||||
void SelectNewRegion(const COORD /*coordStart*/, const COORD /*coordEnd*/) override
|
||||
{
|
||||
}
|
||||
|
||||
const COORD GetSelectionAnchor() const noexcept
|
||||
{
|
||||
return COORD{};
|
||||
}
|
||||
|
||||
const COORD GetSelectionEnd() const noexcept
|
||||
{
|
||||
return COORD{};
|
||||
}
|
||||
|
||||
void ColorSelection(const COORD /*coordSelectionStart*/, const COORD /*coordSelectionEnd*/, const TextAttribute /*attr*/)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
void VtIoTests::RendererDtorAndThread()
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
@@ -400,10 +261,9 @@ void VtIoTests::RendererDtorAndThread()
|
||||
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
auto data = std::make_unique<MockRenderData>();
|
||||
auto thread = std::make_unique<Microsoft::Console::Render::RenderThread>();
|
||||
auto* pThread = thread.get();
|
||||
auto pRenderer = std::make_unique<Microsoft::Console::Render::Renderer>(data.get(), nullptr, 0, std::move(thread));
|
||||
auto pRenderer = std::make_unique<Microsoft::Console::Render::Renderer>(nullptr, nullptr, 0, std::move(thread));
|
||||
VERIFY_SUCCEEDED(pThread->Initialize(pRenderer.get()));
|
||||
// Sleep for a hot sec to make sure the thread starts before we enable painting
|
||||
// If you don't, the thread might wait on the paint enabled event AFTER
|
||||
@@ -426,10 +286,9 @@ void VtIoTests::RendererDtorAndThreadAndDx()
|
||||
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
auto data = std::make_unique<MockRenderData>();
|
||||
auto thread = std::make_unique<Microsoft::Console::Render::RenderThread>();
|
||||
auto* pThread = thread.get();
|
||||
auto pRenderer = std::make_unique<Microsoft::Console::Render::Renderer>(data.get(), nullptr, 0, std::move(thread));
|
||||
auto pRenderer = std::make_unique<Microsoft::Console::Render::Renderer>(nullptr, nullptr, 0, std::move(thread));
|
||||
VERIFY_SUCCEEDED(pThread->Initialize(pRenderer.get()));
|
||||
|
||||
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
|
||||
|
||||
@@ -32,8 +32,8 @@ constexpr BYTE CONIME_CURSOR_LEFT = 0x20;
|
||||
[[nodiscard]] HRESULT ImeEndComposition();
|
||||
|
||||
[[nodiscard]] HRESULT ImeComposeData(std::wstring_view text,
|
||||
gsl::span<const BYTE> attributes,
|
||||
gsl::span<const WORD> colorArray);
|
||||
std::basic_string_view<BYTE> attributes,
|
||||
std::basic_string_view<WORD> colorArray);
|
||||
|
||||
[[nodiscard]] HRESULT ImeClearComposeData();
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "til/rectangle.h"
|
||||
#include "til/bitmap.h"
|
||||
#include "til/u8u16convert.h"
|
||||
#include "til/spsc.h"
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
|
||||
@@ -5,52 +5,17 @@
|
||||
|
||||
namespace til
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
// This was lifted from gsl::details::is_span.
|
||||
template<class T>
|
||||
struct is_span_oracle : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
#ifdef GSL_SPAN_H
|
||||
template<class ElementType, std::size_t Extent>
|
||||
struct is_span_oracle<gsl::span<ElementType, Extent>> : std::true_type
|
||||
{
|
||||
};
|
||||
#endif
|
||||
|
||||
template<class T>
|
||||
struct is_span : public is_span_oracle<std::remove_cv_t<T>>
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
// The at function declares that you've already sufficiently checked that your array access
|
||||
// is in range before retrieving an item inside it at an offset.
|
||||
// This is to save double/triple/quadruple testing in circumstances where you are already
|
||||
// pivoting on the length of a set and now want to pull elements out of it by offset
|
||||
// without checking again.
|
||||
// gsl::at will do the check again. As will .at(). And using [] will have a warning in audit.
|
||||
// This template is explicitly disabled if T is of type gsl::span, as it would interfere with
|
||||
// the overload below.
|
||||
template<class T, std::enable_if_t<!details::is_span<T>::value, int> = 0>
|
||||
template<class T>
|
||||
constexpr auto at(T& cont, const size_t i) -> decltype(cont[cont.size()])
|
||||
{
|
||||
#pragma warning(suppress : 26482) // Suppress bounds.2 check for indexing with constant expressions
|
||||
#pragma warning(suppress : 26446) // Suppress bounds.4 check for subscript operator.
|
||||
return cont[i];
|
||||
}
|
||||
|
||||
#ifdef GSL_SPAN_H
|
||||
// This is an overload of til::at for span that access its backing buffer directly (UNCHECKED)
|
||||
template<typename ElementType, size_t Extent>
|
||||
constexpr auto at(gsl::span<ElementType, Extent> span, const std::ptrdiff_t i) -> decltype(span[span.size()])
|
||||
{
|
||||
#pragma warning(suppress : 26481) // Suppress bounds.1 check for doing pointer arithmetic
|
||||
#pragma warning(suppress : 26482) // Suppress bounds.2 check for indexing with constant expressions
|
||||
#pragma warning(suppress : 26446) // Suppress bounds.4 check for subscript operator.
|
||||
return span.data()[i];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -315,7 +315,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
|
||||
// Copy any regions that overlap from this map to the new one.
|
||||
// Just iterate our runs...
|
||||
for (const auto& run : *this)
|
||||
for (const auto run : *this)
|
||||
{
|
||||
// intersect them with the new map
|
||||
// so we don't attempt to set bits that fit outside
|
||||
|
||||
@@ -1,643 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
// til::spsc::details::arc requires std::atomic<size_type>::wait() and ::notify_one() and at the time of writing no
|
||||
// STL supports these. Since both Windows and Linux offer a Futex implementation we can easily implement this though.
|
||||
// On other platforms we fall back to using a std::condition_variable.
|
||||
#if __cpp_lib_atomic_wait >= 201907
|
||||
#define _TIL_SPSC_DETAIL_POSITION_IMPL_NATIVE 1
|
||||
#elif defined(_WIN32_WINNT) && _WIN32_WINNT >= _WIN32_WINNT_WIN8
|
||||
#define _TIL_SPSC_DETAIL_POSITION_IMPL_WIN 1
|
||||
#elif __linux__
|
||||
#define _TIL_SPSC_DETAIL_POSITION_IMPL_LINUX 1
|
||||
#else
|
||||
#define _TIL_SPSC_DETAIL_POSITION_IMPL_FALLBACK 1
|
||||
#endif
|
||||
|
||||
// til: Terminal Implementation Library. Also: "Today I Learned".
|
||||
// spsc: Single Producer Single Consumer. A SPSC queue/channel sends data from exactly one sender to one receiver.
|
||||
namespace til::spsc
|
||||
{
|
||||
using size_type = uint32_t;
|
||||
|
||||
namespace details
|
||||
{
|
||||
static constexpr size_type position_mask = std::numeric_limits<size_type>::max() >> 2u; // 0b00111....
|
||||
static constexpr size_type revolution_flag = 1u << (std::numeric_limits<size_type>::digits - 2u); // 0b01000....
|
||||
static constexpr size_type drop_flag = 1u << (std::numeric_limits<size_type>::digits - 1u); // 0b10000....
|
||||
|
||||
struct block_initially_policy
|
||||
{
|
||||
using _spsc_policy = int;
|
||||
static constexpr bool _block_forever = false;
|
||||
};
|
||||
|
||||
struct block_forever_policy
|
||||
{
|
||||
using _spsc_policy = int;
|
||||
static constexpr bool _block_forever = true;
|
||||
};
|
||||
|
||||
template<typename WaitPolicy>
|
||||
using enable_if_wait_policy_t = typename std::remove_reference_t<WaitPolicy>::_spsc_policy;
|
||||
|
||||
#if _TIL_SPSC_DETAIL_POSITION_IMPL_NATIVE
|
||||
using atomic_size_type = std::atomic<size_type>;
|
||||
#else
|
||||
// atomic_size_type is a fallback if native std::atomic<size_type>::wait()
|
||||
// and ::notify_one() methods are unavailable in the STL.
|
||||
struct atomic_size_type
|
||||
{
|
||||
size_type load(std::memory_order order) const noexcept
|
||||
{
|
||||
return _value.load(order);
|
||||
}
|
||||
|
||||
void store(size_type desired, std::memory_order order) noexcept
|
||||
{
|
||||
#if _TIL_SPSC_DETAIL_POSITION_IMPL_FALLBACK
|
||||
// We must use a lock here to prevent us from modifying the value
|
||||
// in between wait() reading the value and the thread being suspended.
|
||||
std::lock_guard<std::mutex> lock{ _m };
|
||||
#endif
|
||||
_value.store(desired, order);
|
||||
}
|
||||
|
||||
void wait(size_type old, [[maybe_unused]] std::memory_order order) const noexcept
|
||||
{
|
||||
#if _TIL_SPSC_DETAIL_POSITION_IMPL_WIN
|
||||
#pragma warning(suppress : 26492) // Don't use const_cast to cast away const or volatile
|
||||
WaitOnAddress(const_cast<std::atomic<size_type>*>(&_value), &old, sizeof(_value), INFINITE);
|
||||
#elif _TIL_SPSC_DETAIL_POSITION_IMPL_LINUX
|
||||
futex(FUTEX_WAIT_PRIVATE, old);
|
||||
#elif _TIL_SPSC_DETAIL_POSITION_IMPL_FALLBACK
|
||||
std::unique_lock<std::mutex> lock{ _m };
|
||||
_cv.wait(lock, [&]() { return _value.load(order) != old; });
|
||||
#endif
|
||||
}
|
||||
|
||||
void notify_one() noexcept
|
||||
{
|
||||
#if _TIL_SPSC_DETAIL_POSITION_IMPL_WIN
|
||||
WakeByAddressSingle(&_value);
|
||||
#elif _TIL_SPSC_DETAIL_POSITION_IMPL_LINUX
|
||||
futex(FUTEX_WAKE_PRIVATE, 1);
|
||||
#elif _TIL_SPSC_DETAIL_POSITION_IMPL_FALLBACK
|
||||
_cv.notify_one();
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#if _TIL_SPSC_DETAIL_POSITION_IMPL_LINUX
|
||||
inline void futex(int futex_op, size_type val) const noexcept
|
||||
{
|
||||
// See: https://man7.org/linux/man-pages/man2/futex.2.html
|
||||
static_assert(sizeof(std::atomic<size_type>) == 4);
|
||||
syscall(SYS_futex, &_value, futex_op, val, nullptr, nullptr, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
std::atomic<size_type> _value{ 0 };
|
||||
|
||||
#if _TIL_SPSC_DETAIL_POSITION_IMPL_FALLBACK
|
||||
private:
|
||||
std::mutex _m;
|
||||
std::condition_variable _cv;
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
inline T* alloc_raw_memory(size_t size)
|
||||
{
|
||||
constexpr auto alignment = alignof(T);
|
||||
if constexpr (alignment <= __STDCPP_DEFAULT_NEW_ALIGNMENT__)
|
||||
{
|
||||
return static_cast<T*>(::operator new(size));
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<T*>(::operator new(size, std::align_val_t(alignment)));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void free_raw_memory(T* ptr) noexcept
|
||||
{
|
||||
constexpr auto alignment = alignof(T);
|
||||
if constexpr (alignment <= __STDCPP_DEFAULT_NEW_ALIGNMENT__)
|
||||
{
|
||||
::operator delete(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
::operator delete(ptr, std::align_val_t(alignment));
|
||||
}
|
||||
}
|
||||
|
||||
struct acquisition
|
||||
{
|
||||
// The index range [begin, end) is the range of slots in the array returned by
|
||||
// arc<T>::data() that may be written to / read from respectively.
|
||||
// If a range has been successfully acquired "end > begin" is true. end thus can't be 0.
|
||||
size_type begin;
|
||||
size_type end;
|
||||
|
||||
// Upon release() of an acquisition, next is the value that's written to the consumer/producer position.
|
||||
// It's basically the same as end, but with the revolution flag mixed in.
|
||||
// If end is equal to capacity, next will be 0 (mixed with the next revolution flag).
|
||||
size_type next;
|
||||
|
||||
// If the other side of the queue hasn't been destroyed yet, alive will be true.
|
||||
bool alive;
|
||||
|
||||
constexpr acquisition(size_type begin, size_type end, size_type next, bool alive) :
|
||||
begin(begin),
|
||||
end(end),
|
||||
next(next),
|
||||
alive(alive)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// The following assumes you know what ring/circular buffers are. You can read about them here:
|
||||
// https://en.wikipedia.org/wiki/Circular_buffer
|
||||
//
|
||||
// Furthermore the implementation solves a problem known as the producer-consumer problem:
|
||||
// https://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem
|
||||
//
|
||||
// arc follows the classic spsc design and manages a ring buffer with two positions: _producer and _consumer.
|
||||
// They contain the position the producer / consumer will next write to / read from respectively.
|
||||
// As usual with ring buffers, these positions are modulo to the _capacity of the underlying buffer.
|
||||
// The producer's writable range is [_producer, _consumer) and the consumer's readable is [_consumer, _producer).
|
||||
//
|
||||
// After you wrote the numbers 0 to 6 into a queue of size 10, a typical state of the ring buffer might be:
|
||||
// [ 0 | 1 | 2 | 3 | 4 | 5 | 6 | _ | _ | _ | _ ]
|
||||
// ^ ^ ^
|
||||
// _consumer = 0 _producer = 7 _capacity = 10
|
||||
//
|
||||
// As you can see the readable range currently is [_consumer, _producer) = [0, 7).
|
||||
// The remaining writable range on the other hand is [_producer, _consumer) = [7, 0).
|
||||
// Wait, what? [7, 0)? How does that work? As all positions are modulo _capacity, 0 mod 10 is the same as 10 mod 10.
|
||||
// If we only want to read forward in the buffer [7, 0) is thus the same as [7, 10).
|
||||
//
|
||||
// If we read 3 items from the queue the contents will be:
|
||||
// [ _ | _ | _ | 3 | 4 | 5 | 6 | _ | _ | _ | _ ]
|
||||
// ^ ^
|
||||
// _consumer = 3 _producer = 7
|
||||
//
|
||||
// Now the writable range is still [_producer, _consumer), but it wraps around the end of the ring buffer.
|
||||
// In this case arc will split the range in two and return each separately in acquire().
|
||||
// The first returned range will be [_producer, _capacity) and the second [0, _consumer).
|
||||
// The same logic applies if the readable range wraps around the end of the ring buffer.
|
||||
//
|
||||
// As these are symmetric, the logic for acquiring and releasing ranges is the same for both sides.
|
||||
// The producer will acquire() and release() ranges with its own position as "mine" and the consumer's
|
||||
// position as "theirs". These arguments are correspondingly flipped for the consumer.
|
||||
//
|
||||
// As part of the producer-consumer problem, the producer cannot write more values ahead of the
|
||||
// consumer than the buffer's capacity. Since both positions are modulo to the capacity we can only
|
||||
// determine positional differences smaller than the capacity. Due to that both producer and
|
||||
// consumer store a "revolution_flag" as the second highest bit within their positions.
|
||||
// This bit is flipped each time the producer/consumer wrap around the end of the ring buffer.
|
||||
// If the positions are identical, except for their "revolution_flag" value, the producer thus must
|
||||
// be capacity-many positions ahead of the consumer and must wait until items have been consumed.
|
||||
//
|
||||
// Inversely the consumer must wait until the producer has written at least one value ahead.
|
||||
// This can be detected by checking whether the positions are identical including the revolution_flag.
|
||||
template<typename T>
|
||||
struct arc
|
||||
{
|
||||
explicit arc(size_type capacity) noexcept :
|
||||
_data(alloc_raw_memory<T>(size_t(capacity) * sizeof(T))),
|
||||
_capacity(capacity)
|
||||
{
|
||||
}
|
||||
|
||||
~arc()
|
||||
{
|
||||
auto beg = _consumer.load(std::memory_order_acquire);
|
||||
auto end = _producer.load(std::memory_order_acquire);
|
||||
auto differentRevolution = ((beg ^ end) & revolution_flag) != 0;
|
||||
|
||||
beg &= position_mask;
|
||||
end &= position_mask;
|
||||
|
||||
// The producer position will always be ahead of the consumer, but since we're dealing
|
||||
// with a ring buffer the producer may be wrapped around the end of the buffer.
|
||||
// We thus need to deal with 3 potential cases:
|
||||
// * No valid data.
|
||||
// If both positions including their revolution bits are identical.
|
||||
// * Valid data in the middle of the ring buffer.
|
||||
// If _producer > _consumer.
|
||||
// * Valid data at both ends of the ring buffer.
|
||||
// If the revolution bits differ, even if the positions are otherwise identical,
|
||||
// which they might be if the channel contains exactly as many values as its capacity.
|
||||
if (end > beg)
|
||||
{
|
||||
std::destroy(_data + beg, _data + end);
|
||||
}
|
||||
else if (differentRevolution)
|
||||
{
|
||||
std::destroy(_data, _data + end);
|
||||
std::destroy(_data + beg, _data + _capacity);
|
||||
}
|
||||
|
||||
free_raw_memory(_data);
|
||||
}
|
||||
|
||||
void drop_producer()
|
||||
{
|
||||
drop(_producer);
|
||||
}
|
||||
|
||||
void drop_consumer()
|
||||
{
|
||||
drop(_consumer);
|
||||
}
|
||||
|
||||
acquisition producer_acquire(size_type slots, bool blocking) noexcept
|
||||
{
|
||||
return acquire(_producer, _consumer, revolution_flag, slots, blocking);
|
||||
}
|
||||
|
||||
void producer_release(acquisition acquisition) noexcept
|
||||
{
|
||||
release(_producer, acquisition);
|
||||
}
|
||||
|
||||
acquisition consumer_acquire(size_type slots, bool blocking) noexcept
|
||||
{
|
||||
return acquire(_consumer, _producer, 0, slots, blocking);
|
||||
}
|
||||
|
||||
void consumer_release(acquisition acquisition) noexcept
|
||||
{
|
||||
release(_consumer, acquisition);
|
||||
}
|
||||
|
||||
T* data() const noexcept
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
private:
|
||||
void drop(atomic_size_type& mine)
|
||||
{
|
||||
// Signal the other side we're dropped. See acquire() for the handling of the drop_flag.
|
||||
// We don't need to use release ordering like release() does as each call to
|
||||
// any of the producer/consumer methods already results in a call to release().
|
||||
// Another release ordered write can't possibly synchronize any more data anyways at this point.
|
||||
const auto myPos = mine.load(std::memory_order_relaxed);
|
||||
mine.store(myPos | drop_flag, std::memory_order_relaxed);
|
||||
mine.notify_one();
|
||||
|
||||
// The first time SPSCBase is dropped (destroyed) we'll set
|
||||
// the flag to true and get false, causing us to return early.
|
||||
// Only the second time we'll get true.
|
||||
// --> The contents are only deleted when both sides have been dropped.
|
||||
if (_eitherSideDropped.exchange(true, std::memory_order_relaxed))
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: waitMask MUST be either 0 (consumer) or revolution_flag (producer).
|
||||
acquisition acquire(atomic_size_type& mine, atomic_size_type& theirs, size_type waitMask, size_type slots, bool blocking) noexcept
|
||||
{
|
||||
size_type myPos = mine.load(std::memory_order_relaxed);
|
||||
size_type theirPos;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// This acquire read synchronizes with the release write in release().
|
||||
theirPos = theirs.load(std::memory_order_acquire);
|
||||
if ((myPos ^ theirPos) != waitMask)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!blocking)
|
||||
{
|
||||
return {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
true,
|
||||
};
|
||||
}
|
||||
|
||||
theirs.wait(theirPos, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// If the other side's position contains a drop flag, as a X -> we need to...
|
||||
// * producer -> stop immediately
|
||||
// FYI: isProducer == (waitMask != 0).
|
||||
// * consumer -> finish consuming all values and then stop
|
||||
// We're finished if the only difference between our
|
||||
// and the other side's position is the drop flag.
|
||||
if ((theirPos & drop_flag) != 0 && (waitMask != 0 || (myPos ^ theirPos) == drop_flag))
|
||||
{
|
||||
return {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
};
|
||||
}
|
||||
|
||||
auto begin = myPos & position_mask;
|
||||
auto end = theirPos & position_mask;
|
||||
|
||||
// [begin, end) is the writable/readable range for the producer/consumer.
|
||||
// The following detects whether we'd be wrapping around the end of the ring buffer
|
||||
// and splits the range into the first half [mine, _capacity).
|
||||
// If acquire() is called again it'll return [0, theirs).
|
||||
end = end > begin ? end : _capacity;
|
||||
|
||||
// Of course we also need to ensure to not return more than we've been asked for.
|
||||
end = std::min(end, begin + slots);
|
||||
|
||||
// "next" will contain the value that's stored into "mine" when release() is called.
|
||||
// It's basically the same as "end", but with the revolution flag spliced in.
|
||||
// If we acquired the range [mine, _capacity) "end" will equal _capacity
|
||||
// and thus wrap around the ring buffer. The next value for "mine" is thus the
|
||||
// position zero | the flipped "revolution" (and 0 | x == x).
|
||||
auto revolution = myPos & revolution_flag;
|
||||
auto next = end != _capacity ? end | revolution : revolution ^ revolution_flag;
|
||||
|
||||
return {
|
||||
begin,
|
||||
end,
|
||||
next,
|
||||
true,
|
||||
};
|
||||
}
|
||||
|
||||
void release(atomic_size_type& mine, acquisition acquisition) noexcept
|
||||
{
|
||||
// This release write synchronizes with the acquire read in acquire().
|
||||
mine.store(acquisition.next, std::memory_order_release);
|
||||
mine.notify_one();
|
||||
}
|
||||
|
||||
T* const _data;
|
||||
const size_type _capacity;
|
||||
|
||||
std::atomic<bool> _eitherSideDropped{ false };
|
||||
|
||||
atomic_size_type _producer;
|
||||
atomic_size_type _consumer;
|
||||
};
|
||||
|
||||
inline void validate_size(size_t v)
|
||||
{
|
||||
if (v > static_cast<size_t>(position_mask))
|
||||
{
|
||||
throw std::overflow_error{ "size too large for spsc" };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Block until at least one item has been written into the sender / read from the receiver.
|
||||
inline constexpr details::block_initially_policy block_initially{};
|
||||
|
||||
// Block until all items have been written into the sender / read from the receiver.
|
||||
inline constexpr details::block_forever_policy block_forever{};
|
||||
|
||||
template<typename T>
|
||||
struct producer
|
||||
{
|
||||
explicit producer(details::arc<T>* arc) noexcept :
|
||||
_arc(arc) {}
|
||||
|
||||
producer<T>(const producer<T>&) = delete;
|
||||
producer<T>& operator=(const producer<T>&) = delete;
|
||||
|
||||
producer(producer<T>&& other) noexcept
|
||||
{
|
||||
drop();
|
||||
_arc = std::exchange(other._arc, nullptr);
|
||||
}
|
||||
|
||||
producer<T>& operator=(producer<T>&& other) noexcept
|
||||
{
|
||||
drop();
|
||||
_arc = std::exchange(other._arc, nullptr);
|
||||
}
|
||||
|
||||
~producer()
|
||||
{
|
||||
drop();
|
||||
}
|
||||
|
||||
// emplace constructs an item in-place at the end of the queue.
|
||||
// It returns true, if the item was successfully placed within the queue.
|
||||
// The return value will be false, if the consumer is gone.
|
||||
template<typename... Args>
|
||||
bool emplace(Args&&... args) const
|
||||
{
|
||||
auto acquisition = _arc->producer_acquire(1, true);
|
||||
if (!acquisition.end)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto data = _arc->data();
|
||||
auto begin = data + acquisition.begin;
|
||||
new (begin) T(std::forward<Args>(args)...);
|
||||
|
||||
_arc->producer_release(acquisition);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename InputIt>
|
||||
std::pair<size_t, bool> push(InputIt first, InputIt last) const
|
||||
{
|
||||
return push_n(block_forever, first, std::distance(first, last));
|
||||
}
|
||||
|
||||
// push writes the items between first and last into the queue.
|
||||
// The amount of successfully written items is returned as the first pair field.
|
||||
// The second pair field will be false if the consumer is gone.
|
||||
template<typename WaitPolicy, typename InputIt, details::enable_if_wait_policy_t<WaitPolicy> = 0>
|
||||
std::pair<size_t, bool> push(WaitPolicy&& policy, InputIt first, InputIt last) const
|
||||
{
|
||||
return push_n(std::forward<WaitPolicy>(policy), first, std::distance(first, last));
|
||||
}
|
||||
|
||||
template<typename InputIt>
|
||||
std::pair<size_t, bool> push_n(InputIt first, size_t count) const
|
||||
{
|
||||
return push_n(block_forever, first, count);
|
||||
}
|
||||
|
||||
// push_n writes count items from first into the queue.
|
||||
// The amount of successfully written items is returned as the first pair field.
|
||||
// The second pair field will be false if the consumer is gone.
|
||||
template<typename WaitPolicy, typename InputIt, details::enable_if_wait_policy_t<WaitPolicy> = 0>
|
||||
std::pair<size_t, bool> push_n(WaitPolicy&&, InputIt first, size_t count) const
|
||||
{
|
||||
details::validate_size(count);
|
||||
|
||||
const auto data = _arc->data();
|
||||
auto remaining = static_cast<size_type>(count);
|
||||
auto blocking = true;
|
||||
auto ok = true;
|
||||
|
||||
while (remaining != 0)
|
||||
{
|
||||
auto acquisition = _arc->producer_acquire(remaining, blocking);
|
||||
if (!acquisition.end)
|
||||
{
|
||||
ok = acquisition.alive;
|
||||
break;
|
||||
}
|
||||
|
||||
const auto begin = data + acquisition.begin;
|
||||
const auto got = acquisition.end - acquisition.begin;
|
||||
std::uninitialized_copy_n(first, got, begin);
|
||||
first += got;
|
||||
remaining -= got;
|
||||
|
||||
_arc->producer_release(acquisition);
|
||||
|
||||
if constexpr (!std::remove_reference_t<WaitPolicy>::_block_forever)
|
||||
{
|
||||
blocking = false;
|
||||
}
|
||||
}
|
||||
|
||||
return { count - remaining, ok };
|
||||
}
|
||||
|
||||
private:
|
||||
void drop()
|
||||
{
|
||||
if (_arc)
|
||||
{
|
||||
_arc->drop_producer();
|
||||
}
|
||||
}
|
||||
|
||||
details::arc<T>* _arc = nullptr;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct consumer
|
||||
{
|
||||
explicit consumer(details::arc<T>* arc) noexcept :
|
||||
_arc(arc) {}
|
||||
|
||||
consumer<T>(const consumer<T>&) = delete;
|
||||
consumer<T>& operator=(const consumer<T>&) = delete;
|
||||
|
||||
consumer(consumer<T>&& other) noexcept
|
||||
{
|
||||
drop();
|
||||
_arc = std::exchange(other._arc, nullptr);
|
||||
}
|
||||
|
||||
consumer<T>& operator=(consumer<T>&& other) noexcept
|
||||
{
|
||||
drop();
|
||||
_arc = std::exchange(other._arc, nullptr);
|
||||
}
|
||||
|
||||
~consumer()
|
||||
{
|
||||
drop();
|
||||
}
|
||||
|
||||
// pop returns the next item in the queue, or std::nullopt if the producer is gone.
|
||||
std::optional<T> pop() const
|
||||
{
|
||||
auto acquisition = _arc->consumer_acquire(1, true);
|
||||
if (!acquisition.end)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto data = _arc->data();
|
||||
auto begin = data + acquisition.begin;
|
||||
|
||||
auto item = std::move(*begin);
|
||||
std::destroy_at(begin);
|
||||
|
||||
_arc->consumer_release(acquisition);
|
||||
return item;
|
||||
}
|
||||
|
||||
template<typename OutputIt>
|
||||
std::pair<size_t, bool> pop_n(OutputIt first, size_t count) const
|
||||
{
|
||||
return pop_n(block_forever, first, count);
|
||||
}
|
||||
|
||||
// pop_n reads up to count items into first.
|
||||
// The amount of successfully read items is returned as the first pair field.
|
||||
// The second pair field will be false if the consumer is gone.
|
||||
template<typename WaitPolicy, typename OutputIt, details::enable_if_wait_policy_t<WaitPolicy> = 0>
|
||||
std::pair<size_t, bool> pop_n(WaitPolicy&&, OutputIt first, size_t count) const
|
||||
{
|
||||
details::validate_size(count);
|
||||
|
||||
const auto data = _arc->data();
|
||||
auto remaining = static_cast<size_type>(count);
|
||||
auto blocking = true;
|
||||
auto ok = true;
|
||||
|
||||
while (remaining != 0)
|
||||
{
|
||||
auto acquisition = _arc->consumer_acquire(remaining, blocking);
|
||||
if (!acquisition.end)
|
||||
{
|
||||
ok = acquisition.alive;
|
||||
break;
|
||||
}
|
||||
|
||||
auto beg = data + acquisition.begin;
|
||||
auto end = data + acquisition.end;
|
||||
auto got = acquisition.end - acquisition.begin;
|
||||
first = std::move(beg, end, first);
|
||||
std::destroy(beg, end);
|
||||
remaining -= got;
|
||||
|
||||
_arc->consumer_release(acquisition);
|
||||
|
||||
if constexpr (!std::remove_reference_t<WaitPolicy>::_block_forever)
|
||||
{
|
||||
blocking = false;
|
||||
}
|
||||
}
|
||||
|
||||
return { count - remaining, ok };
|
||||
}
|
||||
|
||||
private:
|
||||
void drop()
|
||||
{
|
||||
if (_arc)
|
||||
{
|
||||
_arc->drop_consumer();
|
||||
}
|
||||
}
|
||||
|
||||
details::arc<T>* _arc = nullptr;
|
||||
};
|
||||
|
||||
// channel returns a bounded, lock-free, single-producer, single-consumer
|
||||
// FIFO queue ("channel") with the given maximum capacity.
|
||||
template<typename T>
|
||||
std::pair<producer<T>, consumer<T>> channel(uint32_t capacity)
|
||||
{
|
||||
if (capacity == 0)
|
||||
{
|
||||
throw std::invalid_argument{ "invalid capacity" };
|
||||
}
|
||||
|
||||
const auto arc = new details::arc<T>(capacity);
|
||||
return { std::piecewise_construct, std::forward_as_tuple(arc), std::forward_as_tuple(arc) };
|
||||
}
|
||||
}
|
||||
@@ -145,7 +145,7 @@ BgfxEngine::BgfxEngine(PVOID SharedViewBase, LONG DisplayHeight, LONG DisplayWid
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT BgfxEngine::PaintBufferLine(const gsl::span<const Cluster> clusters,
|
||||
[[nodiscard]] HRESULT BgfxEngine::PaintBufferLine(const std::basic_string_view<Cluster> clusters,
|
||||
const COORD coord,
|
||||
const bool /*trimLeft*/,
|
||||
const bool /*lineWrapped*/) noexcept
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] HRESULT ScrollFrame() noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT PaintBackground() noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferLine(const gsl::span<const Cluster> clusters,
|
||||
[[nodiscard]] HRESULT PaintBufferLine(const std::basic_string_view<Cluster> clusters,
|
||||
const COORD coord,
|
||||
const bool trimLeft,
|
||||
const bool lineWrapped) noexcept override;
|
||||
|
||||
@@ -23,17 +23,12 @@
|
||||
#pragma hdrstop
|
||||
|
||||
using namespace Microsoft::Console::Interactivity::Win32;
|
||||
using namespace Microsoft::Console::VirtualTerminal;
|
||||
using Microsoft::Console::Interactivity::ServiceLocator;
|
||||
// For usage with WM_SYSKEYDOWN message processing.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646286(v=vs.85).aspx
|
||||
// Bit 29 is whether ALT was held when the message was posted.
|
||||
#define WM_SYSKEYDOWN_ALT_PRESSED (0x20000000)
|
||||
|
||||
// This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx
|
||||
// "If the high-order bit is 1, the key is down; otherwise, it is up."
|
||||
static constexpr short KeyPressed{ gsl::narrow_cast<short>(0x8000) };
|
||||
|
||||
// ----------------------------
|
||||
// Helpers
|
||||
// ----------------------------
|
||||
@@ -128,20 +123,7 @@ bool HandleTerminalMouseEvent(const COORD cMousePosition,
|
||||
// Virtual terminal input mode
|
||||
if (IsInVirtualTerminalInputMode())
|
||||
{
|
||||
const TerminalInput::MouseButtonState state{
|
||||
WI_IsFlagSet(GetKeyState(VK_LBUTTON), KeyPressed),
|
||||
WI_IsFlagSet(GetKeyState(VK_MBUTTON), KeyPressed),
|
||||
WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed)
|
||||
};
|
||||
|
||||
// GH#6401: VT applications should be able to receive mouse events from outside the
|
||||
// terminal buffer. This is likely to happen when the user drags the cursor offscreen.
|
||||
// We shouldn't throw away perfectly good events when they're offscreen, so we just
|
||||
// clamp them to be within the range [(0, 0), (W, H)].
|
||||
auto clampedPosition{ cMousePosition };
|
||||
const auto clampViewport{ gci.GetActiveOutputBuffer().GetViewport().ToOrigin() };
|
||||
clampViewport.Clamp(clampedPosition);
|
||||
fWasHandled = gci.GetActiveInputBuffer()->GetTerminalInput().HandleMouse(clampedPosition, uiButton, sModifierKeystate, sWheelDelta, state);
|
||||
fWasHandled = gci.GetActiveInputBuffer()->GetTerminalInput().HandleMouse(cMousePosition, uiButton, sModifierKeystate, sWheelDelta);
|
||||
}
|
||||
|
||||
return fWasHandled;
|
||||
@@ -653,25 +635,6 @@ BOOL HandleMouseEvent(const SCREEN_INFORMATION& ScreenInfo,
|
||||
|
||||
if (HandleTerminalMouseEvent(MousePosition, Message, GET_KEYSTATE_WPARAM(wParam), sDelta))
|
||||
{
|
||||
// GH#6401: Capturing the mouse ensures that we get drag/release events
|
||||
// even if the user moves outside the window.
|
||||
// HandleTerminalMouseEvent returns false if the terminal's not in VT mode,
|
||||
// so capturing/releasing here should not impact other console mouse event
|
||||
// consumers.
|
||||
switch (Message)
|
||||
{
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
SetCapture(ServiceLocator::LocateConsoleWindow()->GetWindowHandle());
|
||||
break;
|
||||
case WM_LBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
ReleaseCapture();
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,12 +26,13 @@ Renderer::Renderer(IRenderData* pData,
|
||||
_In_reads_(cEngines) IRenderEngine** const rgpEngines,
|
||||
const size_t cEngines,
|
||||
std::unique_ptr<IRenderThread> thread) :
|
||||
_pData(THROW_HR_IF_NULL(E_INVALIDARG, pData)),
|
||||
_pData(pData),
|
||||
_pThread{ std::move(thread) },
|
||||
_destructing{ false },
|
||||
_clusterBuffer{},
|
||||
_viewport{ pData->GetViewport() }
|
||||
_clusterBuffer{}
|
||||
{
|
||||
_srViewportPrevious = { 0 };
|
||||
|
||||
for (size_t i = 0; i < cEngines; i++)
|
||||
{
|
||||
IRenderEngine* engine = rgpEngines[i];
|
||||
@@ -207,15 +208,16 @@ void Renderer::TriggerSystemRedraw(const RECT* const prcDirtyClient)
|
||||
// - <none>
|
||||
void Renderer::TriggerRedraw(const Viewport& region)
|
||||
{
|
||||
Viewport view = _viewport;
|
||||
Viewport view = _pData->GetViewport();
|
||||
SMALL_RECT srUpdateRegion = region.ToExclusive();
|
||||
|
||||
if (view.TrimToViewport(&srUpdateRegion))
|
||||
{
|
||||
view.ConvertToOrigin(&srUpdateRegion);
|
||||
std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) {
|
||||
for (auto pEngine : _rgpEngines)
|
||||
{
|
||||
LOG_IF_FAILED(pEngine->Invalidate(&srUpdateRegion));
|
||||
});
|
||||
}
|
||||
|
||||
_NotifyPaintFrame();
|
||||
}
|
||||
@@ -356,7 +358,7 @@ void Renderer::TriggerSelection()
|
||||
// - True if something changed and we scrolled. False otherwise.
|
||||
bool Renderer::_CheckViewportAndScroll()
|
||||
{
|
||||
SMALL_RECT const srOldViewport = _viewport.ToInclusive();
|
||||
SMALL_RECT const srOldViewport = _srViewportPrevious;
|
||||
SMALL_RECT const srNewViewport = _pData->GetViewport().ToInclusive();
|
||||
|
||||
COORD coordDelta;
|
||||
@@ -368,7 +370,7 @@ bool Renderer::_CheckViewportAndScroll()
|
||||
LOG_IF_FAILED(engine->UpdateViewport(srNewViewport));
|
||||
}
|
||||
|
||||
_viewport = Viewport::FromInclusive(srNewViewport);
|
||||
_srViewportPrevious = srNewViewport;
|
||||
|
||||
// If we're keeping some buffers between calls, let them know about the viewport size
|
||||
// so they can prepare the buffers for changes to either preallocate memory at once
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace Microsoft::Console::Render
|
||||
|
||||
[[nodiscard]] HRESULT _PerformScrolling(_In_ IRenderEngine* const pEngine);
|
||||
|
||||
Microsoft::Console::Types::Viewport _viewport;
|
||||
SMALL_RECT _srViewportPrevious;
|
||||
|
||||
static constexpr float _shrinkThreshold = 0.8f;
|
||||
std::vector<Cluster> _clusterBuffer;
|
||||
|
||||
@@ -78,7 +78,7 @@ CATCH_RETURN()
|
||||
// - clusters - From the backing buffer, the text to be displayed clustered by the columns it should consume.
|
||||
// Return Value:
|
||||
// - S_OK or suitable memory management issue.
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::AppendClusters(const gsl::span<const ::Microsoft::Console::Render::Cluster> clusters)
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::AppendClusters(const std::basic_string_view<::Microsoft::Console::Render::Cluster> clusters)
|
||||
try
|
||||
{
|
||||
_textClusterColumns.reserve(_textClusterColumns.size() + clusters.size());
|
||||
@@ -847,113 +847,57 @@ CATCH_RETURN();
|
||||
auto mutableOrigin = origin;
|
||||
|
||||
// Draw each run separately.
|
||||
for (INT32 runIndex = 0; runIndex < gsl::narrow<INT32>(_runs.size()); ++runIndex)
|
||||
for (UINT32 runIndex = 0; runIndex < _runs.size(); ++runIndex)
|
||||
{
|
||||
// Get the run
|
||||
const Run& run = _runs.at(runIndex);
|
||||
|
||||
if (!WI_IsFlagSet(run.bidiLevel, 1))
|
||||
// Prepare the glyph run and description objects by converting our
|
||||
// internal storage representation into something that matches DWrite's structures.
|
||||
DWRITE_GLYPH_RUN glyphRun;
|
||||
glyphRun.bidiLevel = run.bidiLevel;
|
||||
glyphRun.fontEmSize = _format->GetFontSize() * run.fontScale;
|
||||
glyphRun.fontFace = run.fontFace.Get();
|
||||
glyphRun.glyphAdvances = &_glyphAdvances.at(run.glyphStart);
|
||||
glyphRun.glyphCount = run.glyphCount;
|
||||
glyphRun.glyphIndices = &_glyphIndices.at(run.glyphStart);
|
||||
glyphRun.glyphOffsets = &_glyphOffsets.at(run.glyphStart);
|
||||
glyphRun.isSideways = false;
|
||||
|
||||
DWRITE_GLYPH_RUN_DESCRIPTION glyphRunDescription;
|
||||
glyphRunDescription.clusterMap = _glyphClusters.data();
|
||||
glyphRunDescription.localeName = _localeName.data();
|
||||
glyphRunDescription.string = _text.data();
|
||||
glyphRunDescription.stringLength = run.textLength;
|
||||
glyphRunDescription.textPosition = run.textStart;
|
||||
|
||||
// Calculate the origin for the next run based on the amount of space
|
||||
// that would be consumed. We are doing this calculation now, not after,
|
||||
// because if the text is RTL then we need to advance immediately, before the
|
||||
// write call since DirectX expects the origin to the RIGHT of the text for RTL.
|
||||
const auto postOriginX = std::accumulate(_glyphAdvances.begin() + run.glyphStart,
|
||||
_glyphAdvances.begin() + run.glyphStart + run.glyphCount,
|
||||
mutableOrigin.x);
|
||||
|
||||
// Check for RTL, if it is, apply space adjustment.
|
||||
if (WI_IsFlagSet(glyphRun.bidiLevel, 1))
|
||||
{
|
||||
RETURN_IF_FAILED(_DrawGlyphRun(clientDrawingContext, renderer, mutableOrigin, run));
|
||||
mutableOrigin.x = postOriginX;
|
||||
}
|
||||
// This is the RTL behavior. We will advance to the last contiguous RTL run, draw that,
|
||||
// and then keep on going backwards from there, and then move runIndex beyond.
|
||||
// Let's say we have runs abcdEFGh, where runs EFG are RTL.
|
||||
// Then we will draw them in the order abcdGFEh
|
||||
else
|
||||
{
|
||||
const INT32 originalRunIndex = runIndex;
|
||||
INT32 lastIndexRTL = runIndex;
|
||||
|
||||
// Step 1: Get to the last contiguous RTL run from here
|
||||
while (lastIndexRTL < gsl::narrow<INT32>(_runs.size()) - 1) // only could ever advance if there's something left
|
||||
{
|
||||
const Run& nextRun = _runs.at(gsl::narrow_cast<size_t>(lastIndexRTL + 1));
|
||||
if (WI_IsFlagSet(nextRun.bidiLevel, 1))
|
||||
{
|
||||
lastIndexRTL++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Try to draw it
|
||||
RETURN_IF_FAILED(renderer->DrawGlyphRun(clientDrawingContext,
|
||||
mutableOrigin.x,
|
||||
mutableOrigin.y,
|
||||
DWRITE_MEASURING_MODE_NATURAL,
|
||||
&glyphRun,
|
||||
&glyphRunDescription,
|
||||
run.drawingEffect.Get()));
|
||||
|
||||
// Go from the last to the first and draw
|
||||
for (runIndex = lastIndexRTL; runIndex >= originalRunIndex; runIndex--)
|
||||
{
|
||||
const Run& currentRun = _runs.at(runIndex);
|
||||
RETURN_IF_FAILED(_DrawGlyphRun(clientDrawingContext, renderer, mutableOrigin, currentRun));
|
||||
}
|
||||
runIndex = lastIndexRTL; // and the for loop will take the increment to the last one
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_RETURN();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Draw the given run
|
||||
// - The origin is updated to be after the run.
|
||||
// Arguments:
|
||||
// - clientDrawingContext - Optional pointer to information that the renderer might need
|
||||
// while attempting to graphically place the text onto the screen
|
||||
// - renderer - The interface to be used for actually putting text onto the screen
|
||||
// - origin - pixel point of top left corner on final surface for drawing
|
||||
// - run - the run to be drawn
|
||||
[[nodiscard]] HRESULT CustomTextLayout::_DrawGlyphRun(_In_opt_ void* clientDrawingContext,
|
||||
gsl::not_null<IDWriteTextRenderer*> renderer,
|
||||
D2D_POINT_2F& mutableOrigin,
|
||||
const Run& run) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
// Prepare the glyph run and description objects by converting our
|
||||
// internal storage representation into something that matches DWrite's structures.
|
||||
DWRITE_GLYPH_RUN glyphRun;
|
||||
glyphRun.bidiLevel = run.bidiLevel;
|
||||
glyphRun.fontEmSize = _format->GetFontSize() * run.fontScale;
|
||||
glyphRun.fontFace = run.fontFace.Get();
|
||||
glyphRun.glyphAdvances = &_glyphAdvances.at(run.glyphStart);
|
||||
glyphRun.glyphCount = run.glyphCount;
|
||||
glyphRun.glyphIndices = &_glyphIndices.at(run.glyphStart);
|
||||
glyphRun.glyphOffsets = &_glyphOffsets.at(run.glyphStart);
|
||||
glyphRun.isSideways = false;
|
||||
|
||||
DWRITE_GLYPH_RUN_DESCRIPTION glyphRunDescription;
|
||||
glyphRunDescription.clusterMap = _glyphClusters.data();
|
||||
glyphRunDescription.localeName = _localeName.data();
|
||||
glyphRunDescription.string = _text.data();
|
||||
glyphRunDescription.stringLength = run.textLength;
|
||||
glyphRunDescription.textPosition = run.textStart;
|
||||
|
||||
// Calculate the origin for the next run based on the amount of space
|
||||
// that would be consumed. We are doing this calculation now, not after,
|
||||
// because if the text is RTL then we need to advance immediately, before the
|
||||
// write call since DirectX expects the origin to the RIGHT of the text for RTL.
|
||||
const auto postOriginX = std::accumulate(_glyphAdvances.begin() + run.glyphStart,
|
||||
_glyphAdvances.begin() + run.glyphStart + run.glyphCount,
|
||||
mutableOrigin.x);
|
||||
|
||||
// Check for RTL, if it is, apply space adjustment.
|
||||
if (WI_IsFlagSet(glyphRun.bidiLevel, 1))
|
||||
{
|
||||
// Either way, we should be at this point by the end of writing this sequence,
|
||||
// whether it was LTR or RTL.
|
||||
mutableOrigin.x = postOriginX;
|
||||
}
|
||||
|
||||
// Try to draw it
|
||||
RETURN_IF_FAILED(renderer->DrawGlyphRun(clientDrawingContext,
|
||||
mutableOrigin.x,
|
||||
mutableOrigin.y,
|
||||
DWRITE_MEASURING_MODE_NATURAL,
|
||||
&glyphRun,
|
||||
&glyphRunDescription,
|
||||
run.drawingEffect.Get()));
|
||||
|
||||
// Either way, we should be at this point by the end of writing this sequence,
|
||||
// whether it was LTR or RTL.
|
||||
mutableOrigin.x = postOriginX;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
return S_OK;
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Microsoft::Console::Render
|
||||
size_t const width,
|
||||
IBoxDrawingEffect* const boxEffect);
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE AppendClusters(const gsl::span<const ::Microsoft::Console::Render::Cluster> clusters);
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE AppendClusters(const std::basic_string_view<::Microsoft::Console::Render::Cluster> clusters);
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE Reset() noexcept;
|
||||
|
||||
@@ -147,10 +147,6 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] HRESULT _DrawGlyphRuns(_In_opt_ void* clientDrawingContext,
|
||||
IDWriteTextRenderer* renderer,
|
||||
const D2D_POINT_2F origin) noexcept;
|
||||
[[nodiscard]] HRESULT _DrawGlyphRun(_In_opt_ void* clientDrawingContext,
|
||||
gsl::not_null<IDWriteTextRenderer*> renderer,
|
||||
D2D_POINT_2F& mutableOrigin,
|
||||
const Run& run) noexcept;
|
||||
|
||||
[[nodiscard]] static constexpr UINT32 _EstimateGlyphCount(const UINT32 textLength) noexcept;
|
||||
|
||||
|
||||
@@ -1393,7 +1393,7 @@ try
|
||||
// Use a transform by the size of one cell to convert cells-to-pixels
|
||||
// as we clear.
|
||||
_d2dDeviceContext->SetTransform(D2D1::Matrix3x2F::Scale(_glyphCell));
|
||||
for (const auto& rect : _invalidMap.runs())
|
||||
for (const auto rect : _invalidMap.runs())
|
||||
{
|
||||
// Use aliased.
|
||||
// For graphics reasons, it'll look better because it will ensure that
|
||||
@@ -1419,7 +1419,7 @@ CATCH_RETURN()
|
||||
// - fTrimLeft - Whether or not to trim off the left half of a double wide character
|
||||
// Return Value:
|
||||
// - S_OK or relevant DirectX error
|
||||
[[nodiscard]] HRESULT DxEngine::PaintBufferLine(gsl::span<const Cluster> const clusters,
|
||||
[[nodiscard]] HRESULT DxEngine::PaintBufferLine(std::basic_string_view<Cluster> const clusters,
|
||||
COORD const coord,
|
||||
const bool /*trimLeft*/,
|
||||
const bool /*lineWrapped*/) noexcept
|
||||
@@ -2252,12 +2252,12 @@ CATCH_RETURN();
|
||||
// - color - GDI Color
|
||||
// Return Value:
|
||||
// - N/A
|
||||
void DxEngine::SetSelectionBackground(const COLORREF color, const float alpha) noexcept
|
||||
void DxEngine::SetSelectionBackground(const COLORREF color) noexcept
|
||||
{
|
||||
_selectionBackground = D2D1::ColorF(GetRValue(color) / 255.0f,
|
||||
GetGValue(color) / 255.0f,
|
||||
GetBValue(color) / 255.0f,
|
||||
alpha);
|
||||
0.5f);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT PaintBackground() noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferLine(gsl::span<const Cluster> const clusters,
|
||||
[[nodiscard]] HRESULT PaintBufferLine(std::basic_string_view<Cluster> const clusters,
|
||||
COORD const coord,
|
||||
bool const fTrimLeft,
|
||||
const bool lineWrapped) noexcept override;
|
||||
@@ -114,7 +114,7 @@ namespace Microsoft::Console::Render
|
||||
|
||||
float GetScaling() const noexcept;
|
||||
|
||||
void SetSelectionBackground(const COLORREF color, const float alpha = 0.5f) noexcept;
|
||||
void SetSelectionBackground(const COLORREF color) noexcept;
|
||||
void SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept;
|
||||
void SetDefaultTextBackgroundOpacity(const float opacity) noexcept;
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] HRESULT ScrollFrame() noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT PaintBackground() noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferLine(gsl::span<const Cluster> const clusters,
|
||||
[[nodiscard]] HRESULT PaintBufferLine(std::basic_string_view<Cluster> const clusters,
|
||||
const COORD coord,
|
||||
const bool trimLeft,
|
||||
const bool lineWrapped) noexcept override;
|
||||
|
||||
@@ -284,7 +284,7 @@ using namespace Microsoft::Console::Render;
|
||||
// See: Win7: 390673, 447839 and then superseded by http://osgvsowi/638274 when FE/non-FE rendering condensed.
|
||||
//#define CONSOLE_EXTTEXTOUT_FLAGS ETO_OPAQUE | ETO_CLIPPED
|
||||
//#define MAX_POLY_LINES 80
|
||||
[[nodiscard]] HRESULT GdiEngine::PaintBufferLine(gsl::span<const Cluster> const clusters,
|
||||
[[nodiscard]] HRESULT GdiEngine::PaintBufferLine(std::basic_string_view<Cluster> const clusters,
|
||||
const COORD coord,
|
||||
const bool trimLeft,
|
||||
const bool /*lineWrapped*/) noexcept
|
||||
@@ -316,7 +316,7 @@ using namespace Microsoft::Console::Render;
|
||||
// Convert data from clusters into the text array and the widths array.
|
||||
for (size_t i = 0; i < cchLine; i++)
|
||||
{
|
||||
const auto& cluster = til::at(clusters, i);
|
||||
const auto& cluster = clusters.at(i);
|
||||
|
||||
// Our GDI renderer hasn't and isn't going to handle things above U+FFFF or sequences.
|
||||
// So replace anything complicated with a replacement character for drawing purposes.
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] virtual HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual HRESULT PaintBackground() noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT PaintBufferLine(gsl::span<const Cluster> const clusters,
|
||||
[[nodiscard]] virtual HRESULT PaintBufferLine(std::basic_string_view<Cluster> const clusters,
|
||||
const COORD coord,
|
||||
const bool fTrimLeft,
|
||||
const bool lineWrapped) noexcept = 0;
|
||||
|
||||
@@ -305,7 +305,7 @@ CATCH_RETURN();
|
||||
// - fTrimLeft - Whether or not to trim off the left half of a double wide character
|
||||
// Return Value:
|
||||
// - S_FALSE
|
||||
[[nodiscard]] HRESULT UiaEngine::PaintBufferLine(gsl::span<const Cluster> const /*clusters*/,
|
||||
[[nodiscard]] HRESULT UiaEngine::PaintBufferLine(std::basic_string_view<Cluster> const /*clusters*/,
|
||||
COORD const /*coord*/,
|
||||
const bool /*trimLeft*/,
|
||||
const bool /*lineWrapped*/) noexcept
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT PaintBackground() noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferLine(gsl::span<const Cluster> const clusters,
|
||||
[[nodiscard]] HRESULT PaintBufferLine(std::basic_string_view<Cluster> const clusters,
|
||||
COORD const coord,
|
||||
bool const fTrimLeft,
|
||||
const bool lineWrapped) noexcept override;
|
||||
|
||||
@@ -498,7 +498,7 @@ CATCH_RETURN();
|
||||
// will be false.
|
||||
// Return Value:
|
||||
// - S_OK or suitable HRESULT error from writing pipe.
|
||||
[[nodiscard]] HRESULT XtermEngine::PaintBufferLine(gsl::span<const Cluster> const clusters,
|
||||
[[nodiscard]] HRESULT XtermEngine::PaintBufferLine(std::basic_string_view<Cluster> const clusters,
|
||||
const COORD coord,
|
||||
const bool /*trimLeft*/,
|
||||
const bool lineWrapped) noexcept
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] virtual HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes,
|
||||
const gsl::not_null<IRenderData*> pData,
|
||||
const bool isSettingDefaultBrushes) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferLine(gsl::span<const Cluster> const clusters,
|
||||
[[nodiscard]] HRESULT PaintBufferLine(std::basic_string_view<Cluster> const clusters,
|
||||
const COORD coord,
|
||||
const bool trimLeft,
|
||||
const bool lineWrapped) noexcept override;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user