Compare commits

..

9 Commits

Author SHA1 Message Date
Carlos Zamora
9f06016864 code format 2026-04-01 19:49:50 -07:00
Carlos Zamora
aba027ff5c Polish interactions with "close all...tabs/panes" actions
Scenarios covered:
- "close all other panes"
- "close all other tabs"
- "close all tabs after the current tab"

Changes:
- display the dialog appropriately with the right text
- show only one dialog instead of multiple annoying ones
- add special handling when no other panes/tabs are there to be closed
- update handling for these event handlers (passthrough, if possible)
2026-04-01 19:41:28 -07:00
Carlos Zamora
cb531fe8ef consolidate logic 2026-04-01 19:14:02 -07:00
Carlos Zamora
466ba771ba Make confirmCloseOn an enum instead of enum flag + add checkbox
The checkboxes were added to QuitDialog and CloseAllDialog too.
confirmCloseOn should affect all of these for consistency.
2026-04-01 18:44:08 -07:00
Carlos Zamora
1154428f9c address Dustin's feedback 2026-03-30 17:50:15 -07:00
Carlos Zamora
197969255a address my own feedback 2026-03-30 11:41:04 -07:00
Mike Griese
34e973fd18 better styling 2026-03-05 10:14:53 -06:00
Mike Griese
f990fc2fbb format 2026-03-05 10:00:59 -06:00
Mike Griese
da1d3224a1 Add support for more nuanced "closeOn" settings 2026-03-05 09:42:48 -06:00
130 changed files with 1232 additions and 1130 deletions

View File

@@ -572,7 +572,6 @@ FGHIJ
fgidx
FGs
FILEDESCRIPTION
filehops
FILESUBTYPE
FILESYSPATH
FILEW
@@ -1773,7 +1772,6 @@ uldash
uldb
ULONGLONG
ulwave
Unaccess
Unadvise
unattend
UNCPRIORITY

View File

@@ -93,7 +93,7 @@ jobs:
steps:
- name: check-spelling
id: spelling
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
uses: check-spelling/check-spelling@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@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
uses: check-spelling/check-spelling@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@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
uses: check-spelling/check-spelling@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@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
uses: check-spelling/check-spelling@v0.0.25
with:
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }}
checkout: true

View File

@@ -860,6 +860,17 @@
<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" />

View File

@@ -15,6 +15,7 @@
- [Via Chocolatey (unofficial)](#via-chocolatey-unofficial)
- [Via Scoop (unofficial)](#via-scoop-unofficial)
- [Installing Windows Terminal Canary](#installing-windows-terminal-canary)
- [Windows Terminal Roadmap](#windows-terminal-roadmap)
- [Terminal \& Console Overview](#terminal--console-overview)
- [Windows Terminal](#windows-terminal)
- [The Windows Console Host](#the-windows-console-host)
@@ -177,6 +178,11 @@ _Learn more about the [types of Windows Terminal distributions](https://learn.mi
---
## Windows Terminal Roadmap
The plan for the Windows Terminal [is described here](/doc/roadmap-2023.md) and
will be updated as the project proceeds.
## Terminal & Console Overview
Please take a few minutes to review the overview below before diving into the

View File

@@ -59,7 +59,6 @@ stages:
name: SHINE-INT-L
${{ else }}:
name: SHINE-OSS-L
demands: ImageOverride -equals SHINE-VS18-Latest
buildPlatforms: [x64]
buildConfigurations: [AuditMode]
buildEverything: true
@@ -83,7 +82,6 @@ stages:
name: SHINE-INT-L
${{ else }}:
name: SHINE-OSS-L
demands: ImageOverride -equals SHINE-VS18-Latest
buildPlatforms:
- ${{ platform }}
buildConfigurations: [Release]

View File

@@ -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-VS18-Latest
demands: ImageOverride -equals SHINE-VS17-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-VS18-Latest
demands: ImageOverride -equals SHINE-VS17-Latest
branding: ${{ parameters.branding }}
buildTerminal: ${{ parameters.buildTerminal }}
buildConPTY: ${{ parameters.buildConPTY }}

View File

@@ -211,7 +211,7 @@ extends:
ob_createvpack_verbose: true
ob_createvpack_vpackdirectory: '$(JobOutputDirectory)\vpack'
ob_createvpack_versionAs: string
ob_createvpack_version: '$(XES_PACKAGEVERSIONNUMBER)'
ob_createvpack_version: '$(XES_APPXMANIFESTVERSION)'
ob_updateOSManifest_gitcheckinConfigPath: '$(Build.SourcesDirectory)\build\config\GitCheckin.json'
# We're skipping the 'fetch' part of the OneBranch rules, but that doesn't mean
# that it doesn't expect to have downloaded a manifest directly to some 'destination'

View File

@@ -1,6 +1,6 @@
steps:
- pwsh: |-
$VsInstallRoot = & 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -requires Microsoft.VisualStudio.Component.Vcpkg -property installationPath -latest
$VsInstallRoot = & 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -requires Microsoft.VisualStudio.Component.Vcpkg -property installationPath
If ([String]::IsNullOrEmpty($VsInstallRoot)) {
Remove-Item -Recurse -Force dep/vcpkg -ErrorAction:Ignore
git clone https://github.com/microsoft/vcpkg dep/vcpkg

View File

@@ -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 v145)
set(VCPKG_PLATFORM_TOOLSET v143)

View File

@@ -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 v145)
set(VCPKG_PLATFORM_TOOLSET v143)
set(VCPKG_CXX_FLAGS /fsanitize=address)
set(VCPKG_C_FLAGS /fsanitize=address)

View File

@@ -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 v145)
set(VCPKG_PLATFORM_TOOLSET v143)
set(VCPKG_CXX_FLAGS /fsanitize=address)
set(VCPKG_C_FLAGS /fsanitize=address)

View File

@@ -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 v145)
set(VCPKG_PLATFORM_TOOLSET v143)
set(VCPKG_CXX_FLAGS /fsanitize=address)
set(VCPKG_C_FLAGS /fsanitize=address)

View File

@@ -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 v145)
set(VCPKG_PLATFORM_TOOLSET v143)

View File

@@ -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 v145)
set(VCPKG_PLATFORM_TOOLSET v143)

View File

@@ -3174,11 +3174,6 @@
"mingw"
],
"type": "string"
},
"dragDropDelimiter": {
"default": " ",
"description": "The delimiter used when dropping multiple files onto the terminal.",
"type": "string"
}
}
},

View File

@@ -219,17 +219,6 @@ 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
@@ -945,10 +934,6 @@ 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();
}
@@ -1156,20 +1141,7 @@ DelimiterClass TextBuffer::_GetDelimiterClassAt(const til::point pos, const std:
return GetRowByOffset(realPos.y).DelimiterClassAt(realPos.x, wordDelimiters);
}
// 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
til::point TextBuffer::GetWordStart2(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()) };
@@ -1202,7 +1174,7 @@ til::point TextBuffer::GetWordStart(til::point pos, const std::wstring_view word
// 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, includeWhitespace);
pos = _GetDelimiterClassRunStart(pos, wordDelimiters);
if (!includeWhitespace || pos.x == bufferSize.Left())
{
// Special case:
@@ -1213,26 +1185,12 @@ til::point TextBuffer::GetWordStart(til::point pos, const std::wstring_view word
else if (initialDelimiter == DelimiterClass::ControlChar)
{
bufferSize.DecrementInExclusiveBounds(pos);
pos = _GetDelimiterClassRunStart(pos, wordDelimiters, includeWhitespace);
pos = _GetDelimiterClassRunStart(pos, wordDelimiters);
}
return pos;
}
// 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
til::point TextBuffer::GetWordEnd2(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()) };
@@ -1265,12 +1223,8 @@ til::point TextBuffer::GetWordEnd(til::point pos, const std::wstring_view wordDe
// 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, includeWhitespace);
if (pos >= limit)
{
return limit;
}
else if (!includeWhitespace || pos.x == bufferSize.RightExclusive())
pos = _GetDelimiterClassRunEnd(pos, wordDelimiters);
if (!includeWhitespace || pos.x == bufferSize.RightExclusive())
{
// Special case:
// we're at the right boundary (and end of a delimiter class run),
@@ -1281,8 +1235,7 @@ til::point TextBuffer::GetWordEnd(til::point pos, const std::wstring_view wordDe
if (const auto nextDelimClass = bufferSize.IsInBounds(pos) ? _GetDelimiterClassAt(pos, wordDelimiters) : DelimiterClass::ControlChar;
nextDelimClass == DelimiterClass::ControlChar)
{
pos = _GetDelimiterClassRunEnd(pos, wordDelimiters, includeWhitespace);
return std::min(pos, limit);
return _GetDelimiterClassRunEnd(pos, wordDelimiters);
}
return pos;
}
@@ -1335,48 +1288,29 @@ bool TextBuffer::IsWordBoundary(const til::point pos, const std::wstring_view wo
return prevDelimiterClass != currentDelimiterClass && currentDelimiterClass != DelimiterClass::ControlChar;
}
// 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
til::point TextBuffer::_GetDelimiterClassRunStart(til::point pos, const std::wstring_view wordDelimiters) const
{
const auto bufferSize = GetSize();
const auto initialDelimClass = bufferSize.IsInBounds(pos) ? _GetDelimiterClassAt(pos, wordDelimiters) : DelimiterClass::ControlChar;
auto nextPos = pos;
while (nextPos != bufferSize.Origin())
for (auto nextPos = pos; nextPos != bufferSize.Origin(); pos = nextPos)
{
bufferSize.DecrementInExclusiveBounds(nextPos);
if (nextPos.x == bufferSize.RightExclusive())
{
// wrapped onto previous line
// wrapped onto previous line,
// check if it was forced to wrap
const auto& row = GetRowByOffset(nextPos.y);
if (!row.WasWrapForced() && !accessibilityMode)
if (!row.WasWrapForced())
{
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;
}
@@ -1386,8 +1320,7 @@ 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
// - 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
til::point TextBuffer::_GetDelimiterClassRunEnd(til::point pos, const std::wstring_view wordDelimiters) const
{
const auto bufferSize = GetSize();
const auto initialDelimClass = bufferSize.IsInBounds(pos) ? _GetDelimiterClassAt(pos, wordDelimiters) : DelimiterClass::ControlChar;
@@ -1400,16 +1333,7 @@ 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 (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)
if (!row.WasWrapForced() || _GetDelimiterClassAt(nextPos, wordDelimiters) != initialDelimClass)
{
return pos;
}
@@ -1424,6 +1348,283 @@ 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
@@ -1470,6 +1671,57 @@ 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:
@@ -3527,34 +3779,3 @@ 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;
}

View File

@@ -172,10 +172,15 @@ public:
void TriggerNewTextNotification(const std::wstring_view newText);
void TriggerSelection();
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;
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;
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;
@@ -310,9 +315,6 @@ 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);
@@ -322,13 +324,16 @@ 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 bool accessibilityMode = false) const;
til::point _GetDelimiterClassRunEnd(til::point pos, const std::wstring_view wordDelimiters, const bool accessibilityMode = false) 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;
void _PruneHyperlinks();
std::wstring _commandForRow(const til::CoordType rowOffset, const til::CoordType bottomInclusive, const bool clipAtCursor = false) const;

View File

@@ -34,16 +34,6 @@ 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;

View File

@@ -26,9 +26,6 @@ TARGETLIBS = \
$(CONSOLE_OBJ_PATH)\types\lib\$(O)\ConTypes.lib \
$(TARGETLIBS) \
DELAYLOAD = \
icu.dll \
# -------------------------------------
# Localization
# -------------------------------------

View File

@@ -192,7 +192,7 @@
ResourceKey="TabViewBackground" />
<SolidColorBrush x:Key="SettingsUiTabBrush"
Color="#282828" />
Color="#0c0c0c" />
<SolidColorBrush x:Key="BroadcastPaneBorderColor"
Color="{StaticResource SystemAccentColorDark2}" />
@@ -211,7 +211,7 @@
ResourceKey="TabViewBackground" />
<SolidColorBrush x:Key="SettingsUiTabBrush"
Color="#F9F9F9" />
Color="#ffffff" />
<SolidColorBrush x:Key="BroadcastPaneBorderColor"
Color="{StaticResource SystemAccentColorLight2}" />
@@ -234,7 +234,7 @@
ResourceKey="SystemColorButtonFaceColorBrush" />
<StaticResource x:Key="SettingsUiTabBrush"
ResourceKey="SystemColorWindowBrush" />
ResourceKey="SystemControlBackgroundBaseLowBrush" />
<SolidColorBrush x:Key="BroadcastPaneBorderColor"
Color="{StaticResource SystemColorHighlightColor}" />

View File

@@ -801,7 +801,7 @@ namespace winrt::TerminalApp::implementation
_RemoveTabs(tabsToRemove);
actionArgs.Handled(true);
actionArgs.Handled(!tabsToRemove.empty());
}
}
@@ -837,7 +837,7 @@ namespace winrt::TerminalApp::implementation
// tab row, until you mouse over them. Probably has something to do
// with tabs not resizing down until there's a mouse exit event.
actionArgs.Handled(true);
actionArgs.Handled(!tabsToRemove.empty());
}
}
@@ -1475,7 +1475,7 @@ namespace winrt::TerminalApp::implementation
WI_IsAnyFlagSet(source, SuggestionsSource::CommandHistory | SuggestionsSource::QuickFixes);
if (const auto& control{ _GetActiveControl() })
{
currentWorkingDirectory = control.WorkingDirectory();
currentWorkingDirectory = control.CurrentWorkingDirectory();
if (shouldGetContext)
{

View File

@@ -496,24 +496,48 @@
<value>Third-Party notices</value>
<comment>A hyperlink name for the Terminal's third-party notices</comment>
</data>
<data name="QuitDialog.CloseButtonText" xml:space="preserve">
<data name="ConfirmCloseDialog_Cancel" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="QuitDialog.PrimaryButtonText" xml:space="preserve">
<value>Close all</value>
</data>
<data name="QuitDialog.Title" xml:space="preserve">
<data name="ConfirmCloseDialog_CloseAllTitle" xml:space="preserve">
<value>Do you want to close all windows?</value>
</data>
<data name="CloseAllDialog.CloseButtonText" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="CloseAllDialog.PrimaryButtonText" xml:space="preserve">
<data name="ConfirmCloseDialog_CloseAllPrimary" xml:space="preserve">
<value>Close all</value>
</data>
<data name="CloseAllDialog.Title" xml:space="preserve">
<data name="ConfirmCloseDialog_WindowTitle" xml:space="preserve">
<value>Do you want to close all tabs?</value>
</data>
<data name="ConfirmCloseDialog_WindowPrimary" xml:space="preserve">
<value>Close all</value>
</data>
<data name="ConfirmCloseDialog_TabTitle" xml:space="preserve">
<value>Do you want to close this tab?</value>
</data>
<data name="ConfirmCloseDialog_TabPrimary" xml:space="preserve">
<value>Close tab</value>
</data>
<data name="ConfirmCloseDialog_PaneTitle" xml:space="preserve">
<value>Do you want to close this pane?</value>
</data>
<data name="ConfirmCloseDialog_PanePrimary" xml:space="preserve">
<value>Close pane</value>
</data>
<data name="ConfirmCloseDialog_MultipleTabsTitle" xml:space="preserve">
<value>Do you want to close these tabs?</value>
</data>
<data name="ConfirmCloseDialog_MultipleTabsPrimary" xml:space="preserve">
<value>Close tabs</value>
</data>
<data name="ConfirmCloseDialog_MultiplePanesTitle" xml:space="preserve">
<value>Do you want to close these panes?</value>
</data>
<data name="ConfirmCloseDialog_MultiplePanesPrimary" xml:space="preserve">
<value>Close panes</value>
</data>
<data name="DontAskAgainCheckBox.Content" xml:space="preserve">
<value>Don't ask me again</value>
</data>
<data name="CloseReadOnlyDialog.CloseButtonText" xml:space="preserve">
<value>Cancel</value>
</data>

View File

@@ -391,10 +391,12 @@ namespace winrt::TerminalApp::implementation
}
// Method Description:
// - Removes the tab (both TerminalControl and XAML) after prompting for approval
// - Removes the tab (both TerminalControl and XAML) after prompting for approval.
// Arguments:
// - tab: the tab to remove
winrt::Windows::Foundation::IAsyncAction TerminalPage::_HandleCloseTabRequested(winrt::TerminalApp::Tab tab)
// - tab: the tab to remove.
// - skipConfirmClose: if true, skip the confirmCloseOn check. Used when
// an aggregate confirmation has already been shown (e.g. close other tabs).
winrt::Windows::Foundation::IAsyncAction TerminalPage::_HandleCloseTabRequested(winrt::TerminalApp::Tab tab, bool skipConfirmClose)
{
winrt::com_ptr<TerminalPage> strong;
@@ -413,6 +415,24 @@ namespace winrt::TerminalApp::implementation
}
}
// Skip the per-tab confirmCloseOn check when the caller has already
// shown an aggregate confirmation dialog (e.g. _RemoveTabs).
if (!skipConfirmClose)
{
const auto tabImpl = _GetTabImpl(tab);
if (tabImpl && _ShouldWarnOnCloseTab(tabImpl))
{
const auto weak = get_weak();
auto warningResult = co_await _ShowConfirmCloseDialog(ConfirmCloseDialogKind::Tab);
strong = weak.get();
if (!strong || warningResult != ContentDialogResult::Primary)
{
co_return;
}
}
}
auto t = winrt::get_self<implementation::Tab>(tab);
auto actions = t->BuildStartupActions(BuildStartupKind::None);
_AddPreviouslyClosedPaneOrTab(std::move(actions));
@@ -782,6 +802,22 @@ namespace winrt::TerminalApp::implementation
if (const auto pane{ activeTab->GetActivePane() })
{
const auto weak = get_weak();
// Check if we should warn before closing a single pane
// (only triggers on Always — Automatic doesn't warn for single pane)
const auto setting = _settings.GlobalSettings().ConfirmCloseOn();
if (setting == ConfirmCloseOn::Always)
{
// If this is the last pane, closing it closes the tab,
// so use the tab dialog text instead.
const auto kind = activeTab->GetLeafPaneCount() == 1 ? ConfirmCloseDialogKind::Tab : ConfirmCloseDialogKind::Pane;
auto warningResult = co_await _ShowConfirmCloseDialog(kind);
if (!weak.get() || warningResult != ContentDialogResult::Primary)
{
co_return;
}
}
if (co_await _PaneConfirmCloseReadOnly(pane))
{
if (const auto strong = weak.get())
@@ -794,15 +830,38 @@ namespace winrt::TerminalApp::implementation
}
// Method Description:
// - Close all panes with the given IDs sequentially.
// - Closes provided panes one by one, showing a single aggregate
// confirmation dialog upfront if the confirmCloseOn setting warrants it.
// Arguments:
// - weakTab: weak reference to the tab that the pane belongs to.
// - weakTab: weak reference to the tab that the panes belong to.
// - paneIds: collection of the IDs of the panes that are marked for removal.
void TerminalPage::_ClosePanes(weak_ref<Tab> weakTab, std::vector<uint32_t> paneIds)
safe_void_coroutine TerminalPage::_ClosePanes(weak_ref<Tab> weakTab, std::vector<uint32_t> paneIds)
{
// Show a single aggregate confirmation for closing multiple panes.
if (_settings.GlobalSettings().ConfirmCloseOn() != ConfirmCloseOn::Never)
{
const auto weak = get_weak();
auto warningResult = co_await _ShowConfirmCloseDialog(ConfirmCloseDialogKind::MultiplePanes);
if (!weak.get() || warningResult != ContentDialogResult::Primary)
{
co_return;
}
}
_CloseRemainingPanes(weakTab, std::move(paneIds));
}
// Method Description:
// - Recursively closes panes by ID, chaining each close via the
// ClosedByParent callback. Called after confirmation has already
// been handled by _ClosePanes.
// Arguments:
// - weakTab: weak reference to the tab that the panes belong to.
// - paneIds: remaining pane IDs to close.
void TerminalPage::_CloseRemainingPanes(weak_ref<Tab> weakTab, std::vector<uint32_t> paneIds)
{
if (auto strongTab{ weakTab.get() })
{
// Close all unfocused panes one by one
while (!paneIds.empty())
{
const auto id = paneIds.back();
@@ -813,11 +872,10 @@ namespace winrt::TerminalApp::implementation
pane->ClosedByParent([ids{ std::move(paneIds) }, weakThis{ get_weak() }, weakTab]() {
if (auto strongThis{ weakThis.get() })
{
strongThis->_ClosePanes(weakTab, std::move(ids));
strongThis->_CloseRemainingPanes(weakTab, std::move(ids));
}
});
// Close the pane which will eventually trigger the closed by parent event
_HandleClosePaneRequested(pane);
break;
}
@@ -840,19 +898,35 @@ namespace winrt::TerminalApp::implementation
}
// Method Description:
// - Closes provided tabs one by one
// - Closes provided tabs one by one, showing a single aggregate
// confirmation dialog upfront if the confirmCloseOn setting warrants it.
// Arguments:
// - tabs - tabs to remove
safe_void_coroutine TerminalPage::_RemoveTabs(const std::vector<winrt::TerminalApp::Tab> tabs)
{
if (tabs.empty())
{
co_return;
}
const auto weak = get_weak();
// Show a single aggregate confirmation instead of per-tab dialogs.
if (_settings.GlobalSettings().ConfirmCloseOn() != ConfirmCloseOn::Never)
{
auto warningResult = co_await _ShowConfirmCloseDialog(ConfirmCloseDialogKind::MultipleTabs);
if (!weak.get() || warningResult != ContentDialogResult::Primary)
{
co_return;
}
}
for (auto& tab : tabs)
{
winrt::Windows::Foundation::IAsyncAction action{ nullptr };
if (const auto strong = weak.get())
{
action = _HandleCloseTabRequested(tab);
action = _HandleCloseTabRequested(tab, true);
}
if (!action)

View File

@@ -884,26 +884,63 @@ namespace winrt::TerminalApp::implementation
}
// Method Description:
// - Displays a dialog to warn the user that they are about to close all open windows.
// Once the user clicks the OK button, shut down the application.
// If cancel is clicked, the dialog will close.
// - Displays the unified close confirmation dialog configured for the
// given scenario. Resets the "don't ask me again" checkbox before showing.
// If the user confirms and checked "don't ask me again", sets
// confirmCloseOn to Never and writes settings to disk.
// - Only one dialog can be visible at a time. If another dialog is visible
// when this is called, nothing happens. See _ShowDialog for details
winrt::Windows::Foundation::IAsyncOperation<ContentDialogResult> TerminalPage::_ShowQuitDialog()
winrt::Windows::Foundation::IAsyncOperation<ContentDialogResult> TerminalPage::_ShowConfirmCloseDialog(ConfirmCloseDialogKind kind)
{
return _ShowDialogHelper(L"QuitDialog");
}
// Load the dialog (triggers x:Load) and configure its strings.
const auto dialog = FindName(L"ConfirmCloseDialog").as<ContentDialog>();
switch (kind)
{
case ConfirmCloseDialogKind::CloseAll:
dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_CloseAllTitle")));
dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_CloseAllPrimary"));
break;
case ConfirmCloseDialogKind::Window:
dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_WindowTitle")));
dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_WindowPrimary"));
break;
case ConfirmCloseDialogKind::Tab:
dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_TabTitle")));
dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_TabPrimary"));
break;
case ConfirmCloseDialogKind::MultiplePanes:
dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_MultiplePanesTitle")));
dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_MultiplePanesPrimary"));
break;
case ConfirmCloseDialogKind::MultipleTabs:
dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_MultipleTabsTitle")));
dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_MultipleTabsPrimary"));
break;
case ConfirmCloseDialogKind::Pane:
dialog.Title(winrt::box_value(RS_(L"ConfirmCloseDialog_PaneTitle")));
dialog.PrimaryButtonText(RS_(L"ConfirmCloseDialog_PanePrimary"));
break;
}
dialog.CloseButtonText(RS_(L"ConfirmCloseDialog_Cancel"));
// Method Description:
// - Displays a dialog for warnings found while closing the terminal app using
// key binding with multiple tabs opened. Display messages to warn user
// that more than 1 tab is opened, and once the user clicks the OK button, remove
// all the tabs and shut down and app. If cancel is clicked, the dialog will close
// - Only one dialog can be visible at a time. If another dialog is visible
// when this is called, nothing happens. See _ShowDialog for details
winrt::Windows::Foundation::IAsyncOperation<ContentDialogResult> TerminalPage::_ShowCloseWarningDialog()
{
return _ShowDialogHelper(L"CloseAllDialog");
// BODGY: After a ContentDialog is dismissed, FindName() can no longer
// resolve children inside it. Use Content() to get the checkbox directly.
const auto checkbox = dialog.Content().as<CheckBox>();
checkbox.IsChecked(false);
auto result = ContentDialogResult::None;
if (auto presenter{ _dialogPresenter.get() })
{
result = co_await presenter.ShowDialog(dialog);
}
if (result == ContentDialogResult::Primary && checkbox.IsChecked().Value())
{
_settings.GlobalSettings().ConfirmCloseOn(ConfirmCloseOn::Never);
_settings.WriteSettingsToDisk();
}
co_return result;
}
// Method Description:
@@ -1600,7 +1637,8 @@ namespace winrt::TerminalApp::implementation
// Replace the Starting directory with the CWD, if given
const auto workingDirectory = control.WorkingDirectory();
if (Utils::IsValidDirectory(workingDirectory.c_str()))
const auto validWorkingDirectory = !workingDirectory.empty();
if (validWorkingDirectory)
{
controlSettings.DefaultSettings()->StartingDirectory(workingDirectory);
}
@@ -2209,12 +2247,13 @@ namespace winrt::TerminalApp::implementation
// signal that we want to close everything.
safe_void_coroutine TerminalPage::RequestQuit()
{
if (!_displayingCloseDialog)
const auto setting = _settings.GlobalSettings().ConfirmCloseOn();
if (setting != ConfirmCloseOn::Never && !_displayingCloseDialog)
{
_displayingCloseDialog = true;
const auto weak = get_weak();
auto warningResult = co_await _ShowQuitDialog();
auto warningResult = co_await _ShowConfirmCloseDialog(ConfirmCloseDialogKind::CloseAll);
const auto strong = weak.get();
if (!strong)
{
@@ -2227,9 +2266,9 @@ namespace winrt::TerminalApp::implementation
{
co_return;
}
QuitRequested.raise(nullptr, nullptr);
}
QuitRequested.raise(nullptr, nullptr);
}
void TerminalPage::PersistState()
@@ -2307,12 +2346,75 @@ namespace winrt::TerminalApp::implementation
}
// Method Description:
// - Close the terminal app. If there is more
// than one tab opened, show a warning dialog.
// - Determines whether a close-window action should show a confirmation
// dialog, based on the confirmCloseOn setting and the current window state.
// Arguments:
// - <none>
// Return Value:
// - true if a warning dialog should be shown before closing the window.
bool TerminalPage::_ShouldWarnOnClose() const
{
const auto setting = _settings.GlobalSettings().ConfirmCloseOn();
switch (setting)
{
case ConfirmCloseOn::Always:
return true;
case ConfirmCloseOn::Automatic:
{
// Warn if there's more than one tab.
if (_HasMultipleTabs())
{
return true;
}
// Warn if the one tab has more than one pane.
if (_GetTabImpl(_tabs.GetAt(0))->GetLeafPaneCount() > 1)
{
return true;
}
return false;
}
case ConfirmCloseOn::Never:
default:
return false;
}
}
// Method Description:
// - Determines whether closing a specific tab should show a confirmation
// dialog, based on the confirmCloseOn setting and the tab's state.
// Arguments:
// - tab: The tab being closed.
// Return Value:
// - true if a warning dialog should be shown before closing the tab.
bool TerminalPage::_ShouldWarnOnCloseTab(const winrt::com_ptr<Tab>& tab) const
{
const auto setting = _settings.GlobalSettings().ConfirmCloseOn();
switch (setting)
{
case ConfirmCloseOn::Always:
return true;
case ConfirmCloseOn::Automatic:
// Warn if this tab has more than one pane.
return tab->GetLeafPaneCount() > 1;
case ConfirmCloseOn::Never:
default:
return false;
}
}
// Method Description:
// - Close the terminal app. If the confirmCloseOn setting indicates we should
// warn for the current window state, show a warning dialog.
safe_void_coroutine TerminalPage::CloseWindow()
{
if (_HasMultipleTabs() &&
_settings.GlobalSettings().ConfirmCloseAllTabs() &&
if (_ShouldWarnOnClose() &&
!_displayingCloseDialog)
{
if (_newTabButton && _newTabButton.Flyout())
@@ -2321,7 +2423,14 @@ namespace winrt::TerminalApp::implementation
}
_DismissTabContextMenus();
_displayingCloseDialog = true;
auto warningResult = co_await _ShowCloseWarningDialog();
const auto weak = get_weak();
auto warningResult = co_await _ShowConfirmCloseDialog(ConfirmCloseDialogKind::Window);
if (!weak.get())
{
co_return;
}
_displayingCloseDialog = false;
if (warningResult != ContentDialogResult::Primary)
@@ -2972,11 +3081,7 @@ namespace winrt::TerminalApp::implementation
text = winrt::hstring{ Utils::TrimPaste(text) };
}
// 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())
if (text.empty())
{
co_return;
}
@@ -3558,7 +3663,8 @@ namespace winrt::TerminalApp::implementation
profile = GetClosestProfileForDuplicationOfProfile(profile);
controlSettings = Settings::TerminalSettings::CreateWithProfile(_settings, profile);
const auto workingDirectory = tabImpl->GetActiveTerminalControl().WorkingDirectory();
if (Utils::IsValidDirectory(workingDirectory.c_str()))
const auto validWorkingDirectory = !workingDirectory.empty();
if (validWorkingDirectory)
{
controlSettings.DefaultSettings()->StartingDirectory(workingDirectory);
}
@@ -3744,13 +3850,7 @@ namespace winrt::TerminalApp::implementation
// for nulls
if (const auto& connection{ _duplicateConnectionForRestart(paneContent) })
{
// 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);
paneContent.GetTermControl().Connection(connection);
connection.Start();
}
}

View File

@@ -54,6 +54,16 @@ namespace winrt::TerminalApp::implementation
ScrollDown = 1
};
enum class ConfirmCloseDialogKind
{
Pane,
Tab,
MultiplePanes,
MultipleTabs,
Window,
CloseAll
};
struct RenameWindowRequestedArgs : RenameWindowRequestedArgsT<RenameWindowRequestedArgs>
{
WINRT_PROPERTY(winrt::hstring, ProposedName);
@@ -301,8 +311,7 @@ namespace winrt::TerminalApp::implementation
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowDialogHelper(const std::wstring_view& name);
void _ShowAboutDialog();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowQuitDialog();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowCloseWarningDialog();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowConfirmCloseDialog(ConfirmCloseDialogKind kind);
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowCloseReadOnlyDialog();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowMultiLinePasteWarningDialog();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowLargePasteWarningDialog();
@@ -349,7 +358,7 @@ namespace winrt::TerminalApp::implementation
safe_void_coroutine _ExportTab(const Tab& tab, winrt::hstring filepath);
winrt::Windows::Foundation::IAsyncAction _HandleCloseTabRequested(winrt::TerminalApp::Tab tab);
winrt::Windows::Foundation::IAsyncAction _HandleCloseTabRequested(winrt::TerminalApp::Tab tab, bool skipConfirmClose = false);
void _CloseTabAtIndex(uint32_t index);
void _RemoveTab(const winrt::TerminalApp::Tab& tab);
safe_void_coroutine _RemoveTabs(const std::vector<winrt::TerminalApp::Tab> tabs);
@@ -400,9 +409,12 @@ namespace winrt::TerminalApp::implementation
TerminalApp::Tab _GetTabByTabViewItem(const IInspectable& tabViewItem) const noexcept;
void _HandleClosePaneRequested(std::shared_ptr<Pane> pane);
bool _ShouldWarnOnClose() const;
bool _ShouldWarnOnCloseTab(const winrt::com_ptr<Tab>& tab) const;
safe_void_coroutine _SetFocusedTab(const winrt::TerminalApp::Tab tab);
safe_void_coroutine _CloseFocusedPane();
void _ClosePanes(weak_ref<Tab> weakTab, std::vector<uint32_t> paneIds);
safe_void_coroutine _ClosePanes(weak_ref<Tab> weakTab, std::vector<uint32_t> paneIds);
void _CloseRemainingPanes(weak_ref<Tab> weakTab, std::vector<uint32_t> paneIds);
winrt::Windows::Foundation::IAsyncOperation<bool> _PaneConfirmCloseReadOnly(std::shared_ptr<Pane> pane);
void _AddPreviouslyClosedPaneOrTab(std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>&& args);

View File

@@ -86,17 +86,12 @@
Grid.Row="2"
x:Load="False" />
<ContentDialog x:Name="QuitDialog"
x:Uid="QuitDialog"
<ContentDialog x:Name="ConfirmCloseDialog"
Grid.Row="2"
x:Load="False"
DefaultButton="Primary" />
<ContentDialog x:Name="CloseAllDialog"
x:Uid="CloseAllDialog"
Grid.Row="2"
x:Load="False"
DefaultButton="Primary" />
DefaultButton="Primary">
<CheckBox x:Uid="DontAskAgainCheckBox" />
</ContentDialog>
<ContentDialog x:Name="CloseReadOnlyDialog"
x:Uid="CloseReadOnlyDialog"

View File

@@ -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(); ::Microsoft::Console::Utils::IsValidDirectory(dir.c_str()))
if (const auto dir = _control.WorkingDirectory(); !dir.empty())
{
args.StartingDirectory(dir);
}

View File

@@ -355,12 +355,6 @@ 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)
@@ -2081,10 +2075,7 @@ 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)
// - 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()))
if (_terminal->IsSelectionActive() && (!shiftEnabled || isOnOriginalPosition))
{
// Reset the selection
_terminal->ClearSelection();
@@ -2426,6 +2417,11 @@ 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;

View File

@@ -191,6 +191,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ContextMenuSelectCommand();
void ContextMenuSelectOutput();
winrt::hstring CurrentWorkingDirectory() const;
#pragma endregion
#pragma region ITerminalInput
@@ -255,7 +257,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TerminalConnection::ITerminalConnection Connection();
void Connection(const TerminalConnection::ITerminalConnection& connection);
void HardResetWithoutErase();
void AnchorContextMenu(til::point viewportRelativeCharacterPosition);

View File

@@ -98,7 +98,6 @@ namespace Microsoft.Terminal.Control
void ApplyAppearance(Boolean focused);
Microsoft.Terminal.TerminalConnection.ITerminalConnection Connection;
void HardResetWithoutErase();
IControlSettings Settings { get; };
IControlAppearance FocusedAppearance { get; };

View File

@@ -54,20 +54,6 @@ 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()
@@ -169,8 +155,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlInteractivity::GotFocus()
{
_focused = true;
if (_uiaEngine.get())
{
THROW_IF_FAILED(_uiaEngine->Enable());
@@ -183,8 +167,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlInteractivity::LostFocus()
{
_focused = false;
if (_uiaEngine.get())
{
THROW_IF_FAILED(_uiaEngine->Disable());
@@ -252,8 +234,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
PasteFromClipboard.raise(*this, std::move(args));
}
void ControlInteractivity::PointerPressed(const uint32_t /*pointerId*/,
Control::MouseButtonState buttonState,
void ControlInteractivity::PointerPressed(Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const uint64_t timestamp,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
@@ -266,10 +247,6 @@ 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) &&
@@ -308,10 +285,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
const auto isOnOriginalPosition = _lastMouseClickPosNoSelection == pixelPosition;
// 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),
// Rounded coordinates for text selection
_core->LeftClickOnTerminal(_getTerminalPosition(til::point{ pixelPosition }, true),
multiClickMapper,
altEnabled,
shiftEnabled,
@@ -321,15 +296,8 @@ 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.
// 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;
}
// so that dragging the mouse will extend the selection rather than starting the new one
_singleClickTouchdownPos = std::nullopt;
}
}
else if (WI_IsFlagSet(buttonState, MouseButtonState::IsRightButtonDown))
@@ -346,41 +314,37 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
else
{
// 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());
// Try to copy the text and clear the selection
const auto successfulCopy = CopySelectionToClipboard(shiftEnabled, false, _core->Settings().CopyFormatting());
_core->ClearSelection();
if (_core->CopyOnSelect() || !copied)
if (_core->CopyOnSelect() || !successfulCopy)
{
// CopyOnSelect: right-click always pastes.
// Otherwise: no selection paste.
// CopyOnSelect: right click always pastes!
// Otherwise: no selection --> paste
RequestPasteTextFromClipboard();
}
}
}
}
void ControlInteractivity::TouchPressed(const Core::Point contactPoint)
void ControlInteractivity::TouchPressed(const winrt::Windows::Foundation::Point contactPoint)
{
_touchAnchor = contactPoint;
}
bool ControlInteractivity::PointerMoved(const uint32_t /*pointerId*/,
Control::MouseButtonState buttonState,
bool ControlInteractivity::PointerMoved(Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const Core::Point pixelPosition)
const bool focused,
const Core::Point pixelPosition,
const bool pointerPressedInBounds)
{
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;
@@ -389,7 +353,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)
{
@@ -435,9 +399,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return handledCompletely;
}
void ControlInteractivity::TouchMoved(const Core::Point newTouchPoint)
void ControlInteractivity::TouchMoved(const winrt::Windows::Foundation::Point newTouchPoint,
const bool focused)
{
if (_focused &&
if (focused &&
_touchAnchor)
{
const auto anchor = _touchAnchor.value();
@@ -471,14 +436,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
void ControlInteractivity::PointerReleased(const uint32_t /*pointerId*/,
Control::MouseButtonState buttonState,
void ControlInteractivity::PointerReleased(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))
@@ -720,9 +682,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - cursorPosition: in pixels, relative to the origin of the control
void ControlInteractivity::SetEndSelectionPoint(const Core::Point pixelPosition)
{
// Don't round in VT mouse mode; cell-level precision matters more
const auto round = !_core->IsVtMouseModeEnabled();
_core->SetEndSelectionPoint(_getTerminalPosition(til::point{ pixelPosition }, round));
_core->SetEndSelectionPoint(_getTerminalPosition(til::point{ pixelPosition }, true));
_selectionNeedsToBeCopied = true;
}

View File

@@ -51,23 +51,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
::Microsoft::Console::Render::IRenderData* GetRenderData() const;
#pragma region Input Methods
void PointerPressed(const uint32_t pointerId,
Control::MouseButtonState buttonState,
void PointerPressed(Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const uint64_t timestamp,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const Core::Point pixelPosition);
void TouchPressed(const Core::Point contactPoint);
void TouchPressed(const winrt::Windows::Foundation::Point contactPoint);
bool PointerMoved(const uint32_t pointerId,
Control::MouseButtonState buttonState,
bool PointerMoved(Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const Core::Point pixelPosition);
void TouchMoved(const Core::Point newTouchPoint);
const bool focused,
const Core::Point pixelPosition,
const bool pointerPressedInBounds);
void TouchMoved(const winrt::Windows::Foundation::Point newTouchPoint,
const bool focused);
void PointerReleased(const uint32_t pointerId,
Control::MouseButtonState buttonState,
void PointerReleased(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<Core::Point> _touchAnchor;
std::optional<winrt::Windows::Foundation::Point> _touchAnchor;
using Timestamp = uint64_t;
@@ -142,9 +142,6 @@ 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;

View File

@@ -36,24 +36,24 @@ namespace Microsoft.Terminal.Control
void RequestPasteTextFromClipboard();
void SetEndSelectionPoint(Microsoft.Terminal.Core.Point point);
void PointerPressed(UInt32 pointerId,
MouseButtonState buttonState,
void PointerPressed(MouseButtonState buttonState,
UInt32 pointerUpdateKind,
UInt64 timestamp,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Microsoft.Terminal.Core.Point pixelPosition);
void TouchPressed(Microsoft.Terminal.Core.Point contactPoint);
void TouchPressed(Windows.Foundation.Point contactPoint);
Boolean PointerMoved(UInt32 pointerId,
MouseButtonState buttonState,
Boolean PointerMoved(MouseButtonState buttonState,
UInt32 pointerUpdateKind,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Microsoft.Terminal.Core.Point pixelPosition);
Boolean focused,
Microsoft.Terminal.Core.Point pixelPosition,
Boolean pointerPressedInBounds);
void TouchMoved(Microsoft.Terminal.Core.Point newTouchPoint);
void TouchMoved(Windows.Foundation.Point newTouchPoint,
Boolean focused);
void PointerReleased(UInt32 pointerId,
MouseButtonState buttonState,
void PointerReleased(MouseButtonState buttonState,
UInt32 pointerUpdateKind,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Microsoft.Terminal.Core.Point pixelPosition);

View File

@@ -76,7 +76,6 @@ 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!
};

View File

@@ -60,5 +60,7 @@ namespace Microsoft.Terminal.Control
void SelectOutput(Boolean goUp);
IVector<ScrollMark> ScrollMarks { get; };
String CurrentWorkingDirectory { get; };
};
}

View File

@@ -49,10 +49,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void InteractivityAutomationPeer::ParentProvider(AutomationPeer parentProvider)
{
// LOAD-BEARING: use _parentProvider->ProviderFromPeer(_parentProvider) instead of this->ProviderFromPeer(*this).
// Since we split the automation peer into TermControlAutomationPeer and InteractivityAutomationPeer,
// using "this" returns null. This can cause issues with some UIA Client scenarios like any navigation in Narrator.
_parentProvider = parentProvider ? parentProvider.as<IAutomationPeerProtected>().ProviderFromPeer(parentProvider) : nullptr;
_parentProvider = parentProvider;
}
// Method Description:
@@ -184,11 +181,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::_CreateXamlUiaTextRange(UIA::ITextRangeProvider* returnVal) const
{
if (!_parentProvider)
// LOAD-BEARING: use _parentProvider->ProviderFromPeer(_parentProvider) instead of this->ProviderFromPeer(*this).
// Since we split the automation peer into TermControlAutomationPeer and InteractivityAutomationPeer,
// using "this" returns null. This can cause issues with some UIA Client scenarios like any navigation in Narrator.
const auto parent{ _parentProvider.get() };
if (!parent)
{
return nullptr;
}
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, _parentProvider);
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parent.as<IAutomationPeerProtected>().ProviderFromPeer(parent));
return xutr.as<XamlAutomation::ITextRangeProvider>();
};
@@ -200,24 +201,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - com_array of Xaml Wrapped UiaTextRange (ITextRangeProviders)
com_array<XamlAutomation::ITextRangeProvider> InteractivityAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges)
{
if (!_parentProvider)
{
return {};
}
// transfer ownership of UiaTextRanges to this new vector
auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::TermControlUiaTextRange>(textRanges);
const auto len = gsl::narrow<uint32_t>(providers.size());
com_array<XamlAutomation::ITextRangeProvider> result{ len };
auto count = gsl::narrow<int>(providers.size());
for (uint32_t i = 0; i < len; ++i)
std::vector<XamlAutomation::ITextRangeProvider> vec;
vec.reserve(count);
for (auto i = 0; i < count; i++)
{
if (auto xutr = _CreateXamlUiaTextRange(providers[i].detach()))
{
result[i] = std::move(xutr);
vec.emplace_back(std::move(xutr));
}
}
com_array<XamlAutomation::ITextRangeProvider> result{ vec };
return result;
}
}

View File

@@ -80,7 +80,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
winrt::Microsoft::Terminal::Control::implementation::ControlInteractivity* _interactivity;
winrt::Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple _parentProvider{ nullptr };
weak_ref<Windows::UI::Xaml::Automation::Peers::AutomationPeer> _parentProvider;
til::rect _controlBounds{};
til::rect _controlPadding{};

View File

@@ -3,7 +3,7 @@
namespace Microsoft.Terminal.Control
{
[default_interface] runtimeclass InteractivityAutomationPeer :
Windows.UI.Xaml.Automation.Peers.FrameworkElementAutomationPeer,
Windows.UI.Xaml.Automation.Peers.AutomationPeer,
Windows.UI.Xaml.Automation.Provider.ITextProvider
{

View File

@@ -532,11 +532,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_core.Connection(newConnection);
}
void TermControl::HardResetWithoutErase()
{
_core.HardResetWithoutErase();
}
void TermControl::_throttledUpdateScrollbar(const ScrollBarUpdate& update)
{
if (!_initializedTerminal)
@@ -1795,16 +1790,6 @@ 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;
@@ -1970,13 +1955,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto point = args.GetCurrentPoint(*this);
const auto type = ptr.PointerDeviceType();
// GH#19908: _focused can be true even when the search box has
// keyboard focus, because GotFocus bubbles from the search box
// child and _GotFocusHandler sets _focused=true. If the user
// click-drags in the terminal while the search box is focused,
// we need to explicitly call Focus() to move keyboard focus back
// to the terminal so that Ctrl+C copies the selection.
if (!_focused || (_searchBox && _searchBox->ContainsFocus()))
if (!_focused)
{
Focus(FocusState::Pointer);
}
@@ -1990,14 +1969,12 @@ 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();
til::point newTouchPoint{ til::math::rounding, contactRect.X, contactRect.Y };
_interactivity.TouchPressed(newTouchPoint.to_core_point());
_interactivity.TouchPressed({ contactRect.X, contactRect.Y });
}
else
{
const auto cursorPosition = point.Position();
_interactivity.PointerPressed(point.PointerId(),
TermControl::GetPressedMouseButtons(point),
_interactivity.PointerPressed(TermControl::GetPressedMouseButtons(point),
TermControl::GetPointerUpdateKind(point),
point.Timestamp(),
ControlKeyStates{ args.KeyModifiers() },
@@ -2036,11 +2013,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (type == Windows::Devices::Input::PointerDeviceType::Mouse ||
type == Windows::Devices::Input::PointerDeviceType::Pen)
{
auto suppressFurtherHandling = _interactivity.PointerMoved(point.PointerId(),
TermControl::GetPressedMouseButtons(point),
auto suppressFurtherHandling = _interactivity.PointerMoved(TermControl::GetPressedMouseButtons(point),
TermControl::GetPointerUpdateKind(point),
ControlKeyStates(args.KeyModifiers()),
pixelPosition);
_focused,
pixelPosition,
_pointerPressedInBounds);
// GH#9109 - Only start an auto-scroll when the drag actually
// started within our bounds. Otherwise, someone could start a drag
@@ -2079,9 +2057,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
else if (type == Windows::Devices::Input::PointerDeviceType::Touch)
{
const auto contactRect = point.Properties().ContactRect();
til::point newTouchPoint{ til::math::rounding, contactRect.X, contactRect.Y };
_interactivity.TouchMoved(newTouchPoint.to_core_point());
_interactivity.TouchMoved({ contactRect.X, contactRect.Y }, _focused);
}
args.Handled(true);
@@ -2114,8 +2090,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (type == Windows::Devices::Input::PointerDeviceType::Mouse ||
type == Windows::Devices::Input::PointerDeviceType::Pen)
{
_interactivity.PointerReleased(point.PointerId(),
TermControl::GetPressedMouseButtons(point),
_interactivity.PointerReleased(TermControl::GetPressedMouseButtons(point),
TermControl::GetPointerUpdateKind(point),
ControlKeyStates(args.KeyModifiers()),
pixelPosition);
@@ -2526,9 +2501,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_updateScrollBar->Run(update);
// If we have a selection with markers (exposed via selection mode),
// update the position of the markers
if (_core.HasSelection() && _core.SelectionMode() >= SelectionInteractionMode::Keyboard)
// if a selection marker is already visible,
// update the position of those markers
if (SelectionStartMarker().Visibility() == Visibility::Visible || SelectionEndMarker().Visibility() == Visibility::Visible)
{
_updateSelectionMarkers(nullptr, winrt::make<UpdateSelectionMarkersEventArgs>(false));
}
@@ -3079,7 +3054,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 the configured delimiter.
// write all the paths to the terminal, separated by spaces.
// Arguments:
// - e: The DragEventArgs from the Drop event
// Return Value:
@@ -3092,11 +3067,6 @@ 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()))
@@ -3200,13 +3170,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
std::wstring allPathsString;
const auto delimiter{ _core.Settings().DragDropDelimiter() };
for (auto& fullPath : fullPaths)
{
// Join the paths with the delimiter
// Join the paths with spaces
if (!allPathsString.empty())
{
allPathsString += delimiter;
allPathsString += L" ";
}
const auto translationStyle{ _core.Settings().PathTranslationStyle() };
@@ -3738,6 +3707,10 @@ 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)
{

View File

@@ -117,6 +117,8 @@ 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);
@@ -190,7 +192,6 @@ 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);

View File

@@ -53,7 +53,6 @@ namespace Microsoft.Terminal.Control
void SetOverrideColorScheme(Microsoft.Terminal.Core.ICoreScheme scheme);
Microsoft.Terminal.TerminalConnection.ITerminalConnection Connection;
void HardResetWithoutErase();
UInt64 ContentId{ get; };

View File

@@ -121,9 +121,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// GH#13978: If the TermControl has already been removed from the UI tree, XAML might run into weird bugs.
// This will prevent the `dispatcher.RunAsync` calls below from raising UIA events on the main thread.
_termControl = {};
// Solve the circular reference between us and the content automation peer.
_contentAutomationPeer.ParentProvider(nullptr);
}
// Method Description:

View File

@@ -55,18 +55,6 @@ 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:
@@ -1534,10 +1522,6 @@ 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) {

View File

@@ -85,7 +85,6 @@ 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);
@@ -132,9 +131,7 @@ 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;

View File

@@ -28,11 +28,6 @@ void Terminal::ReturnResponse(const std::wstring_view response)
}
}
bool Terminal::IsConPTY() const noexcept
{
return false;
}
Microsoft::Console::VirtualTerminal::StateMachine& Terminal::GetStateMachine() noexcept
{
return *_stateMachine;

View File

@@ -313,7 +313,7 @@ std::pair<til::point, til::point> Terminal::_ExpandSelectionAnchors(std::pair<ti
break;
case SelectionExpansion::Word:
{
start = buffer.GetWordStart(start, _wordDelimiters, false);
start = buffer.GetWordStart2(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.GetWordEnd(end, _wordDelimiters, false);
end = buffer.GetWordEnd2(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.GetWordStart(selection->start, _wordDelimiters, false);
selection->start = buffer.GetWordStart2(selection->start, _wordDelimiters, false);
selection->pivot = selection->start;
selection->end = buffer.GetWordEnd(selection->end, _wordDelimiters, false);
selection->end = buffer.GetWordEnd2(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.GetWordStart(nextPos, _wordDelimiters, true);
nextPos = buffer.GetWordStart2(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.GetWordStart(nextPos, _wordDelimiters, true);
nextPos = buffer.GetWordStart2(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.GetWordEnd(nextPos, _wordDelimiters, true, mutableViewportEndExclusive);
nextPos = buffer.GetWordEnd2(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.GetWordEnd(nextPos, _wordDelimiters, true, mutableViewportEndExclusive);
nextPos = buffer.GetWordEnd2(nextPos, _wordDelimiters, true, mutableViewportEndExclusive);
}
pos = nextPos;
break;
}
case SelectionDirection::Up:
_MoveByChar(direction, pos);
pos = buffer.GetWordStart(pos, _wordDelimiters, true);
pos = buffer.GetWordStart2(pos, _wordDelimiters, true);
break;
case SelectionDirection::Down:
_MoveByChar(direction, pos);
pos = buffer.GetWordEnd(pos, _wordDelimiters, true);
pos = buffer.GetWordEnd2(pos, _wordDelimiters, true);
break;
}
}

View File

@@ -353,7 +353,6 @@ namespace winrt::Microsoft::Terminal::Settings
_AllowVtChecksumReport = profile.AllowVtChecksumReport();
_AllowVtClipboardWrite = profile.AllowVtClipboardWrite();
_PathTranslationStyle = profile.PathTranslationStyle();
_DragDropDelimiter = profile.DragDropDelimiter();
}
// Method Description:

View File

@@ -43,8 +43,7 @@
</local:SettingContainer>
<!-- Ambiguous Width -->
<local:SettingContainer x:Name="AmbiguousWidth"
x:Uid="Globals_AmbiguousWidth">
<local:SettingContainer x:Uid="Globals_AmbiguousWidth">
<ComboBox AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind ViewModel.AmbiguousWidthList}"

View File

@@ -135,11 +135,14 @@
<TextBlock x:Uid="Globals_WarningsHeader"
Style="{StaticResource TextBlockSubHeaderStyle}" />
<!-- Close All Tabs Warning -->
<local:SettingContainer x:Name="ConfirmCloseAllTabs"
x:Uid="Globals_ConfirmCloseAllTabs">
<ToggleSwitch IsOn="{x:Bind ViewModel.ConfirmCloseAllTabs, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
<!-- Confirm Close On -->
<local:SettingContainer x:Name="ConfirmCloseOn"
x:Uid="Globals_ConfirmCloseOn">
<ComboBox AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind ViewModel.ConfirmCloseOnList}"
SelectedItem="{x:Bind ViewModel.CurrentConfirmCloseOn, Mode=TwoWay}"
Style="{StaticResource ComboBoxSettingStyle}" />
</local:SettingContainer>
<!-- Input Service Warning -->

View File

@@ -17,5 +17,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
INITIALIZE_BINDABLE_ENUM_SETTING(TabSwitcherMode, TabSwitcherMode, TabSwitcherMode, L"Globals_TabSwitcherMode", L"Content");
INITIALIZE_BINDABLE_ENUM_SETTING(CopyFormat, CopyFormat, winrt::Microsoft::Terminal::Control::CopyFormat, L"Globals_CopyFormat", L"Content");
INITIALIZE_BINDABLE_ENUM_SETTING(ConfirmCloseOn, ConfirmCloseOn, Model::ConfirmCloseOn, L"Globals_ConfirmCloseOn", L"Content");
}
}

View File

@@ -19,6 +19,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
GETSET_BINDABLE_ENUM_SETTING(TabSwitcherMode, Model::TabSwitcherMode, _GlobalSettings.TabSwitcherMode);
GETSET_BINDABLE_ENUM_SETTING(CopyFormat, winrt::Microsoft::Terminal::Control::CopyFormat, _GlobalSettings.CopyFormatting);
GETSET_BINDABLE_ENUM_SETTING(ConfirmCloseOn, Model::ConfirmCloseOn, _GlobalSettings.ConfirmCloseOn);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, CopyOnSelect);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, TrimBlockSelection);
@@ -30,7 +31,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, DetectURLs);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, SearchWebDefaultQueryUrl);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, WordDelimiters);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ConfirmCloseAllTabs);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, InputServiceWarning);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, WarnAboutLargePaste);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, WarnAboutMultiLinePaste);

View File

@@ -12,10 +12,13 @@ namespace Microsoft.Terminal.Settings.Editor
InteractionViewModel(Microsoft.Terminal.Settings.Model.GlobalAppSettings globalSettings);
IInspectable CurrentTabSwitcherMode;
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> TabSwitcherModeList { get; };
Windows.Foundation.Collections.IObservableVector<EnumEntry> TabSwitcherModeList { get; };
IInspectable CurrentCopyFormat;
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> CopyFormatList { get; };
Windows.Foundation.Collections.IObservableVector<EnumEntry> CopyFormatList { get; };
IInspectable CurrentConfirmCloseOn;
Windows.Foundation.Collections.IObservableVector<EnumEntry> ConfirmCloseOnList { get; };
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, CopyOnSelect);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, TrimBlockSelection);
@@ -27,7 +30,6 @@ namespace Microsoft.Terminal.Settings.Editor
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, DetectURLs);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(String, SearchWebDefaultQueryUrl);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(String, WordDelimiters);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, ConfirmCloseAllTabs);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, InputServiceWarning);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, WarnAboutLargePaste);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Microsoft.Terminal.Control.WarnAboutMultiLinePaste, WarnAboutMultiLinePaste);

View File

@@ -44,28 +44,6 @@ 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{};
@@ -372,13 +350,6 @@ 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:
@@ -1127,17 +1098,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
}
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;
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;
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

View File

@@ -329,8 +329,7 @@
</local:SettingContainer>
<!-- Icon -->
<local:SettingContainer x:Name="CurrentFolderIcon"
x:Uid="NewTabMenu_CurrentFolderIcon"
<local:SettingContainer x:Uid="NewTabMenu_CurrentFolderIcon"
CurrentValueAccessibleName="{x:Bind ViewModel.CurrentFolderLocalizedIcon, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
<local:SettingContainer.CurrentValue>
@@ -498,8 +497,7 @@
</local:SettingContainer>
<!-- Add Remaining Profiles -->
<local:SettingContainer x:Name="AddRemainingProfiles"
x:Uid="NewTabMenu_AddRemainingProfiles"
<local:SettingContainer x:Uid="NewTabMenu_AddRemainingProfiles"
FontIconGlyph="&#xE902;"
Style="{StaticResource SettingContainerWithIcon}">
<Button x:Name="AddRemainingProfilesButton"

View File

@@ -145,7 +145,6 @@ 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);

View File

@@ -139,7 +139,6 @@ 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);
}
}

View File

@@ -272,16 +272,6 @@
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>

View File

@@ -2197,6 +2197,26 @@
<value>Warn when closing more than one tab</value>
<comment>Header for a control to toggle whether to show a confirm dialog box when closing the application with multiple tabs open.</comment>
</data>
<data name="Globals_ConfirmCloseOn.Header" xml:space="preserve">
<value>Warn when closing</value>
<comment>Header for a dropdown controlling when to show a confirmation dialog before closing.</comment>
</data>
<data name="Globals_ConfirmCloseOn.HelpText" xml:space="preserve">
<value>Controls when a confirmation dialog appears before closing tabs or windows. "Always" presents the dialog when closing any pane.</value>
<comment>Help text associated with Globals_ConfirmCloseOn. "Always" refers to Globals_ConfirmCloseOnAlways.Content.</comment>
</data>
<data name="Globals_ConfirmCloseOnNever.Content" xml:space="preserve">
<value>Never</value>
<comment>Option associated with Globals_ConfirmCloseOn. "Never" means that the system will never display a warning when closing.</comment>
</data>
<data name="Globals_ConfirmCloseOnAlways.Content" xml:space="preserve">
<value>Always</value>
<comment>Option associated with Globals_ConfirmCloseOn. "Always" means that the system will always display a warning when closing.</comment>
</data>
<data name="Globals_ConfirmCloseOnAutomatic.Content" xml:space="preserve">
<value>Multiple tabs or panes</value>
<comment>Option associated with Globals_ConfirmCloseOn. The system will display a warning when multiple tabs or panes are present.</comment>
</data>
<data name="Globals_InputServiceWarning.Header" xml:space="preserve">
<value>Warn when "Touch Keyboard and Handwriting Panel Service" is disabled</value>
</data>
@@ -2748,12 +2768,4 @@
<value>Type to filter icons</value>
<comment>Placeholder text for a text box to filter and select an icon.</comment>
</data>
<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>
</root>

View File

@@ -647,12 +647,9 @@ void CascadiaSettings::_validateMediaResources()
_globals->ResolveMediaResources(mediaResourceResolver);
if (Feature_WarnOnInvalidSettingsMediaResources::IsEnabled())
if (_foundInvalidUserResources)
{
if (_foundInvalidUserResources)
{
_warnings.Append(SettingsLoadWarnings::InvalidMediaResource);
}
_warnings.Append(SettingsLoadWarnings::InvalidMediaResource);
}
}

View File

@@ -43,6 +43,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
DEFINE_ENUM_MAP(Microsoft::Terminal::Control::TextMeasurement, TextMeasurement);
DEFINE_ENUM_MAP(Microsoft::Terminal::Control::AmbiguousWidth, AmbiguousWidth);
DEFINE_ENUM_MAP(Microsoft::Terminal::Control::WarnAboutMultiLinePaste, WarnAboutMultiLinePaste);
DEFINE_ENUM_MAP(Model::ConfirmCloseOn, ConfirmCloseOn);
// Profile Settings
DEFINE_ENUM_MAP(Model::CloseOnExitMode, CloseOnExitMode);

View File

@@ -40,6 +40,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Control::TextMeasurement> TextMeasurement();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Control::AmbiguousWidth> AmbiguousWidth();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Control::WarnAboutMultiLinePaste> WarnAboutMultiLinePaste();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, ConfirmCloseOn> ConfirmCloseOn();
// Profile Settings
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, CloseOnExitMode> CloseOnExitMode();

View File

@@ -22,6 +22,7 @@ namespace Microsoft.Terminal.Settings.Model
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Control.TextMeasurement> TextMeasurement { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Control.AmbiguousWidth> AmbiguousWidth { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Control.WarnAboutMultiLinePaste> WarnAboutMultiLinePaste { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.ConfirmCloseOn> ConfirmCloseOn { get; };
// Profile Settings
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.CloseOnExitMode> CloseOnExitMode { get; };

View File

@@ -160,7 +160,16 @@ void GlobalAppSettings::LayerJson(const Json::Value& json, const OriginTag origi
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyInputServiceWarningKey, _InputServiceWarning) || _fixupsAppliedDuringLoad;
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyWarnAboutLargePasteKey, _WarnAboutLargePaste) || _fixupsAppliedDuringLoad;
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyWarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste) || _fixupsAppliedDuringLoad;
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyConfirmCloseAllTabsKey, _ConfirmCloseAllTabs) || _fixupsAppliedDuringLoad;
// GH#6549 - Migrate legacy "confirmCloseAllTabs" boolean to the new
// "confirmCloseOn" enum. true -> Automatic, false -> Never.
{
std::optional<bool> legacyConfirmClose;
if (JsonUtils::GetValueForKey(json, LegacyConfirmCloseAllTabsKey, legacyConfirmClose))
{
_ConfirmCloseOn = legacyConfirmClose.value() ? ConfirmCloseOn::Automatic : ConfirmCloseOn::Never;
_fixupsAppliedDuringLoad = true;
}
}
#define GLOBAL_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \
JsonUtils::GetValueForKey(json, jsonKey, _##name); \

View File

@@ -50,6 +50,13 @@ namespace Microsoft.Terminal.Settings.Model
AfterCurrentTab,
};
enum ConfirmCloseOn
{
Never = 0,
Automatic = 1,
Always = 2,
};
[default_interface] runtimeclass GlobalAppSettings {
Guid DefaultProfile;
@@ -61,7 +68,7 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_SETTING(Boolean, ShowTabsFullscreen);
INHERITABLE_SETTING(NewTabPosition, NewTabPosition);
INHERITABLE_SETTING(Boolean, ShowTitleInTitlebar);
INHERITABLE_SETTING(Boolean, ConfirmCloseAllTabs);
INHERITABLE_SETTING(ConfirmCloseOn, ConfirmCloseOn);
INHERITABLE_SETTING(String, Language);
INHERITABLE_SETTING(Microsoft.UI.Xaml.Controls.TabViewWidthMode, TabWidthMode);
INHERITABLE_SETTING(Boolean, UseAcrylicInTabRow);

View File

@@ -38,7 +38,7 @@ Author(s):
X(bool, AlwaysShowTabs, "alwaysShowTabs", true) \
X(Model::NewTabPosition, NewTabPosition, "newTabPosition", Model::NewTabPosition::AfterLastTab) \
X(bool, ShowTitleInTitlebar, "showTerminalTitleInTitlebar", true) \
X(bool, ConfirmCloseAllTabs, "warning.confirmCloseAllTabs", true) \
X(Model::ConfirmCloseOn, ConfirmCloseOn, "warning.confirmCloseOn", Model::ConfirmCloseOn::Automatic) \
X(Model::ThemePair, Theme, "theme") \
X(hstring, Language, "language") \
X(winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabWidthMode, "tabWidthMode", winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::Equal) \
@@ -108,7 +108,6 @@ 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:

View File

@@ -94,6 +94,5 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_PROFILE_SETTING(Boolean, AllowVtClipboardWrite);
INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.PathTranslationStyle, PathTranslationStyle);
INHERITABLE_PROFILE_SETTING(String, DragDropDelimiter);
}
}

View File

@@ -88,6 +88,25 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Core::MatchMode)
};
};
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::ConfirmCloseOn)
{
JSON_MAPPINGS(3) = {
pair_type{ "never", ValueType::Never },
pair_type{ "always", ValueType::Always },
pair_type{ "automatic", ValueType::Automatic },
};
auto FromJson(const Json::Value& json)
{
return BaseEnumMapper::FromJson(json);
}
bool CanConvert(const Json::Value& json)
{
return BaseEnumMapper::CanConvert(json);
}
};
JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::BellStyle)
{
static constexpr std::array<pair_type, 6> mappings = {

View File

@@ -8,16 +8,6 @@
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
@@ -31,10 +21,9 @@ 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) });
auto isPwsh = _IsPwshAvailable();
profile->Commandline(winrt::hstring{ GetProfileCommandLine(instance, isPwsh) });
profile->Commandline(winrt::hstring{ GetProfileCommandLine(instance) });
profile->StartingDirectory(winrt::hstring{ instance.GetInstallationPath() });
profile->Icon(winrt::hstring{ GetProfileIconPath(isPwsh) });
profile->Icon(winrt::hstring{ GetProfileIconPath() });
profile->Hidden(hidden);
profiles.emplace_back(std::move(profile));
}
@@ -48,15 +37,20 @@ std::wstring VsDevShellGenerator::GetProfileName(const VsSetupConfiguration::VsS
return name;
}
std::wstring VsDevShellGenerator::GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance, bool isPwsh) const
std::wstring VsDevShellGenerator::GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const
{
// Build this in stages, so reserve space now
std::wstring commandLine;
commandLine.reserve(256);
if (isPwsh)
// 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))
{
commandLine.append(L"pwsh.exe");
commandLine.append(pwshExeName);
}
else
{

View File

@@ -37,14 +37,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model
return L"VsDevShell" + instance.GetInstanceId();
}
std::wstring GetProfileIconPath(bool isPwsh) const
std::wstring GetProfileIconPath() const
{
return isPwsh ? L"ms-appx:///ProfileIcons/vs-pwsh.png" :
L"ms-appx:///ProfileIcons/vs-powershell.png";
return L"ms-appx:///ProfileIcons/vs-powershell.png";
}
std::wstring GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const;
std::wstring GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance, bool isPwsh) const;
std::wstring GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const;
std::wstring GetDevShellModulePath(const VsSetupConfiguration::VsSetupInstance& instance) const;
};
};

View File

@@ -24,7 +24,7 @@
"showAdminShield": true,
// Miscellaneous
"confirmCloseAllTabs": true,
"warning.confirmCloseOn": "automatic",
"theme": "dark",
"snapToGridOnResize": true,
"disableAnimations": false,

View File

@@ -313,13 +313,10 @@ 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(0,
leftMouseDown,
interactivity->PointerPressed(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -332,28 +329,29 @@ 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(0,
leftMouseDown,
interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
cursorPosition1.to_core_point());
true, // focused,
cursorPosition1.to_core_point(),
true);
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(0,
leftMouseDown,
interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
cursorPosition2.to_core_point());
true, // focused,
cursorPosition2.to_core_point(),
true);
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(0,
noMouseDown,
interactivity->PointerReleased(noMouseDown,
WM_LBUTTONUP, //pointerUpdateKind
modifiers,
cursorPosition2.to_core_point());
@@ -363,8 +361,7 @@ namespace ControlUnitTests
Log::Comment(L"click outside the current selection");
const til::point terminalPosition3{ 2, 2 };
const auto cursorPosition3 = terminalPosition3 * fontSize;
interactivity->PointerPressed(0,
leftMouseDown,
interactivity->PointerPressed(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -375,11 +372,12 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse");
const til::point terminalPosition4{ 3, 2 };
const auto cursorPosition4 = terminalPosition4 * fontSize;
interactivity->PointerMoved(0,
leftMouseDown,
interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
cursorPosition4.to_core_point());
true, // focused,
cursorPosition4.to_core_point(),
true);
Log::Comment(L"Verify that there's now one selection");
VERIFY_IS_TRUE(core->HasSelection());
}
@@ -410,17 +408,10 @@ 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(0,
leftMouseDown,
interactivity->PointerPressed(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -435,11 +426,12 @@ 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(0,
leftMouseDown,
interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
cursorPosition1.to_core_point());
true, // focused,
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
@@ -567,12 +559,9 @@ 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(0,
leftMouseDown,
interactivity->PointerPressed(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -586,11 +575,12 @@ 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(0,
leftMouseDown,
interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
cursorPosition1.to_core_point());
true, // focused,
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
@@ -612,13 +602,10 @@ 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(0,
leftMouseDown,
interactivity->PointerPressed(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -632,11 +619,12 @@ 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(0,
leftMouseDown,
interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
cursorPosition1.to_core_point());
true, // focused,
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
@@ -646,8 +634,7 @@ 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(0,
noMouseDown,
interactivity->PointerReleased(noMouseDown,
WM_LBUTTONUP,
modifiers,
cursorPosition1.to_core_point());
@@ -657,11 +644,12 @@ 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(0,
leftMouseDown,
interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
cursorPosition2.to_core_point());
true, // focused,
cursorPosition2.to_core_point(),
false);
Log::Comment(L"The selection should be unchanged.");
VERIFY_ARE_EQUAL(expectedAnchor, core->_terminal->GetSelectionAnchor());
@@ -688,12 +676,6 @@ 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());
@@ -740,8 +722,7 @@ namespace ControlUnitTests
Log::Comment(L"Click on the terminal");
const til::point terminalPosition0{ 4, 4 };
const auto cursorPosition0 = terminalPosition0 * fontSize;
interactivity->PointerPressed(0,
leftMouseDown,
interactivity->PointerPressed(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -754,11 +735,12 @@ namespace ControlUnitTests
// move the mouse as if to make a selection
const til::point terminalPosition1{ 10, 4 };
const auto cursorPosition1 = terminalPosition1 * fontSize;
interactivity->PointerMoved(0,
leftMouseDown,
interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
cursorPosition1.to_core_point());
true, // focused,
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's still no selection");
VERIFY_IS_FALSE(core->HasSelection());
}
@@ -788,13 +770,10 @@ 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(0,
leftMouseDown,
interactivity->PointerPressed(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -809,11 +788,12 @@ 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(0,
leftMouseDown,
interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
cursorPosition1.to_core_point());
true, // focused,
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
@@ -870,11 +850,12 @@ namespace ControlUnitTests
// character in the buffer (if, albeit in a new location).
//
// This helps test GH #14462, a regression from #10749.
interactivity->PointerMoved(0,
leftMouseDown,
interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
cursorPosition0.to_core_point());
true, // focused,
cursorPosition0.to_core_point(),
true);
VERIFY_IS_TRUE(core->HasSelection());
{
const auto anchor{ core->_terminal->GetSelectionAnchor() };
@@ -895,11 +876,12 @@ namespace ControlUnitTests
expectedAnchor.y -= 1;
expectedEnd.y -= 1;
VERIFY_ARE_EQUAL(scrollbackLength - 3, core->_terminal->GetScrollOffset());
interactivity->PointerMoved(0,
leftMouseDown,
interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
cursorPosition1.to_core_point());
true, // focused,
cursorPosition1.to_core_point(),
true);
VERIFY_IS_TRUE(core->HasSelection());
{
const auto anchor{ core->_terminal->GetSelectionAnchor() };
@@ -947,8 +929,7 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 };
const til::point terminalPosition0{ 5, 5 };
const auto cursorPosition0{ terminalPosition0 * fontSize };
interactivity->PointerPressed(0,
leftMouseDown,
interactivity->PointerPressed(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -997,8 +978,7 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 };
const til::point terminalPosition0{ 5, 5 };
const auto cursorPosition0{ terminalPosition0 * fontSize };
interactivity->PointerPressed(0,
leftMouseDown,
interactivity->PointerPressed(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -1014,8 +994,7 @@ 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(0,
leftMouseDown,
interactivity->PointerPressed(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -1030,8 +1009,7 @@ namespace ControlUnitTests
// will be clamped to the top line.
expectedOutput.push_back(L"\x1b[M &!"); // 5, 1
interactivity->PointerPressed(0,
leftMouseDown,
interactivity->PointerPressed(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -1047,8 +1025,7 @@ 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(0,
leftMouseDown,
interactivity->PointerPressed(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -1062,8 +1039,7 @@ 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(0,
leftMouseDown,
interactivity->PointerPressed(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,

View File

@@ -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().template as<NewTabArgs>();
const auto& realArgs = actionAndArgs.Args().as<NewTabArgs>();
VERIFY_IS_NOT_NULL(realArgs.ContentArgs());
const auto terminalArgs{ realArgs.ContentArgs().template try_as<NewTerminalArgs>() };
const auto terminalArgs{ realArgs.ContentArgs().try_as<NewTerminalArgs>() };
return terminalArgs.Hash();
};

View File

@@ -125,7 +125,7 @@ namespace SettingsModelUnitTests
"trimPaste": true,
"warning.confirmCloseAllTabs" : true,
"warning.confirmCloseOn": "automatic",
"warning.inputService" : true,
"warning.largePaste" : true,
"warning.multiLinePaste" : "automatic",

View File

@@ -315,29 +315,16 @@ 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())
{
@@ -349,10 +336,8 @@ 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
}
@@ -366,15 +351,6 @@ 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

View File

@@ -87,5 +87,4 @@
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::hstring, DragDropDelimiter, L" ")
X(winrt::Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, winrt::Microsoft::Terminal::Control::PathTranslationStyle::None)

View File

@@ -324,11 +324,9 @@ std::vector<wil::com_ptr<T>> SafeArrayToOwningVector(SAFEARRAY* safeArray)
std::vector<wil::com_ptr<T>> result{ gsl::narrow<std::size_t>(count) };
for (int i = 0; i < count; i++)
{
result[i] = pVals[i];
result[i].attach(pVals[i]);
}
THROW_IF_FAILED(SafeArrayUnaccessData(safeArray));
THROW_IF_FAILED(SafeArrayDestroy(safeArray));
return result;
}

View File

@@ -75,7 +75,7 @@
</ItemGroup>
<PropertyGroup Label="Globals">
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0.26100.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0.22621.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion Condition="'$(WindowsTargetPlatformMinVersion)' == ''">10.0.18362.0</WindowsTargetPlatformMinVersion>
</PropertyGroup>
@@ -95,8 +95,7 @@
<!-- For ALL build types-->
<PropertyGroup Label="Configuration">
<PlatformToolset Condition="'$(VisualStudioVersion)' &gt;= '18.0'">v145</PlatformToolset>
<PlatformToolset Condition="'$(PlatformToolset)' == ''">v143</PlatformToolset>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<LinkIncremental>false</LinkIncremental>
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
@@ -125,8 +124,6 @@
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
@@ -139,7 +136,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;4875;5105;26434;26445;26456;26478;26494;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<DisableSpecificWarnings>4201;4312;4467;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>
@@ -201,6 +198,7 @@
</ClCompile>
<Link>
<SetChecksum>false</SetChecksum>
<GenerateDebugInformation>DebugFastLink</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>

View File

@@ -187,11 +187,4 @@
<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>

View File

@@ -400,16 +400,20 @@ static FillConsoleResult FillConsoleImpl(SCREEN_INFORMATION& screenInfo, FillCon
// See FillConsoleOutputCharacterWImpl and its identical code.
if (enablePowershellShim)
{
const auto currentBufferDimensions{ OutContext.GetBufferSize().Dimensions() };
const auto wroteWholeBuffer = lengthToWrite == (currentBufferDimensions.area<size_t>());
const auto startedAtOrigin = startingCoordinate == til::point{ 0, 0 };
const auto wroteSpaces = attribute == (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
if (wroteWholeBuffer && startedAtOrigin && wroteSpaces)
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
if (const auto writer = gci.GetVtWriterForBuffer(&OutContext))
{
// PowerShell has previously called FillConsoleOutputCharacterW() which triggered a call to WriteClearScreen().
cellsModified = lengthToWrite;
return S_OK;
const auto currentBufferDimensions{ OutContext.GetBufferSize().Dimensions() };
const auto wroteWholeBuffer = lengthToWrite == (currentBufferDimensions.area<size_t>());
const auto startedAtOrigin = startingCoordinate == til::point{ 0, 0 };
const auto wroteSpaces = attribute == (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
if (wroteWholeBuffer && startedAtOrigin && wroteSpaces)
{
// PowerShell has previously called FillConsoleOutputCharacterW() which triggered a call to WriteClearScreen().
cellsModified = lengthToWrite;
return S_OK;
}
}
}
@@ -453,23 +457,21 @@ static FillConsoleResult FillConsoleImpl(SCREEN_INFORMATION& screenInfo, FillCon
// their entire buffer will be cleared as well.
if (enablePowershellShim)
{
const auto currentBufferDimensions{ OutContext.GetBufferSize().Dimensions() };
const auto wroteWholeBuffer = lengthToWrite == (currentBufferDimensions.area<size_t>());
const auto startedAtOrigin = startingCoordinate == til::point{ 0, 0 };
const auto wroteSpaces = character == UNICODE_SPACE;
if (wroteWholeBuffer && startedAtOrigin && wroteSpaces)
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
if (auto writer = gci.GetVtWriterForBuffer(&OutContext))
{
WriteClearScreen(OutContext);
const auto currentBufferDimensions{ OutContext.GetBufferSize().Dimensions() };
const auto wroteWholeBuffer = lengthToWrite == (currentBufferDimensions.area<size_t>());
const auto startedAtOrigin = startingCoordinate == til::point{ 0, 0 };
const auto wroteSpaces = character == UNICODE_SPACE;
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
if (auto writer = gci.GetVtWriterForBuffer(&OutContext))
if (wroteWholeBuffer && startedAtOrigin && wroteSpaces)
{
WriteClearScreen(OutContext);
writer.Submit();
cellsModified = lengthToWrite;
return S_OK;
}
cellsModified = lengthToWrite;
return S_OK;
}
}

View File

@@ -670,7 +670,7 @@ void FileTests::TestReadFileBasicEmpty()
const auto hIn = GetStdInputHandle();
VERIFY_IS_NOT_NULL(hIn, L"Verify we have the standard input handle.");
DWORD dwMode = ENABLE_PROCESSED_INPUT; // ^Z is only handled when processed input is enabled.
DWORD dwMode = 0;
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(hIn, dwMode), L"Set input mode for test.");
VERIFY_WIN32_BOOL_SUCCEEDED(FlushConsoleInputBuffer(hIn), L"Flush input buffer in preparation for test.");

View File

@@ -40,12 +40,6 @@
"Execution": {
"AdditionalParameter": "/select:\"@IsPerfTest=true\""
}
},
{
"Name": "NoAppPlatform",
"Execution": {
"AdditionalParameter": "/select:\"not(@Name='PolicyTests::*')\""
}
}
]
}
}

View File

@@ -982,20 +982,23 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont
// A null character will get translated to whitespace.
fillCharacter = Microsoft::Console::VirtualTerminal::VtIo::SanitizeUCS2(fillCharacter);
// GH#3126 - This is a shim for cmd's `cls` function. In the
// legacy console, `cls` is supposed to clear the entire buffer.
// We always use a VT sequence, even if ConPTY isn't used, because those are faster nowadays.
if (enableCmdShim &&
source.left <= 0 && source.top <= 0 &&
source.right >= bufferSize.RightInclusive() && source.bottom >= bufferSize.BottomInclusive() &&
target.x == 0 && target.y <= -bufferSize.BottomExclusive() &&
!clip &&
fillCharacter == UNICODE_SPACE && fillAttribute == buffer.GetAttributes().GetLegacyAttributes())
{
WriteClearScreen(context);
}
else if (writer)
if (writer)
{
// GH#3126 - This is a shim for cmd's `cls` function. In the
// legacy console, `cls` is supposed to clear the entire buffer.
// We always use a VT sequence, even if ConPTY isn't used, because those are faster nowadays.
if (enableCmdShim &&
source.left <= 0 && source.top <= 0 &&
source.right >= bufferSize.RightInclusive() && source.bottom >= bufferSize.BottomInclusive() &&
target.x == 0 && target.y <= -bufferSize.BottomExclusive() &&
!clip &&
fillCharacter == UNICODE_SPACE && fillAttribute == buffer.GetAttributes().GetLegacyAttributes())
{
WriteClearScreen(context);
writer.Submit();
return S_OK;
}
const auto clipViewport = clip ? Viewport::FromInclusive(*clip).Clamp(bufferSize) : bufferSize;
const auto sourceViewport = Viewport::FromInclusive(source);
const auto fillViewport = sourceViewport.Clamp(clipViewport);
@@ -1081,6 +1084,8 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont
RETURN_IF_FAILED(WriteConsoleOutputWImplHelper(context, fill, w, fillViewport, writtenViewport));
RETURN_IF_FAILED(WriteConsoleOutputWImplHelper(context, backup, w, Viewport::FromDimensions(target, readViewport.Dimensions()).Clamp(clipViewport), writtenViewport));
}
writer.Submit();
}
else
{
@@ -1088,11 +1093,6 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont
ScrollRegion(buffer, source, clip, target, fillCharacter, useThisAttr);
}
if (writer)
{
writer.Submit();
}
return S_OK;
}
CATCH_RETURN();

View File

@@ -24,22 +24,6 @@ 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.
@@ -64,12 +48,6 @@ 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:

View File

@@ -29,10 +29,8 @@ 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;

View File

@@ -1313,15 +1313,6 @@ 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;

View File

@@ -859,15 +859,14 @@ PWSTR TranslateConsoleTitle(_In_ PCWSTR pwszConsoleTitle, const BOOL fUnexpand,
return Status;
}
if (ConsoleConnectionDeservesVisibleWindow(p))
// Allow the renderer to paint once the rest of the console is hooked up.
if (g.pRender)
{
// 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();
}
g.pRender->EnablePainting();
}
if (SUCCEEDED_NTSTATUS(Status) && ConsoleConnectionDeservesVisibleWindow(p))
{
HANDLE Thread = nullptr;
IConsoleInputThread* pNewThread = nullptr;

View File

@@ -18,17 +18,10 @@ class SearchTests
{
TEST_CLASS(SearchTests);
CommonState* m_state{ nullptr };
CommonState* m_state;
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;
@@ -36,11 +29,8 @@ class SearchTests
TEST_CLASS_CLEANUP(ClassCleanup)
{
if (m_state)
{
m_state->CleanupGlobalScreenBuffer();
delete m_state;
}
m_state->CleanupGlobalScreenBuffer();
delete m_state;
return true;
}

View File

@@ -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 (includeWhitespace = false)
// - til::point - expected result (includeWhitespace = true)
// - til::point - expected result (accessibilityMode = false)
// - til::point - expected result (accessibilityMode = true)
struct ExpectedResult
{
til::point selectionMode;
til::point accessibilityMode;
til::point accessibilityModeDisabled;
til::point accessibilityModeEnabled;
};
struct Test
@@ -2056,8 +2056,7 @@ void TextBufferTests::GetWordBoundaries()
// Set testData for GetWordStart tests
// clang-format off
std::vector<Test> testData = {
// tests for first line of text ("word other" + spaces)
// selectionMode accessibilityMode
// tests for first line of text
{ { 0, 0 }, {{ 0, 0 }, { 0, 0 }} },
{ { 1, 0 }, {{ 0, 0 }, { 0, 0 }} },
{ { 3, 0 }, {{ 0, 0 }, { 0, 0 }} },
@@ -2067,7 +2066,7 @@ void TextBufferTests::GetWordBoundaries()
{ { 20, 0 }, {{ 10, 0 }, { 5, 0 }} },
{ { 79, 0 }, {{ 10, 0 }, { 5, 0 }} },
// tests for second line of text (" more words" + spaces)
// tests for second line of text
{ { 0, 1 }, {{ 0, 1 }, { 5, 0 }} },
{ { 1, 1 }, {{ 0, 1 }, { 5, 0 }} },
{ { 2, 1 }, {{ 2, 1 }, { 2, 1 }} },
@@ -2083,56 +2082,54 @@ void TextBufferTests::GetWordBoundaries()
// clang-format on
BEGIN_TEST_METHOD_PROPERTIES()
TEST_METHOD_PROPERTY(L"Data:includeWhitespace", L"{false, true}")
TEST_METHOD_PROPERTY(L"Data:accessibilityMode", L"{false, true}")
END_TEST_METHOD_PROPERTIES();
bool includeWhitespace;
VERIFY_SUCCEEDED(TestData::TryGetValue(L"includeWhitespace", includeWhitespace), L"Get includeWhitespace variant");
bool accessibilityMode;
VERIFY_SUCCEEDED(TestData::TryGetValue(L"accessibilityMode", accessibilityMode), L"Get accessibility mode 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, includeWhitespace);
const auto expected = includeWhitespace ? test.expected.accessibilityMode : test.expected.selectionMode;
const auto result = _buffer->GetWordStart(test.startPos, delimiters, accessibilityMode);
const auto expected = accessibilityMode ? test.expected.accessibilityModeEnabled : test.expected.accessibilityModeDisabled;
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 ("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 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 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 } } },
// 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 } } },
};
// 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, includeWhitespace);
const auto expected = includeWhitespace ? test.expected.accessibilityMode : test.expected.selectionMode;
auto result = _buffer->GetWordEnd(test.startPos, delimiters, accessibilityMode);
const auto expected = accessibilityMode ? test.expected.accessibilityModeEnabled : test.expected.accessibilityModeDisabled;
VERIFY_ARE_EQUAL(expected, result);
}
@@ -2164,7 +2161,6 @@ 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 } } },
@@ -2177,11 +2173,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 selection mode now also crosses wrapped rows for ControlChar
{ { 1, 4 }, { { 6, 3 }, { 0, 2 } } },
// v accessibility does not consider wrapping
{ { 1, 4 }, { { 0, 4 }, { 0, 2 } } },
{ { 4, 4 }, { { 4, 4 }, { 4, 4 } } },
{ { 8, 4 }, { { 4, 4 }, { 4, 4 } } },
@@ -2192,8 +2188,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, includeWhitespace);
const auto expected = includeWhitespace ? test.expected.accessibilityMode : test.expected.selectionMode;
const auto result = _buffer->GetWordStart(test.startPos, delimiters, accessibilityMode);
const auto expected = accessibilityMode ? test.expected.accessibilityModeEnabled : test.expected.accessibilityModeDisabled;
VERIFY_ARE_EQUAL(expected, result);
}
@@ -2209,43 +2205,123 @@ void TextBufferTests::GetWordBoundaries()
// clang-format off
testData = {
// tests for first line of text
// 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 } } },
{ { 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 } } },
{ { 4, 1 }, { { 8, 1 }, { 0, 2 } } },
{ { 7, 1 }, { { 8, 1 }, { 0, 2 } } },
{ { 9, 1 }, { { 10, 1 }, { 0, 2 } } },
{ { 4, 1 }, { { 7, 1 }, { 0, 2 } } },
{ { 7, 1 }, { { 7, 1 }, { 0, 2 } } },
{ { 9, 1 }, { { 9, 1 }, { 0, 2 } } },
{ { 0, 2 }, { { 10, 2 }, { 4, 4 } } },
{ { 9, 2 }, { { 10, 2 }, { 4, 4 } } },
{ { 0, 2 }, { { 9, 2 }, { 4, 4 } } },
{ { 9, 2 }, { { 9, 2 }, { 4, 4 } } },
{ { 0, 3 }, { { 6, 3 }, { 4, 4 } } },
{ { 7, 3 }, { { 4, 4 }, { 4, 4 } } },
{ { 0, 3 }, { { 5, 3 }, { 4, 4 } } },
{ { 7, 3 }, { { 9, 3 }, { 4, 4 } } },
{ { 1, 4 }, { { 4, 4 }, { 4, 4 } } },
{ { 4, 4 }, { { 1, 5 }, { 2, 5 } } },
{ { 8, 4 }, { { 1, 5 }, { 2, 5 } } },
{ { 1, 4 }, { { 3, 4 }, { 4, 4 } } },
{ { 4, 4 }, { { 0, 5 }, { 2, 5 } } },
{ { 8, 4 }, { { 0, 5 }, { 2, 5 } } },
{ { 0, 5 }, { { 1, 5 }, { 2, 5 } } },
{ { 1, 5 }, { { 2, 5 }, { 2, 5 } } },
{ { 4, 5 }, { { 10, 5 }, { 10, 5 } } },
{ { 9, 5 }, { { 10, 5 }, { 10, 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 } } },
};
// 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, includeWhitespace);
const auto expected = includeWhitespace ? test.expected.accessibilityMode : test.expected.selectionMode;
auto result = _buffer->GetWordEnd(test.startPos, delimiters, accessibilityMode);
const auto expected = accessibilityMode ? test.expected.accessibilityModeEnabled : test.expected.accessibilityModeDisabled;
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

View File

@@ -63,8 +63,9 @@
#include <wil/nt_result_macros.h>
// GSL
#include <gsl/narrow>
#include <gsl/util>
// 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/pointers>
// CppCoreCheck

View File

@@ -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);

View File

@@ -444,15 +444,7 @@ 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

View File

@@ -951,11 +951,6 @@ 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);

View File

@@ -12,11 +12,7 @@ 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.

View File

@@ -30,8 +30,7 @@
#include <VersionHelpers.h>
#include <wincodec.h>
#include <gsl/narrow>
#include <gsl/util>
#include <gsl/gsl_util>
#include <gsl/pointers>
#include <wil/com.h>
#include <wil/filesystem.h>

View File

@@ -789,8 +789,22 @@ bool Renderer::_CheckViewportAndScroll()
void Renderer::_scheduleRenditionBlink()
{
const auto& buffer = _pData->GetTextBuffer();
const auto blinkUsed = buffer.ContainsBlinkAttributeInRegion(_viewport);
bool blinkUsed = false;
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)
@@ -1516,7 +1530,7 @@ void Renderer::_updateCursorInfo()
_currentCursorOptions.lineRendition = lineRendition;
_currentCursorOptions.ulCursorHeightPercent = cursorHeight;
_currentCursorOptions.cursorPixelWidth = _pData->GetCursorPixelWidth();
_currentCursorOptions.fIsDoubleWidth = buffer.IsGlyphDoubleWidthAt(cursorPosition);
_currentCursorOptions.fIsDoubleWidth = buffer.GetRowByOffset(cursorPosition.y).DbcsAttrAt(cursorPosition.x) != DbcsAttribute::Single;
_currentCursorOptions.cursorType = cursor.GetType();
_currentCursorOptions.fUseColor = useColor;
_currentCursorOptions.cursorColor = cursorColor;

View File

@@ -270,16 +270,6 @@ constexpr T saturate(auto val)
}
CATCH_RETURN();
if (a->ProcessControlZ)
{
// ProcessControlZ is only set for CONSOLE_IO_RAW_READ. To restore
// the behavior from Windows 7 (see filehops.c:123) we need to honor
// ^Z only if PROCESSED_INPUT is enabled.
ULONG InputMode{ 0 };
m->_pApiRoutines->GetConsoleInputModeImpl(*pInputBuffer, InputMode);
a->ProcessControlZ = (InputMode & ENABLE_PROCESSED_INPUT) != 0;
}
TraceConsoleAPICallWithOrigin(
"ReadConsole",
TraceLoggingBoolean(a->Unicode, "Unicode"),

View File

@@ -35,7 +35,6 @@ 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;
@@ -82,7 +81,6 @@ 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
@@ -141,7 +139,7 @@ public:
virtual void AnnounceCodeStructure(const VTInt ansiLevel) = 0; // ACS
virtual void SoftReset() = 0; // DECSTR
virtual void HardReset(bool erase) = 0; // RIS
virtual void HardReset() = 0; // RIS
virtual void ScreenAlignmentPattern() = 0; // DECALN
virtual void SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) = 0; // DECSCUSR
@@ -157,9 +155,13 @@ 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,

View File

@@ -37,7 +37,6 @@ 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
@@ -47,7 +46,6 @@ 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;

View File

@@ -48,11 +48,6 @@ AdaptDispatch::AdaptDispatch(ITerminalApi& api, Renderer* renderer, RenderSettin
{
}
void AdaptDispatch::UnknownSequence() noexcept
{
_api.UnknownSequence();
}
// Routine Description:
// - Translates and displays a single character
// Arguments:
@@ -2078,13 +2073,6 @@ 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);
@@ -2093,11 +2081,6 @@ 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));
}
@@ -2105,11 +2088,6 @@ 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);
}
@@ -2117,11 +2095,6 @@ 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);
}
@@ -2615,27 +2588,6 @@ 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:
@@ -3030,7 +2982,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. (if erase=true)
// - Clears the screen.
// * 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)
@@ -3040,8 +2992,8 @@ void AdaptDispatch::SoftReset()
// - Sets all character sets to the default.
// * G0(USASCII)
//Arguments:
// - erase: if true, erase the screen and scrollback
void AdaptDispatch::HardReset(bool erase)
// <none>
void AdaptDispatch::HardReset()
{
// If in the alt buffer, switch back to main before doing anything else.
if (_usingAltBuffer)
@@ -3068,12 +3020,9 @@ void AdaptDispatch::HardReset(bool erase)
// to ensure that it clears with the default background color.
SoftReset();
if (erase)
{
// Clears the screen - Needs to be done in two operations.
EraseInDisplay(DispatchTypes::EraseType::All);
EraseInDisplay(DispatchTypes::EraseType::Scrollback);
}
// 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();
@@ -3084,14 +3033,8 @@ void AdaptDispatch::HardReset(bool erase)
_renderer->SynchronizedOutputChanged();
}
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);
}
// Cursor to 1,1 - the Soft Reset guarantees this is absolute
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
@@ -3642,10 +3585,6 @@ void AdaptDispatch::DoConEmuAction(const std::wstring_view string)
_pages.ActivePage().Buffer().StartCommand();
_api.NotifyShellIntegrationMark();
}
else
{
_api.UnknownSequence();
}
}
// Method Description:
@@ -3677,10 +3616,6 @@ void AdaptDispatch::DoITerm2Action(const std::wstring_view string)
_pages.ActivePage().Buffer().StartPrompt();
_api.NotifyShellIntegrationMark();
}
else
{
_api.UnknownSequence();
}
}
// Method Description:
@@ -3751,14 +3686,9 @@ 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
@@ -3833,10 +3763,6 @@ void AdaptDispatch::DoVsCodeAction(const std::wstring_view string)
// If it's poorly formatted, just eat it
}
else
{
_api.UnknownSequence();
}
}
// Method Description:

View File

@@ -38,7 +38,6 @@ 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;
@@ -114,7 +113,6 @@ 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
@@ -130,7 +128,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(bool erase) override; // RIS
void HardReset() override; // RIS
void ScreenAlignmentPattern() override; // DECALN
void SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) override; // DECSCUSR

Some files were not shown because too many files have changed in this diff Show More