mirror of
https://github.com/microsoft/terminal.git
synced 2026-05-21 06:18:34 +00:00
Compare commits
38 Commits
dev/lhecke
...
dev/duhowe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1441a53b23 | ||
|
|
d53fbafe13 | ||
|
|
62468c97b2 | ||
|
|
720dfc99fe | ||
|
|
75b6cefb9a | ||
|
|
fddc80c98c | ||
|
|
1507cf55f6 | ||
|
|
48c935df99 | ||
|
|
f5ea864a05 | ||
|
|
c334f91f80 | ||
|
|
14f4271954 | ||
|
|
3104c8feb2 | ||
|
|
01f8a4031d | ||
|
|
2870a702f4 | ||
|
|
ec939aabda | ||
|
|
be5dcf8e7a | ||
|
|
4ce79d7289 | ||
|
|
69e4590bc5 | ||
|
|
a1a43a4ff5 | ||
|
|
3ab2acc2ad | ||
|
|
eea035cb8e | ||
|
|
a7a928cfc9 | ||
|
|
da0446a7d1 | ||
|
|
e0400150d0 | ||
|
|
c562dad15d | ||
|
|
0f5d883c59 | ||
|
|
ad7b34e55f | ||
|
|
ddf514e99e | ||
|
|
d4425a397c | ||
|
|
0e19570468 | ||
|
|
f8b4e19e51 | ||
|
|
2e33056fd8 | ||
|
|
14ee19fc27 | ||
|
|
987fce20a1 | ||
|
|
040c730a44 | ||
|
|
6d7fac999a | ||
|
|
e6ea8ace6d | ||
|
|
36fb444d3e |
8
.github/workflows/spelling2.yml
vendored
8
.github/workflows/spelling2.yml
vendored
@@ -93,7 +93,7 @@ jobs:
|
||||
steps:
|
||||
- name: check-spelling
|
||||
id: spelling
|
||||
uses: check-spelling/check-spelling@v0.0.25
|
||||
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
|
||||
with:
|
||||
suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }}
|
||||
checkout: true
|
||||
@@ -153,7 +153,7 @@ jobs:
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push'
|
||||
steps:
|
||||
- name: comment
|
||||
uses: check-spelling/check-spelling@v0.0.25
|
||||
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
|
||||
with:
|
||||
checkout: true
|
||||
spell_check_this: microsoft/terminal@main
|
||||
@@ -171,7 +171,7 @@ jobs:
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request')
|
||||
steps:
|
||||
- name: comment
|
||||
uses: check-spelling/check-spelling@v0.0.25
|
||||
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
|
||||
with:
|
||||
checkout: true
|
||||
spell_check_this: microsoft/terminal@main
|
||||
@@ -197,7 +197,7 @@ jobs:
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- name: apply spelling updates
|
||||
uses: check-spelling/check-spelling@v0.0.25
|
||||
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
|
||||
with:
|
||||
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }}
|
||||
checkout: true
|
||||
|
||||
@@ -860,17 +860,6 @@
|
||||
<Folder Name="/_Dependencies/Console/">
|
||||
<File Path="dep/Console/winconp.h" />
|
||||
</Folder>
|
||||
<Folder Name="/_Dependencies/gsl/">
|
||||
<File Path="dep/gsl/include/gsl/gsl" />
|
||||
<File Path="dep/gsl/include/gsl/gsl_algorithm" />
|
||||
<File Path="dep/gsl/include/gsl/gsl_assert" />
|
||||
<File Path="dep/gsl/include/gsl/gsl_byte" />
|
||||
<File Path="dep/gsl/include/gsl/gsl_util" />
|
||||
<File Path="dep/gsl/include/gsl/multi_span" />
|
||||
<File Path="dep/gsl/include/gsl/pointers" />
|
||||
<File Path="dep/gsl/include/gsl/span" />
|
||||
<File Path="dep/gsl/include/gsl/string_span" />
|
||||
</Folder>
|
||||
<Folder Name="/_Dependencies/wil/">
|
||||
<File Path="dep/wil/include/wil/common.h" />
|
||||
<File Path="dep/wil/include/wil/filesystem.h" />
|
||||
|
||||
@@ -59,6 +59,7 @@ stages:
|
||||
name: SHINE-INT-L
|
||||
${{ else }}:
|
||||
name: SHINE-OSS-L
|
||||
demands: ImageOverride -equals SHINE-VS18-Latest
|
||||
buildPlatforms: [x64]
|
||||
buildConfigurations: [AuditMode]
|
||||
buildEverything: true
|
||||
@@ -82,6 +83,7 @@ stages:
|
||||
name: SHINE-INT-L
|
||||
${{ else }}:
|
||||
name: SHINE-OSS-L
|
||||
demands: ImageOverride -equals SHINE-VS18-Latest
|
||||
buildPlatforms:
|
||||
- ${{ platform }}
|
||||
buildConfigurations: [Release]
|
||||
|
||||
@@ -58,7 +58,7 @@ parameters:
|
||||
type: object
|
||||
default:
|
||||
name: SHINE-INT-S # By default, send jobs to the small agent pool.
|
||||
demands: ImageOverride -equals SHINE-VS17-Latest
|
||||
demands: ImageOverride -equals SHINE-VS18-Latest
|
||||
|
||||
variables:
|
||||
- template: variables-nuget-package-version.yml
|
||||
@@ -81,7 +81,7 @@ stages:
|
||||
parameters:
|
||||
pool:
|
||||
name: SHINE-INT-L # Run the compilation on the large agent pool, rather than the default small one.
|
||||
demands: ImageOverride -equals SHINE-VS17-Latest
|
||||
demands: ImageOverride -equals SHINE-VS18-Latest
|
||||
branding: ${{ parameters.branding }}
|
||||
buildTerminal: ${{ parameters.buildTerminal }}
|
||||
buildConPTY: ${{ parameters.buildConPTY }}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
steps:
|
||||
- pwsh: |-
|
||||
$VsInstallRoot = & 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -requires Microsoft.VisualStudio.Component.Vcpkg -property installationPath
|
||||
$VsInstallRoot = & 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -requires Microsoft.VisualStudio.Component.Vcpkg -property installationPath -latest
|
||||
If ([String]::IsNullOrEmpty($VsInstallRoot)) {
|
||||
Remove-Item -Recurse -Force dep/vcpkg -ErrorAction:Ignore
|
||||
git clone https://github.com/microsoft/vcpkg dep/vcpkg
|
||||
|
||||
@@ -5,4 +5,4 @@ set(VCPKG_LIBRARY_LINKAGE static)
|
||||
|
||||
# ...but with explicit platform toolset, so that future toolsets
|
||||
# aren't automatically picked up (it defaults to the latest one).
|
||||
set(VCPKG_PLATFORM_TOOLSET v143)
|
||||
set(VCPKG_PLATFORM_TOOLSET v145)
|
||||
|
||||
@@ -5,7 +5,7 @@ set(VCPKG_LIBRARY_LINKAGE static)
|
||||
|
||||
# ...but with explicit platform toolset, so that future toolsets
|
||||
# aren't automatically picked up (it defaults to the latest one).
|
||||
set(VCPKG_PLATFORM_TOOLSET v143)
|
||||
set(VCPKG_PLATFORM_TOOLSET v145)
|
||||
|
||||
set(VCPKG_CXX_FLAGS /fsanitize=address)
|
||||
set(VCPKG_C_FLAGS /fsanitize=address)
|
||||
|
||||
@@ -5,7 +5,7 @@ set(VCPKG_LIBRARY_LINKAGE static)
|
||||
|
||||
# ...but with explicit platform toolset, so that future toolsets
|
||||
# aren't automatically picked up (it defaults to the latest one).
|
||||
set(VCPKG_PLATFORM_TOOLSET v143)
|
||||
set(VCPKG_PLATFORM_TOOLSET v145)
|
||||
|
||||
set(VCPKG_CXX_FLAGS /fsanitize=address)
|
||||
set(VCPKG_C_FLAGS /fsanitize=address)
|
||||
|
||||
@@ -5,7 +5,7 @@ set(VCPKG_LIBRARY_LINKAGE static)
|
||||
|
||||
# ...but with explicit platform toolset, so that future toolsets
|
||||
# aren't automatically picked up (it defaults to the latest one).
|
||||
set(VCPKG_PLATFORM_TOOLSET v143)
|
||||
set(VCPKG_PLATFORM_TOOLSET v145)
|
||||
|
||||
set(VCPKG_CXX_FLAGS /fsanitize=address)
|
||||
set(VCPKG_C_FLAGS /fsanitize=address)
|
||||
|
||||
@@ -5,4 +5,4 @@ set(VCPKG_LIBRARY_LINKAGE static)
|
||||
|
||||
# ...but with explicit platform toolset, so that future toolsets
|
||||
# aren't automatically picked up (it defaults to the latest one).
|
||||
set(VCPKG_PLATFORM_TOOLSET v143)
|
||||
set(VCPKG_PLATFORM_TOOLSET v145)
|
||||
|
||||
@@ -5,4 +5,4 @@ set(VCPKG_LIBRARY_LINKAGE static)
|
||||
|
||||
# ...but with explicit platform toolset, so that future toolsets
|
||||
# aren't automatically picked up (it defaults to the latest one).
|
||||
set(VCPKG_PLATFORM_TOOLSET v143)
|
||||
set(VCPKG_PLATFORM_TOOLSET v145)
|
||||
|
||||
@@ -3174,6 +3174,11 @@
|
||||
"mingw"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"dragDropDelimiter": {
|
||||
"default": " ",
|
||||
"description": "The delimiter used when dropping multiple files onto the terminal.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -219,6 +219,17 @@ til::CoordType TextBuffer::_estimateOffsetOfLastCommittedRow() const noexcept
|
||||
return std::max(0, gsl::narrow_cast<til::CoordType>(lastRowOffset - 2));
|
||||
}
|
||||
|
||||
bool TextBuffer::_isRowCommitted(til::CoordType y) const noexcept
|
||||
{
|
||||
auto offset = (_firstRow + y + 1 /* account for the scratch row */) % _height;
|
||||
if (offset < 0)
|
||||
{
|
||||
offset += _height;
|
||||
}
|
||||
const auto row = _buffer.get() + _bufferRowStride * offset;
|
||||
return row < _commitWatermark;
|
||||
}
|
||||
|
||||
// Retrieves a row from the buffer by its offset from the first row of the text buffer
|
||||
// (what corresponds to the top row of the screen buffer).
|
||||
const ROW& TextBuffer::GetRowByOffset(const til::CoordType index) const
|
||||
@@ -934,6 +945,10 @@ void TextBuffer::ResetLineRenditionRange(const til::CoordType startRow, const ti
|
||||
|
||||
LineRendition TextBuffer::GetLineRendition(const til::CoordType row) const
|
||||
{
|
||||
if (!_isRowCommitted(row)) [[unlikely]]
|
||||
{
|
||||
return LineRendition::SingleWidth;
|
||||
}
|
||||
return GetRowByOffset(row).GetLineRendition();
|
||||
}
|
||||
|
||||
@@ -1141,7 +1156,20 @@ DelimiterClass TextBuffer::_GetDelimiterClassAt(const til::point pos, const std:
|
||||
return GetRowByOffset(realPos.y).DelimiterClassAt(realPos.x, wordDelimiters);
|
||||
}
|
||||
|
||||
til::point TextBuffer::GetWordStart2(til::point pos, const std::wstring_view wordDelimiters, bool includeWhitespace, std::optional<til::point> limitOptional) const
|
||||
// Method Description:
|
||||
// - Get the start of the word at or before the given position
|
||||
// - When includeWhitespace is false, returns the start of the current delimiter class run
|
||||
// (selection behavior: stops at non-wrapped row boundaries)
|
||||
// - When includeWhitespace is true, also skips backward past any leading ControlChars
|
||||
// to include the preceding word (accessibility/UIA behavior: crosses all row boundaries)
|
||||
// Arguments:
|
||||
// - pos - the buffer position to start from
|
||||
// - wordDelimiters - characters considered as DelimiterClass::DelimiterChar
|
||||
// - includeWhitespace - when true, skip past leading whitespace to find the word start
|
||||
// - limitOptional - (optional) the last possible position in the buffer that can be explored
|
||||
// Return Value:
|
||||
// - The position of the first character of the word (inclusive)
|
||||
til::point TextBuffer::GetWordStart(til::point pos, const std::wstring_view wordDelimiters, bool includeWhitespace, std::optional<til::point> limitOptional) const
|
||||
{
|
||||
const auto bufferSize{ GetSize() };
|
||||
const auto limit{ limitOptional.value_or(bufferSize.BottomInclusiveRightExclusive()) };
|
||||
@@ -1174,7 +1202,7 @@ til::point TextBuffer::GetWordStart2(til::point pos, const std::wstring_view wor
|
||||
// 1. move to the beginning of the delimiter class run
|
||||
// 2. (includeWhitespace) if we were on a ControlChar, go back one more delimiter class run
|
||||
const auto initialDelimiter = bufferSize.IsInBounds(pos) ? _GetDelimiterClassAt(pos, wordDelimiters) : DelimiterClass::ControlChar;
|
||||
pos = _GetDelimiterClassRunStart(pos, wordDelimiters);
|
||||
pos = _GetDelimiterClassRunStart(pos, wordDelimiters, includeWhitespace);
|
||||
if (!includeWhitespace || pos.x == bufferSize.Left())
|
||||
{
|
||||
// Special case:
|
||||
@@ -1185,12 +1213,26 @@ til::point TextBuffer::GetWordStart2(til::point pos, const std::wstring_view wor
|
||||
else if (initialDelimiter == DelimiterClass::ControlChar)
|
||||
{
|
||||
bufferSize.DecrementInExclusiveBounds(pos);
|
||||
pos = _GetDelimiterClassRunStart(pos, wordDelimiters);
|
||||
pos = _GetDelimiterClassRunStart(pos, wordDelimiters, includeWhitespace);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
til::point TextBuffer::GetWordEnd2(til::point pos, const std::wstring_view wordDelimiters, bool includeWhitespace, std::optional<til::point> limitOptional) const
|
||||
// Method Description:
|
||||
// - Get the exclusive end of the word at or after the given position
|
||||
// - When includeWhitespace is false, returns the exclusive end of the current delimiter class run
|
||||
// (selection behavior: stops at non-wrapped row boundaries)
|
||||
// - When includeWhitespace is true, also skips forward past any trailing ControlChars
|
||||
// to include trailing whitespace (accessibility/UIA behavior: crosses all row boundaries)
|
||||
// - The result is clamped to limitOptional when provided
|
||||
// Arguments:
|
||||
// - pos - the buffer position to start from
|
||||
// - wordDelimiters - characters considered as DelimiterClass::DelimiterChar
|
||||
// - includeWhitespace - when true, skip past trailing whitespace to find the next word boundary
|
||||
// - limitOptional - (optional) the last possible position in the buffer that can be explored
|
||||
// Return Value:
|
||||
// - The exclusive end position of the word
|
||||
til::point TextBuffer::GetWordEnd(til::point pos, const std::wstring_view wordDelimiters, bool includeWhitespace, std::optional<til::point> limitOptional) const
|
||||
{
|
||||
const auto bufferSize{ GetSize() };
|
||||
const auto limit{ limitOptional.value_or(bufferSize.BottomInclusiveRightExclusive()) };
|
||||
@@ -1223,8 +1265,12 @@ til::point TextBuffer::GetWordEnd2(til::point pos, const std::wstring_view wordD
|
||||
// So the heuristic we use is:
|
||||
// 1. move to the end of the delimiter class run
|
||||
// 2. (includeWhitespace) if the next delimiter class run is a ControlChar, go forward one more delimiter class run
|
||||
pos = _GetDelimiterClassRunEnd(pos, wordDelimiters);
|
||||
if (!includeWhitespace || pos.x == bufferSize.RightExclusive())
|
||||
pos = _GetDelimiterClassRunEnd(pos, wordDelimiters, includeWhitespace);
|
||||
if (pos >= limit)
|
||||
{
|
||||
return limit;
|
||||
}
|
||||
else if (!includeWhitespace || pos.x == bufferSize.RightExclusive())
|
||||
{
|
||||
// Special case:
|
||||
// we're at the right boundary (and end of a delimiter class run),
|
||||
@@ -1235,7 +1281,8 @@ til::point TextBuffer::GetWordEnd2(til::point pos, const std::wstring_view wordD
|
||||
if (const auto nextDelimClass = bufferSize.IsInBounds(pos) ? _GetDelimiterClassAt(pos, wordDelimiters) : DelimiterClass::ControlChar;
|
||||
nextDelimClass == DelimiterClass::ControlChar)
|
||||
{
|
||||
return _GetDelimiterClassRunEnd(pos, wordDelimiters);
|
||||
pos = _GetDelimiterClassRunEnd(pos, wordDelimiters, includeWhitespace);
|
||||
return std::min(pos, limit);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
@@ -1288,29 +1335,48 @@ bool TextBuffer::IsWordBoundary(const til::point pos, const std::wstring_view wo
|
||||
return prevDelimiterClass != currentDelimiterClass && currentDelimiterClass != DelimiterClass::ControlChar;
|
||||
}
|
||||
|
||||
til::point TextBuffer::_GetDelimiterClassRunStart(til::point pos, const std::wstring_view wordDelimiters) const
|
||||
// Method Description:
|
||||
// - Get the start position for the current delimiter class run, scanning backward
|
||||
// - Stops when the delimiter class changes or a row boundary is reached
|
||||
// - When accessibilityMode is true, freely crosses non-wrapped row boundaries
|
||||
// - When accessibilityMode is false, only crosses wrap-forced row boundaries
|
||||
// Arguments:
|
||||
// - pos - the buffer position to start scanning from
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// - accessibilityMode - when true, cross non-wrapped row boundaries freely
|
||||
// Return Value:
|
||||
// - The position of the first character in the current delimiter class run (inclusive)
|
||||
til::point TextBuffer::_GetDelimiterClassRunStart(til::point pos, const std::wstring_view wordDelimiters, const bool accessibilityMode) const
|
||||
{
|
||||
const auto bufferSize = GetSize();
|
||||
const auto initialDelimClass = bufferSize.IsInBounds(pos) ? _GetDelimiterClassAt(pos, wordDelimiters) : DelimiterClass::ControlChar;
|
||||
for (auto nextPos = pos; nextPos != bufferSize.Origin(); pos = nextPos)
|
||||
auto nextPos = pos;
|
||||
while (nextPos != bufferSize.Origin())
|
||||
{
|
||||
bufferSize.DecrementInExclusiveBounds(nextPos);
|
||||
|
||||
if (nextPos.x == bufferSize.RightExclusive())
|
||||
{
|
||||
// wrapped onto previous line,
|
||||
// check if it was forced to wrap
|
||||
// wrapped onto previous line
|
||||
const auto& row = GetRowByOffset(nextPos.y);
|
||||
if (!row.WasWrapForced())
|
||||
if (!row.WasWrapForced() && !accessibilityMode)
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
// In accessibility mode (or if row was wrap-forced), continue
|
||||
// across the row boundary. The actual last character of the
|
||||
// previous row will be checked on the next iteration.
|
||||
// Don't update pos to avoid storing a transient position
|
||||
}
|
||||
else if (_GetDelimiterClassAt(nextPos, wordDelimiters) != initialDelimClass)
|
||||
{
|
||||
// if we changed delim class, we're done (don't apply move)
|
||||
return pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = nextPos;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
@@ -1320,7 +1386,8 @@ til::point TextBuffer::_GetDelimiterClassRunStart(til::point pos, const std::wst
|
||||
// Arguments:
|
||||
// - pos - the buffer position being within the current delimiter class
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
til::point TextBuffer::_GetDelimiterClassRunEnd(til::point pos, const std::wstring_view wordDelimiters) const
|
||||
// - accessibilityMode - when true, cross non-wrapped row boundaries freely
|
||||
til::point TextBuffer::_GetDelimiterClassRunEnd(til::point pos, const std::wstring_view wordDelimiters, const bool accessibilityMode) const
|
||||
{
|
||||
const auto bufferSize = GetSize();
|
||||
const auto initialDelimClass = bufferSize.IsInBounds(pos) ? _GetDelimiterClassAt(pos, wordDelimiters) : DelimiterClass::ControlChar;
|
||||
@@ -1333,7 +1400,16 @@ til::point TextBuffer::_GetDelimiterClassRunEnd(til::point pos, const std::wstri
|
||||
// wrapped onto next line,
|
||||
// check if it was forced to wrap or switched delimiter class
|
||||
const auto& row = GetRowByOffset(pos.y);
|
||||
if (!row.WasWrapForced() || _GetDelimiterClassAt(nextPos, wordDelimiters) != initialDelimClass)
|
||||
if (accessibilityMode)
|
||||
{
|
||||
// In accessibility mode, always cross row boundaries,
|
||||
// but still stop if the delimiter class changes
|
||||
if (_GetDelimiterClassAt(nextPos, wordDelimiters) != initialDelimClass)
|
||||
{
|
||||
return nextPos;
|
||||
}
|
||||
}
|
||||
else if (!row.WasWrapForced() || _GetDelimiterClassAt(nextPos, wordDelimiters) != initialDelimClass)
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
@@ -1348,283 +1424,6 @@ til::point TextBuffer::_GetDelimiterClassRunEnd(til::point pos, const std::wstri
|
||||
return pos;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the til::point for the beginning of the word you are on
|
||||
// Arguments:
|
||||
// - target - a til::point on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// - accessibilityMode - when enabled, we continue expanding left until we are at the beginning of a readable word.
|
||||
// Otherwise, expand left until a character of a new delimiter class is found
|
||||
// (or a row boundary is encountered)
|
||||
// - limitOptional - (optional) the last possible position in the buffer that can be explored. This can be used to improve performance.
|
||||
// Return Value:
|
||||
// - The til::point for the first character on the "word" (inclusive)
|
||||
til::point TextBuffer::GetWordStart(const til::point target, const std::wstring_view wordDelimiters, bool accessibilityMode, std::optional<til::point> limitOptional) const
|
||||
{
|
||||
// Consider a buffer with this text in it:
|
||||
// " word other "
|
||||
// In selection (accessibilityMode = false),
|
||||
// a "word" is defined as the range between two delimiters
|
||||
// so the words in the example include [" ", "word", " ", "other", " "]
|
||||
// In accessibility (accessibilityMode = true),
|
||||
// a "word" includes the delimiters after a range of readable characters
|
||||
// so the words in the example include ["word ", "other "]
|
||||
// NOTE: the start anchor (this one) is inclusive, whereas the end anchor (GetWordEnd) is exclusive
|
||||
|
||||
#pragma warning(suppress : 26496)
|
||||
auto copy{ target };
|
||||
const auto bufferSize{ GetSize() };
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
if (target == bufferSize.Origin())
|
||||
{
|
||||
// can't expand left
|
||||
return target;
|
||||
}
|
||||
else if (target == bufferSize.EndExclusive())
|
||||
{
|
||||
// GH#7664: Treat EndExclusive as EndInclusive so
|
||||
// that it actually points to a space in the buffer
|
||||
copy = bufferSize.BottomRightInclusive();
|
||||
}
|
||||
else if (bufferSize.CompareInBounds(target, limit, true) >= 0)
|
||||
{
|
||||
// if at/past the limit --> clamp to limit
|
||||
copy = limitOptional.value_or(bufferSize.BottomRightInclusive());
|
||||
}
|
||||
|
||||
if (accessibilityMode)
|
||||
{
|
||||
return _GetWordStartForAccessibility(copy, wordDelimiters);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _GetWordStartForSelection(copy, wordDelimiters);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper method for GetWordStart(). Get the til::point for the beginning of the word (accessibility definition) you are on
|
||||
// Arguments:
|
||||
// - target - a til::point on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// Return Value:
|
||||
// - The til::point for the first character on the current/previous READABLE "word" (inclusive)
|
||||
til::point TextBuffer::_GetWordStartForAccessibility(const til::point target, const std::wstring_view wordDelimiters) const
|
||||
{
|
||||
auto result = target;
|
||||
const auto bufferSize = GetSize();
|
||||
|
||||
// ignore left boundary. Continue until readable text found
|
||||
while (_GetDelimiterClassAt(result, wordDelimiters) != DelimiterClass::RegularChar)
|
||||
{
|
||||
if (result == bufferSize.Origin())
|
||||
{
|
||||
//looped around and hit origin (no word between origin and target)
|
||||
return result;
|
||||
}
|
||||
bufferSize.DecrementInBounds(result);
|
||||
}
|
||||
|
||||
// make sure we expand to the left boundary or the beginning of the word
|
||||
while (_GetDelimiterClassAt(result, wordDelimiters) == DelimiterClass::RegularChar)
|
||||
{
|
||||
if (result == bufferSize.Origin())
|
||||
{
|
||||
// first char in buffer is a RegularChar
|
||||
// we can't move any further back
|
||||
return result;
|
||||
}
|
||||
bufferSize.DecrementInBounds(result);
|
||||
}
|
||||
|
||||
// move off of delimiter
|
||||
bufferSize.IncrementInBounds(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper method for GetWordStart(). Get the til::point for the beginning of the word (selection definition) you are on
|
||||
// Arguments:
|
||||
// - target - a til::point on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// Return Value:
|
||||
// - The til::point for the first character on the current word or delimiter run (stopped by the left margin)
|
||||
til::point TextBuffer::_GetWordStartForSelection(const til::point target, const std::wstring_view wordDelimiters) const
|
||||
{
|
||||
auto result = target;
|
||||
const auto bufferSize = GetSize();
|
||||
|
||||
const auto initialDelimiter = _GetDelimiterClassAt(result, wordDelimiters);
|
||||
const bool isControlChar = initialDelimiter == DelimiterClass::ControlChar;
|
||||
|
||||
// expand left until we hit the left boundary or a different delimiter class
|
||||
while (result != bufferSize.Origin() && _GetDelimiterClassAt(result, wordDelimiters) == initialDelimiter)
|
||||
{
|
||||
if (result.x == bufferSize.Left())
|
||||
{
|
||||
// Prevent wrapping to the previous line if the selection begins on whitespace
|
||||
if (isControlChar)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (result.y > 0)
|
||||
{
|
||||
// Prevent wrapping to the previous line if it was hard-wrapped (e.g. not forced by us to wrap)
|
||||
const auto& priorRow = GetRowByOffset(result.y - 1);
|
||||
if (!priorRow.WasWrapForced())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
bufferSize.DecrementInBounds(result);
|
||||
}
|
||||
|
||||
if (_GetDelimiterClassAt(result, wordDelimiters) != initialDelimiter)
|
||||
{
|
||||
// move off of delimiter
|
||||
bufferSize.IncrementInBounds(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the til::point for the beginning of the NEXT word
|
||||
// Arguments:
|
||||
// - target - a til::point on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// - accessibilityMode - when enabled, we continue expanding right until we are at the beginning of the next READABLE word
|
||||
// Otherwise, expand right until a character of a new delimiter class is found
|
||||
// (or a row boundary is encountered)
|
||||
// - limitOptional - (optional) the last possible position in the buffer that can be explored. This can be used to improve performance.
|
||||
// Return Value:
|
||||
// - The til::point for the last character on the "word" (inclusive)
|
||||
til::point TextBuffer::GetWordEnd(const til::point target, const std::wstring_view wordDelimiters, bool accessibilityMode, std::optional<til::point> limitOptional) const
|
||||
{
|
||||
// Consider a buffer with this text in it:
|
||||
// " word other "
|
||||
// In selection (accessibilityMode = false),
|
||||
// a "word" is defined as the range between two delimiters
|
||||
// so the words in the example include [" ", "word", " ", "other", " "]
|
||||
// In accessibility (accessibilityMode = true),
|
||||
// a "word" includes the delimiters after a range of readable characters
|
||||
// so the words in the example include ["word ", "other "]
|
||||
// NOTE: the end anchor (this one) is exclusive, whereas the start anchor (GetWordStart) is inclusive
|
||||
|
||||
// Already at/past the limit. Can't move forward.
|
||||
const auto bufferSize{ GetSize() };
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
if (bufferSize.CompareInBounds(target, limit, true) >= 0)
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
if (accessibilityMode)
|
||||
{
|
||||
return _GetWordEndForAccessibility(target, wordDelimiters, limit);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _GetWordEndForSelection(target, wordDelimiters);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper method for GetWordEnd(). Get the til::point for the beginning of the next READABLE word
|
||||
// Arguments:
|
||||
// - target - a til::point on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// - limit - the last "valid" position in the text buffer (to improve performance)
|
||||
// Return Value:
|
||||
// - The til::point for the first character of the next readable "word". If no next word, return one past the end of the buffer
|
||||
til::point TextBuffer::_GetWordEndForAccessibility(const til::point target, const std::wstring_view wordDelimiters, const til::point limit) const
|
||||
{
|
||||
const auto bufferSize{ GetSize() };
|
||||
auto result{ target };
|
||||
|
||||
if (bufferSize.CompareInBounds(target, limit, true) >= 0)
|
||||
{
|
||||
// if we're already on/past the last RegularChar,
|
||||
// clamp result to that position
|
||||
result = limit;
|
||||
|
||||
// make the result exclusive
|
||||
bufferSize.IncrementInBounds(result, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (result != limit && result != bufferSize.BottomRightInclusive() && _GetDelimiterClassAt(result, wordDelimiters) == DelimiterClass::RegularChar)
|
||||
{
|
||||
// Iterate through readable text
|
||||
bufferSize.IncrementInBounds(result);
|
||||
}
|
||||
|
||||
while (result != limit && result != bufferSize.BottomRightInclusive() && _GetDelimiterClassAt(result, wordDelimiters) != DelimiterClass::RegularChar)
|
||||
{
|
||||
// expand to the beginning of the NEXT word
|
||||
bufferSize.IncrementInBounds(result);
|
||||
}
|
||||
|
||||
// Special case: we tried to move one past the end of the buffer
|
||||
// Manually increment onto the EndExclusive point.
|
||||
if (result == bufferSize.BottomRightInclusive())
|
||||
{
|
||||
bufferSize.IncrementInBounds(result, true);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper method for GetWordEnd(). Get the til::point for the beginning of the NEXT word
|
||||
// Arguments:
|
||||
// - target - a til::point on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// Return Value:
|
||||
// - The til::point for the last character of the current word or delimiter run (stopped by right margin)
|
||||
til::point TextBuffer::_GetWordEndForSelection(const til::point target, const std::wstring_view wordDelimiters) const
|
||||
{
|
||||
const auto bufferSize = GetSize();
|
||||
|
||||
auto result = target;
|
||||
const auto initialDelimiter = _GetDelimiterClassAt(result, wordDelimiters);
|
||||
const bool isControlChar = initialDelimiter == DelimiterClass::ControlChar;
|
||||
|
||||
// expand right until we hit the right boundary as a ControlChar or a different delimiter class
|
||||
while (result != bufferSize.BottomRightInclusive() && _GetDelimiterClassAt(result, wordDelimiters) == initialDelimiter)
|
||||
{
|
||||
if (result.x == bufferSize.RightInclusive())
|
||||
{
|
||||
// Prevent wrapping to the next line if the selection begins on whitespace
|
||||
if (isControlChar)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Prevent wrapping to the next line if this one was hard-wrapped (e.g. not forced by us to wrap)
|
||||
const auto& row = GetRowByOffset(result.y);
|
||||
if (!row.WasWrapForced())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bufferSize.IncrementInBounds(result);
|
||||
}
|
||||
|
||||
if (_GetDelimiterClassAt(result, wordDelimiters) != initialDelimiter)
|
||||
{
|
||||
// move off of delimiter
|
||||
bufferSize.DecrementInBounds(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void TextBuffer::_PruneHyperlinks()
|
||||
{
|
||||
// Check the old first row for hyperlink references
|
||||
@@ -1671,57 +1470,6 @@ void TextBuffer::_PruneHyperlinks()
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update pos to be the position of the first character of the next word. This is used for accessibility
|
||||
// Arguments:
|
||||
// - pos - a til::point on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// - limitOptional - (optional) the last possible position in the buffer that can be explored. This can be used to improve performance.
|
||||
// Return Value:
|
||||
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
|
||||
// - pos - The til::point for the first character on the "word" (inclusive)
|
||||
bool TextBuffer::MoveToNextWord(til::point& pos, const std::wstring_view wordDelimiters, std::optional<til::point> limitOptional) const
|
||||
{
|
||||
// move to the beginning of the next word
|
||||
// NOTE: _GetWordEnd...() returns the exclusive position of the "end of the word"
|
||||
// This is also the inclusive start of the next word.
|
||||
const auto bufferSize{ GetSize() };
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
const auto copy{ _GetWordEndForAccessibility(pos, wordDelimiters, limit) };
|
||||
|
||||
if (bufferSize.CompareInBounds(copy, limit, true) >= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pos = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update pos to be the position of the first character of the previous word. This is used for accessibility
|
||||
// Arguments:
|
||||
// - pos - a til::point on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// Return Value:
|
||||
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
|
||||
// - pos - The til::point for the first character on the "word" (inclusive)
|
||||
bool TextBuffer::MoveToPreviousWord(til::point& pos, std::wstring_view wordDelimiters) const
|
||||
{
|
||||
// move to the beginning of the current word
|
||||
auto copy{ GetWordStart(pos, wordDelimiters, true) };
|
||||
|
||||
if (!GetSize().DecrementInBounds(copy, true))
|
||||
{
|
||||
// can't move behind current word
|
||||
return false;
|
||||
}
|
||||
|
||||
// move to the beginning of the previous word
|
||||
pos = GetWordStart(copy, wordDelimiters, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update pos to be the beginning of the current glyph/character. This is used for accessibility
|
||||
// Arguments:
|
||||
@@ -3779,3 +3527,34 @@ void TextBuffer::ManuallyMarkRowAsPrompt(til::CoordType y)
|
||||
attr.SetMarkAttributes(MarkKind::Prompt);
|
||||
}
|
||||
}
|
||||
|
||||
// This is an optimization used by the renderer to avoid scheduling a timer if not necessary;
|
||||
// unlike the renderer, we know the committed range of our own buffer.
|
||||
bool TextBuffer::ContainsBlinkAttributeInRegion(const Microsoft::Console::Types::Viewport& region) const
|
||||
{
|
||||
const auto top = region.Top();
|
||||
auto bottom = std::min(region.BottomInclusive(), _estimateOffsetOfLastCommittedRow());
|
||||
|
||||
for (auto row = top; row < bottom; ++row)
|
||||
{
|
||||
const auto& r = GetRowByOffset(row);
|
||||
for (const auto& attr : r.Attributes())
|
||||
{
|
||||
if (attr.IsBlinking())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TextBuffer::IsGlyphDoubleWidthAt(const til::point at) const
|
||||
{
|
||||
if (!_isRowCommitted(at.y)) [[unlikely]]
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return _getRow(at.y).DbcsAttrAt(at.x) != DbcsAttribute::Single;
|
||||
}
|
||||
|
||||
@@ -172,15 +172,10 @@ public:
|
||||
void TriggerNewTextNotification(const std::wstring_view newText);
|
||||
void TriggerSelection();
|
||||
|
||||
til::point GetWordStart(const til::point target, const std::wstring_view wordDelimiters, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
til::point GetWordEnd(const til::point target, const std::wstring_view wordDelimiters, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
|
||||
til::point GetWordStart2(til::point pos, const std::wstring_view wordDelimiters, bool includeWhitespace, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
til::point GetWordEnd2(til::point pos, const std::wstring_view wordDelimiters, bool includeWhitespace, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
til::point GetWordStart(til::point pos, const std::wstring_view wordDelimiters, bool includeWhitespace, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
til::point GetWordEnd(til::point pos, const std::wstring_view wordDelimiters, bool includeWhitespace, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
|
||||
bool IsWordBoundary(const til::point pos, const std::wstring_view wordDelimiters) const;
|
||||
bool MoveToNextWord(til::point& pos, const std::wstring_view wordDelimiters, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
bool MoveToPreviousWord(til::point& pos, const std::wstring_view wordDelimiters) const;
|
||||
|
||||
til::point GetGlyphStart(const til::point pos, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
til::point GetGlyphEnd(const til::point pos, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
@@ -315,6 +310,9 @@ public:
|
||||
void SetScrollbarData(ScrollbarData mark, til::CoordType y);
|
||||
void ManuallyMarkRowAsPrompt(til::CoordType y);
|
||||
|
||||
bool ContainsBlinkAttributeInRegion(const Microsoft::Console::Types::Viewport& region) const;
|
||||
bool IsGlyphDoubleWidthAt(const til::point at) const;
|
||||
|
||||
private:
|
||||
void _reserve(til::size screenBufferSize, const TextAttribute& defaultAttributes);
|
||||
void _commit(const std::byte* row);
|
||||
@@ -324,16 +322,13 @@ private:
|
||||
ROW& _getRowByOffsetDirect(size_t offset);
|
||||
ROW& _getRow(til::CoordType y) const;
|
||||
til::CoordType _estimateOffsetOfLastCommittedRow() const noexcept;
|
||||
bool _isRowCommitted(til::CoordType y) const noexcept;
|
||||
|
||||
void _SetFirstRowIndex(const til::CoordType FirstRowIndex) noexcept;
|
||||
void _ExpandTextRow(til::inclusive_rect& selectionRow) const;
|
||||
DelimiterClass _GetDelimiterClassAt(const til::point pos, const std::wstring_view wordDelimiters) const;
|
||||
til::point _GetDelimiterClassRunStart(til::point pos, const std::wstring_view wordDelimiters) const;
|
||||
til::point _GetDelimiterClassRunEnd(til::point pos, const std::wstring_view wordDelimiters) const;
|
||||
til::point _GetWordStartForAccessibility(const til::point target, const std::wstring_view wordDelimiters) const;
|
||||
til::point _GetWordStartForSelection(const til::point target, const std::wstring_view wordDelimiters) const;
|
||||
til::point _GetWordEndForAccessibility(const til::point target, const std::wstring_view wordDelimiters, const til::point limit) const;
|
||||
til::point _GetWordEndForSelection(const til::point target, const std::wstring_view wordDelimiters) const;
|
||||
til::point _GetDelimiterClassRunStart(til::point pos, const std::wstring_view wordDelimiters, const bool accessibilityMode = false) const;
|
||||
til::point _GetDelimiterClassRunEnd(til::point pos, const std::wstring_view wordDelimiters, const bool accessibilityMode = false) const;
|
||||
void _PruneHyperlinks();
|
||||
|
||||
std::wstring _commandForRow(const til::CoordType rowOffset, const til::CoordType bottomInclusive, const bool clipAtCursor = false) const;
|
||||
|
||||
@@ -34,6 +34,16 @@ class UTextAdapterTests
|
||||
{
|
||||
TEST_CLASS(UTextAdapterTests);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
wil::unique_hmodule icu{ LoadLibraryExW(L"icu.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32) };
|
||||
if (!icu)
|
||||
{
|
||||
WEX::Logging::Log::Result(WEX::Logging::TestResults::Skipped, L"ICU is not present");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_METHOD(Unicode)
|
||||
{
|
||||
DummyRenderer renderer;
|
||||
|
||||
@@ -26,6 +26,9 @@ TARGETLIBS = \
|
||||
$(CONSOLE_OBJ_PATH)\types\lib\$(O)\ConTypes.lib \
|
||||
$(TARGETLIBS) \
|
||||
|
||||
DELAYLOAD = \
|
||||
icu.dll \
|
||||
|
||||
# -------------------------------------
|
||||
# Localization
|
||||
# -------------------------------------
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
ResourceKey="TabViewBackground" />
|
||||
|
||||
<SolidColorBrush x:Key="SettingsUiTabBrush"
|
||||
Color="#0c0c0c" />
|
||||
Color="#282828" />
|
||||
|
||||
<SolidColorBrush x:Key="BroadcastPaneBorderColor"
|
||||
Color="{StaticResource SystemAccentColorDark2}" />
|
||||
@@ -211,7 +211,7 @@
|
||||
ResourceKey="TabViewBackground" />
|
||||
|
||||
<SolidColorBrush x:Key="SettingsUiTabBrush"
|
||||
Color="#ffffff" />
|
||||
Color="#F9F9F9" />
|
||||
|
||||
<SolidColorBrush x:Key="BroadcastPaneBorderColor"
|
||||
Color="{StaticResource SystemAccentColorLight2}" />
|
||||
@@ -234,7 +234,7 @@
|
||||
ResourceKey="SystemColorButtonFaceColorBrush" />
|
||||
|
||||
<StaticResource x:Key="SettingsUiTabBrush"
|
||||
ResourceKey="SystemControlBackgroundBaseLowBrush" />
|
||||
ResourceKey="SystemColorWindowBrush" />
|
||||
|
||||
<SolidColorBrush x:Key="BroadcastPaneBorderColor"
|
||||
Color="{StaticResource SystemColorHighlightColor}" />
|
||||
|
||||
@@ -1475,7 +1475,7 @@ namespace winrt::TerminalApp::implementation
|
||||
WI_IsAnyFlagSet(source, SuggestionsSource::CommandHistory | SuggestionsSource::QuickFixes);
|
||||
if (const auto& control{ _GetActiveControl() })
|
||||
{
|
||||
currentWorkingDirectory = control.CurrentWorkingDirectory();
|
||||
currentWorkingDirectory = control.WorkingDirectory();
|
||||
|
||||
if (shouldGetContext)
|
||||
{
|
||||
|
||||
@@ -1600,8 +1600,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// Replace the Starting directory with the CWD, if given
|
||||
const auto workingDirectory = control.WorkingDirectory();
|
||||
const auto validWorkingDirectory = !workingDirectory.empty();
|
||||
if (validWorkingDirectory)
|
||||
if (Utils::IsValidDirectory(workingDirectory.c_str()))
|
||||
{
|
||||
controlSettings.DefaultSettings()->StartingDirectory(workingDirectory);
|
||||
}
|
||||
@@ -2973,7 +2972,11 @@ namespace winrt::TerminalApp::implementation
|
||||
text = winrt::hstring{ Utils::TrimPaste(text) };
|
||||
}
|
||||
|
||||
if (text.empty())
|
||||
// LOAD BEARING: Send an empty bracketed paste even if the clipboard was empty.
|
||||
// Bracketed Paste provides an application a way to know whether the
|
||||
// user pasted, even if there was no applicable content on it. This
|
||||
// behavior is observed in GNOME Terminal, among others.
|
||||
if (!bracketedPaste && text.empty())
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
@@ -3555,8 +3558,7 @@ namespace winrt::TerminalApp::implementation
|
||||
profile = GetClosestProfileForDuplicationOfProfile(profile);
|
||||
controlSettings = Settings::TerminalSettings::CreateWithProfile(_settings, profile);
|
||||
const auto workingDirectory = tabImpl->GetActiveTerminalControl().WorkingDirectory();
|
||||
const auto validWorkingDirectory = !workingDirectory.empty();
|
||||
if (validWorkingDirectory)
|
||||
if (Utils::IsValidDirectory(workingDirectory.c_str()))
|
||||
{
|
||||
controlSettings.DefaultSettings()->StartingDirectory(workingDirectory);
|
||||
}
|
||||
@@ -3742,7 +3744,13 @@ namespace winrt::TerminalApp::implementation
|
||||
// for nulls
|
||||
if (const auto& connection{ _duplicateConnectionForRestart(paneContent) })
|
||||
{
|
||||
paneContent.GetTermControl().Connection(connection);
|
||||
// Reset the terminal's VT state before attaching the new connection.
|
||||
// The previous client may have left dirty modes (e.g., bracketed
|
||||
// paste, mouse tracking, alternate buffer, kitty keyboard) that
|
||||
// would corrupt input/output for the new shell process.
|
||||
const auto& termControl = paneContent.GetTermControl();
|
||||
termControl.HardResetWithoutErase();
|
||||
termControl.Connection(connection);
|
||||
connection.Start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
args.Profile(::Microsoft::Console::Utils::GuidToString(_profile.Guid()));
|
||||
// If we know the user's working directory use it instead of the profile.
|
||||
if (const auto dir = _control.WorkingDirectory(); !dir.empty())
|
||||
if (const auto dir = _control.WorkingDirectory(); ::Microsoft::Console::Utils::IsValidDirectory(dir.c_str()))
|
||||
{
|
||||
args.StartingDirectory(dir);
|
||||
}
|
||||
|
||||
@@ -355,6 +355,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void ControlCore::HardResetWithoutErase()
|
||||
{
|
||||
const auto lock = _terminal->LockForWriting();
|
||||
_terminal->HardResetWithoutErase();
|
||||
}
|
||||
|
||||
bool ControlCore::Initialize(const float actualWidth,
|
||||
const float actualHeight,
|
||||
const float compositionScale)
|
||||
@@ -2075,7 +2081,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// the selection (we need to reset selection on double-click or
|
||||
// triple-click, so it captures the word or the line, rather than
|
||||
// extending the selection)
|
||||
if (_terminal->IsSelectionActive() && (!shiftEnabled || isOnOriginalPosition))
|
||||
// - GH#9608: VT mouse mode is enabled. In this mode, Shift is used
|
||||
// to override mouse input, so Shift+Click should start a fresh
|
||||
// selection rather than extending the previous one.
|
||||
if (_terminal->IsSelectionActive() && (!shiftEnabled || isOnOriginalPosition || _terminal->IsTrackingMouseInput()))
|
||||
{
|
||||
// Reset the selection
|
||||
_terminal->ClearSelection();
|
||||
@@ -2417,11 +2426,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return *context;
|
||||
}
|
||||
|
||||
winrt::hstring ControlCore::CurrentWorkingDirectory() const
|
||||
{
|
||||
return winrt::hstring{ _terminal->GetWorkingDirectory() };
|
||||
}
|
||||
|
||||
bool ControlCore::QuickFixesAvailable() const noexcept
|
||||
{
|
||||
return _cachedQuickFixes && _cachedQuickFixes.Size() > 0;
|
||||
|
||||
@@ -191,8 +191,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void ContextMenuSelectCommand();
|
||||
void ContextMenuSelectOutput();
|
||||
|
||||
winrt::hstring CurrentWorkingDirectory() const;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ITerminalInput
|
||||
@@ -257,6 +255,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
TerminalConnection::ITerminalConnection Connection();
|
||||
void Connection(const TerminalConnection::ITerminalConnection& connection);
|
||||
void HardResetWithoutErase();
|
||||
|
||||
void AnchorContextMenu(til::point viewportRelativeCharacterPosition);
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ namespace Microsoft.Terminal.Control
|
||||
void ApplyAppearance(Boolean focused);
|
||||
|
||||
Microsoft.Terminal.TerminalConnection.ITerminalConnection Connection;
|
||||
void HardResetWithoutErase();
|
||||
|
||||
IControlSettings Settings { get; };
|
||||
IControlAppearance FocusedAppearance { get; };
|
||||
|
||||
@@ -54,6 +54,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
self->Attached.raise(*self, nullptr);
|
||||
}
|
||||
});
|
||||
|
||||
// GH#14464: Mark mode and quick-edit (shift+arrow) selections update
|
||||
// the selection through ControlCore, bypassing SetEndSelectionPoint.
|
||||
// Listen for selection changes so _selectionNeedsToBeCopied is set
|
||||
// for ALL selection types, not just mouse drag.
|
||||
_core->UpdateSelectionMarkers([weakThis = get_weak()](auto&&, auto&&) {
|
||||
if (auto self{ weakThis.get() })
|
||||
{
|
||||
if (self->_core->HasSelection())
|
||||
{
|
||||
self->_selectionNeedsToBeCopied = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
uint64_t ControlInteractivity::Id()
|
||||
@@ -155,6 +169,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void ControlInteractivity::GotFocus()
|
||||
{
|
||||
_focused = true;
|
||||
|
||||
if (_uiaEngine.get())
|
||||
{
|
||||
THROW_IF_FAILED(_uiaEngine->Enable());
|
||||
@@ -167,6 +183,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void ControlInteractivity::LostFocus()
|
||||
{
|
||||
_focused = false;
|
||||
|
||||
if (_uiaEngine.get())
|
||||
{
|
||||
THROW_IF_FAILED(_uiaEngine->Disable());
|
||||
@@ -234,7 +252,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
PasteFromClipboard.raise(*this, std::move(args));
|
||||
}
|
||||
|
||||
void ControlInteractivity::PointerPressed(Control::MouseButtonState buttonState,
|
||||
void ControlInteractivity::PointerPressed(const uint32_t /*pointerId*/,
|
||||
Control::MouseButtonState buttonState,
|
||||
const unsigned int pointerUpdateKind,
|
||||
const uint64_t timestamp,
|
||||
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
|
||||
@@ -247,6 +266,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const auto shiftEnabled = modifiers.IsShiftPressed();
|
||||
const auto ctrlEnabled = modifiers.IsCtrlPressed();
|
||||
|
||||
// Mark that this pointer event actually started within our bounds.
|
||||
// We'll need this later, for PointerMoved events.
|
||||
_pointerPressedInBounds = true;
|
||||
|
||||
// GH#9396: we prioritize hyper-link over VT mouse events
|
||||
auto hyperlink = _core->GetHyperlink(terminalPosition.to_core_point());
|
||||
if (WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown) &&
|
||||
@@ -285,8 +308,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
const auto isOnOriginalPosition = _lastMouseClickPosNoSelection == pixelPosition;
|
||||
|
||||
// Rounded coordinates for text selection
|
||||
_core->LeftClickOnTerminal(_getTerminalPosition(til::point{ pixelPosition }, true),
|
||||
// Rounded coordinates for text selection.
|
||||
// Don't round in VT mouse mode; cell-level precision matters more
|
||||
const auto round = !_core->IsVtMouseModeEnabled();
|
||||
_core->LeftClickOnTerminal(_getTerminalPosition(til::point{ pixelPosition }, round),
|
||||
multiClickMapper,
|
||||
altEnabled,
|
||||
shiftEnabled,
|
||||
@@ -296,8 +321,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
if (_core->HasSelection())
|
||||
{
|
||||
// GH#9787: if selection is active we don't want to track the touchdown position
|
||||
// so that dragging the mouse will extend the selection rather than starting the new one
|
||||
_singleClickTouchdownPos = std::nullopt;
|
||||
// so that dragging the mouse will extend the selection rather than starting the new one.
|
||||
// In VT mouse mode, keep tracking the touchdown point so that PointerMoved
|
||||
// can re-anchor the selection based on drag direction (the dx < 0 adjustment).
|
||||
// Without this, dragging left wouldn't include the initially clicked cell
|
||||
// because floored coordinates place the anchor on the cell's left edge.
|
||||
if (!_core->IsVtMouseModeEnabled())
|
||||
{
|
||||
_singleClickTouchdownPos = std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (WI_IsFlagSet(buttonState, MouseButtonState::IsRightButtonDown))
|
||||
@@ -314,37 +346,41 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to copy the text and clear the selection
|
||||
const auto successfulCopy = CopySelectionToClipboard(shiftEnabled, false, _core->Settings().CopyFormatting());
|
||||
// GH#19942, GH#14464: Don't re-copy a selection that was
|
||||
// already copied via copyOnSelect on mouse-up. But DO copy
|
||||
// if the selection was made via mark mode or modified with
|
||||
// quick-edit keys (shift+arrow), since those paths never
|
||||
// triggered an automatic copy.
|
||||
const auto copied = (_selectionNeedsToBeCopied || !_core->CopyOnSelect()) &&
|
||||
CopySelectionToClipboard(shiftEnabled, false, _core->Settings().CopyFormatting());
|
||||
_core->ClearSelection();
|
||||
if (_core->CopyOnSelect() || !successfulCopy)
|
||||
if (_core->CopyOnSelect() || !copied)
|
||||
{
|
||||
// CopyOnSelect: right click always pastes!
|
||||
// Otherwise: no selection --> paste
|
||||
// CopyOnSelect: right-click always pastes.
|
||||
// Otherwise: no selection → paste.
|
||||
RequestPasteTextFromClipboard();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ControlInteractivity::TouchPressed(const winrt::Windows::Foundation::Point contactPoint)
|
||||
void ControlInteractivity::TouchPressed(const Core::Point contactPoint)
|
||||
{
|
||||
_touchAnchor = contactPoint;
|
||||
}
|
||||
|
||||
bool ControlInteractivity::PointerMoved(Control::MouseButtonState buttonState,
|
||||
bool ControlInteractivity::PointerMoved(const uint32_t /*pointerId*/,
|
||||
Control::MouseButtonState buttonState,
|
||||
const unsigned int pointerUpdateKind,
|
||||
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
|
||||
const bool focused,
|
||||
const Core::Point pixelPosition,
|
||||
const bool pointerPressedInBounds)
|
||||
const Core::Point pixelPosition)
|
||||
{
|
||||
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, false);
|
||||
// Returning true from this function indicates that the caller should do no further processing of this movement.
|
||||
bool handledCompletely = false;
|
||||
|
||||
// Short-circuit isReadOnly check to avoid warning dialog
|
||||
if (focused && !_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
|
||||
if (_focused && !_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
|
||||
{
|
||||
_sendMouseEventHelper(terminalPosition, pointerUpdateKind, modifiers, 0, buttonState);
|
||||
handledCompletely = true;
|
||||
@@ -353,7 +389,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// actually start _in_ the control bounds. Case in point - someone drags
|
||||
// a file into the bounds of the control. That shouldn't send the
|
||||
// selection into space.
|
||||
else if (focused && pointerPressedInBounds && WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
|
||||
else if (_focused && _pointerPressedInBounds && WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
|
||||
{
|
||||
if (_singleClickTouchdownPos)
|
||||
{
|
||||
@@ -399,10 +435,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return handledCompletely;
|
||||
}
|
||||
|
||||
void ControlInteractivity::TouchMoved(const winrt::Windows::Foundation::Point newTouchPoint,
|
||||
const bool focused)
|
||||
void ControlInteractivity::TouchMoved(const Core::Point newTouchPoint)
|
||||
{
|
||||
if (focused &&
|
||||
if (_focused &&
|
||||
_touchAnchor)
|
||||
{
|
||||
const auto anchor = _touchAnchor.value();
|
||||
@@ -436,11 +471,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void ControlInteractivity::PointerReleased(Control::MouseButtonState buttonState,
|
||||
void ControlInteractivity::PointerReleased(const uint32_t /*pointerId*/,
|
||||
Control::MouseButtonState buttonState,
|
||||
const unsigned int pointerUpdateKind,
|
||||
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
|
||||
const Core::Point pixelPosition)
|
||||
{
|
||||
_pointerPressedInBounds = false;
|
||||
|
||||
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, false);
|
||||
// Short-circuit isReadOnly check to avoid warning dialog
|
||||
if (!_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
|
||||
@@ -682,7 +720,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - cursorPosition: in pixels, relative to the origin of the control
|
||||
void ControlInteractivity::SetEndSelectionPoint(const Core::Point pixelPosition)
|
||||
{
|
||||
_core->SetEndSelectionPoint(_getTerminalPosition(til::point{ pixelPosition }, true));
|
||||
// Don't round in VT mouse mode; cell-level precision matters more
|
||||
const auto round = !_core->IsVtMouseModeEnabled();
|
||||
_core->SetEndSelectionPoint(_getTerminalPosition(til::point{ pixelPosition }, round));
|
||||
_selectionNeedsToBeCopied = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,23 +51,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
::Microsoft::Console::Render::IRenderData* GetRenderData() const;
|
||||
|
||||
#pragma region Input Methods
|
||||
void PointerPressed(Control::MouseButtonState buttonState,
|
||||
void PointerPressed(const uint32_t pointerId,
|
||||
Control::MouseButtonState buttonState,
|
||||
const unsigned int pointerUpdateKind,
|
||||
const uint64_t timestamp,
|
||||
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
|
||||
const Core::Point pixelPosition);
|
||||
void TouchPressed(const winrt::Windows::Foundation::Point contactPoint);
|
||||
void TouchPressed(const Core::Point contactPoint);
|
||||
|
||||
bool PointerMoved(Control::MouseButtonState buttonState,
|
||||
bool PointerMoved(const uint32_t pointerId,
|
||||
Control::MouseButtonState buttonState,
|
||||
const unsigned int pointerUpdateKind,
|
||||
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
|
||||
const bool focused,
|
||||
const Core::Point pixelPosition,
|
||||
const bool pointerPressedInBounds);
|
||||
void TouchMoved(const winrt::Windows::Foundation::Point newTouchPoint,
|
||||
const bool focused);
|
||||
const Core::Point pixelPosition);
|
||||
void TouchMoved(const Core::Point newTouchPoint);
|
||||
|
||||
void PointerReleased(Control::MouseButtonState buttonState,
|
||||
void PointerReleased(const uint32_t pointerId,
|
||||
Control::MouseButtonState buttonState,
|
||||
const unsigned int pointerUpdateKind,
|
||||
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
|
||||
const Core::Point pixelPosition);
|
||||
@@ -115,7 +115,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
// If this is set, then we assume we are in the middle of panning the
|
||||
// viewport via touch input.
|
||||
std::optional<winrt::Windows::Foundation::Point> _touchAnchor;
|
||||
std::optional<Core::Point> _touchAnchor;
|
||||
|
||||
using Timestamp = uint64_t;
|
||||
|
||||
@@ -142,6 +142,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
uint64_t _id;
|
||||
static std::atomic<uint64_t> _nextId;
|
||||
|
||||
bool _focused{ false };
|
||||
bool _pointerPressedInBounds{ false };
|
||||
|
||||
unsigned int _numberOfClicks(Core::Point clickPos, Timestamp clickTime);
|
||||
void _updateSystemParameterSettings() noexcept;
|
||||
|
||||
|
||||
@@ -36,24 +36,24 @@ namespace Microsoft.Terminal.Control
|
||||
void RequestPasteTextFromClipboard();
|
||||
void SetEndSelectionPoint(Microsoft.Terminal.Core.Point point);
|
||||
|
||||
void PointerPressed(MouseButtonState buttonState,
|
||||
void PointerPressed(UInt32 pointerId,
|
||||
MouseButtonState buttonState,
|
||||
UInt32 pointerUpdateKind,
|
||||
UInt64 timestamp,
|
||||
Microsoft.Terminal.Core.ControlKeyStates modifiers,
|
||||
Microsoft.Terminal.Core.Point pixelPosition);
|
||||
void TouchPressed(Windows.Foundation.Point contactPoint);
|
||||
void TouchPressed(Microsoft.Terminal.Core.Point contactPoint);
|
||||
|
||||
Boolean PointerMoved(MouseButtonState buttonState,
|
||||
Boolean PointerMoved(UInt32 pointerId,
|
||||
MouseButtonState buttonState,
|
||||
UInt32 pointerUpdateKind,
|
||||
Microsoft.Terminal.Core.ControlKeyStates modifiers,
|
||||
Boolean focused,
|
||||
Microsoft.Terminal.Core.Point pixelPosition,
|
||||
Boolean pointerPressedInBounds);
|
||||
Microsoft.Terminal.Core.Point pixelPosition);
|
||||
|
||||
void TouchMoved(Windows.Foundation.Point newTouchPoint,
|
||||
Boolean focused);
|
||||
void TouchMoved(Microsoft.Terminal.Core.Point newTouchPoint);
|
||||
|
||||
void PointerReleased(MouseButtonState buttonState,
|
||||
void PointerReleased(UInt32 pointerId,
|
||||
MouseButtonState buttonState,
|
||||
UInt32 pointerUpdateKind,
|
||||
Microsoft.Terminal.Core.ControlKeyStates modifiers,
|
||||
Microsoft.Terminal.Core.Point pixelPosition);
|
||||
|
||||
@@ -76,6 +76,7 @@ namespace Microsoft.Terminal.Control
|
||||
Boolean RepositionCursorWithMouse { get; };
|
||||
|
||||
PathTranslationStyle PathTranslationStyle { get; };
|
||||
String DragDropDelimiter { get; };
|
||||
|
||||
// NOTE! When adding something here, make sure to update ControlProperties.h too!
|
||||
};
|
||||
|
||||
@@ -60,7 +60,5 @@ namespace Microsoft.Terminal.Control
|
||||
void SelectOutput(Boolean goUp);
|
||||
IVector<ScrollMark> ScrollMarks { get; };
|
||||
|
||||
String CurrentWorkingDirectory { get; };
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -532,6 +532,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_core.Connection(newConnection);
|
||||
}
|
||||
|
||||
void TermControl::HardResetWithoutErase()
|
||||
{
|
||||
_core.HardResetWithoutErase();
|
||||
}
|
||||
|
||||
void TermControl::_throttledUpdateScrollbar(const ScrollBarUpdate& update)
|
||||
{
|
||||
if (!_initializedTerminal)
|
||||
@@ -1790,6 +1795,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return true;
|
||||
}
|
||||
|
||||
// While TSF has an active composition, key events must not be forwarded
|
||||
// to the PTY directly. The IME re-injects navigation/confirmation keys
|
||||
// after composition ends, at which point HasActiveComposition() == false
|
||||
// and they are forwarded normally. This mirrors conhost v1 behavior in
|
||||
// src/interactivity/win32/windowio.cpp.
|
||||
if (GetTSFHandle().HasActiveComposition())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_TrySendKeyEvent(vkey, scanCode, modifiers, keyDown))
|
||||
{
|
||||
return true;
|
||||
@@ -1975,12 +1990,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// NB: I don't think this is correct because the touch should be in the center of the rect.
|
||||
// I suspect the point.Position() would be correct.
|
||||
const auto contactRect = point.Properties().ContactRect();
|
||||
_interactivity.TouchPressed({ contactRect.X, contactRect.Y });
|
||||
til::point newTouchPoint{ til::math::rounding, contactRect.X, contactRect.Y };
|
||||
_interactivity.TouchPressed(newTouchPoint.to_core_point());
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto cursorPosition = point.Position();
|
||||
_interactivity.PointerPressed(TermControl::GetPressedMouseButtons(point),
|
||||
_interactivity.PointerPressed(point.PointerId(),
|
||||
TermControl::GetPressedMouseButtons(point),
|
||||
TermControl::GetPointerUpdateKind(point),
|
||||
point.Timestamp(),
|
||||
ControlKeyStates{ args.KeyModifiers() },
|
||||
@@ -2019,12 +2036,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
if (type == Windows::Devices::Input::PointerDeviceType::Mouse ||
|
||||
type == Windows::Devices::Input::PointerDeviceType::Pen)
|
||||
{
|
||||
auto suppressFurtherHandling = _interactivity.PointerMoved(TermControl::GetPressedMouseButtons(point),
|
||||
auto suppressFurtherHandling = _interactivity.PointerMoved(point.PointerId(),
|
||||
TermControl::GetPressedMouseButtons(point),
|
||||
TermControl::GetPointerUpdateKind(point),
|
||||
ControlKeyStates(args.KeyModifiers()),
|
||||
_focused,
|
||||
pixelPosition,
|
||||
_pointerPressedInBounds);
|
||||
pixelPosition);
|
||||
|
||||
// GH#9109 - Only start an auto-scroll when the drag actually
|
||||
// started within our bounds. Otherwise, someone could start a drag
|
||||
@@ -2063,7 +2079,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
else if (type == Windows::Devices::Input::PointerDeviceType::Touch)
|
||||
{
|
||||
const auto contactRect = point.Properties().ContactRect();
|
||||
_interactivity.TouchMoved({ contactRect.X, contactRect.Y }, _focused);
|
||||
til::point newTouchPoint{ til::math::rounding, contactRect.X, contactRect.Y };
|
||||
|
||||
_interactivity.TouchMoved(newTouchPoint.to_core_point());
|
||||
}
|
||||
|
||||
args.Handled(true);
|
||||
@@ -2096,7 +2114,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
if (type == Windows::Devices::Input::PointerDeviceType::Mouse ||
|
||||
type == Windows::Devices::Input::PointerDeviceType::Pen)
|
||||
{
|
||||
_interactivity.PointerReleased(TermControl::GetPressedMouseButtons(point),
|
||||
_interactivity.PointerReleased(point.PointerId(),
|
||||
TermControl::GetPressedMouseButtons(point),
|
||||
TermControl::GetPointerUpdateKind(point),
|
||||
ControlKeyStates(args.KeyModifiers()),
|
||||
pixelPosition);
|
||||
@@ -2507,9 +2526,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
_updateScrollBar->Run(update);
|
||||
|
||||
// if a selection marker is already visible,
|
||||
// update the position of those markers
|
||||
if (SelectionStartMarker().Visibility() == Visibility::Visible || SelectionEndMarker().Visibility() == Visibility::Visible)
|
||||
// If we have a selection with markers (exposed via selection mode),
|
||||
// update the position of the markers
|
||||
if (_core.HasSelection() && _core.SelectionMode() >= SelectionInteractionMode::Keyboard)
|
||||
{
|
||||
_updateSelectionMarkers(nullptr, winrt::make<UpdateSelectionMarkersEventArgs>(false));
|
||||
}
|
||||
@@ -3060,7 +3079,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// the full path of the file to our terminal connection. Like conhost, if
|
||||
// the path contains a space, we'll wrap the path in quotes.
|
||||
// - Unlike conhost, if multiple files are dropped onto the terminal, we'll
|
||||
// write all the paths to the terminal, separated by spaces.
|
||||
// write all the paths to the terminal, separated by the configured delimiter.
|
||||
// Arguments:
|
||||
// - e: The DragEventArgs from the Drop event
|
||||
// Return Value:
|
||||
@@ -3073,6 +3092,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
co_return;
|
||||
}
|
||||
|
||||
if (const auto hwnd = reinterpret_cast<HWND>(OwningHwnd()))
|
||||
{
|
||||
SetForegroundWindow(hwnd);
|
||||
}
|
||||
|
||||
const auto weak = get_weak();
|
||||
|
||||
if (e.DataView().Contains(StandardDataFormats::ApplicationLink()))
|
||||
@@ -3176,12 +3200,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
|
||||
std::wstring allPathsString;
|
||||
const auto delimiter{ _core.Settings().DragDropDelimiter() };
|
||||
for (auto& fullPath : fullPaths)
|
||||
{
|
||||
// Join the paths with spaces
|
||||
// Join the paths with the delimiter
|
||||
if (!allPathsString.empty())
|
||||
{
|
||||
allPathsString += L" ";
|
||||
allPathsString += delimiter;
|
||||
}
|
||||
|
||||
const auto translationStyle{ _core.Settings().PathTranslationStyle() };
|
||||
@@ -3713,10 +3738,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
return _core.CommandHistory();
|
||||
}
|
||||
winrt::hstring TermControl::CurrentWorkingDirectory() const
|
||||
{
|
||||
return _core.CurrentWorkingDirectory();
|
||||
}
|
||||
|
||||
void TermControl::UpdateWinGetSuggestions(Windows::Foundation::Collections::IVector<hstring> suggestions)
|
||||
{
|
||||
|
||||
@@ -117,8 +117,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void ScrollToMark(const Control::ScrollToMarkDirection& direction);
|
||||
void SelectCommand(const bool goUp);
|
||||
void SelectOutput(const bool goUp);
|
||||
|
||||
winrt::hstring CurrentWorkingDirectory() const;
|
||||
#pragma endregion
|
||||
|
||||
void ScrollViewport(int viewTop);
|
||||
@@ -192,6 +190,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
TerminalConnection::ITerminalConnection Connection();
|
||||
void Connection(const TerminalConnection::ITerminalConnection& connection);
|
||||
void HardResetWithoutErase();
|
||||
|
||||
Control::CursorDisplayState CursorVisibility() const noexcept;
|
||||
void CursorVisibility(Control::CursorDisplayState cursorVisibility);
|
||||
|
||||
@@ -53,6 +53,7 @@ namespace Microsoft.Terminal.Control
|
||||
void SetOverrideColorScheme(Microsoft.Terminal.Core.ICoreScheme scheme);
|
||||
|
||||
Microsoft.Terminal.TerminalConnection.ITerminalConnection Connection;
|
||||
void HardResetWithoutErase();
|
||||
|
||||
UInt64 ContentId{ get; };
|
||||
|
||||
|
||||
@@ -55,6 +55,18 @@ void Terminal::Create(til::size viewportSize, til::CoordType scrollbackLines, Re
|
||||
_stateMachine = std::make_unique<StateMachine>(std::move(engine));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Resets all VT state to defaults without clearing the buffer content.
|
||||
// Called when a connection is restarted so that any dirty modes left
|
||||
// behind by a crashed application don't affect the new connection.
|
||||
void Terminal::HardResetWithoutErase()
|
||||
{
|
||||
_assertLocked();
|
||||
_stateMachine->ResetState();
|
||||
auto& engine = reinterpret_cast<OutputStateMachineEngine&>(_stateMachine->Engine());
|
||||
engine.Dispatch().HardReset(false);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Initializes the Terminal from the given set of settings.
|
||||
// Arguments:
|
||||
@@ -1522,6 +1534,10 @@ void Terminal::SerializeMainBuffer(HANDLE handle) const
|
||||
_mainBuffer->SerializeTo(handle);
|
||||
}
|
||||
|
||||
void Terminal::UnknownSequence() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void Terminal::ColorSelection(const TextAttribute& attr, winrt::Microsoft::Terminal::Core::MatchMode matchMode)
|
||||
{
|
||||
const auto colorSelection = [this](const til::point coordStartInclusive, const til::point coordEndExclusive, const TextAttribute& attr) {
|
||||
|
||||
@@ -85,6 +85,7 @@ public:
|
||||
void Create(til::size viewportSize,
|
||||
til::CoordType scrollbackLines,
|
||||
Microsoft::Console::Render::Renderer& renderer);
|
||||
void HardResetWithoutErase();
|
||||
|
||||
void CreateFromSettings(winrt::Microsoft::Terminal::Core::ICoreSettings settings,
|
||||
Microsoft::Console::Render::Renderer& renderer);
|
||||
@@ -131,7 +132,9 @@ public:
|
||||
|
||||
#pragma region ITerminalApi
|
||||
// These methods are defined in TerminalApi.cpp
|
||||
void UnknownSequence() noexcept override;
|
||||
void ReturnResponse(const std::wstring_view response) override;
|
||||
bool IsConPTY() const noexcept override;
|
||||
Microsoft::Console::VirtualTerminal::StateMachine& GetStateMachine() noexcept override;
|
||||
BufferState GetBufferAndViewport() noexcept override;
|
||||
void SetViewportPosition(const til::point position) noexcept override;
|
||||
|
||||
@@ -28,6 +28,11 @@ void Terminal::ReturnResponse(const std::wstring_view response)
|
||||
}
|
||||
}
|
||||
|
||||
bool Terminal::IsConPTY() const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Microsoft::Console::VirtualTerminal::StateMachine& Terminal::GetStateMachine() noexcept
|
||||
{
|
||||
return *_stateMachine;
|
||||
|
||||
@@ -313,7 +313,7 @@ std::pair<til::point, til::point> Terminal::_ExpandSelectionAnchors(std::pair<ti
|
||||
break;
|
||||
case SelectionExpansion::Word:
|
||||
{
|
||||
start = buffer.GetWordStart2(start, _wordDelimiters, false);
|
||||
start = buffer.GetWordStart(start, _wordDelimiters, false);
|
||||
|
||||
// GH#5099: We round to the nearest cell boundary,
|
||||
// so we would normally prematurely expand to the next word
|
||||
@@ -325,7 +325,7 @@ std::pair<til::point, til::point> Terminal::_ExpandSelectionAnchors(std::pair<ti
|
||||
{
|
||||
bufferSize.DecrementInExclusiveBounds(end);
|
||||
}
|
||||
end = buffer.GetWordEnd2(end, _wordDelimiters, false);
|
||||
end = buffer.GetWordEnd(end, _wordDelimiters, false);
|
||||
break;
|
||||
}
|
||||
case SelectionExpansion::Char:
|
||||
@@ -435,9 +435,9 @@ void Terminal::ExpandSelectionToWord()
|
||||
const auto& buffer = _activeBuffer();
|
||||
auto selection{ _selection.write() };
|
||||
wil::hide_name _selection;
|
||||
selection->start = buffer.GetWordStart2(selection->start, _wordDelimiters, false);
|
||||
selection->start = buffer.GetWordStart(selection->start, _wordDelimiters, false);
|
||||
selection->pivot = selection->start;
|
||||
selection->end = buffer.GetWordEnd2(selection->end, _wordDelimiters, false);
|
||||
selection->end = buffer.GetWordEnd(selection->end, _wordDelimiters, false);
|
||||
|
||||
// if we're targeting both endpoints, instead just target "end"
|
||||
if (WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::Start) && WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::End))
|
||||
@@ -826,13 +826,13 @@ void Terminal::_MoveByWord(SelectionDirection direction, til::point& pos)
|
||||
case SelectionDirection::Left:
|
||||
{
|
||||
auto nextPos = pos;
|
||||
nextPos = buffer.GetWordStart2(nextPos, _wordDelimiters, true);
|
||||
nextPos = buffer.GetWordStart(nextPos, _wordDelimiters, true);
|
||||
if (nextPos == pos)
|
||||
{
|
||||
// didn't move because we're already at the beginning of a word,
|
||||
// so move to the beginning of the previous word
|
||||
buffer.GetSize().DecrementInExclusiveBounds(nextPos);
|
||||
nextPos = buffer.GetWordStart2(nextPos, _wordDelimiters, true);
|
||||
nextPos = buffer.GetWordStart(nextPos, _wordDelimiters, true);
|
||||
}
|
||||
pos = nextPos;
|
||||
break;
|
||||
@@ -841,24 +841,24 @@ void Terminal::_MoveByWord(SelectionDirection direction, til::point& pos)
|
||||
{
|
||||
const auto mutableViewportEndExclusive = _GetMutableViewport().BottomInclusiveRightExclusive();
|
||||
auto nextPos = pos;
|
||||
nextPos = buffer.GetWordEnd2(nextPos, _wordDelimiters, true, mutableViewportEndExclusive);
|
||||
nextPos = buffer.GetWordEnd(nextPos, _wordDelimiters, true, mutableViewportEndExclusive);
|
||||
if (nextPos == pos)
|
||||
{
|
||||
// didn't move because we're already at the end of a word,
|
||||
// so move to the end of the next word
|
||||
buffer.GetSize().IncrementInExclusiveBounds(nextPos);
|
||||
nextPos = buffer.GetWordEnd2(nextPos, _wordDelimiters, true, mutableViewportEndExclusive);
|
||||
nextPos = buffer.GetWordEnd(nextPos, _wordDelimiters, true, mutableViewportEndExclusive);
|
||||
}
|
||||
pos = nextPos;
|
||||
break;
|
||||
}
|
||||
case SelectionDirection::Up:
|
||||
_MoveByChar(direction, pos);
|
||||
pos = buffer.GetWordStart2(pos, _wordDelimiters, true);
|
||||
pos = buffer.GetWordStart(pos, _wordDelimiters, true);
|
||||
break;
|
||||
case SelectionDirection::Down:
|
||||
_MoveByChar(direction, pos);
|
||||
pos = buffer.GetWordEnd2(pos, _wordDelimiters, true);
|
||||
pos = buffer.GetWordEnd(pos, _wordDelimiters, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,6 +353,7 @@ namespace winrt::Microsoft::Terminal::Settings
|
||||
_AllowVtChecksumReport = profile.AllowVtChecksumReport();
|
||||
_AllowVtClipboardWrite = profile.AllowVtClipboardWrite();
|
||||
_PathTranslationStyle = profile.PathTranslationStyle();
|
||||
_DragDropDelimiter = profile.DragDropDelimiter();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -44,6 +44,28 @@ using namespace winrt::Windows::Foundation::Collections;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
// GH#19927 - Walk the visual tree of the given element to find any
|
||||
// Popups and set the theme on their Child element. In XAML Islands,
|
||||
// popup children render in the PopupRoot (separate from the content
|
||||
// tree), so they don't inherit our page's RequestedTheme
|
||||
static void _setThemeOnPopups(const winrt::Windows::UI::Xaml::DependencyObject& element,
|
||||
const winrt::Windows::UI::Xaml::ElementTheme theme)
|
||||
{
|
||||
const auto childCount = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(element);
|
||||
for (int32_t i = 0; i < childCount; i++)
|
||||
{
|
||||
const auto child = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::GetChild(element, i);
|
||||
if (const auto popup = child.try_as<winrt::Windows::UI::Xaml::Controls::Primitives::Popup>())
|
||||
{
|
||||
if (const auto popupChild = popup.Child().try_as<winrt::Windows::UI::Xaml::FrameworkElement>())
|
||||
{
|
||||
popupChild.RequestedTheme(theme);
|
||||
}
|
||||
}
|
||||
_setThemeOnPopups(child, theme);
|
||||
}
|
||||
}
|
||||
|
||||
static WUX::Controls::FontIcon _fontIconForNavTag(const std::wstring_view navTag)
|
||||
{
|
||||
WUX::Controls::FontIcon icon{};
|
||||
@@ -350,6 +372,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
_Navigate(tag, BreadcrumbSubPage::None);
|
||||
}
|
||||
}
|
||||
|
||||
// GH#19927 - Theme the search box's internal popup now that the
|
||||
// visual tree is ready and control templates are applied
|
||||
const auto theme = _settingsSource.GlobalSettings().CurrentTheme();
|
||||
const auto hasThemeForSettings{ theme.Settings() != nullptr };
|
||||
const auto requestedTheme = hasThemeForSettings ? theme.Settings().RequestedTheme() : theme.RequestedTheme();
|
||||
_setThemeOnPopups(SettingsSearchBox(), requestedTheme);
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
@@ -1098,13 +1127,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
}
|
||||
|
||||
const auto& theme = _settingsSource.GlobalSettings().CurrentTheme();
|
||||
const bool hasThemeForSettings{ theme.Settings() != nullptr };
|
||||
const auto& appTheme = theme.RequestedTheme();
|
||||
const auto& requestedTheme = (hasThemeForSettings) ? theme.Settings().RequestedTheme() : appTheme;
|
||||
const auto theme = _settingsSource.GlobalSettings().CurrentTheme();
|
||||
const auto hasThemeForSettings{ theme.Settings() != nullptr };
|
||||
const auto appTheme = theme.RequestedTheme();
|
||||
const auto requestedTheme = (hasThemeForSettings) ? theme.Settings().RequestedTheme() : appTheme;
|
||||
|
||||
RequestedTheme(requestedTheme);
|
||||
|
||||
// GH#19927 - Theme the search box's internal popup so its
|
||||
// dropdown matches the app theme instead of the system theme
|
||||
_setThemeOnPopups(SettingsSearchBox(), requestedTheme);
|
||||
|
||||
// Mica gets it's appearance from the app's theme, not necessarily the
|
||||
// Page's theme. In the case of dark app, light settings, mica will be a
|
||||
// dark color, and the text will also be dark, making the UI _very_ hard
|
||||
|
||||
@@ -145,6 +145,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, AnswerbackMessage);
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, RainbowSuggestions);
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, PathTranslationStyle);
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, DragDropDelimiter);
|
||||
|
||||
WINRT_PROPERTY(bool, IsBaseLayer, false);
|
||||
WINRT_PROPERTY(bool, FocusDeleteButton, false);
|
||||
|
||||
@@ -139,6 +139,7 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(String, AnswerbackMessage);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, RainbowSuggestions);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.PathTranslationStyle, PathTranslationStyle);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(String, DragDropDelimiter);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AllowVtClipboardWrite);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,6 +272,16 @@
|
||||
Style="{StaticResource ComboBoxSettingStyle}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Drag Drop Delimiter -->
|
||||
<local:SettingContainer x:Name="DragDropDelimiter"
|
||||
x:Uid="Profile_DragDropDelimiter"
|
||||
ClearSettingValue="{x:Bind Profile.ClearDragDropDelimiter}"
|
||||
HasSettingValue="{x:Bind Profile.HasDragDropDelimiter, Mode=OneWay}"
|
||||
SettingOverrideSource="{x:Bind Profile.DragDropDelimiterOverrideSource, Mode=OneWay}">
|
||||
<TextBox Style="{StaticResource TextBoxSettingStyle}"
|
||||
Text="{x:Bind Profile.DragDropDelimiter, Mode=TwoWay}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
||||
|
||||
@@ -2748,4 +2748,12 @@
|
||||
<value>Type to filter icons</value>
|
||||
<comment>Placeholder text for a text box to filter and select an icon.</comment>
|
||||
</data>
|
||||
</root>
|
||||
<data name="Profile_DragDropDelimiter.Header" xml:space="preserve">
|
||||
<value>Drag and drop delimiter</value>
|
||||
<comment>Header for a control to set the delimiter used when dragging multiple files into the terminal.</comment>
|
||||
</data>
|
||||
<data name="Profile_DragDropDelimiter.HelpText" xml:space="preserve">
|
||||
<value>This text will be inserted between the paths of multiple files dropped into the terminal.</value>
|
||||
<comment>A description for what the "drag drop delimiter" setting does.</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -647,9 +647,12 @@ void CascadiaSettings::_validateMediaResources()
|
||||
|
||||
_globals->ResolveMediaResources(mediaResourceResolver);
|
||||
|
||||
if (_foundInvalidUserResources)
|
||||
if (Feature_WarnOnInvalidSettingsMediaResources::IsEnabled())
|
||||
{
|
||||
_warnings.Append(SettingsLoadWarnings::InvalidMediaResource);
|
||||
if (_foundInvalidUserResources)
|
||||
{
|
||||
_warnings.Append(SettingsLoadWarnings::InvalidMediaResource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,6 +108,7 @@ Author(s):
|
||||
X(bool, AllowVtChecksumReport, "compatibility.allowDECRQCRA", false) \
|
||||
X(bool, AllowVtClipboardWrite, "compatibility.allowOSC52", true) \
|
||||
X(bool, AllowKeypadMode, "compatibility.allowDECNKM", false) \
|
||||
X(hstring, DragDropDelimiter, "dragDropDelimiter", L" ") \
|
||||
X(Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, "pathTranslationStyle", Microsoft::Terminal::Control::PathTranslationStyle::None)
|
||||
|
||||
// Intentionally omitted Profile settings:
|
||||
|
||||
@@ -94,5 +94,6 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
INHERITABLE_PROFILE_SETTING(Boolean, AllowVtClipboardWrite);
|
||||
|
||||
INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.PathTranslationStyle, PathTranslationStyle);
|
||||
INHERITABLE_PROFILE_SETTING(String, DragDropDelimiter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,16 @@
|
||||
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
static bool _IsPwshAvailable()
|
||||
{
|
||||
// Try to detect if `pwsh.exe` is available in the PATH, if so we want to use that
|
||||
// Allow some extra space in case user put it somewhere odd
|
||||
// We do need to allocate space for the full path even if we don't want to paste the whole thing in
|
||||
wchar_t pwshPath[MAX_PATH] = { 0 };
|
||||
const auto pwshExeName = L"pwsh.exe";
|
||||
return SearchPathW(nullptr, pwshExeName, nullptr, MAX_PATH, pwshPath, nullptr);
|
||||
}
|
||||
|
||||
void VsDevShellGenerator::GenerateProfiles(const VsSetupConfiguration::VsSetupInstance& instance, bool hidden, std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const
|
||||
{
|
||||
try
|
||||
@@ -21,9 +31,10 @@ void VsDevShellGenerator::GenerateProfiles(const VsSetupConfiguration::VsSetupIn
|
||||
const winrt::guid profileGuid{ ::Microsoft::Console::Utils::CreateV5Uuid(TERMINAL_PROFILE_NAMESPACE_GUID, std::as_bytes(std::span{ seed })) };
|
||||
auto profile = winrt::make_self<implementation::Profile>(profileGuid);
|
||||
profile->Name(winrt::hstring{ GetProfileName(instance) });
|
||||
profile->Commandline(winrt::hstring{ GetProfileCommandLine(instance) });
|
||||
auto isPwsh = _IsPwshAvailable();
|
||||
profile->Commandline(winrt::hstring{ GetProfileCommandLine(instance, isPwsh) });
|
||||
profile->StartingDirectory(winrt::hstring{ instance.GetInstallationPath() });
|
||||
profile->Icon(winrt::hstring{ GetProfileIconPath() });
|
||||
profile->Icon(winrt::hstring{ GetProfileIconPath(isPwsh) });
|
||||
profile->Hidden(hidden);
|
||||
profiles.emplace_back(std::move(profile));
|
||||
}
|
||||
@@ -37,20 +48,15 @@ std::wstring VsDevShellGenerator::GetProfileName(const VsSetupConfiguration::VsS
|
||||
return name;
|
||||
}
|
||||
|
||||
std::wstring VsDevShellGenerator::GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const
|
||||
std::wstring VsDevShellGenerator::GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance, bool isPwsh) const
|
||||
{
|
||||
// Build this in stages, so reserve space now
|
||||
std::wstring commandLine;
|
||||
commandLine.reserve(256);
|
||||
|
||||
// Try to detect if `pwsh.exe` is available in the PATH, if so we want to use that
|
||||
// Allow some extra space in case user put it somewhere odd
|
||||
// We do need to allocate space for the full path even if we don't want to paste the whole thing in
|
||||
wchar_t pwshPath[MAX_PATH] = { 0 };
|
||||
const auto pwshExeName = L"pwsh.exe";
|
||||
if (SearchPathW(nullptr, pwshExeName, nullptr, MAX_PATH, pwshPath, nullptr))
|
||||
if (isPwsh)
|
||||
{
|
||||
commandLine.append(pwshExeName);
|
||||
commandLine.append(L"pwsh.exe");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -37,13 +37,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model
|
||||
return L"VsDevShell" + instance.GetInstanceId();
|
||||
}
|
||||
|
||||
std::wstring GetProfileIconPath() const
|
||||
std::wstring GetProfileIconPath(bool isPwsh) const
|
||||
{
|
||||
return L"ms-appx:///ProfileIcons/vs-powershell.png";
|
||||
return isPwsh ? L"ms-appx:///ProfileIcons/vs-pwsh.png" :
|
||||
L"ms-appx:///ProfileIcons/vs-powershell.png";
|
||||
}
|
||||
|
||||
std::wstring GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const;
|
||||
std::wstring GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const;
|
||||
std::wstring GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance, bool isPwsh) const;
|
||||
std::wstring GetDevShellModulePath(const VsSetupConfiguration::VsSetupInstance& instance) const;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -313,10 +313,13 @@ namespace ControlUnitTests
|
||||
|
||||
const til::size fontSize{ 9, 21 };
|
||||
|
||||
interactivity->GotFocus();
|
||||
|
||||
Log::Comment(L"Click on the terminal");
|
||||
const til::point terminalPosition0{ 0, 0 };
|
||||
const auto cursorPosition0 = terminalPosition0 * fontSize;
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -329,29 +332,28 @@ namespace ControlUnitTests
|
||||
// move not quite a whole cell, but enough to start a selection
|
||||
const til::point terminalPosition1{ 0, 0 };
|
||||
const til::point cursorPosition1{ 6, 0 };
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition1.to_core_point(),
|
||||
true);
|
||||
cursorPosition1.to_core_point());
|
||||
Log::Comment(L"Verify that there's one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
|
||||
Log::Comment(L"Drag the mouse down a whole row");
|
||||
const til::point terminalPosition2{ 1, 1 };
|
||||
const auto cursorPosition2 = terminalPosition2 * fontSize;
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition2.to_core_point(),
|
||||
true);
|
||||
cursorPosition2.to_core_point());
|
||||
Log::Comment(L"Verify that there's now two selections (one on each row)");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
|
||||
Log::Comment(L"Release the mouse");
|
||||
interactivity->PointerReleased(noMouseDown,
|
||||
interactivity->PointerReleased(0,
|
||||
noMouseDown,
|
||||
WM_LBUTTONUP, //pointerUpdateKind
|
||||
modifiers,
|
||||
cursorPosition2.to_core_point());
|
||||
@@ -361,7 +363,8 @@ namespace ControlUnitTests
|
||||
Log::Comment(L"click outside the current selection");
|
||||
const til::point terminalPosition3{ 2, 2 };
|
||||
const auto cursorPosition3 = terminalPosition3 * fontSize;
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -372,12 +375,11 @@ namespace ControlUnitTests
|
||||
Log::Comment(L"Drag the mouse");
|
||||
const til::point terminalPosition4{ 3, 2 };
|
||||
const auto cursorPosition4 = terminalPosition4 * fontSize;
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition4.to_core_point(),
|
||||
true);
|
||||
cursorPosition4.to_core_point());
|
||||
Log::Comment(L"Verify that there's now one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
}
|
||||
@@ -408,10 +410,17 @@ namespace ControlUnitTests
|
||||
|
||||
const til::size fontSize{ 9, 21 };
|
||||
|
||||
interactivity->GotFocus();
|
||||
// This test is sensitive to the number of rows scrolled per scroll wheel,
|
||||
// which is reloaded from the system parameters when focus is received.
|
||||
// Reset it.
|
||||
interactivity->_rowsToScroll = 1;
|
||||
|
||||
Log::Comment(L"Click on the terminal");
|
||||
const til::point terminalPosition0{ 5, 5 };
|
||||
const auto cursorPosition0{ terminalPosition0 * fontSize };
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -426,12 +435,11 @@ namespace ControlUnitTests
|
||||
Log::Comment(L"Drag the mouse just a little");
|
||||
// move not quite a whole cell, but enough to start a selection
|
||||
const auto cursorPosition1{ cursorPosition0 + til::point{ 6, 0 } };
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition1.to_core_point(),
|
||||
true);
|
||||
cursorPosition1.to_core_point());
|
||||
Log::Comment(L"Verify that there's one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
|
||||
@@ -559,9 +567,12 @@ namespace ControlUnitTests
|
||||
|
||||
const til::size fontSize{ 9, 21 };
|
||||
|
||||
interactivity->GotFocus();
|
||||
|
||||
Log::Comment(L"Click on the terminal");
|
||||
const til::point cursorPosition0{ 6, 0 };
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -575,12 +586,11 @@ namespace ControlUnitTests
|
||||
|
||||
Log::Comment(L"Drag the mouse a lot. This simulates dragging the mouse real fast.");
|
||||
const til::point cursorPosition1{ 6 + fontSize.width * 2, 0 };
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition1.to_core_point(),
|
||||
true);
|
||||
cursorPosition1.to_core_point());
|
||||
Log::Comment(L"Verify that there's one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
|
||||
@@ -602,10 +612,13 @@ namespace ControlUnitTests
|
||||
const auto leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown };
|
||||
const Control::MouseButtonState noMouseDown{};
|
||||
|
||||
interactivity->GotFocus();
|
||||
|
||||
const til::size fontSize{ 9, 21 };
|
||||
Log::Comment(L"Click on the terminal");
|
||||
const til::point cursorPosition0{ 6, 0 };
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -619,12 +632,11 @@ namespace ControlUnitTests
|
||||
|
||||
Log::Comment(L"Drag the mouse a lot. This simulates dragging the mouse real fast.");
|
||||
const til::point cursorPosition1{ 6 + fontSize.width * 2, 0 };
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition1.to_core_point(),
|
||||
true);
|
||||
cursorPosition1.to_core_point());
|
||||
Log::Comment(L"Verify that there's one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
|
||||
@@ -634,7 +646,8 @@ namespace ControlUnitTests
|
||||
til::point expectedEnd{ 3, 0 }; // add 1 to x-coordinate because end is exclusive
|
||||
VERIFY_ARE_EQUAL(expectedEnd, core->_terminal->GetSelectionEnd());
|
||||
|
||||
interactivity->PointerReleased(noMouseDown,
|
||||
interactivity->PointerReleased(0,
|
||||
noMouseDown,
|
||||
WM_LBUTTONUP,
|
||||
modifiers,
|
||||
cursorPosition1.to_core_point());
|
||||
@@ -644,12 +657,11 @@ namespace ControlUnitTests
|
||||
|
||||
Log::Comment(L"Simulate dragging the mouse into the control, without first clicking into the control");
|
||||
const til::point cursorPosition2{ fontSize.width * 10, 0 };
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition2.to_core_point(),
|
||||
false);
|
||||
cursorPosition2.to_core_point());
|
||||
|
||||
Log::Comment(L"The selection should be unchanged.");
|
||||
VERIFY_ARE_EQUAL(expectedAnchor, core->_terminal->GetSelectionAnchor());
|
||||
@@ -676,6 +688,12 @@ namespace ControlUnitTests
|
||||
auto expectedViewHeight = 20;
|
||||
auto expectedBufferHeight = 20;
|
||||
|
||||
interactivity->GotFocus();
|
||||
// This test is sensitive to the number of rows scrolled per scroll wheel,
|
||||
// which is reloaded from the system parameters when focus is received.
|
||||
// Reset it.
|
||||
interactivity->_rowsToScroll = 1;
|
||||
|
||||
auto scrollChangedHandler = [&](auto&&, const Control::ScrollPositionChangedArgs& args) mutable {
|
||||
VERIFY_ARE_EQUAL(expectedTop, args.ViewTop());
|
||||
VERIFY_ARE_EQUAL(expectedViewHeight, args.ViewHeight());
|
||||
@@ -722,7 +740,8 @@ namespace ControlUnitTests
|
||||
Log::Comment(L"Click on the terminal");
|
||||
const til::point terminalPosition0{ 4, 4 };
|
||||
const auto cursorPosition0 = terminalPosition0 * fontSize;
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -735,12 +754,11 @@ namespace ControlUnitTests
|
||||
// move the mouse as if to make a selection
|
||||
const til::point terminalPosition1{ 10, 4 };
|
||||
const auto cursorPosition1 = terminalPosition1 * fontSize;
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition1.to_core_point(),
|
||||
true);
|
||||
cursorPosition1.to_core_point());
|
||||
Log::Comment(L"Verify that there's still no selection");
|
||||
VERIFY_IS_FALSE(core->HasSelection());
|
||||
}
|
||||
@@ -770,10 +788,13 @@ namespace ControlUnitTests
|
||||
|
||||
const til::size fontSize{ 9, 21 };
|
||||
|
||||
interactivity->GotFocus();
|
||||
|
||||
Log::Comment(L"Click on the terminal");
|
||||
const til::point terminalPosition0{ 5, 5 };
|
||||
const auto cursorPosition0{ terminalPosition0 * fontSize };
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -788,12 +809,11 @@ namespace ControlUnitTests
|
||||
Log::Comment(L"Drag the mouse just a little");
|
||||
// move not quite a whole cell, but enough to start a selection
|
||||
const auto cursorPosition1{ cursorPosition0 + til::point{ 6, 0 } };
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition1.to_core_point(),
|
||||
true);
|
||||
cursorPosition1.to_core_point());
|
||||
Log::Comment(L"Verify that there's one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
|
||||
@@ -850,12 +870,11 @@ namespace ControlUnitTests
|
||||
// character in the buffer (if, albeit in a new location).
|
||||
//
|
||||
// This helps test GH #14462, a regression from #10749.
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition0.to_core_point(),
|
||||
true);
|
||||
cursorPosition0.to_core_point());
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
{
|
||||
const auto anchor{ core->_terminal->GetSelectionAnchor() };
|
||||
@@ -876,12 +895,11 @@ namespace ControlUnitTests
|
||||
expectedAnchor.y -= 1;
|
||||
expectedEnd.y -= 1;
|
||||
VERIFY_ARE_EQUAL(scrollbackLength - 3, core->_terminal->GetScrollOffset());
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
interactivity->PointerMoved(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition1.to_core_point(),
|
||||
true);
|
||||
cursorPosition1.to_core_point());
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
{
|
||||
const auto anchor{ core->_terminal->GetSelectionAnchor() };
|
||||
@@ -929,7 +947,8 @@ namespace ControlUnitTests
|
||||
const til::size fontSize{ 9, 21 };
|
||||
const til::point terminalPosition0{ 5, 5 };
|
||||
const auto cursorPosition0{ terminalPosition0 * fontSize };
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -978,7 +997,8 @@ namespace ControlUnitTests
|
||||
const til::size fontSize{ 9, 21 };
|
||||
const til::point terminalPosition0{ 5, 5 };
|
||||
const auto cursorPosition0{ terminalPosition0 * fontSize };
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -994,7 +1014,8 @@ namespace ControlUnitTests
|
||||
// The viewport is only 30 wide, so clamping 35 to the buffer size gets
|
||||
// us 29, which converted is (32 + 29 + 1) = 62 = '>'
|
||||
expectedOutput.push_back(L"\x1b[M >&");
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -1009,7 +1030,8 @@ namespace ControlUnitTests
|
||||
// will be clamped to the top line.
|
||||
|
||||
expectedOutput.push_back(L"\x1b[M &!"); // 5, 1
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -1025,7 +1047,8 @@ namespace ControlUnitTests
|
||||
VERIFY_ARE_EQUAL(0, core->ScrollOffset());
|
||||
Log::Comment(L" --- Click on a spot that's still outside the buffer ---");
|
||||
expectedOutput.push_back(L"\x1b[M >&");
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
@@ -1039,7 +1062,8 @@ namespace ControlUnitTests
|
||||
Log::Comment(L" --- Click on a spot that's NOW INSIDE the buffer ---");
|
||||
// (32 + 35 + 1) = 68 = 'D'
|
||||
expectedOutput.push_back(L"\x1b[M D&");
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
interactivity->PointerPressed(0,
|
||||
leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
|
||||
@@ -176,9 +176,9 @@ namespace SettingsModelUnitTests
|
||||
auto hashFromKey = [&](auto& kc) {
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().as<NewTabArgs>();
|
||||
const auto& realArgs = actionAndArgs.Args().template as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs.ContentArgs());
|
||||
const auto terminalArgs{ realArgs.ContentArgs().try_as<NewTerminalArgs>() };
|
||||
const auto terminalArgs{ realArgs.ContentArgs().template try_as<NewTerminalArgs>() };
|
||||
return terminalArgs.Hash();
|
||||
};
|
||||
|
||||
|
||||
@@ -315,16 +315,29 @@ AppHost* WindowEmperor::_mostRecentWindow() const noexcept
|
||||
|
||||
void WindowEmperor::HandleCommandlineArgs(int nCmdShow)
|
||||
{
|
||||
// When running without package identity, set an explicit AppUserModelID so
|
||||
// that toast notifications (and other shell features like taskbar grouping)
|
||||
// work correctly. We include...
|
||||
// - a hash of the executable path
|
||||
// - a hash of the user SID
|
||||
// This prevents crosstalk between different portable/unpackaged installations.
|
||||
// The same isolation strategy is used for the single-instance mutex below.
|
||||
std::wstring unpackagedAumid;
|
||||
|
||||
std::wstring windowClassName;
|
||||
windowClassName.reserve(64); // "Windows Terminal Preview Admin 0123456789012345 0123456789012345"
|
||||
#if defined(WT_BRANDING_RELEASE)
|
||||
windowClassName.append(L"Windows Terminal");
|
||||
unpackagedAumid = L"Microsoft.WindowsTerminal";
|
||||
#elif defined(WT_BRANDING_PREVIEW)
|
||||
windowClassName.append(L"Windows Terminal Preview");
|
||||
unpackagedAumid = L"Microsoft.WindowsTerminalPreview";
|
||||
#elif defined(WT_BRANDING_CANARY)
|
||||
windowClassName.append(L"Windows Terminal Canary");
|
||||
unpackagedAumid = L"Microsoft.WindowsTerminalCanary";
|
||||
#else
|
||||
windowClassName.append(L"Windows Terminal Dev");
|
||||
unpackagedAumid = L"WindowsTerminalDev";
|
||||
#endif
|
||||
if (Utils::IsRunningElevated())
|
||||
{
|
||||
@@ -336,8 +349,10 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow)
|
||||
const auto hash = til::hash(path);
|
||||
#ifdef _WIN64
|
||||
fmt::format_to(std::back_inserter(windowClassName), FMT_COMPILE(L" {:016x}"), hash);
|
||||
fmt::format_to(std::back_inserter(unpackagedAumid), FMT_COMPILE(L".{:016x}"), hash);
|
||||
#else
|
||||
fmt::format_to(std::back_inserter(windowClassName), FMT_COMPILE(L" {:08x}"), hash);
|
||||
fmt::format_to(std::back_inserter(unpackagedAumid), FMT_COMPILE(L".{:08x}"), hash);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -351,6 +366,15 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow)
|
||||
#else
|
||||
fmt::format_to(std::back_inserter(windowClassName), FMT_COMPILE(L" {:08x}"), hash);
|
||||
#endif
|
||||
if (!IsPackaged())
|
||||
{
|
||||
#ifdef _WIN64
|
||||
fmt::format_to(std::back_inserter(unpackagedAumid), FMT_COMPILE(L".{:016x}"), hash);
|
||||
#else
|
||||
fmt::format_to(std::back_inserter(unpackagedAumid), FMT_COMPILE(L".{:08x}"), hash);
|
||||
#endif
|
||||
LOG_IF_FAILED(SetCurrentProcessExplicitAppUserModelID(unpackagedAumid.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
// Windows Terminal is a single-instance application. Either acquire ownership
|
||||
|
||||
@@ -87,4 +87,5 @@
|
||||
X(bool, ShowMarks, false) \
|
||||
X(winrt::Microsoft::Terminal::Control::CopyFormat, CopyFormatting, 0) \
|
||||
X(bool, RightClickContextMenu, false) \
|
||||
X(winrt::Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, winrt::Microsoft::Terminal::Control::PathTranslationStyle::None)
|
||||
X(winrt::Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, winrt::Microsoft::Terminal::Control::PathTranslationStyle::None) \
|
||||
X(winrt::hstring, DragDropDelimiter, L" ")
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Label="Globals">
|
||||
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0.22621.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0.26100.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion Condition="'$(WindowsTargetPlatformMinVersion)' == ''">10.0.18362.0</WindowsTargetPlatformMinVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -95,7 +95,8 @@
|
||||
|
||||
<!-- For ALL build types-->
|
||||
<PropertyGroup Label="Configuration">
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<PlatformToolset Condition="'$(VisualStudioVersion)' >= '18.0'">v145</PlatformToolset>
|
||||
<PlatformToolset Condition="'$(PlatformToolset)' == ''">v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
@@ -124,6 +125,8 @@
|
||||
Conhost code converts DWORDs to HANDLEs for instance.
|
||||
C4467: usage of ATL attributes is deprecated
|
||||
Conhost code still uses ATL.
|
||||
C4875: a non-string literal argument to [[gsl::suppress]] is deprecated ...
|
||||
This diagnostic will be fixed in a newer version of GSL.
|
||||
C26445: Do not assign std::span or std::string_view to a reference. They are cheap to construct and are not owners of the underlying data. (gsl.view).
|
||||
Even for MSVC v19.32 this is actually far from true. Copying (as opposed to referencing) larger
|
||||
than register-sized structures is fairly expensive. Example: https://godbolt.org/z/oPco88PaP
|
||||
@@ -136,7 +139,7 @@
|
||||
C26494: Variable 'index' is uninitialized. Always initialize an object (type. 5).
|
||||
This diagnostic is broken in VS 17.7 which our CI currently uses. It's fixed in 17.8.
|
||||
-->
|
||||
<DisableSpecificWarnings>4201;4312;4467;5105;26434;26445;26456;26478;26494;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<DisableSpecificWarnings>4201;4312;4467;4875;5105;26434;26445;26456;26478;26494;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<PreprocessorDefinitions>_WINDOWS;EXTERNAL_BUILD;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
|
||||
@@ -198,7 +201,6 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SetChecksum>false</SetChecksum>
|
||||
<GenerateDebugInformation>DebugFastLink</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
|
||||
@@ -187,4 +187,11 @@
|
||||
<alwaysDisabledReleaseTokens/>
|
||||
</feature>
|
||||
|
||||
<feature>
|
||||
<name>Feature_WarnOnInvalidSettingsMediaResources</name>
|
||||
<description>Controls whether Terminal should display a warning dialog when icon, backgroundImage, shader, etc. could not be found.</description>
|
||||
<stage>AlwaysEnabled</stage>
|
||||
<alwaysDisabledReleaseTokens/>
|
||||
</feature>
|
||||
|
||||
</featureStaging>
|
||||
|
||||
@@ -40,6 +40,12 @@
|
||||
"Execution": {
|
||||
"AdditionalParameter": "/select:\"@IsPerfTest=true\""
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "NoAppPlatform",
|
||||
"Execution": {
|
||||
"AdditionalParameter": "/select:\"not(@Name='PolicyTests::*')\""
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,22 @@ ConhostInternalGetSet::ConhostInternalGetSet(_In_ IIoProvider& io) :
|
||||
{
|
||||
}
|
||||
|
||||
void ConhostInternalGetSet::UnknownSequence() noexcept
|
||||
{
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
|
||||
// VT sequences unknown to us may cause the cursor position to change in a way that
|
||||
// we don't know about. In this case, we need to mark the cursor position as "dirty".
|
||||
//
|
||||
// The worst offender is likely PowerShell. It uses VT sequences but also calls
|
||||
// GetConsoleScreenBufferInfoEx for *every single line of output* (!!!). This prevents
|
||||
// us from using a more conservative solution (e.g. always fetching the cursor position).
|
||||
if (gci.IsInVtIoMode())
|
||||
{
|
||||
gci.GetActiveOutputBuffer().SetConptyCursorPositionMayBeWrong();
|
||||
}
|
||||
}
|
||||
|
||||
// - Sends a string response to the input stream of the console.
|
||||
// - Used by various commands where the program attached would like a reply to one of the commands issued.
|
||||
// - This will generate two "key presses" (one down, one up) for every character in the string and place them into the head of the console's input stream.
|
||||
@@ -48,6 +64,12 @@ void ConhostInternalGetSet::ReturnResponse(const std::wstring_view response)
|
||||
_io.GetActiveInputBuffer()->WriteString(response);
|
||||
}
|
||||
|
||||
bool ConhostInternalGetSet::IsConPTY() const noexcept
|
||||
{
|
||||
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
return gci.IsInVtIoMode();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Retrieves the state machine for the active output buffer.
|
||||
// Arguments:
|
||||
|
||||
@@ -29,8 +29,10 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal::
|
||||
public:
|
||||
ConhostInternalGetSet(_In_ Microsoft::Console::IIoProvider& io);
|
||||
|
||||
void UnknownSequence() noexcept override;
|
||||
void ReturnResponse(const std::wstring_view response) override;
|
||||
|
||||
bool IsConPTY() const noexcept override;
|
||||
Microsoft::Console::VirtualTerminal::StateMachine& GetStateMachine() override;
|
||||
BufferState GetBufferAndViewport() override;
|
||||
void SetViewportPosition(const til::point position) override;
|
||||
|
||||
@@ -1313,6 +1313,15 @@ COOKED_READ_DATA::LayoutResult COOKED_READ_DATA::_layoutLine(std::wstring& outpu
|
||||
til::CoordType cols = 0;
|
||||
const auto len = textBuffer.FitTextIntoColumns(text, columnLimit - column, cols);
|
||||
|
||||
// GH#19922: We need to account for terminals that are just 1 column wide, as we may deadlock otherwise.
|
||||
// `columnLimit - column == 1` will then prevent `FitTextIntoColumns` from fitting any wide glyphs.
|
||||
// We can detect this by checking for `len == 0`, skip the offending glyph and break out of the deadlock.
|
||||
if (len == 0) [[unlikely]]
|
||||
{
|
||||
it += textBuffer.GraphemeNext(text, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
output.append(text, 0, len);
|
||||
column += cols;
|
||||
it += len;
|
||||
|
||||
@@ -859,14 +859,15 @@ PWSTR TranslateConsoleTitle(_In_ PCWSTR pwszConsoleTitle, const BOOL fUnexpand,
|
||||
return Status;
|
||||
}
|
||||
|
||||
// Allow the renderer to paint once the rest of the console is hooked up.
|
||||
if (g.pRender)
|
||||
if (ConsoleConnectionDeservesVisibleWindow(p))
|
||||
{
|
||||
g.pRender->EnablePainting();
|
||||
}
|
||||
// Allow the renderer to paint once the rest of the console is hooked up,
|
||||
// but only if there's actually something to paint on.
|
||||
if (g.pRender)
|
||||
{
|
||||
g.pRender->EnablePainting();
|
||||
}
|
||||
|
||||
if (SUCCEEDED_NTSTATUS(Status) && ConsoleConnectionDeservesVisibleWindow(p))
|
||||
{
|
||||
HANDLE Thread = nullptr;
|
||||
|
||||
IConsoleInputThread* pNewThread = nullptr;
|
||||
|
||||
@@ -18,10 +18,17 @@ class SearchTests
|
||||
{
|
||||
TEST_CLASS(SearchTests);
|
||||
|
||||
CommonState* m_state;
|
||||
CommonState* m_state{ nullptr };
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
wil::unique_hmodule icu{ LoadLibraryExW(L"icu.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32) };
|
||||
if (!icu)
|
||||
{
|
||||
Log::Result(TestResults::Skipped, L"ICU is not present");
|
||||
return true;
|
||||
}
|
||||
|
||||
m_state = new CommonState();
|
||||
m_state->PrepareGlobalScreenBuffer();
|
||||
return true;
|
||||
@@ -29,8 +36,11 @@ class SearchTests
|
||||
|
||||
TEST_CLASS_CLEANUP(ClassCleanup)
|
||||
{
|
||||
m_state->CleanupGlobalScreenBuffer();
|
||||
delete m_state;
|
||||
if (m_state)
|
||||
{
|
||||
m_state->CleanupGlobalScreenBuffer();
|
||||
delete m_state;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ class TextBufferTests
|
||||
|
||||
void WriteLinesToBuffer(const std::vector<std::wstring>& text, TextBuffer& buffer);
|
||||
TEST_METHOD(GetWordBoundaries);
|
||||
TEST_METHOD(MoveByWord);
|
||||
|
||||
TEST_METHOD(GetGlyphBoundaries);
|
||||
|
||||
TEST_METHOD(GetTextRects);
|
||||
@@ -2039,12 +2039,12 @@ void TextBufferTests::GetWordBoundaries()
|
||||
|
||||
// Test Data:
|
||||
// - til::point - starting position
|
||||
// - til::point - expected result (accessibilityMode = false)
|
||||
// - til::point - expected result (accessibilityMode = true)
|
||||
// - til::point - expected result (includeWhitespace = false)
|
||||
// - til::point - expected result (includeWhitespace = true)
|
||||
struct ExpectedResult
|
||||
{
|
||||
til::point accessibilityModeDisabled;
|
||||
til::point accessibilityModeEnabled;
|
||||
til::point selectionMode;
|
||||
til::point accessibilityMode;
|
||||
};
|
||||
|
||||
struct Test
|
||||
@@ -2056,7 +2056,8 @@ void TextBufferTests::GetWordBoundaries()
|
||||
// Set testData for GetWordStart tests
|
||||
// clang-format off
|
||||
std::vector<Test> testData = {
|
||||
// tests for first line of text
|
||||
// tests for first line of text ("word other" + spaces)
|
||||
// selectionMode accessibilityMode
|
||||
{ { 0, 0 }, {{ 0, 0 }, { 0, 0 }} },
|
||||
{ { 1, 0 }, {{ 0, 0 }, { 0, 0 }} },
|
||||
{ { 3, 0 }, {{ 0, 0 }, { 0, 0 }} },
|
||||
@@ -2066,7 +2067,7 @@ void TextBufferTests::GetWordBoundaries()
|
||||
{ { 20, 0 }, {{ 10, 0 }, { 5, 0 }} },
|
||||
{ { 79, 0 }, {{ 10, 0 }, { 5, 0 }} },
|
||||
|
||||
// tests for second line of text
|
||||
// tests for second line of text (" more words" + spaces)
|
||||
{ { 0, 1 }, {{ 0, 1 }, { 5, 0 }} },
|
||||
{ { 1, 1 }, {{ 0, 1 }, { 5, 0 }} },
|
||||
{ { 2, 1 }, {{ 2, 1 }, { 2, 1 }} },
|
||||
@@ -2082,54 +2083,56 @@ void TextBufferTests::GetWordBoundaries()
|
||||
// clang-format on
|
||||
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Data:accessibilityMode", L"{false, true}")
|
||||
TEST_METHOD_PROPERTY(L"Data:includeWhitespace", L"{false, true}")
|
||||
END_TEST_METHOD_PROPERTIES();
|
||||
|
||||
bool accessibilityMode;
|
||||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"accessibilityMode", accessibilityMode), L"Get accessibility mode variant");
|
||||
bool includeWhitespace;
|
||||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"includeWhitespace", includeWhitespace), L"Get includeWhitespace variant");
|
||||
|
||||
const std::wstring_view delimiters = L" ";
|
||||
for (const auto& test : testData)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"til::point (%hd, %hd)", test.startPos.x, test.startPos.y));
|
||||
const auto result = _buffer->GetWordStart(test.startPos, delimiters, accessibilityMode);
|
||||
const auto expected = accessibilityMode ? test.expected.accessibilityModeEnabled : test.expected.accessibilityModeDisabled;
|
||||
const auto result = _buffer->GetWordStart(test.startPos, delimiters, includeWhitespace);
|
||||
const auto expected = includeWhitespace ? test.expected.accessibilityMode : test.expected.selectionMode;
|
||||
VERIFY_ARE_EQUAL(expected, result);
|
||||
}
|
||||
|
||||
// Update testData for GetWordEnd tests
|
||||
// Note: GetWordEnd returns exclusive end positions
|
||||
// clang-format off
|
||||
testData = {
|
||||
// tests for first line of text
|
||||
{ { 0, 0 }, { { 3, 0 }, { 5, 0 } } },
|
||||
{ { 1, 0 }, { { 3, 0 }, { 5, 0 } } },
|
||||
{ { 3, 0 }, { { 3, 0 }, { 5, 0 } } },
|
||||
{ { 4, 0 }, { { 4, 0 }, { 5, 0 } } },
|
||||
{ { 5, 0 }, { { 9, 0 }, { 2, 1 } } },
|
||||
{ { 6, 0 }, { { 9, 0 }, { 2, 1 } } },
|
||||
{ { 20, 0 }, { { 79, 0 }, { 2, 1 } } },
|
||||
{ { 79, 0 }, { { 79, 0 }, { 2, 1 } } },
|
||||
// tests for first line of text ("word other" + spaces)
|
||||
// selectionMode accessibilityMode
|
||||
{ { 0, 0 }, { { 4, 0 }, { 5, 0 } } },
|
||||
{ { 1, 0 }, { { 4, 0 }, { 5, 0 } } },
|
||||
{ { 3, 0 }, { { 4, 0 }, { 5, 0 } } },
|
||||
{ { 4, 0 }, { { 5, 0 }, { 5, 0 } } },
|
||||
{ { 5, 0 }, { { 10, 0 }, { 2, 1 } } },
|
||||
{ { 6, 0 }, { { 10, 0 }, { 2, 1 } } },
|
||||
{ { 20, 0 }, { { 80, 0 }, { 2, 1 } } },
|
||||
{ { 79, 0 }, { { 80, 0 }, { 2, 1 } } },
|
||||
|
||||
// tests for second line of text
|
||||
{ { 0, 1 }, { { 1, 1 }, { 2, 1 } } },
|
||||
{ { 1, 1 }, { { 1, 1 }, { 2, 1 } } },
|
||||
{ { 2, 1 }, { { 5, 1 }, { 9, 1 } } },
|
||||
{ { 3, 1 }, { { 5, 1 }, { 9, 1 } } },
|
||||
{ { 5, 1 }, { { 5, 1 }, { 9, 1 } } },
|
||||
{ { 6, 1 }, { { 8, 1 }, { 9, 1 } } },
|
||||
{ { 7, 1 }, { { 8, 1 }, { 9, 1 } } },
|
||||
{ { 9, 1 }, { { 13, 1 }, { 0, 9001 } } },
|
||||
{ { 10, 1 }, { { 13, 1 }, { 0, 9001 } } },
|
||||
{ { 20, 1 }, { { 79, 1 }, { 0, 9001 } } },
|
||||
{ { 79, 1 }, { { 79, 1 }, { 0, 9001 } } },
|
||||
// tests for second line of text (" more words" + spaces)
|
||||
{ { 0, 1 }, { { 2, 1 }, { 2, 1 } } },
|
||||
{ { 1, 1 }, { { 2, 1 }, { 2, 1 } } },
|
||||
{ { 2, 1 }, { { 6, 1 }, { 9, 1 } } },
|
||||
{ { 3, 1 }, { { 6, 1 }, { 9, 1 } } },
|
||||
{ { 5, 1 }, { { 6, 1 }, { 9, 1 } } },
|
||||
{ { 6, 1 }, { { 9, 1 }, { 9, 1 } } },
|
||||
{ { 7, 1 }, { { 9, 1 }, { 9, 1 } } },
|
||||
{ { 9, 1 }, { { 14, 1 }, { 80, 9000 } } },
|
||||
{ { 10, 1 }, { { 14, 1 }, { 80, 9000 } } },
|
||||
{ { 20, 1 }, { { 80, 1 }, { 80, 9000 } } },
|
||||
{ { 79, 1 }, { { 80, 1 }, { 80, 9000 } } },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
for (const auto& test : testData)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"til::point (%hd, %hd)", test.startPos.x, test.startPos.y));
|
||||
auto result = _buffer->GetWordEnd(test.startPos, delimiters, accessibilityMode);
|
||||
const auto expected = accessibilityMode ? test.expected.accessibilityModeEnabled : test.expected.accessibilityModeDisabled;
|
||||
auto result = _buffer->GetWordEnd(test.startPos, delimiters, includeWhitespace);
|
||||
const auto expected = includeWhitespace ? test.expected.accessibilityMode : test.expected.selectionMode;
|
||||
VERIFY_ARE_EQUAL(expected, result);
|
||||
}
|
||||
|
||||
@@ -2161,6 +2164,7 @@ void TextBufferTests::GetWordBoundaries()
|
||||
|
||||
// clang-format off
|
||||
testData = {
|
||||
// selectionMode accessibilityMode
|
||||
{ { 0, 0 }, { { 0, 0 }, { 0, 0 } } },
|
||||
{ { 1, 0 }, { { 0, 0 }, { 0, 0 } } },
|
||||
{ { 4, 0 }, { { 4, 0 }, { 0, 0 } } },
|
||||
@@ -2173,11 +2177,11 @@ void TextBufferTests::GetWordBoundaries()
|
||||
|
||||
{ { 0, 2 }, { { 0, 2 }, { 0, 2 } } },
|
||||
{ { 9, 2 }, { { 0, 2 }, { 0, 2 } } },
|
||||
// v accessibility does not consider wrapping
|
||||
|
||||
{ { 0, 3 }, { { 0, 3 }, { 0, 2 } } },
|
||||
{ { 7, 3 }, { { 6, 3 }, { 0, 2 } } },
|
||||
// v accessibility does not consider wrapping
|
||||
{ { 1, 4 }, { { 0, 4 }, { 0, 2 } } },
|
||||
// v selection mode now also crosses wrapped rows for ControlChar
|
||||
{ { 1, 4 }, { { 6, 3 }, { 0, 2 } } },
|
||||
{ { 4, 4 }, { { 4, 4 }, { 4, 4 } } },
|
||||
{ { 8, 4 }, { { 4, 4 }, { 4, 4 } } },
|
||||
|
||||
@@ -2188,8 +2192,8 @@ void TextBufferTests::GetWordBoundaries()
|
||||
for (const auto& test : testData)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"Testing til::point (%hd, %hd)", test.startPos.x, test.startPos.y));
|
||||
const auto result = _buffer->GetWordStart(test.startPos, delimiters, accessibilityMode);
|
||||
const auto expected = accessibilityMode ? test.expected.accessibilityModeEnabled : test.expected.accessibilityModeDisabled;
|
||||
const auto result = _buffer->GetWordStart(test.startPos, delimiters, includeWhitespace);
|
||||
const auto expected = includeWhitespace ? test.expected.accessibilityMode : test.expected.selectionMode;
|
||||
VERIFY_ARE_EQUAL(expected, result);
|
||||
}
|
||||
|
||||
@@ -2205,123 +2209,43 @@ void TextBufferTests::GetWordBoundaries()
|
||||
// clang-format off
|
||||
testData = {
|
||||
// tests for first line of text
|
||||
{ { 0, 0 }, { { 3, 0 }, { 5, 0 } } },
|
||||
{ { 1, 0 }, { { 3, 0 }, { 5, 0 } } },
|
||||
{ { 4, 0 }, { { 4, 0 }, { 5, 0 } } },
|
||||
{ { 5, 0 }, { { 7, 1 }, { 0, 2 } } },
|
||||
{ { 7, 0 }, { { 7, 1 }, { 0, 2 } } },
|
||||
// selectionMode accessibilityMode
|
||||
{ { 0, 0 }, { { 4, 0 }, { 5, 0 } } },
|
||||
{ { 1, 0 }, { { 4, 0 }, { 5, 0 } } },
|
||||
{ { 4, 0 }, { { 5, 0 }, { 5, 0 } } },
|
||||
{ { 5, 0 }, { { 8, 1 }, { 0, 2 } } },
|
||||
{ { 7, 0 }, { { 8, 1 }, { 0, 2 } } },
|
||||
|
||||
{ { 4, 1 }, { { 7, 1 }, { 0, 2 } } },
|
||||
{ { 7, 1 }, { { 7, 1 }, { 0, 2 } } },
|
||||
{ { 9, 1 }, { { 9, 1 }, { 0, 2 } } },
|
||||
{ { 4, 1 }, { { 8, 1 }, { 0, 2 } } },
|
||||
{ { 7, 1 }, { { 8, 1 }, { 0, 2 } } },
|
||||
{ { 9, 1 }, { { 10, 1 }, { 0, 2 } } },
|
||||
|
||||
{ { 0, 2 }, { { 9, 2 }, { 4, 4 } } },
|
||||
{ { 9, 2 }, { { 9, 2 }, { 4, 4 } } },
|
||||
{ { 0, 2 }, { { 10, 2 }, { 4, 4 } } },
|
||||
{ { 9, 2 }, { { 10, 2 }, { 4, 4 } } },
|
||||
|
||||
{ { 0, 3 }, { { 5, 3 }, { 4, 4 } } },
|
||||
{ { 7, 3 }, { { 9, 3 }, { 4, 4 } } },
|
||||
{ { 0, 3 }, { { 6, 3 }, { 4, 4 } } },
|
||||
{ { 7, 3 }, { { 4, 4 }, { 4, 4 } } },
|
||||
|
||||
{ { 1, 4 }, { { 3, 4 }, { 4, 4 } } },
|
||||
{ { 4, 4 }, { { 0, 5 }, { 2, 5 } } },
|
||||
{ { 8, 4 }, { { 0, 5 }, { 2, 5 } } },
|
||||
{ { 1, 4 }, { { 4, 4 }, { 4, 4 } } },
|
||||
{ { 4, 4 }, { { 1, 5 }, { 2, 5 } } },
|
||||
{ { 8, 4 }, { { 1, 5 }, { 2, 5 } } },
|
||||
|
||||
{ { 0, 5 }, { { 0, 5 }, { 2, 5 } } },
|
||||
{ { 1, 5 }, { { 1, 5 }, { 2, 5 } } },
|
||||
{ { 4, 5 }, { { 9, 5 }, { 0, 6 } } },
|
||||
{ { 9, 5 }, { { 9, 5 }, { 0, 6 } } },
|
||||
{ { 0, 5 }, { { 1, 5 }, { 2, 5 } } },
|
||||
{ { 1, 5 }, { { 2, 5 }, { 2, 5 } } },
|
||||
{ { 4, 5 }, { { 10, 5 }, { 10, 5 } } },
|
||||
{ { 9, 5 }, { { 10, 5 }, { 10, 5 } } },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
for (const auto& test : testData)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"TestEnd til::point (%hd, %hd)", test.startPos.x, test.startPos.y));
|
||||
auto result = _buffer->GetWordEnd(test.startPos, delimiters, accessibilityMode);
|
||||
const auto expected = accessibilityMode ? test.expected.accessibilityModeEnabled : test.expected.accessibilityModeDisabled;
|
||||
auto result = _buffer->GetWordEnd(test.startPos, delimiters, includeWhitespace);
|
||||
const auto expected = includeWhitespace ? test.expected.accessibilityMode : test.expected.selectionMode;
|
||||
VERIFY_ARE_EQUAL(expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
void TextBufferTests::MoveByWord()
|
||||
{
|
||||
til::size bufferSize{ 80, 9001 };
|
||||
UINT cursorSize = 12;
|
||||
TextAttribute attr{ 0x7f };
|
||||
auto _buffer = std::make_unique<TextBuffer>(bufferSize, attr, cursorSize, false, &_renderer);
|
||||
|
||||
// Setup: Write lines of text to the buffer
|
||||
const std::vector<std::wstring> text = { L"word other",
|
||||
L" more words" };
|
||||
WriteLinesToBuffer(text, *_buffer);
|
||||
|
||||
// Test Data:
|
||||
// - til::point - starting position
|
||||
// - til::point - expected result (moving forwards)
|
||||
// - til::point - expected result (moving backwards)
|
||||
struct ExpectedResult
|
||||
{
|
||||
til::point moveForwards;
|
||||
til::point moveBackwards;
|
||||
};
|
||||
|
||||
struct Test
|
||||
{
|
||||
til::point startPos;
|
||||
ExpectedResult expected;
|
||||
};
|
||||
|
||||
// Set testData for GetWordStart tests
|
||||
// clang-format off
|
||||
std::vector<Test> testData = {
|
||||
// tests for first line of text
|
||||
{ { 0, 0 }, {{ 5, 0 }, { 0, 0 }} },
|
||||
{ { 1, 0 }, {{ 5, 0 }, { 1, 0 }} },
|
||||
{ { 3, 0 }, {{ 5, 0 }, { 3, 0 }} },
|
||||
{ { 4, 0 }, {{ 5, 0 }, { 4, 0 }} },
|
||||
{ { 5, 0 }, {{ 2, 1 }, { 0, 0 }} },
|
||||
{ { 6, 0 }, {{ 2, 1 }, { 0, 0 }} },
|
||||
{ { 20, 0 }, {{ 2, 1 }, { 0, 0 }} },
|
||||
{ { 79, 0 }, {{ 2, 1 }, { 0, 0 }} },
|
||||
|
||||
// tests for second line of text
|
||||
{ { 0, 1 }, {{ 2, 1 }, { 0, 0 }} },
|
||||
{ { 1, 1 }, {{ 2, 1 }, { 0, 0 }} },
|
||||
{ { 2, 1 }, {{ 9, 1 }, { 5, 0 }} },
|
||||
{ { 3, 1 }, {{ 9, 1 }, { 5, 0 }} },
|
||||
{ { 5, 1 }, {{ 9, 1 }, { 5, 0 }} },
|
||||
{ { 6, 1 }, {{ 9, 1 }, { 5, 0 }} },
|
||||
{ { 7, 1 }, {{ 9, 1 }, { 5, 0 }} },
|
||||
{ { 9, 1 }, {{ 9, 1 }, { 2, 1 }} },
|
||||
{ { 10, 1 }, {{10, 1 }, { 2, 1 }} },
|
||||
{ { 20, 1 }, {{20, 1 }, { 2, 1 }} },
|
||||
{ { 79, 1 }, {{79, 1 }, { 2, 1 }} },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Data:movingForwards", L"{false, true}")
|
||||
END_TEST_METHOD_PROPERTIES();
|
||||
|
||||
bool movingForwards;
|
||||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"movingForwards", movingForwards), L"Get movingForwards variant");
|
||||
|
||||
const std::wstring_view delimiters = L" ";
|
||||
const auto lastCharPos = _buffer->GetLastNonSpaceCharacter();
|
||||
for (const auto& test : testData)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"COORD (%hd, %hd)", test.startPos.x, test.startPos.y));
|
||||
auto pos{ test.startPos };
|
||||
const auto result = movingForwards ?
|
||||
_buffer->MoveToNextWord(pos, delimiters, lastCharPos) :
|
||||
_buffer->MoveToPreviousWord(pos, delimiters);
|
||||
const auto expected = movingForwards ? test.expected.moveForwards : test.expected.moveBackwards;
|
||||
VERIFY_ARE_EQUAL(expected, pos);
|
||||
|
||||
// if we moved, result is true and pos != startPos.
|
||||
// otherwise, result is false and pos == startPos.
|
||||
VERIFY_ARE_EQUAL(result, pos != test.startPos);
|
||||
}
|
||||
}
|
||||
|
||||
void TextBufferTests::GetGlyphBoundaries()
|
||||
{
|
||||
struct ExpectedResult
|
||||
|
||||
@@ -63,9 +63,8 @@
|
||||
#include <wil/nt_result_macros.h>
|
||||
|
||||
// GSL
|
||||
// Block GSL Multi Span include because it both has C++17 deprecated iterators
|
||||
// and uses the C-namespaced "max" which conflicts with Windows definitions.
|
||||
#include <gsl/gsl_util>
|
||||
#include <gsl/narrow>
|
||||
#include <gsl/util>
|
||||
#include <gsl/pointers>
|
||||
|
||||
// CppCoreCheck
|
||||
|
||||
@@ -42,7 +42,7 @@ CONPTY_EXPORT HRESULT WINAPI ConptyCreatePseudoConsoleAsUser(HANDLE hToken, COOR
|
||||
|
||||
CONPTY_EXPORT HRESULT WINAPI ConptyResizePseudoConsole(HPCON hPC, COORD size);
|
||||
CONPTY_EXPORT HRESULT WINAPI ConptyClearPseudoConsole(HPCON hPC, BOOL keepCursorRow);
|
||||
CONPTY_EXPORT HRESULT WINAPI ConptyShowHidePseudoConsole(HPCON hPC, bool show);
|
||||
CONPTY_EXPORT HRESULT WINAPI ConptyShowHidePseudoConsole(HPCON hPC, BOOL show);
|
||||
CONPTY_EXPORT HRESULT WINAPI ConptyReparentPseudoConsole(HPCON hPC, HWND newParent);
|
||||
CONPTY_EXPORT HRESULT WINAPI ConptyReleasePseudoConsole(HPCON hPC);
|
||||
|
||||
|
||||
@@ -444,7 +444,15 @@ VOID ConIoSrvComm::HandleFocusEvent(const CIS_EVENT* const Event)
|
||||
// TODO: MSFT: 11833883 - Determine action when wait on paint operation via
|
||||
// DirectX on OneCoreUAP times out while switching console
|
||||
// applications.
|
||||
|
||||
UnlockConsole();
|
||||
// Teardown may need to wait for an entire frame to finish, but it will not be
|
||||
// able to do so while we are holding the console lock. Relinquish the lock
|
||||
// for as long as it takes to quiesce the render thread, and then take it back
|
||||
// afterwards. This is globally safe because ConIoSrv will not process any other
|
||||
// requests while the focus event is outstanding.
|
||||
Renderer->TriggerTeardown();
|
||||
LockConsole();
|
||||
|
||||
// Relinquish control of the graphics device (only one
|
||||
// DirectX application may control the device at any one
|
||||
|
||||
@@ -951,6 +951,11 @@ LRESULT Window::_HandleGetDpiScaledSize(UINT dpiNew, _Inout_ SIZE* pSizeNew) con
|
||||
// - <none>
|
||||
void Window::_HandleDrop(const WPARAM wParam) const
|
||||
{
|
||||
if (const auto hwnd = GetWindowHandle())
|
||||
{
|
||||
SetForegroundWindow(hwnd);
|
||||
}
|
||||
|
||||
const auto drop = reinterpret_cast<HDROP>(wParam);
|
||||
Clipboard::Instance().PasteDrop(drop);
|
||||
DragFinish(drop);
|
||||
|
||||
@@ -12,7 +12,11 @@ namespace Microsoft::Console::Render::Atlas
|
||||
#ifdef NDEBUG
|
||||
#define ATLAS_DEBUG__IS_DEBUG 0
|
||||
#else
|
||||
#ifdef __INSIDE_WINDOWS
|
||||
#define ATLAS_DEBUG__IS_DEBUG 0
|
||||
#else
|
||||
#define ATLAS_DEBUG__IS_DEBUG 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// If set to 1, this will cause the entire viewport to be invalidated at all times.
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
#include <VersionHelpers.h>
|
||||
#include <wincodec.h>
|
||||
|
||||
#include <gsl/gsl_util>
|
||||
#include <gsl/narrow>
|
||||
#include <gsl/util>
|
||||
#include <gsl/pointers>
|
||||
#include <wil/com.h>
|
||||
#include <wil/filesystem.h>
|
||||
|
||||
@@ -789,22 +789,8 @@ bool Renderer::_CheckViewportAndScroll()
|
||||
void Renderer::_scheduleRenditionBlink()
|
||||
{
|
||||
const auto& buffer = _pData->GetTextBuffer();
|
||||
bool blinkUsed = false;
|
||||
const auto blinkUsed = buffer.ContainsBlinkAttributeInRegion(_viewport);
|
||||
|
||||
for (auto row = _viewport.Top(); row < _viewport.BottomExclusive(); ++row)
|
||||
{
|
||||
const auto& r = buffer.GetRowByOffset(row);
|
||||
for (const auto& attr : r.Attributes())
|
||||
{
|
||||
if (attr.IsBlinking())
|
||||
{
|
||||
blinkUsed = true;
|
||||
goto why_does_cpp_not_have_labeled_loops;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
why_does_cpp_not_have_labeled_loops:
|
||||
if (blinkUsed != IsTimerRunning(_renditionBlinker))
|
||||
{
|
||||
if (blinkUsed)
|
||||
@@ -1530,7 +1516,7 @@ void Renderer::_updateCursorInfo()
|
||||
_currentCursorOptions.lineRendition = lineRendition;
|
||||
_currentCursorOptions.ulCursorHeightPercent = cursorHeight;
|
||||
_currentCursorOptions.cursorPixelWidth = _pData->GetCursorPixelWidth();
|
||||
_currentCursorOptions.fIsDoubleWidth = buffer.GetRowByOffset(cursorPosition.y).DbcsAttrAt(cursorPosition.x) != DbcsAttribute::Single;
|
||||
_currentCursorOptions.fIsDoubleWidth = buffer.IsGlyphDoubleWidthAt(cursorPosition);
|
||||
_currentCursorOptions.cursorType = cursor.GetType();
|
||||
_currentCursorOptions.fUseColor = useColor;
|
||||
_currentCursorOptions.cursorColor = cursorColor;
|
||||
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
#pragma warning(disable : 26432) // suppress rule of 5 violation on interface because tampering with this is fraught with peril
|
||||
virtual ~ITermDispatch() = 0;
|
||||
|
||||
virtual void UnknownSequence() noexcept = 0;
|
||||
virtual void Print(const wchar_t wchPrintable) = 0;
|
||||
virtual void PrintString(const std::wstring_view string) = 0;
|
||||
|
||||
@@ -81,6 +82,7 @@ public:
|
||||
virtual void BackIndex() = 0; // DECBI
|
||||
virtual void ForwardIndex() = 0; // DECFI
|
||||
virtual void SetWindowTitle(std::wstring_view title) = 0; // DECSWT, OscWindowTitle
|
||||
virtual void SetCurrentWorkingDirectory(const std::wstring_view uri) = 0; // OSC 7
|
||||
virtual void HorizontalTabSet() = 0; // HTS
|
||||
virtual void ForwardTab(const VTInt numTabs) = 0; // CHT, HT
|
||||
virtual void BackwardsTab(const VTInt numTabs) = 0; // CBT
|
||||
@@ -139,7 +141,7 @@ public:
|
||||
virtual void AnnounceCodeStructure(const VTInt ansiLevel) = 0; // ACS
|
||||
|
||||
virtual void SoftReset() = 0; // DECSTR
|
||||
virtual void HardReset() = 0; // RIS
|
||||
virtual void HardReset(bool erase) = 0; // RIS
|
||||
virtual void ScreenAlignmentPattern() = 0; // DECALN
|
||||
|
||||
virtual void SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) = 0; // DECSCUSR
|
||||
@@ -155,13 +157,9 @@ public:
|
||||
virtual void EndHyperlink() = 0;
|
||||
|
||||
virtual void DoConEmuAction(const std::wstring_view string) = 0;
|
||||
|
||||
virtual void DoITerm2Action(const std::wstring_view string) = 0;
|
||||
|
||||
virtual void DoFinalTermAction(const std::wstring_view string) = 0;
|
||||
|
||||
virtual void DoVsCodeAction(const std::wstring_view string) = 0;
|
||||
|
||||
virtual void DoWTAction(const std::wstring_view string) = 0;
|
||||
|
||||
virtual StringHandler DefineSixelImage(const VTInt macroParameter,
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
ITerminalApi& operator=(const ITerminalApi&) = delete;
|
||||
ITerminalApi& operator=(ITerminalApi&&) = delete;
|
||||
|
||||
virtual void UnknownSequence() noexcept = 0;
|
||||
virtual void ReturnResponse(const std::wstring_view response) = 0;
|
||||
|
||||
struct BufferState
|
||||
@@ -46,6 +47,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
bool isMainBuffer;
|
||||
};
|
||||
|
||||
virtual bool IsConPTY() const noexcept = 0;
|
||||
virtual StateMachine& GetStateMachine() = 0;
|
||||
virtual BufferState GetBufferAndViewport() = 0;
|
||||
virtual void SetViewportPosition(const til::point position) = 0;
|
||||
|
||||
@@ -48,6 +48,11 @@ AdaptDispatch::AdaptDispatch(ITerminalApi& api, Renderer* renderer, RenderSettin
|
||||
{
|
||||
}
|
||||
|
||||
void AdaptDispatch::UnknownSequence() noexcept
|
||||
{
|
||||
_api.UnknownSequence();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Translates and displays a single character
|
||||
// Arguments:
|
||||
@@ -2073,6 +2078,13 @@ void AdaptDispatch::SetAnsiMode(const bool ansiMode)
|
||||
// CSI = flags ; mode u - Sets kitty keyboard protocol flags
|
||||
void AdaptDispatch::SetKittyKeyboardProtocol(const VTParameter flags, const VTParameter mode) noexcept
|
||||
{
|
||||
// Avoid setting KKP flags in `_terminalInput` when we're ConPTY. Otherwise, we'd be translating
|
||||
// W32IM to KKP, even when KKP is not supported by the hosting terminal (possibly intentionally).
|
||||
if (_api.IsConPTY())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto kittyFlags = gsl::narrow_cast<uint8_t>(flags.value_or(0));
|
||||
const auto KittyKeyboardProtocol = static_cast<TerminalInput::KittyKeyboardProtocolMode>(mode.value_or(1));
|
||||
_terminalInput.SetKittyKeyboardProtocol(kittyFlags, KittyKeyboardProtocol);
|
||||
@@ -2081,6 +2093,11 @@ void AdaptDispatch::SetKittyKeyboardProtocol(const VTParameter flags, const VTPa
|
||||
// CSI ? u - Queries current kitty keyboard protocol flags
|
||||
void AdaptDispatch::QueryKittyKeyboardProtocol()
|
||||
{
|
||||
if (_api.IsConPTY())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto flags = static_cast<VTInt>(_terminalInput.GetKittyFlags());
|
||||
_ReturnCsiResponse(fmt::format(FMT_COMPILE(L"?{}u"), flags));
|
||||
}
|
||||
@@ -2088,6 +2105,11 @@ void AdaptDispatch::QueryKittyKeyboardProtocol()
|
||||
// CSI > flags u - Pushes current kitty keyboard flags onto the stack and sets new flags
|
||||
void AdaptDispatch::PushKittyKeyboardProtocol(const VTParameter flags)
|
||||
{
|
||||
if (_api.IsConPTY())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto kittyFlags = gsl::narrow_cast<uint8_t>(flags.value_or(0));
|
||||
_terminalInput.PushKittyFlags(kittyFlags);
|
||||
}
|
||||
@@ -2095,6 +2117,11 @@ void AdaptDispatch::PushKittyKeyboardProtocol(const VTParameter flags)
|
||||
// CSI < count u - Pops one or more entries from the kitty keyboard stack
|
||||
void AdaptDispatch::PopKittyKeyboardProtocol(const VTParameter count)
|
||||
{
|
||||
if (_api.IsConPTY())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto popCount = static_cast<size_t>(count.value_or(1));
|
||||
_terminalInput.PopKittyFlags(popCount);
|
||||
}
|
||||
@@ -2588,6 +2615,27 @@ void AdaptDispatch::SetWindowTitle(std::wstring_view title)
|
||||
_api.SetWindowTitle(title);
|
||||
}
|
||||
|
||||
// OSC 7 - Set Current Working Directory
|
||||
// While ConEmu's OSC 9;9 works well for native Windows paths,
|
||||
// OSC 7 uses file URIs, which may not always work.
|
||||
void AdaptDispatch::SetCurrentWorkingDirectory(std::wstring_view uri)
|
||||
{
|
||||
// Ensure that the URI has a null terminator.
|
||||
std::wstring path{ uri };
|
||||
|
||||
// PathCreateFromUrlW supports writing to the input pointer,
|
||||
// and the resulting path can never be longer than the URI.
|
||||
const auto ptr = path.data();
|
||||
auto len = gsl::narrow<DWORD>(path.size());
|
||||
THROW_IF_FAILED(PathCreateFromUrlW(ptr, ptr, &len, 0));
|
||||
path.resize(len);
|
||||
|
||||
if (til::is_legal_path(path))
|
||||
{
|
||||
_api.SetWorkingDirectory(path);
|
||||
}
|
||||
}
|
||||
|
||||
//Routine Description:
|
||||
// HTS - sets a VT tab stop in the cursor's current column.
|
||||
//Arguments:
|
||||
@@ -2982,7 +3030,7 @@ void AdaptDispatch::SoftReset()
|
||||
// - Clears UDKs.
|
||||
// - Clears a down-line-loaded character set.
|
||||
// * The soft font is reset in the renderer and the font buffer is deleted.
|
||||
// - Clears the screen.
|
||||
// - Clears the screen. (if erase=true)
|
||||
// * This is like Erase in Display (3), also clearing scrollback, as well as ED(2)
|
||||
// - Returns the cursor to the upper-left corner of the screen.
|
||||
// * CUP(1;1)
|
||||
@@ -2992,8 +3040,8 @@ void AdaptDispatch::SoftReset()
|
||||
// - Sets all character sets to the default.
|
||||
// * G0(USASCII)
|
||||
//Arguments:
|
||||
// <none>
|
||||
void AdaptDispatch::HardReset()
|
||||
// - erase: if true, erase the screen and scrollback
|
||||
void AdaptDispatch::HardReset(bool erase)
|
||||
{
|
||||
// If in the alt buffer, switch back to main before doing anything else.
|
||||
if (_usingAltBuffer)
|
||||
@@ -3020,9 +3068,12 @@ void AdaptDispatch::HardReset()
|
||||
// to ensure that it clears with the default background color.
|
||||
SoftReset();
|
||||
|
||||
// Clears the screen - Needs to be done in two operations.
|
||||
EraseInDisplay(DispatchTypes::EraseType::All);
|
||||
EraseInDisplay(DispatchTypes::EraseType::Scrollback);
|
||||
if (erase)
|
||||
{
|
||||
// Clears the screen - Needs to be done in two operations.
|
||||
EraseInDisplay(DispatchTypes::EraseType::All);
|
||||
EraseInDisplay(DispatchTypes::EraseType::Scrollback);
|
||||
}
|
||||
|
||||
// Set the color table and render modes back to their initial startup values.
|
||||
_renderSettings.RestoreDefaultSettings();
|
||||
@@ -3033,8 +3084,14 @@ void AdaptDispatch::HardReset()
|
||||
_renderer->SynchronizedOutputChanged();
|
||||
}
|
||||
|
||||
// Cursor to 1,1 - the Soft Reset guarantees this is absolute
|
||||
CursorPosition(1, 1);
|
||||
if (erase)
|
||||
{
|
||||
// Cursor to 1,1 - the Soft Reset guarantees this is absolute.
|
||||
// Only done when clearing buffers, because when preserving content
|
||||
// the cursor should stay where the previous shell left it so the
|
||||
// new shell prompt appears in the right place.
|
||||
CursorPosition(1, 1);
|
||||
}
|
||||
|
||||
// We only reset the system line feed mode if the input mode is set. If it
|
||||
// isn't set, that either means they're both reset, and there's nothing for
|
||||
@@ -3585,6 +3642,10 @@ void AdaptDispatch::DoConEmuAction(const std::wstring_view string)
|
||||
_pages.ActivePage().Buffer().StartCommand();
|
||||
_api.NotifyShellIntegrationMark();
|
||||
}
|
||||
else
|
||||
{
|
||||
_api.UnknownSequence();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -3616,6 +3677,10 @@ void AdaptDispatch::DoITerm2Action(const std::wstring_view string)
|
||||
_pages.ActivePage().Buffer().StartPrompt();
|
||||
_api.NotifyShellIntegrationMark();
|
||||
}
|
||||
else
|
||||
{
|
||||
_api.UnknownSequence();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -3686,9 +3751,14 @@ void AdaptDispatch::DoFinalTermAction(const std::wstring_view string)
|
||||
break;
|
||||
}
|
||||
default:
|
||||
_api.UnknownSequence();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_api.UnknownSequence();
|
||||
}
|
||||
|
||||
// When we add the rest of the FTCS sequences (GH#11000), we should add a
|
||||
// simple state machine here to track the most recently emitted mark from
|
||||
@@ -3763,6 +3833,10 @@ void AdaptDispatch::DoVsCodeAction(const std::wstring_view string)
|
||||
|
||||
// If it's poorly formatted, just eat it
|
||||
}
|
||||
else
|
||||
{
|
||||
_api.UnknownSequence();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
public:
|
||||
AdaptDispatch(ITerminalApi& api, Renderer* renderer, RenderSettings& renderSettings, TerminalInput& terminalInput) noexcept;
|
||||
|
||||
void UnknownSequence() noexcept override;
|
||||
void Print(const wchar_t wchPrintable) override;
|
||||
void PrintString(const std::wstring_view string) override;
|
||||
|
||||
@@ -113,6 +114,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
void BackIndex() override; // DECBI
|
||||
void ForwardIndex() override; // DECFI
|
||||
void SetWindowTitle(const std::wstring_view title) override; // DECSWT, OSCWindowTitle
|
||||
void SetCurrentWorkingDirectory(std::wstring_view uri) override; // OSC 7
|
||||
void HorizontalTabSet() override; // HTS
|
||||
void ForwardTab(const VTInt numTabs) override; // CHT, HT
|
||||
void BackwardsTab(const VTInt numTabs) override; // CBT
|
||||
@@ -128,7 +130,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
void SendC1Controls(const bool enabled) override; // S8C1T, S7C1T
|
||||
void AnnounceCodeStructure(const VTInt ansiLevel) override; // ACS
|
||||
void SoftReset() override; // DECSTR
|
||||
void HardReset() override; // RIS
|
||||
void HardReset(bool erase) override; // RIS
|
||||
void ScreenAlignmentPattern() override; // DECALN
|
||||
void SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) override; // DECSCUSR
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ Abstract:
|
||||
// This includes support libraries from the CRT, STL, WIL, and GSL
|
||||
#include "LibraryIncludes.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <Shlwapi.h>
|
||||
|
||||
#define ENABLE_INTSAFE_SIGNED_FUNCTIONS
|
||||
#include <intsafe.h>
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Console::VirtualTerminal::ITermDispatch
|
||||
{
|
||||
public:
|
||||
void UnknownSequence() noexcept override {}
|
||||
void Print(const wchar_t wchPrintable) override = 0;
|
||||
void PrintString(const std::wstring_view string) override = 0;
|
||||
|
||||
@@ -68,6 +69,7 @@ public:
|
||||
void BackIndex() override {} // DECBI
|
||||
void ForwardIndex() override {} // DECFI
|
||||
void SetWindowTitle(std::wstring_view /*title*/) override {} // DECSWT, OscWindowTitle
|
||||
void SetCurrentWorkingDirectory(std::wstring_view /*uri*/) override {} // OSC 7
|
||||
void HorizontalTabSet() override {} // HTS
|
||||
void ForwardTab(const VTInt /*numTabs*/) override {} // CHT, HT
|
||||
void BackwardsTab(const VTInt /*numTabs*/) override {} // CBT
|
||||
@@ -126,7 +128,7 @@ public:
|
||||
void AnnounceCodeStructure(const VTInt /*ansiLevel*/) override {} // ACS
|
||||
|
||||
void SoftReset() override {} // DECSTR
|
||||
void HardReset() override {} // RIS
|
||||
void HardReset(bool /*erase*/) override {} // RIS
|
||||
void ScreenAlignmentPattern() override {} // DECALN
|
||||
|
||||
void SetCursorStyle(const DispatchTypes::CursorStyle /*cursorStyle*/) override {} // DECSCUSR
|
||||
|
||||
@@ -60,6 +60,10 @@ using namespace Microsoft::Console::VirtualTerminal;
|
||||
class TestGetSet final : public ITerminalApi
|
||||
{
|
||||
public:
|
||||
void UnknownSequence() noexcept override
|
||||
{
|
||||
}
|
||||
|
||||
void ReturnResponse(const std::wstring_view response) override
|
||||
{
|
||||
Log::Comment(L"ReturnResponse MOCK called...");
|
||||
@@ -76,6 +80,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool IsConPTY() const noexcept override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
StateMachine& GetStateMachine() override
|
||||
{
|
||||
return *_stateMachine;
|
||||
@@ -1651,7 +1660,7 @@ public:
|
||||
|
||||
Log::Comment(L"Test 3: Verify space reset");
|
||||
_testGetSet->PrepData();
|
||||
_pDispatch->HardReset();
|
||||
_pDispatch->HardReset(true);
|
||||
_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::MacroSpaceReport, {});
|
||||
|
||||
swprintf_s(pwszBuffer, ARRAYSIZE(pwszBuffer), L"\x1b[%zu*{", availableSpace);
|
||||
@@ -1683,7 +1692,7 @@ public:
|
||||
|
||||
Log::Comment(L"Test 3: Verify checksum resets to 0");
|
||||
_testGetSet->PrepData();
|
||||
_pDispatch->HardReset();
|
||||
_pDispatch->HardReset(true);
|
||||
_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::MemoryChecksum, 56);
|
||||
|
||||
_testGetSet->ValidateInputEvent(L"\033P56!~0000\033\\");
|
||||
|
||||
@@ -228,7 +228,8 @@ TerminalInput::OutputType TerminalInput::HandleKey(const INPUT_RECORD& event)
|
||||
// GH#4999 - If we're in win32-input mode, skip straight to doing that.
|
||||
// Since this mode handles all types of key events, do nothing else.
|
||||
//
|
||||
// The kitty keyboard protocol takes precedence, because it's cross-platform.
|
||||
// ConPTY assumes that W32IM always remains enabled. We have to prefer
|
||||
// the kitty keyboard protocol, because otherwise it would never be used.
|
||||
if (_inputMode.test(Mode::Win32) && !_forceDisableWin32InputMode && !_kittyFlags)
|
||||
{
|
||||
return _makeWin32Output(event.Event.KeyEvent);
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
IStateMachineEngine& operator=(const IStateMachineEngine&) = default;
|
||||
IStateMachineEngine& operator=(IStateMachineEngine&&) = default;
|
||||
|
||||
virtual void UnknownSequence() noexcept = 0;
|
||||
virtual bool EncounteredWin32InputModeSequence() const noexcept = 0;
|
||||
|
||||
virtual bool ActionExecute(const wchar_t wch) = 0;
|
||||
|
||||
@@ -129,6 +129,10 @@ til::enumset<DeviceAttribute, uint64_t> InputStateMachineEngine::WaitUntilDA1(DW
|
||||
return til::enumset<DeviceAttribute, uint64_t>::from_bits(val);
|
||||
}
|
||||
|
||||
void InputStateMachineEngine::UnknownSequence() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
bool InputStateMachineEngine::EncounteredWin32InputModeSequence() const noexcept
|
||||
{
|
||||
return _encounteredWin32InputModeSequence;
|
||||
|
||||
@@ -166,6 +166,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
void CaptureNextCursorPositionReport() noexcept;
|
||||
til::enumset<DeviceAttribute, uint64_t> WaitUntilDA1(DWORD timeout) noexcept;
|
||||
|
||||
void UnknownSequence() noexcept override;
|
||||
bool EncounteredWin32InputModeSequence() const noexcept override;
|
||||
|
||||
bool ActionExecute(const wchar_t wch) override;
|
||||
|
||||
@@ -24,6 +24,11 @@ OutputStateMachineEngine::OutputStateMachineEngine(std::unique_ptr<ITermDispatch
|
||||
THROW_HR_IF_NULL(E_INVALIDARG, _dispatch.get());
|
||||
}
|
||||
|
||||
void OutputStateMachineEngine::UnknownSequence() noexcept
|
||||
{
|
||||
_dispatch->UnknownSequence();
|
||||
}
|
||||
|
||||
bool OutputStateMachineEngine::EncounteredWin32InputModeSequence() const noexcept
|
||||
{
|
||||
return false;
|
||||
@@ -230,7 +235,7 @@ bool OutputStateMachineEngine::ActionEscDispatch(const VTID id)
|
||||
_dispatch->DeviceAttributes();
|
||||
break;
|
||||
case EscActionCodes::RIS_ResetToInitialState:
|
||||
_dispatch->HardReset();
|
||||
_dispatch->HardReset(true);
|
||||
break;
|
||||
case EscActionCodes::SS2_SingleShift:
|
||||
_dispatch->SingleShift(2);
|
||||
@@ -683,6 +688,7 @@ bool OutputStateMachineEngine::ActionCsiDispatch(const VTID id, const VTParamete
|
||||
_dispatch->PopKittyKeyboardProtocol(parameters.at(0));
|
||||
break;
|
||||
default:
|
||||
_dispatch->UnknownSequence();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -737,7 +743,7 @@ IStateMachineEngine::StringHandler OutputStateMachineEngine::ActionDcsDispatch(c
|
||||
handler = _dispatch->RestorePresentationState(parameters.at(0));
|
||||
break;
|
||||
default:
|
||||
handler = nullptr;
|
||||
_dispatch->UnknownSequence();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -858,6 +864,9 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OscActionCodes::CurrentWorkingDirectory:
|
||||
_dispatch->SetCurrentWorkingDirectory(string);
|
||||
break;
|
||||
case OscActionCodes::Hyperlink:
|
||||
{
|
||||
std::wstring params;
|
||||
@@ -901,6 +910,7 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s
|
||||
break;
|
||||
}
|
||||
default:
|
||||
_dispatch->UnknownSequence();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -921,6 +931,7 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s
|
||||
bool OutputStateMachineEngine::ActionSs3Dispatch(const wchar_t /*wch*/, const VTParameters /*parameters*/) noexcept
|
||||
{
|
||||
// The output engine doesn't handle any SS3 sequences.
|
||||
_dispatch->UnknownSequence();
|
||||
_ClearLastChar();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
|
||||
OutputStateMachineEngine(std::unique_ptr<ITermDispatch> pDispatch);
|
||||
|
||||
void UnknownSequence() noexcept override;
|
||||
bool EncounteredWin32InputModeSequence() const noexcept override;
|
||||
|
||||
bool ActionExecute(const wchar_t wch) override;
|
||||
@@ -203,13 +204,14 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
ExitVt52Mode = VTID("<")
|
||||
};
|
||||
|
||||
enum OscActionCodes : unsigned int
|
||||
enum OscActionCodes : size_t
|
||||
{
|
||||
SetIconAndWindowTitle = 0,
|
||||
SetWindowIcon = 1,
|
||||
SetWindowTitle = 2,
|
||||
SetWindowProperty = 3, // Not implemented
|
||||
SetColor = 4,
|
||||
CurrentWorkingDirectory = 7,
|
||||
Hyperlink = 8,
|
||||
ConEmuAction = 9,
|
||||
SetForegroundColor = 10,
|
||||
|
||||
@@ -1044,6 +1044,7 @@ void StateMachine::_EnterSosPmApcString() noexcept
|
||||
{
|
||||
_state = VTStates::SosPmApcString;
|
||||
_cachedSequence.reset();
|
||||
_engine->UnknownSequence();
|
||||
_trace.TraceStateChange(L"SosPmApcString");
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,10 @@ public:
|
||||
dcsDataString.clear();
|
||||
}
|
||||
|
||||
void UnknownSequence() noexcept override
|
||||
{
|
||||
}
|
||||
|
||||
bool EncounteredWin32InputModeSequence() const noexcept override
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -2,5 +2,8 @@
|
||||
"$schema": "http://universaltest/schema/testlist-2.json",
|
||||
"Testlists": [
|
||||
{ "FilePath": "Microsoft.Console.Tests.testlist" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"TestMDs" : [
|
||||
{ "FilePath": "Microsoft.Console.Host.FeatureTests.testmd" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,5 +2,8 @@
|
||||
"$schema": "http://universaltest/schema/testlist-2.json",
|
||||
"Testlists": [
|
||||
{ "FilePath": "Microsoft.Console.Tests.testlist" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"TestMDs" : [
|
||||
{ "FilePath": "Microsoft.Console.Host.FeatureTests.testmd" }
|
||||
]
|
||||
}
|
||||
|
||||
14
src/testlist/Microsoft.Console.TestLab.Win3.testlist
Normal file
14
src/testlist/Microsoft.Console.TestLab.Win3.testlist
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "http://universaltest/schema/testlist-3.json",
|
||||
"Testlists": [
|
||||
{
|
||||
"FilePath": "Microsoft.Console.Tests.testlist"
|
||||
}
|
||||
],
|
||||
"TestMDs": [
|
||||
{
|
||||
"FilePath": "Microsoft.Console.Host.FeatureTests.testmd",
|
||||
"Profile": "NoAppPlatform"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
"TestMDs" : [
|
||||
{ "FilePath": "Microsoft.Console.Host.UnitTests.testmd" },
|
||||
{ "FilePath": "Microsoft.Console.TextBuffer.UnitTests.testmd" },
|
||||
{ "FilePath": "Microsoft.Console.Host.FeatureTests.testmd" },
|
||||
{ "FilePath": "Microsoft.Console.VirtualTerminal.Adapter.UnitTests.testmd" },
|
||||
{ "FilePath": "Microsoft.Console.VirtualTerminal.Parser.UnitTests.testmd" },
|
||||
{ "FilePath": "Microsoft.Console.Conpty.UnitTests.testmd" },
|
||||
|
||||
@@ -8,6 +8,7 @@ TESTLIST_SOURCES=\
|
||||
Microsoft.Console.TestLab.Desktop.testlist \
|
||||
Microsoft.Console.TestLab.OneCoreUap.testlist \
|
||||
Microsoft.Console.TestLab.Performance.testlist \
|
||||
Microsoft.Console.TestLab.Win3.testlist \
|
||||
|
||||
TESTLIST_REFERENCE_DIRS=\
|
||||
$(OBJ_PATH)\..\interactivity\win32\ut_interactivity_win32\$O \
|
||||
|
||||
@@ -14,7 +14,7 @@ Handle Handle::Create()
|
||||
handle._impl = new Implementation();
|
||||
if (FAILED(handle._impl->Initialize()))
|
||||
{
|
||||
delete handle._impl;
|
||||
handle._destroy();
|
||||
handle._impl = nullptr;
|
||||
}
|
||||
return handle;
|
||||
@@ -32,11 +32,7 @@ void Handle::SetDefaultScopeAlphanumericHalfWidth(bool enable)
|
||||
|
||||
Handle::~Handle()
|
||||
{
|
||||
if (_impl)
|
||||
{
|
||||
_impl->Uninitialize();
|
||||
_impl->Release();
|
||||
}
|
||||
_destroy();
|
||||
}
|
||||
|
||||
Handle::Handle(Handle&& other) noexcept :
|
||||
@@ -94,3 +90,12 @@ bool Handle::HasActiveComposition() const noexcept
|
||||
{
|
||||
return _impl ? _impl->HasActiveComposition() : false;
|
||||
}
|
||||
|
||||
void Handle::_destroy() noexcept
|
||||
{
|
||||
if (_impl)
|
||||
{
|
||||
_impl->Uninitialize();
|
||||
_impl->Release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ namespace Microsoft::Console::TSF
|
||||
bool HasActiveComposition() const noexcept;
|
||||
|
||||
private:
|
||||
void _destroy() noexcept;
|
||||
|
||||
Implementation* _impl = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1490,19 +1490,25 @@ void UiaTextRangeBase::_moveEndpointByUnitWord(_In_ const int moveCount,
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
else if (buffer.MoveToNextWord(nextPos, _wordDelimiters, documentEnd))
|
||||
{
|
||||
resultPos = nextPos;
|
||||
(*pAmountMoved)++;
|
||||
}
|
||||
else if (allowBottomExclusive)
|
||||
{
|
||||
resultPos = documentEnd;
|
||||
(*pAmountMoved)++;
|
||||
}
|
||||
else
|
||||
{
|
||||
success = false;
|
||||
// Move to the end of the current word (including trailing whitespace),
|
||||
// which is also the start of the next word
|
||||
const auto wordEnd = buffer.GetWordEnd(nextPos, _wordDelimiters, true, documentEnd);
|
||||
if (wordEnd < documentEnd)
|
||||
{
|
||||
resultPos = wordEnd;
|
||||
(*pAmountMoved)++;
|
||||
}
|
||||
else if (allowBottomExclusive)
|
||||
{
|
||||
resultPos = documentEnd;
|
||||
(*pAmountMoved)++;
|
||||
}
|
||||
else
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1520,14 +1526,22 @@ void UiaTextRangeBase::_moveEndpointByUnitWord(_In_ const int moveCount,
|
||||
// to the next branch and move to the previous word!
|
||||
(*pAmountMoved)--;
|
||||
}
|
||||
else if (buffer.MoveToPreviousWord(nextPos, _wordDelimiters))
|
||||
{
|
||||
resultPos = nextPos;
|
||||
(*pAmountMoved)--;
|
||||
}
|
||||
else
|
||||
{
|
||||
resultPos = bufferOrigin;
|
||||
// Move to the beginning of the current word
|
||||
auto copy = buffer.GetWordStart(nextPos, _wordDelimiters, true, documentEnd);
|
||||
|
||||
// Step back one position, then find the start of that word
|
||||
if (!buffer.GetSize().DecrementInBounds(copy, true))
|
||||
{
|
||||
// can't move behind current word
|
||||
resultPos = bufferOrigin;
|
||||
}
|
||||
else
|
||||
{
|
||||
resultPos = buffer.GetWordStart(copy, _wordDelimiters, true, documentEnd);
|
||||
(*pAmountMoved)--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -128,6 +128,8 @@ namespace Microsoft::Console::Utils
|
||||
|
||||
const wchar_t* FindActionableControlCharacter(const wchar_t* beg, const size_t len) noexcept;
|
||||
|
||||
bool IsValidDirectory(const wchar_t* path) noexcept;
|
||||
|
||||
// Same deal, but in TerminalPage::_evaluatePathForCwd
|
||||
std::wstring EvaluateStartingDirectory(std::wstring_view cwd, std::wstring_view startingDirectory);
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ TARGETLIBS = \
|
||||
$(ONECORE_EXTERNAL_SDK_LIB_PATH)\ntdll.lib \
|
||||
$(TARGETLIBS) \
|
||||
|
||||
DELAYLOAD = \
|
||||
icu.dll \
|
||||
|
||||
# -------------------------------------
|
||||
# Localization
|
||||
# -------------------------------------
|
||||
|
||||
@@ -1237,6 +1237,19 @@ plainSearch:
|
||||
return it;
|
||||
}
|
||||
|
||||
// Returns true if it's a valid path to a directory.
|
||||
bool Utils::IsValidDirectory(const wchar_t* path) noexcept
|
||||
{
|
||||
if (path == nullptr || *path == L'\0')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA data;
|
||||
const auto ok = GetFileAttributesExW(path, GetFileExInfoStandard, &data);
|
||||
return ok && (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
}
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
std::wstring Utils::EvaluateStartingDirectory(
|
||||
|
||||
@@ -39,7 +39,8 @@ static Pipes createPipes()
|
||||
VERIFY_IS_TRUE(SetHandleInformation(p.our.out.get(), HANDLE_FLAG_INHERIT, 0));
|
||||
|
||||
// ConPTY requests a DA1 report on startup. Emulate the response from the terminal.
|
||||
WriteFile(p.our.in.get(), "\x1b[?61c", 6, nullptr, nullptr);
|
||||
DWORD written{};
|
||||
WriteFile(p.our.in.get(), "\x1b[?61c", 6, &written, nullptr);
|
||||
|
||||
return p;
|
||||
}
|
||||
@@ -125,6 +126,7 @@ static HRESULT AttachPseudoConsole(HPCON hPC, std::wstring command, PROCESS_INFO
|
||||
|
||||
STARTUPINFOEXW siEx{};
|
||||
siEx.StartupInfo.cb = sizeof(STARTUPINFOEXW);
|
||||
siEx.StartupInfo.dwFlags = STARTF_USESTDHANDLES; // we will leave the handles empty to ensure none are inherited.
|
||||
siEx.lpAttributeList = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(buffer.get());
|
||||
|
||||
RETURN_IF_WIN32_BOOL_FALSE(InitializeProcThreadAttributeList(siEx.lpAttributeList, 1, 0, &size));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildThisProjectDirectory)\..\Microsoft.Windows.Console.ConPTY.props" />
|
||||
<Import Project="$(MSBuildThisFileDirectory)..\Microsoft.Windows.Console.ConPTY.props" />
|
||||
</Project>
|
||||
|
||||
@@ -332,7 +332,7 @@ static HRESULT _ClearPseudoConsole(_In_ const PseudoConsole* const pPty, BOOL ke
|
||||
// Return Value:
|
||||
// - S_OK if the call succeeded, else an appropriate HRESULT for failing to
|
||||
// write the clear message to the pty.
|
||||
HRESULT _ShowHidePseudoConsole(_In_ const PseudoConsole* const pPty, const bool show)
|
||||
HRESULT _ShowHidePseudoConsole(_In_ const PseudoConsole* const pPty, const BOOL show) noexcept
|
||||
{
|
||||
if (pPty == nullptr)
|
||||
{
|
||||
@@ -340,7 +340,7 @@ HRESULT _ShowHidePseudoConsole(_In_ const PseudoConsole* const pPty, const bool
|
||||
}
|
||||
unsigned short signalPacket[2];
|
||||
signalPacket[0] = PTY_SIGNAL_SHOWHIDE_WINDOW;
|
||||
signalPacket[1] = show;
|
||||
signalPacket[1] = show ? 1 : 0;
|
||||
|
||||
const BOOL fSuccess = WriteFile(pPty->hSignal, signalPacket, sizeof(signalPacket), nullptr, nullptr);
|
||||
return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError());
|
||||
@@ -539,7 +539,7 @@ extern "C" HRESULT WINAPI ConptyClearPseudoConsole(_In_ HPCON hPC, BOOL keepCurs
|
||||
// to keep ConPTY's internal HWND state in sync with the state of whatever the
|
||||
// hosting window is.
|
||||
// - For more information, refer to GH#12515.
|
||||
extern "C" HRESULT WINAPI ConptyShowHidePseudoConsole(_In_ HPCON hPC, bool show)
|
||||
extern "C" HRESULT WINAPI ConptyShowHidePseudoConsole(_In_ HPCON hPC, BOOL show)
|
||||
{
|
||||
// _ShowHidePseudoConsole will return E_INVALIDARG for us if the hPC is nullptr.
|
||||
return _ShowHidePseudoConsole((PseudoConsole*)hPC, show);
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
},
|
||||
{
|
||||
"name": "ms-gsl",
|
||||
"version": "3.1.0"
|
||||
"version": "4.2.1"
|
||||
},
|
||||
{
|
||||
"name": "jsoncpp",
|
||||
|
||||
Reference in New Issue
Block a user