Merge remote-tracking branch 'origin/main' into dev/migrie/oop/ragnarok

This commit is contained in:
Mike Griese 2021-10-14 15:55:18 -05:00
commit 1413d0145a
197 changed files with 5519 additions and 1559 deletions

2
.gitattributes vendored
View file

@ -3,6 +3,8 @@
############################################################################### ###############################################################################
* -text * -text
*.inc linguist-language=cpp
############################################################################### ###############################################################################
# Set default behavior for command prompt diff. # Set default behavior for command prompt diff.
# #

View file

@ -25,6 +25,7 @@ DERR
dlldata dlldata
DONTADDTORECENT DONTADDTORECENT
DWORDLONG DWORDLONG
enumset
environstrings environstrings
EXPCMDFLAGS EXPCMDFLAGS
EXPCMDSTATE EXPCMDSTATE

View file

@ -1,3 +1,11 @@
atan
CPrime
HBar
HPrime
isnan isnan
LPrime
LStep
powf powf
RSub
sqrtf sqrtf
ULP

View file

@ -1,5 +1,6 @@
ACLs ACLs
ADMINS ADMINS
advapi
altform altform
altforms altforms
appendwttlogging appendwttlogging
@ -15,6 +16,7 @@ CPLs
cpptools cpptools
cppvsdbg cppvsdbg
CPRs CPRs
cryptbase
DACL DACL
DACLs DACLs
diffs diffs
@ -24,7 +26,9 @@ DTDs
DWINRT DWINRT
enablewttlogging enablewttlogging
Intelli Intelli
IVisual
LKG LKG
LOCKFILE
Lxss Lxss
mfcribbon mfcribbon
microsoft microsoft
@ -44,6 +48,7 @@ powershell
propkey propkey
pscustomobject pscustomobject
QWORD QWORD
regedit
robocopy robocopy
SACLs SACLs
sdkddkver sdkddkver

View file

@ -189,13 +189,12 @@ cacafire
callee callee
capslock capslock
CARETBLINKINGENABLED CARETBLINKINGENABLED
carlos
CARRIAGERETURN CARRIAGERETURN
cascadia cascadia
cassert cassert
castsi castsi
catid catid
carlos
zamora
cazamor cazamor
CBash CBash
cbegin cbegin
@ -669,6 +668,7 @@ dwriteglyphrundescriptionclustermap
dxgi dxgi
dxgidwm dxgidwm
dxinterop dxinterop
dxsm
dxttbmp dxttbmp
eachother eachother
eae eae
@ -1232,6 +1232,7 @@ KLF
KLMNO KLMNO
KLMNOPQRST KLMNOPQRST
KLMNOPQRSTQQQQQ KLMNOPQRSTQQQQQ
KPRIORITY
KVM KVM
langid langid
LANGUAGELIST LANGUAGELIST
@ -1642,6 +1643,7 @@ onecoreuapuuid
onecoreuuid onecoreuuid
ONECOREWINDOWS ONECOREWINDOWS
onehalf onehalf
oneseq
ONLCR ONLCR
openbash openbash
opencode opencode
@ -1708,6 +1710,7 @@ pcch
PCCHAR PCCHAR
PCCONSOLE PCCONSOLE
PCD PCD
pcg
pch pch
PCHAR PCHAR
PCIDLIST PCIDLIST
@ -1803,6 +1806,7 @@ POSX
POSXSCROLL POSXSCROLL
POSYSCROLL POSYSCROLL
ppci ppci
PPEB
ppf ppf
ppguid ppguid
ppidl ppidl
@ -2022,6 +2026,7 @@ Rike
RIPMSG RIPMSG
RIS RIS
RMENU RMENU
rng
roadmap roadmap
robomac robomac
roundtrip roundtrip
@ -2724,6 +2729,7 @@ wixproj
wline wline
wlinestream wlinestream
wmain wmain
wmemory
WMSZ WMSZ
wnd wnd
WNDALLOC WNDALLOC
@ -2850,6 +2856,7 @@ YSize
YSubstantial YSubstantial
YVIRTUALSCREEN YVIRTUALSCREEN
YWalk YWalk
zamora
ZCmd ZCmd
ZCtrl ZCtrl
zsh zsh

View file

@ -1,5 +1,7 @@
http http
www www
easyrgb
php
ecma ecma
rapidtables rapidtables
WCAG WCAG

View file

@ -24,3 +24,4 @@ VERIFY_ARE_EQUAL\(L"[^"]+"
std::memory_order_[\w]+ std::memory_order_[\w]+
D2DERR_SHADER_COMPILE_FAILED D2DERR_SHADER_COMPILE_FAILED
TIL_FEATURE_[0-9A-Z_]+ TIL_FEATURE_[0-9A-Z_]+
vcvars\w*

View file

@ -117,7 +117,6 @@ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
``` ```
## dynamic_bitset ## dynamic_bitset
@ -148,7 +147,6 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
``` ```
## \{fmt\} ## \{fmt\}
@ -215,7 +213,6 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
``` ```
@ -249,7 +246,71 @@ SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
```
## PCG Random Number Generation
**Source**: [https://github.com/imneme/pcg-cpp](https://github.com/imneme/pcg-cpp)
### License
```
Copyright (c) 2014-2017 Melissa O'Neill and PCG Project contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
## ConEmu
**Source**: [https://github.com/Maximus5/ConEmu](https://github.com/Maximus5/ConEmu)
### License
```
BSD 3-Clause License
Copyright (c) 2009-2017, Maximus5 <ConEmu.Maximus5@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
``` ```
# Microsoft Open Source # Microsoft Open Source

View file

@ -15,7 +15,7 @@ Import-Module .\tools\OpenConsole.psm1
Set-MsBuildDevEnvironment Set-MsBuildDevEnvironment
Get-Format Get-Format
``` ```
After, go to Tools > Options > Text Editor > C++ > Formatting and checking "Use custom clang-format.exe file" in Visual Studio and choose the clang-format.exe in the repository at /packages/clang-format.win-x86.10.0.0/tools/clang-format.exe by clicking "browse" right under the check box. After, go to Tools > Options > Text Editor > C++ > Formatting and check "Use custom clang-format.exe file" in Visual Studio and choose the clang-format.exe in the repository at /packages/clang-format.win-x86.10.0.0/tools/clang-format.exe by clicking "browse" right under the check box.
### Building in PowerShell ### Building in PowerShell

File diff suppressed because it is too large Load diff

201
oss/pcg/LICENSE-APACHE.txt Normal file
View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

19
oss/pcg/LICENSE-MIT.txt Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2014-2017 Melissa O'Neill and PCG Project contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

14
oss/pcg/cgmanifest.json Normal file
View file

@ -0,0 +1,14 @@
{
"Registrations": [
{
"component": {
"type": "git",
"git": {
"repositoryUrl": "https://github.com/imneme/pcg-cpp",
"commitHash": "ffd522e7188bef30a00c74dc7eb9de5faff90092"
}
}
}
],
"Version": 1
}

View file

@ -0,0 +1,82 @@
// PCG Random Number Generation for C++
//
// Copyright 2014-2019 Melissa O'Neill <oneill@pcg-random.org>,
// and the PCG Project contributors.
//
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//
// Licensed under the Apache License, Version 2.0 (provided in
// LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0)
// or under the MIT license (provided in LICENSE-MIT.txt and at
// http://opensource.org/licenses/MIT), at your option. This file may not
// be copied, modified, or distributed except according to those terms.
//
// Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either
// express or implied. See your chosen license for details.
//
// For additional information about the PCG random number generation scheme,
// visit http://www.pcg-random.org/.
//
// -----------------------------------------------------------------------------
//
// Leonard Hecker <lhecker@microsoft.com>:
// The following contents are an extract of pcg_engines::oneseq_dxsm_64_32
// reduced down to the bare essentials, while retaining base functionality.
namespace pcg_engines {
class oneseq_dxsm_64_32 {
using xtype = uint32_t;
using itype = uint64_t;
itype state_;
static constexpr uint64_t multiplier() {
return 6364136223846793005ULL;
}
static constexpr uint64_t increment() {
return 1442695040888963407ULL;
}
static itype bump(itype state) {
return state * multiplier() + increment();
}
itype base_generate0() {
itype old_state = state_;
state_ = bump(state_);
return old_state;
}
public:
explicit oneseq_dxsm_64_32(itype state = 0xcafef00dd15ea5e5ULL) : state_(bump(state + increment())) {
}
// Returns a value in the interval [0, UINT32_MAX].
xtype operator()() {
constexpr auto xtypebits = uint8_t(sizeof(xtype) * 8);
constexpr auto itypebits = uint8_t(sizeof(itype) * 8);
auto internal = base_generate0();
auto hi = xtype(internal >> (itypebits - xtypebits));
auto lo = xtype(internal);
lo |= 1;
hi ^= hi >> (xtypebits / 2);
hi *= xtype(multiplier());
hi ^= hi >> (3 * (xtypebits / 4));
hi *= lo;
return hi;
}
// Returns a value in the interval [0, upper_bound).
xtype operator()(xtype upper_bound) {
uint32_t threshold = (UINT64_MAX + uint32_t(1) - upper_bound) % upper_bound;
for (;;) {
auto r = operator()();
if (r >= threshold)
return r % upper_bound;
}
}
};
}

View file

@ -12,6 +12,7 @@
<EventProvider Id="EventProvider_TerminalWin32Host" Name="56c06166-2e2e-5f4d-7ff3-74f4b78c87d6" /> <EventProvider Id="EventProvider_TerminalWin32Host" Name="56c06166-2e2e-5f4d-7ff3-74f4b78c87d6" />
<EventProvider Id="EventProvider_TerminalRemoting" Name="d6f04aad-629f-539a-77c1-73f5c3e4aa7b" /> <EventProvider Id="EventProvider_TerminalRemoting" Name="d6f04aad-629f-539a-77c1-73f5c3e4aa7b" />
<EventProvider Id="EventProvider_TerminalDirectX" Name="c93e739e-ae50-5a14-78e7-f171e947535d" /> <EventProvider Id="EventProvider_TerminalDirectX" Name="c93e739e-ae50-5a14-78e7-f171e947535d" />
<EventProvider Id="EventProvider_TerminalUIA" Name="e7ebce59-2161-572d-b263-2f16a6afb9e5"/>
<Profile Id="Terminal.Verbose.File" Name="Terminal" Description="Terminal" LoggingMode="File" DetailLevel="Verbose"> <Profile Id="Terminal.Verbose.File" Name="Terminal" Description="Terminal" LoggingMode="File" DetailLevel="Verbose">
<Collectors> <Collectors>
<EventCollectorId Value="EventCollector_Terminal"> <EventCollectorId Value="EventCollector_Terminal">
@ -23,6 +24,7 @@
<EventProviderId Value="EventProvider_TerminalWin32Host" /> <EventProviderId Value="EventProvider_TerminalWin32Host" />
<EventProviderId Value="EventProvider_TerminalRemoting" /> <EventProviderId Value="EventProvider_TerminalRemoting" />
<EventProviderId Value="EventProvider_TerminalDirectX" /> <EventProviderId Value="EventProvider_TerminalDirectX" />
<EventProviderId Value="EventProvider_TerminalUIA" />
</EventProviders> </EventProviders>
</EventCollectorId> </EventCollectorId>
</Collectors> </Collectors>

View file

@ -4,6 +4,8 @@
#include "precomp.h" #include "precomp.h"
#include "TextColor.h" #include "TextColor.h"
#include <til/bit.h>
// clang-format off // clang-format off
// A table mapping 8-bit RGB colors, in the form RRRGGGBB, // A table mapping 8-bit RGB colors, in the form RRRGGGBB,
@ -186,7 +188,7 @@ COLORREF TextColor::GetColor(const std::array<COLORREF, 256>& colorTable, const
// the result will be something like 0b00100000. // the result will be something like 0b00100000.
// 5. Use BitScanForward (bsf) to find the index of the most significant 1 bit. // 5. Use BitScanForward (bsf) to find the index of the most significant 1 bit.
const auto haystack = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(colorTable.data())); // 1. const auto haystack = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(colorTable.data())); // 1.
const auto needle = _mm256_set1_epi32(__builtin_bit_cast(int, defaultColor)); // 2. const auto needle = _mm256_set1_epi32(til::bit_cast<int>(defaultColor)); // 2.
const auto result = _mm256_cmpeq_epi32(haystack, needle); // 3. const auto result = _mm256_cmpeq_epi32(haystack, needle); // 3.
const auto mask = _mm256_movemask_ps(_mm256_castsi256_ps(result)); // 4. const auto mask = _mm256_movemask_ps(_mm256_castsi256_ps(result)); // 4.
unsigned long index; unsigned long index;
@ -203,7 +205,7 @@ COLORREF TextColor::GetColor(const std::array<COLORREF, 256>& colorTable, const
// --> the index returned by _BitScanForward must be divided by 2. // --> the index returned by _BitScanForward must be divided by 2.
const auto haystack1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(colorTable.data() + 0)); const auto haystack1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(colorTable.data() + 0));
const auto haystack2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(colorTable.data() + 4)); const auto haystack2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(colorTable.data() + 4));
const auto needle = _mm_set1_epi32(__builtin_bit_cast(int, defaultColor)); const auto needle = _mm_set1_epi32(til::bit_cast<int>(defaultColor));
const auto result1 = _mm_cmpeq_epi32(haystack1, needle); const auto result1 = _mm_cmpeq_epi32(haystack1, needle);
const auto result2 = _mm_cmpeq_epi32(haystack2, needle); const auto result2 = _mm_cmpeq_epi32(haystack2, needle);
const auto result = _mm_packs_epi32(result1, result2); // 3.5 const auto result = _mm_packs_epi32(result1, result2); // 3.5

View file

@ -74,7 +74,7 @@
Enabled="false" Enabled="false"
DisplayName="ms-resource:AppName" /> DisplayName="ms-resource:AppName" />
</uap5:Extension> </uap5:Extension>
<!-- <uap3:Extension Category="windows.appExtension"> <uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="com.microsoft.windows.console.host" <uap3:AppExtension Name="com.microsoft.windows.console.host"
Id="OpenConsole" Id="OpenConsole"
DisplayName="OpenConsole" DisplayName="OpenConsole"
@ -102,15 +102,15 @@
<com:Interface Id="E686C757-9A35-4A1C-B3CE-0BCC8B5C69F4" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/> <com:Interface Id="E686C757-9A35-4A1C-B3CE-0BCC8B5C69F4" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/>
<com:Interface Id="59D55CCE-FC8A-48B4-ACE8-0A9286C6557F" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/> <com:Interface Id="59D55CCE-FC8A-48B4-ACE8-0A9286C6557F" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/>
</com:ComInterface> </com:ComInterface>
</com:Extension> --> </com:Extension>
<com:Extension Category="windows.comServer"> <com:Extension Category="windows.comServer">
<com:ComServer> <com:ComServer>
<!-- <com:ExeServer DisplayName="OpenConsole" Executable="OpenConsole.exe"> <com:ExeServer DisplayName="OpenConsole" Executable="OpenConsole.exe">
<com:Class Id="2EACA947-7F5F-4CFA-BA87-8F7FBEEFBE69"/> <com:Class Id="2EACA947-7F5F-4CFA-BA87-8F7FBEEFBE69"/>
</com:ExeServer> </com:ExeServer>
<com:ExeServer DisplayName="WindowsTerminal" Executable="WindowsTerminal.exe"> <com:ExeServer DisplayName="WindowsTerminal" Executable="WindowsTerminal.exe">
<com:Class Id="E12CFF52-A866-4C77-9A90-F570A7AA2C6B"/> <com:Class Id="E12CFF52-A866-4C77-9A90-F570A7AA2C6B"/>
</com:ExeServer> --> </com:ExeServer>
<com:SurrogateServer DisplayName="WindowsTerminalShellExt"> <com:SurrogateServer DisplayName="WindowsTerminalShellExt">
<com:Class Id="9f156763-7844-4dc4-b2b1-901f640f5155" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/> <com:Class Id="9f156763-7844-4dc4-b2b1-901f640f5155" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/>
</com:SurrogateServer> </com:SurrogateServer>

View file

@ -465,6 +465,10 @@ namespace SettingsModelLocalTests
"name":"action7_startingDirectoryWithTrailingSlash", "name":"action7_startingDirectoryWithTrailingSlash",
"command": { "action": "newWindow", "startingDirectory":"C:\\", "commandline": "bar.exe" } "command": { "action": "newWindow", "startingDirectory":"C:\\", "commandline": "bar.exe" }
}, },
{
"name":"action8_tabTitleEscaping",
"command": { "action": "newWindow", "tabTitle":"\\\";foo\\" }
}
])" }; ])" };
const auto commands0Json = VerifyParseSucceeded(commands0String); const auto commands0Json = VerifyParseSucceeded(commands0String);
@ -473,7 +477,7 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(0u, commands.Size()); VERIFY_ARE_EQUAL(0u, commands.Size());
auto warnings = implementation::Command::LayerJson(commands, commands0Json); auto warnings = implementation::Command::LayerJson(commands, commands0Json);
VERIFY_ARE_EQUAL(0u, warnings.size()); VERIFY_ARE_EQUAL(0u, warnings.size());
VERIFY_ARE_EQUAL(8u, commands.Size()); VERIFY_ARE_EQUAL(9u, commands.Size());
{ {
auto command = commands.Lookup(L"action0"); auto command = commands.Lookup(L"action0");
@ -586,5 +590,20 @@ namespace SettingsModelLocalTests
L"cmdline: \"%s\"", cmdline.c_str())); L"cmdline: \"%s\"", cmdline.c_str()));
VERIFY_ARE_EQUAL(L"--startingDirectory \"C:\\\\\" -- \"bar.exe\"", terminalArgs.ToCommandline()); VERIFY_ARE_EQUAL(L"--startingDirectory \"C:\\\\\" -- \"bar.exe\"", terminalArgs.ToCommandline());
} }
{
auto command = commands.Lookup(L"action8_tabTitleEscaping");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(realArgs);
const auto& terminalArgs = realArgs.TerminalArgs();
VERIFY_IS_NOT_NULL(terminalArgs);
auto cmdline = terminalArgs.ToCommandline();
Log::Comment(NoThrowString().Format(
L"cmdline: \"%s\"", cmdline.c_str()));
VERIFY_ARE_EQUAL(LR"-(--title "\\\"\;foo\\")-", terminalArgs.ToCommandline());
}
} }
} }

View file

@ -1092,7 +1092,7 @@ namespace SettingsModelLocalTests
}, },
{ {
"name": "profile1", "name": "profile1",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}", "guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"source": "Terminal.App.UnitTest.1", "source": "Terminal.App.UnitTest.1",
"historySize": 2222 "historySize": 2222
}, },

View file

@ -272,20 +272,30 @@ namespace SettingsModelLocalTests
void ProfileTests::DuplicateProfileTest() void ProfileTests::DuplicateProfileTest()
{ {
static constexpr std::string_view userProfiles{ R"({ static constexpr std::string_view userProfiles{ R"({
"profiles": [ "profiles": {
{ "defaults": {
"name": "profile0", "font": {
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}", "size": 123
"backgroundImage": "file:///some/path", }
"hidden": false, },
} "list": [
] {
"name": "profile0",
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"backgroundImage": "file:///some/path",
"hidden": false,
}
]
}
})" }; })" };
const auto settings = winrt::make_self<implementation::CascadiaSettings>(userProfiles); const auto settings = winrt::make_self<implementation::CascadiaSettings>(userProfiles);
const auto profile = settings->AllProfiles().GetAt(0); const auto profile = settings->AllProfiles().GetAt(0);
const auto duplicatedProfile = settings->DuplicateProfile(profile); const auto duplicatedProfile = settings->DuplicateProfile(profile);
// GH#11392: Ensure duplicated profiles properly inherit the base layer, even for nested objects.
VERIFY_ARE_EQUAL(123, duplicatedProfile.FontInfo().FontSize());
duplicatedProfile.Guid(profile.Guid()); duplicatedProfile.Guid(profile.Guid());
duplicatedProfile.Name(profile.Name()); duplicatedProfile.Name(profile.Name());
@ -300,6 +310,17 @@ namespace SettingsModelLocalTests
// the GUID generated for a dynamic profile (with a source) is different // the GUID generated for a dynamic profile (with a source) is different
// than that of a profile without a source. // than that of a profile without a source.
static constexpr std::string_view inboxSettings{ R"({
"profiles": [
{
"name" : "profile0",
"source": "Terminal.App.UnitTest.0"
},
{
"name" : "profile1"
}
]
})" };
static constexpr std::string_view userSettings{ R"({ static constexpr std::string_view userSettings{ R"({
"profiles": [ "profiles": [
{ {
@ -312,9 +333,9 @@ namespace SettingsModelLocalTests
] ]
})" }; })" };
const auto settings = winrt::make_self<implementation::CascadiaSettings>(userSettings, DefaultJson); const auto settings = winrt::make_self<implementation::CascadiaSettings>(userSettings, inboxSettings);
VERIFY_ARE_EQUAL(4u, settings->AllProfiles().Size()); VERIFY_ARE_EQUAL(3u, settings->AllProfiles().Size());
VERIFY_ARE_EQUAL(L"profile0", settings->AllProfiles().GetAt(0).Name()); VERIFY_ARE_EQUAL(L"profile0", settings->AllProfiles().GetAt(0).Name());
VERIFY_IS_TRUE(settings->AllProfiles().GetAt(0).HasGuid()); VERIFY_IS_TRUE(settings->AllProfiles().GetAt(0).HasGuid());

View file

@ -53,7 +53,7 @@ namespace SettingsModelLocalTests
// Return Value: // Return Value:
// - the JsonObject representing this instance // - the JsonObject representing this instance
template<typename T> template<typename T>
void RoundtripTest(const std::string& jsonString) void RoundtripTest(const std::string_view& jsonString)
{ {
const auto json{ VerifyParseSucceeded(jsonString) }; const auto json{ VerifyParseSucceeded(jsonString) };
const auto settings{ T::FromJson(json) }; const auto settings{ T::FromJson(json) };
@ -69,7 +69,7 @@ namespace SettingsModelLocalTests
void SerializationTests::GlobalSettings() void SerializationTests::GlobalSettings()
{ {
const std::string globalsString{ R"( static constexpr std::string_view globalsString{ R"(
{ {
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", "defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
@ -105,7 +105,7 @@ namespace SettingsModelLocalTests
"actions": [] "actions": []
})" }; })" };
const std::string smallGlobalsString{ R"( static constexpr std::string_view smallGlobalsString{ R"(
{ {
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", "defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"actions": [] "actions": []
@ -117,7 +117,7 @@ namespace SettingsModelLocalTests
void SerializationTests::Profile() void SerializationTests::Profile()
{ {
const std::string profileString{ R"( static constexpr std::string_view profileString{ R"(
{ {
"name": "Windows PowerShell", "name": "Windows PowerShell",
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
@ -152,7 +152,7 @@ namespace SettingsModelLocalTests
"selectionBackground": "#CCAABB", "selectionBackground": "#CCAABB",
"useAcrylic": false, "useAcrylic": false,
"acrylicOpacity": 0.5, "opacity": 50,
"backgroundImage": "made_you_look.jpeg", "backgroundImage": "made_you_look.jpeg",
"backgroundImageStretchMode": "uniformToFill", "backgroundImageStretchMode": "uniformToFill",
@ -167,7 +167,7 @@ namespace SettingsModelLocalTests
"experimental.retroTerminalEffect": false "experimental.retroTerminalEffect": false
})" }; })" };
const std::string smallProfileString{ R"( static constexpr std::string_view smallProfileString{ R"(
{ {
"name": "Custom Profile" "name": "Custom Profile"
})" }; })" };
@ -175,7 +175,7 @@ namespace SettingsModelLocalTests
// Setting "tabColor" to null tests two things: // Setting "tabColor" to null tests two things:
// - null should count as an explicit user-set value, not falling back to the parent's value // - null should count as an explicit user-set value, not falling back to the parent's value
// - null should be acceptable even though we're working with colors // - null should be acceptable even though we're working with colors
const std::string weirdProfileString{ R"( static constexpr std::string_view weirdProfileString{ R"(
{ {
"guid" : "{8b039d4d-77ca-5a83-88e1-dfc8e895a127}", "guid" : "{8b039d4d-77ca-5a83-88e1-dfc8e895a127}",
"name": "Weird Profile", "name": "Weird Profile",
@ -192,7 +192,7 @@ namespace SettingsModelLocalTests
void SerializationTests::ColorScheme() void SerializationTests::ColorScheme()
{ {
const std::string schemeString{ R"({ static constexpr std::string_view schemeString{ R"({
"name": "Campbell", "name": "Campbell",
"cursorColor": "#FFFFFF", "cursorColor": "#FFFFFF",
@ -225,56 +225,56 @@ namespace SettingsModelLocalTests
void SerializationTests::Actions() void SerializationTests::Actions()
{ {
// simple command // simple command
const std::string actionsString1{ R"([ static constexpr std::string_view actionsString1{ R"([
{ "command": "paste" } { "command": "paste" }
])" }; ])" };
// complex command // complex command
const std::string actionsString2A{ R"([ static constexpr std::string_view actionsString2A{ R"([
{ "command": { "action": "setTabColor" } } { "command": { "action": "setTabColor" } }
])" }; ])" };
const std::string actionsString2B{ R"([ static constexpr std::string_view actionsString2B{ R"([
{ "command": { "action": "setTabColor", "color": "#112233" } } { "command": { "action": "setTabColor", "color": "#112233" } }
])" }; ])" };
const std::string actionsString2C{ R"([ static constexpr std::string_view actionsString2C{ R"([
{ "command": { "action": "copy" } }, { "command": { "action": "copy" } },
{ "command": { "action": "copy", "singleLine": true, "copyFormatting": "html" } } { "command": { "action": "copy", "singleLine": true, "copyFormatting": "html" } }
])" }; ])" };
// simple command with key chords // simple command with key chords
const std::string actionsString3{ R"([ static constexpr std::string_view actionsString3{ R"([
{ "command": "toggleAlwaysOnTop", "keys": "ctrl+a" }, { "command": "toggleAlwaysOnTop", "keys": "ctrl+a" },
{ "command": "toggleAlwaysOnTop", "keys": "ctrl+b" } { "command": "toggleAlwaysOnTop", "keys": "ctrl+b" }
])" }; ])" };
// complex command with key chords // complex command with key chords
const std::string actionsString4A{ R"([ static constexpr std::string_view actionsString4A{ R"([
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+c" }, { "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+c" },
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+d" } { "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+d" }
])" }; ])" };
const std::string actionsString4B{ R"([ static constexpr std::string_view actionsString4B{ R"([
{ "command": { "action": "findMatch", "direction": "next" }, "keys": "ctrl+shift+s" }, { "command": { "action": "findMatch", "direction": "next" }, "keys": "ctrl+shift+s" },
{ "command": { "action": "findMatch", "direction": "prev" }, "keys": "ctrl+shift+r" } { "command": { "action": "findMatch", "direction": "prev" }, "keys": "ctrl+shift+r" }
])" }; ])" };
// command with name and icon and multiple key chords // command with name and icon and multiple key chords
const std::string actionsString5{ R"([ static constexpr std::string_view actionsString5{ R"([
{ "icon": "image.png", "name": "Scroll To Top Name", "command": "scrollToTop", "keys": "ctrl+e" }, { "icon": "image.png", "name": "Scroll To Top Name", "command": "scrollToTop", "keys": "ctrl+e" },
{ "command": "scrollToTop", "keys": "ctrl+f" } { "command": "scrollToTop", "keys": "ctrl+f" }
])" }; ])" };
// complex command with new terminal args // complex command with new terminal args
const std::string actionsString6{ R"([ static constexpr std::string_view actionsString6{ R"([
{ "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+g" }, { "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+g" },
])" }; ])" };
// complex command with meaningful null arg // complex command with meaningful null arg
const std::string actionsString7{ R"([ static constexpr std::string_view actionsString7{ R"([
{ "command": { "action": "renameWindow", "name": null }, "keys": "ctrl+h" } { "command": { "action": "renameWindow", "name": null }, "keys": "ctrl+h" }
])" }; ])" };
// nested command // nested command
const std::string actionsString8{ R"([ static constexpr std::string_view actionsString8{ R"([
{ {
"name": "Change font size...", "name": "Change font size...",
"commands": [ "commands": [
@ -286,7 +286,7 @@ namespace SettingsModelLocalTests
])" }; ])" };
// iterable command // iterable command
const std::string actionsString9A{ R"([ static constexpr std::string_view actionsString9A{ R"([
{ {
"name": "New tab", "name": "New tab",
"commands": [ "commands": [
@ -299,7 +299,7 @@ namespace SettingsModelLocalTests
] ]
} }
])" }; ])" };
const std::string actionsString9B{ R"([ static constexpr std::string_view actionsString9B{ R"([
{ {
"commands": "commands":
[ [
@ -315,7 +315,7 @@ namespace SettingsModelLocalTests
"name": "Send Input ..." "name": "Send Input ..."
} }
])" }; ])" };
const std::string actionsString9C{ R""([ static constexpr std::string_view actionsString9C{ R""([
{ {
"commands": "commands":
[ [
@ -338,7 +338,7 @@ namespace SettingsModelLocalTests
"name": "Send Input (Evil) ..." "name": "Send Input (Evil) ..."
} }
])"" }; ])"" };
const std::string actionsString9D{ R""([ static constexpr std::string_view actionsString9D{ R""([
{ {
"command": "command":
{ {
@ -352,7 +352,7 @@ namespace SettingsModelLocalTests
])"" }; ])"" };
// unbound command // unbound command
const std::string actionsString10{ R"([ static constexpr std::string_view actionsString10{ R"([
{ "command": "unbound", "keys": "ctrl+c" } { "command": "unbound", "keys": "ctrl+c" }
])" }; ])" };
@ -395,7 +395,7 @@ namespace SettingsModelLocalTests
void SerializationTests::CascadiaSettings() void SerializationTests::CascadiaSettings()
{ {
const std::string settingsString{ R"({ static constexpr std::string_view settingsString{ R"({
"$help" : "https://aka.ms/terminal-documentation", "$help" : "https://aka.ms/terminal-documentation",
"$schema" : "https://aka.ms/terminal-profiles-schema", "$schema" : "https://aka.ms/terminal-profiles-schema",
"defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}", "defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
@ -465,7 +465,7 @@ namespace SettingsModelLocalTests
void SerializationTests::LegacyFontSettings() void SerializationTests::LegacyFontSettings()
{ {
const std::string profileString{ R"( static constexpr std::string_view profileString{ R"(
{ {
"name": "Profile with legacy font settings", "name": "Profile with legacy font settings",
@ -474,7 +474,7 @@ namespace SettingsModelLocalTests
"fontWeight": "normal" "fontWeight": "normal"
})" }; })" };
const std::string expectedOutput{ R"( static constexpr std::string_view expectedOutput{ R"(
{ {
"name": "Profile with legacy font settings", "name": "Profile with legacy font settings",

View file

@ -3,6 +3,8 @@
#include "pch.h" #include "pch.h"
#include <til/rand.h>
#include "../TerminalSettingsModel/CascadiaSettings.h" #include "../TerminalSettingsModel/CascadiaSettings.h"
#include "../TerminalSettingsModel/TerminalSettings.h" #include "../TerminalSettingsModel/TerminalSettings.h"
#include "TestUtils.h" #include "TestUtils.h"
@ -34,14 +36,12 @@ namespace SettingsModelLocalTests
END_TEST_CLASS() END_TEST_CLASS()
TEST_METHOD(TryCreateWinRTType); TEST_METHOD(TryCreateWinRTType);
TEST_METHOD(TestTerminalArgsForBinding); TEST_METHOD(TestTerminalArgsForBinding);
TEST_METHOD(CommandLineToArgvW);
TEST_METHOD(GetProfileForArgsWithCommandline);
TEST_METHOD(MakeSettingsForProfile); TEST_METHOD(MakeSettingsForProfile);
TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist); TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist);
TEST_METHOD(TestLayerProfileOnColorScheme); TEST_METHOD(TestLayerProfileOnColorScheme);
TEST_METHOD(TestCommandlineToTitlePromotion); TEST_METHOD(TestCommandlineToTitlePromotion);
TEST_CLASS_SETUP(ClassSetup) TEST_CLASS_SETUP(ClassSetup)
@ -60,6 +60,139 @@ namespace SettingsModelLocalTests
VERIFY_ARE_NOT_EQUAL(oldFontSize, newFontSize); VERIFY_ARE_NOT_EQUAL(oldFontSize, newFontSize);
} }
// CascadiaSettings::_normalizeCommandLine abuses some aspects from CommandLineToArgvW
// to simplify the implementation. It assumes that all arguments returned by
// CommandLineToArgvW are returned back to back in memory as "arg1\0arg2\0arg3\0...".
// This test ensures CommandLineToArgvW doesn't change just to be sure.
void TerminalSettingsTests::CommandLineToArgvW()
{
pcg_engines::oneseq_dxsm_64_32 rng{ til::gen_random<uint64_t>() };
const auto expectedArgc = static_cast<int>(rng(16) + 1);
std::wstring expectedArgv;
std::wstring input;
// We generate up to 16 arguments. Each argument is up to 64 chars long, is quoted
// (2 chars, only applies to the input) and separated by a whitespace (1 char).
expectedArgv.reserve(expectedArgc * 65);
input.reserve(expectedArgc * 67);
for (int i = 0; i < expectedArgc; ++i)
{
const bool useQuotes = static_cast<bool>(rng(2));
const auto count = static_cast<size_t>(rng(64));
const auto ch = static_cast<wchar_t>(rng('z' - 'a' + 1) + 'a');
if (i != 0)
{
expectedArgv.push_back(L'\0');
input.push_back(L' ');
}
if (useQuotes)
{
input.push_back(L'"');
}
expectedArgv.append(count, ch);
input.append(count, ch);
if (useQuotes)
{
input.push_back(L'"');
}
}
int argc;
wil::unique_hlocal_ptr<PWSTR[]> argv{ ::CommandLineToArgvW(input.c_str(), &argc) };
VERIFY_ARE_EQUAL(expectedArgc, argc);
VERIFY_IS_NOT_NULL(argv);
const auto lastArg = argv[argc - 1];
const auto beg = argv[0];
const auto end = lastArg + wcslen(lastArg);
VERIFY_IS_GREATER_THAN(end, beg);
VERIFY_ARE_EQUAL(expectedArgv.size(), static_cast<size_t>(end - beg));
VERIFY_ARE_EQUAL(0, memcmp(beg, expectedArgv.data(), expectedArgv.size()));
}
void TerminalSettingsTests::GetProfileForArgsWithCommandline()
{
// I'm exclusively using cmd.exe as I know exactly where it resides at.
static constexpr std::string_view settingsJson{ R"({
"profiles": {
"defaults": {
"historySize": 123
},
"list": [
{
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"commandline": "%SystemRoot%\\System32\\cmd.exe"
},
{
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"commandline": "cmd.exe /A"
},
{
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"commandline": "cmd.exe /A /B"
},
{
"guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
"commandline": "cmd.exe /A /C",
"connectionType": "{9a9977a7-1fe0-49c0-b6c0-13a0cd1c98a1}"
}
]
}
})" };
const auto settings = winrt::make_self<implementation::CascadiaSettings>(settingsJson);
struct TestCase
{
std::wstring_view input;
int expected;
};
static constexpr std::array testCases{
// Base test.
TestCase{ L"cmd.exe", 0 },
// SearchPathW() normalization + case insensitive matching.
TestCase{ L"cmd.exe /a", 1 },
TestCase{ L"C:\\Windows\\System32\\cmd.exe /A", 1 },
// Test that we don't pick the equally long but different "/A /B" variant.
TestCase{ L"C:\\Windows\\System32\\cmd.exe /A /C", 1 },
// Test that we don't pick the shorter "/A" variant,
// but do pick the shorter "/A /B" variant for longer inputs.
TestCase{ L"cmd.exe /A /B", 2 },
TestCase{ L"cmd.exe /A /B /C", 2 },
// Ignore profiles with a connection type, like the Azure cloud shell.
// Instead it should pick any other prefix.
TestCase{ L"C:\\Windows\\System32\\cmd.exe /A /C", 1 },
// Return base layer profile for missing profiles.
TestCase{ L"C:\\Windows\\regedit.exe", -1 },
};
for (const auto& testCase : testCases)
{
NewTerminalArgs args;
args.Commandline(testCase.input);
const auto profile = settings->GetProfileForArgs(args);
VERIFY_IS_NOT_NULL(profile);
if (testCase.expected < 0)
{
VERIFY_ARE_EQUAL(123, profile.HistorySize());
}
else
{
GUID expectedGUID{ 0x6239a42c, static_cast<uint16_t>(0x1111 * testCase.expected), 0x49a3, { 0x80, 0xbd, 0xe8, 0xfd, 0xd0, 0x45, 0x18, 0x5c } };
VERIFY_ARE_EQUAL(expectedGUID, static_cast<const GUID&>(profile.Guid()));
}
}
}
void TerminalSettingsTests::TestTerminalArgsForBinding() void TerminalSettingsTests::TestTerminalArgsForBinding()
{ {
static constexpr std::string_view settingsJson{ R"( static constexpr std::string_view settingsJson{ R"(

View file

@ -1031,9 +1031,11 @@ namespace TerminalAppLocalTests
// The first action is going to always be a new-tab action // The first action is going to always be a new-tab action
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action()); VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
auto actionAndArgs = appArgs._startupActions.at(1); const auto actionAndArgs = appArgs._startupActions.at(1);
VERIFY_ARE_EQUAL(ShortcutAction::NextTab, actionAndArgs.Action()); VERIFY_ARE_EQUAL(ShortcutAction::NextTab, actionAndArgs.Action());
VERIFY_IS_NULL(actionAndArgs.Args()); VERIFY_IS_NOT_NULL(actionAndArgs.Args());
const auto myArgs = actionAndArgs.Args().as<NextTabArgs>();
VERIFY_ARE_EQUAL(TabSwitcherMode::Disabled, myArgs.SwitcherMode().Value());
} }
{ {
AppCommandlineArgs appArgs{}; AppCommandlineArgs appArgs{};
@ -1047,7 +1049,9 @@ namespace TerminalAppLocalTests
auto actionAndArgs = appArgs._startupActions.at(1); auto actionAndArgs = appArgs._startupActions.at(1);
VERIFY_ARE_EQUAL(ShortcutAction::PrevTab, actionAndArgs.Action()); VERIFY_ARE_EQUAL(ShortcutAction::PrevTab, actionAndArgs.Action());
VERIFY_IS_NULL(actionAndArgs.Args()); VERIFY_IS_NOT_NULL(actionAndArgs.Args());
const auto myArgs = actionAndArgs.Args().as<PrevTabArgs>();
VERIFY_ARE_EQUAL(TabSwitcherMode::Disabled, myArgs.SwitcherMode().Value());
} }
{ {
AppCommandlineArgs appArgs{}; AppCommandlineArgs appArgs{};

View file

@ -16,6 +16,31 @@ using namespace winrt::Microsoft::Terminal::Control;
namespace TerminalAppLocalTests namespace TerminalAppLocalTests
{ {
static constexpr std::wstring_view inboxSettings{ LR"({
"schemes": [{
"name": "Campbell",
"foreground": "#CCCCCC",
"background": "#0C0C0C",
"cursorColor": "#FFFFFF",
"black": "#0C0C0C",
"red": "#C50F1F",
"green": "#13A10E",
"yellow": "#C19C00",
"blue": "#0037DA",
"purple": "#881798",
"cyan": "#3A96DD",
"white": "#CCCCCC",
"brightBlack": "#767676",
"brightRed": "#E74856",
"brightGreen": "#16C60C",
"brightYellow": "#F9F1A5",
"brightBlue": "#3B78FF",
"brightPurple": "#B4009E",
"brightCyan": "#61D6D6",
"brightWhite": "#F2F2F2"
}]
})" };
// TODO:microsoft/terminal#3838: // TODO:microsoft/terminal#3838:
// Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for
// an updated TAEF that will let us install framework packages when the test // an updated TAEF that will let us install framework packages when the test
@ -107,11 +132,10 @@ namespace TerminalAppLocalTests
"iterateOn": "profiles", "iterateOn": "profiles",
"command": { "action": "splitPane", "profile": "${profile.name}" } "command": { "action": "splitPane", "profile": "${profile.name}" }
}, },
], ]
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" }; })" };
CascadiaSettings settings{ settingsJson, {} }; CascadiaSettings settings{ settingsJson, inboxSettings };
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
@ -231,11 +255,10 @@ namespace TerminalAppLocalTests
"iterateOn": "profiles", "iterateOn": "profiles",
"command": { "action": "splitPane", "profile": "${profile.name}" } "command": { "action": "splitPane", "profile": "${profile.name}" }
}, },
], ]
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" }; })" };
CascadiaSettings settings{ settingsJson, {} }; CascadiaSettings settings{ settingsJson, inboxSettings };
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
@ -357,11 +380,10 @@ namespace TerminalAppLocalTests
"iterateOn": "profiles", "iterateOn": "profiles",
"command": { "action": "splitPane", "profile": "${profile.name}" } "command": { "action": "splitPane", "profile": "${profile.name}" }
}, },
], ]
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" }; })" };
CascadiaSettings settings{ settingsJson, {} }; CascadiaSettings settings{ settingsJson, inboxSettings };
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
@ -495,11 +517,10 @@ namespace TerminalAppLocalTests
} }
] ]
}, },
], ]
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" }; })" };
CascadiaSettings settings{ settingsJson, {} }; CascadiaSettings settings{ settingsJson, inboxSettings };
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size()); VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
@ -590,11 +611,10 @@ namespace TerminalAppLocalTests
}, },
] ]
}, },
], ]
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" }; })" };
CascadiaSettings settings{ settingsJson, {} }; CascadiaSettings settings{ settingsJson, inboxSettings };
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size()); VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
@ -714,11 +734,10 @@ namespace TerminalAppLocalTests
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "down" } } { "command": { "action": "splitPane", "profile": "${profile.name}", "split": "down" } }
] ]
} }
], ]
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" }; })" };
CascadiaSettings settings{ settingsJson, {} }; CascadiaSettings settings{ settingsJson, inboxSettings };
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size()); VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
@ -851,11 +870,10 @@ namespace TerminalAppLocalTests
} }
] ]
} }
], ]
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" }; })" };
CascadiaSettings settings{ settingsJson, {} }; CascadiaSettings settings{ settingsJson, inboxSettings };
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size()); VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
@ -954,11 +972,10 @@ namespace TerminalAppLocalTests
} }
] ]
} }
], ]
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" }; })" };
CascadiaSettings settings{ settingsJson, {} }; CascadiaSettings settings{ settingsJson, inboxSettings };
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size()); VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
@ -1085,9 +1102,72 @@ namespace TerminalAppLocalTests
} }
], ],
"schemes": [ "schemes": [
{ "name": "scheme_0" }, {
{ "name": "scheme_1" }, "name": "Campbell",
{ "name": "scheme_2" }, "foreground": "#CCCCCC",
"background": "#0C0C0C",
"cursorColor": "#FFFFFF",
"black": "#0C0C0C",
"red": "#C50F1F",
"green": "#13A10E",
"yellow": "#C19C00",
"blue": "#0037DA",
"purple": "#881798",
"cyan": "#3A96DD",
"white": "#CCCCCC",
"brightBlack": "#767676",
"brightRed": "#E74856",
"brightGreen": "#16C60C",
"brightYellow": "#F9F1A5",
"brightBlue": "#3B78FF",
"brightPurple": "#B4009E",
"brightCyan": "#61D6D6",
"brightWhite": "#F2F2F2"
},
{
"name": "Campbell PowerShell",
"foreground": "#CCCCCC",
"background": "#012456",
"cursorColor": "#FFFFFF",
"black": "#0C0C0C",
"red": "#C50F1F",
"green": "#13A10E",
"yellow": "#C19C00",
"blue": "#0037DA",
"purple": "#881798",
"cyan": "#3A96DD",
"white": "#CCCCCC",
"brightBlack": "#767676",
"brightRed": "#E74856",
"brightGreen": "#16C60C",
"brightYellow": "#F9F1A5",
"brightBlue": "#3B78FF",
"brightPurple": "#B4009E",
"brightCyan": "#61D6D6",
"brightWhite": "#F2F2F2"
},
{
"name": "Vintage",
"foreground": "#C0C0C0",
"background": "#000000",
"cursorColor": "#FFFFFF",
"black": "#000000",
"red": "#800000",
"green": "#008000",
"yellow": "#808000",
"blue": "#000080",
"purple": "#800080",
"cyan": "#008080",
"white": "#C0C0C0",
"brightBlack": "#808080",
"brightRed": "#FF0000",
"brightGreen": "#00FF00",
"brightYellow": "#FFFF00",
"brightBlue": "#0000FF",
"brightPurple": "#FF00FF",
"brightCyan": "#00FFFF",
"brightWhite": "#FFFFFF"
}
], ],
"actions": [ "actions": [
{ {
@ -1100,10 +1180,6 @@ namespace TerminalAppLocalTests
CascadiaSettings settings{ settingsJson, {} }; CascadiaSettings settings{ settingsJson, {} };
// Since at least one profile does not reference a color scheme,
// we add a warning saying "the color scheme is unknown"
VERIFY_ARE_EQUAL(1u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size()); VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
auto nameMap{ settings.ActionMap().NameMap() }; auto nameMap{ settings.ActionMap().NameMap() };
@ -1130,8 +1206,6 @@ namespace TerminalAppLocalTests
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes()); auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView()); _logCommandNames(expandedCommands.GetView());
// This is the same warning as above
VERIFY_ARE_EQUAL(1u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, expandedCommands.Size()); VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
// Yes, this test is testing splitPane with profiles named after each // Yes, this test is testing splitPane with profiles named after each
@ -1139,7 +1213,7 @@ namespace TerminalAppLocalTests
// just easy tests to write. // just easy tests to write.
{ {
auto command = expandedCommands.Lookup(L"iterable command scheme_0"); auto command = expandedCommands.Lookup(L"iterable command Campbell");
VERIFY_IS_NOT_NULL(command); VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.ActionAndArgs(); auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs); VERIFY_IS_NOT_NULL(actionAndArgs);
@ -1153,11 +1227,11 @@ namespace TerminalAppLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"scheme_0", realArgs.TerminalArgs().Profile()); VERIFY_ARE_EQUAL(L"Campbell", realArgs.TerminalArgs().Profile());
} }
{ {
auto command = expandedCommands.Lookup(L"iterable command scheme_1"); auto command = expandedCommands.Lookup(L"iterable command Campbell PowerShell");
VERIFY_IS_NOT_NULL(command); VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.ActionAndArgs(); auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs); VERIFY_IS_NOT_NULL(actionAndArgs);
@ -1171,11 +1245,11 @@ namespace TerminalAppLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"scheme_1", realArgs.TerminalArgs().Profile()); VERIFY_ARE_EQUAL(L"Campbell PowerShell", realArgs.TerminalArgs().Profile());
} }
{ {
auto command = expandedCommands.Lookup(L"iterable command scheme_2"); auto command = expandedCommands.Lookup(L"iterable command Vintage");
VERIFY_IS_NOT_NULL(command); VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.ActionAndArgs(); auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs); VERIFY_IS_NOT_NULL(actionAndArgs);
@ -1189,7 +1263,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty()); VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty()); VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"scheme_2", realArgs.TerminalArgs().Profile()); VERIFY_ARE_EQUAL(L"Vintage", realArgs.TerminalArgs().Profile());
} }
} }

View file

@ -751,7 +751,7 @@ namespace TerminalAppLocalTests
}); });
VERIFY_SUCCEEDED(result); VERIFY_SUCCEEDED(result);
Log::Comment(L"Move focus. This will cause us to un-zoom."); Log::Comment(L"Move focus. We should still be zoomed.");
result = RunOnUIThread([&page]() { result = RunOnUIThread([&page]() {
// Set up action // Set up action
MoveFocusArgs args{ FocusDirection::Left }; MoveFocusArgs args{ FocusDirection::Left };
@ -761,7 +761,7 @@ namespace TerminalAppLocalTests
auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
VERIFY_IS_FALSE(firstTab->IsZoomed()); VERIFY_IS_TRUE(firstTab->IsZoomed());
}); });
VERIFY_SUCCEEDED(result); VERIFY_SUCCEEDED(result);
} }
@ -1357,7 +1357,8 @@ namespace TerminalAppLocalTests
Log::Comment(L"Color should be changed to the preview"); Log::Comment(L"Color should be changed to the preview");
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground()); VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(originalSettings, page->_originalSettings); // And we should have stored a function to revert the change.
VERIFY_ARE_EQUAL(1u, page->_restorePreviewFuncs.size());
}); });
TestOnUIThread([&page]() { TestOnUIThread([&page]() {
@ -1383,7 +1384,8 @@ namespace TerminalAppLocalTests
Log::Comment(L"Color should be changed"); Log::Comment(L"Color should be changed");
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground()); VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(nullptr, page->_originalSettings); // After preview there should be no more restore functions to execute.
VERIFY_ARE_EQUAL(0u, page->_restorePreviewFuncs.size());
}); });
} }
@ -1428,7 +1430,6 @@ namespace TerminalAppLocalTests
Log::Comment(L"Color should be changed to the preview"); Log::Comment(L"Color should be changed to the preview");
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground()); VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(originalSettings, page->_originalSettings);
}); });
TestOnUIThread([&page]() { TestOnUIThread([&page]() {
@ -1451,7 +1452,6 @@ namespace TerminalAppLocalTests
Log::Comment(L"Color should be the same as it originally was"); Log::Comment(L"Color should be the same as it originally was");
VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground()); VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(nullptr, page->_originalSettings);
}); });
} }
@ -1498,7 +1498,6 @@ namespace TerminalAppLocalTests
Log::Comment(L"Color should be changed to the preview"); Log::Comment(L"Color should be changed to the preview");
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground()); VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(originalSettings, page->_originalSettings);
}); });
TestOnUIThread([&page]() { TestOnUIThread([&page]() {
@ -1522,7 +1521,6 @@ namespace TerminalAppLocalTests
Log::Comment(L"Color should be changed to the preview"); Log::Comment(L"Color should be changed to the preview");
VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, controlSettings.DefaultBackground()); VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(originalSettings, page->_originalSettings);
}); });
TestOnUIThread([&page]() { TestOnUIThread([&page]() {
@ -1548,7 +1546,6 @@ namespace TerminalAppLocalTests
Log::Comment(L"Color should be changed"); Log::Comment(L"Color should be changed");
VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, controlSettings.DefaultBackground()); VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(nullptr, page->_originalSettings);
}); });
} }

View file

@ -0,0 +1,5 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "GetWindowLayoutArgs.h"
#include "GetWindowLayoutArgs.g.cpp"

View file

@ -0,0 +1,32 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Class Name:
- GetWindowLayoutArgs.h
Abstract:
- This is a helper class for getting the window layout from a peasant.
Depending on if we are running on the monarch or on a peasant we might need
to switch what thread we are executing on. This gives us the option of
either returning the json result synchronously, or as a promise.
--*/
#pragma once
#include "GetWindowLayoutArgs.g.h"
#include "../cascadia/inc/cppwinrt_utils.h"
namespace winrt::Microsoft::Terminal::Remoting::implementation
{
struct GetWindowLayoutArgs : public GetWindowLayoutArgsT<GetWindowLayoutArgs>
{
WINRT_PROPERTY(winrt::hstring, WindowLayoutJson, L"");
WINRT_PROPERTY(winrt::Windows::Foundation::IAsyncOperation<winrt::hstring>, WindowLayoutJsonAsync, nullptr)
};
}
namespace winrt::Microsoft::Terminal::Remoting::factory_implementation
{
BASIC_FACTORY(GetWindowLayoutArgs);
}

View file

@ -12,7 +12,6 @@
</PropertyGroup> </PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" /> <Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" /> <Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<!-- ========================= Headers ======================== --> <!-- ========================= Headers ======================== -->
<ItemGroup> <ItemGroup>
<ClInclude Include="Monarch.h"> <ClInclude Include="Monarch.h">
@ -36,6 +35,12 @@
<ClInclude Include="WindowActivatedArgs.h"> <ClInclude Include="WindowActivatedArgs.h">
<DependentUpon>Peasant.idl</DependentUpon> <DependentUpon>Peasant.idl</DependentUpon>
</ClInclude> </ClInclude>
<ClInclude Include="GetWindowLayoutArgs.h">
<DependentUpon>Peasant.idl</DependentUpon>
</ClInclude>
<ClInclude Include="QuitAllRequestedArgs.h">
<DependentUpon>Monarch.idl</DependentUpon>
</ClInclude>
<ClInclude Include="pch.h" /> <ClInclude Include="pch.h" />
<ClInclude Include="MonarchFactory.h" /> <ClInclude Include="MonarchFactory.h" />
<ClInclude Include="Peasant.h"> <ClInclude Include="Peasant.h">
@ -71,6 +76,12 @@
<ClCompile Include="WindowActivatedArgs.cpp"> <ClCompile Include="WindowActivatedArgs.cpp">
<DependentUpon>Peasant.idl</DependentUpon> <DependentUpon>Peasant.idl</DependentUpon>
</ClCompile> </ClCompile>
<ClCompile Include="GetWindowLayoutArgs.cpp">
<DependentUpon>Peasant.idl</DependentUpon>
</ClCompile>
<ClCompile Include="QuitAllRequestedArgs.cpp">
<DependentUpon>Monarch.idl</DependentUpon>
</ClCompile>
<ClCompile Include="pch.cpp"> <ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader> <PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile> </ClCompile>
@ -128,6 +139,5 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<!-- ========================= Globals ======================== --> <!-- ========================= Globals ======================== -->
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" /> <Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" /> <Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
</Project> </Project>

View file

@ -6,6 +6,7 @@
#include "Monarch.h" #include "Monarch.h"
#include "CommandlineArgs.h" #include "CommandlineArgs.h"
#include "FindTargetWindowArgs.h" #include "FindTargetWindowArgs.h"
#include "QuitAllRequestedArgs.h"
#include "ProposeCommandlineResult.h" #include "ProposeCommandlineResult.h"
#include "Monarch.g.cpp" #include "Monarch.g.cpp"
@ -135,12 +136,18 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// - <none> used // - <none> used
// Return Value: // Return Value:
// - <none> // - <none>
void Monarch::_handleQuitAll(const winrt::Windows::Foundation::IInspectable& /*sender*/, winrt::fire_and_forget Monarch::_handleQuitAll(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*args*/) const winrt::Windows::Foundation::IInspectable& /*args*/)
{ {
// Let the process hosting the monarch run any needed logic before // Let the process hosting the monarch run any needed logic before
// closing all windows. // closing all windows.
_QuitAllRequestedHandlers(*this, nullptr); auto args = winrt::make_self<implementation::QuitAllRequestedArgs>();
_QuitAllRequestedHandlers(*this, *args);
if (const auto action = args->BeforeQuitAllAction())
{
co_await action;
}
_quitting.store(true); _quitting.store(true);
// Tell all peasants to exit. // Tell all peasants to exit.
@ -994,4 +1001,28 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
_forEachPeasant(func, onError); _forEachPeasant(func, onError);
} }
// Method Description:
// - Ask all peasants to return their window layout as json
// Arguments:
// - <none>
// Return Value:
// - The collection of window layouts from each peasant.
Windows::Foundation::Collections::IVector<winrt::hstring> Monarch::GetAllWindowLayouts()
{
std::vector<winrt::hstring> vec;
auto callback = [&](const auto& /*id*/, const auto& p) {
vec.emplace_back(p.GetWindowLayout());
};
auto onError = [](auto&& id) {
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_GetAllWindowLayouts_Failed",
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not get a window layout from"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
};
_forEachPeasant(callback, onError);
return winrt::single_threaded_vector(std::move(vec));
}
} }

View file

@ -59,13 +59,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void SummonAllWindows(); void SummonAllWindows();
bool DoesQuakeWindowExist(); bool DoesQuakeWindowExist();
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos(); Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
Windows::Foundation::Collections::IVector<winrt::hstring> GetAllWindowLayouts();
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs);
private: private:
uint64_t _ourPID; uint64_t _ourPID;
@ -103,8 +104,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void _renameRequested(const winrt::Windows::Foundation::IInspectable& sender, void _renameRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args); const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
void _handleQuitAll(const winrt::Windows::Foundation::IInspectable& sender, winrt::fire_and_forget _handleQuitAll(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args); const winrt::Windows::Foundation::IInspectable& args);
// Method Description: // Method Description:
// - Helper for doing something on each and every peasant. // - Helper for doing something on each and every peasant.
@ -177,6 +178,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
} }
} }
_clearOldMruEntries(peasantsToErase); _clearOldMruEntries(peasantsToErase);
// A peasant died, let the app host know that the number of
// windows has changed.
_WindowClosedHandlers(nullptr, nullptr);
} }
} }

View file

@ -31,6 +31,12 @@ namespace Microsoft.Terminal.Remoting
Windows.Foundation.IReference<UInt64> WindowID; Windows.Foundation.IReference<UInt64> WindowID;
} }
[default_interface] runtimeclass QuitAllRequestedArgs
{
QuitAllRequestedArgs();
Windows.Foundation.IAsyncAction BeforeQuitAllAction;
}
struct PeasantInfo struct PeasantInfo
{ {
UInt64 Id; UInt64 Id;
@ -52,12 +58,13 @@ namespace Microsoft.Terminal.Remoting
void SummonAllWindows(); void SummonAllWindows();
Boolean DoesQuakeWindowExist(); Boolean DoesQuakeWindowExist();
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos { get; }; Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos { get; };
Windows.Foundation.Collections.IVector<String> GetAllWindowLayouts();
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested; event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested; event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested; event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated; event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed; event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested; event Windows.Foundation.TypedEventHandler<Object, QuitAllRequestedArgs> QuitAllRequested;
}; };
} }

View file

@ -5,6 +5,7 @@
#include "Peasant.h" #include "Peasant.h"
#include "CommandlineArgs.h" #include "CommandlineArgs.h"
#include "SummonWindowBehavior.h" #include "SummonWindowBehavior.h"
#include "GetWindowLayoutArgs.h"
#include "Peasant.g.cpp" #include "Peasant.g.cpp"
#include "../../types/inc/utils.hpp" #include "../../types/inc/utils.hpp"
@ -289,4 +290,24 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE)); TraceLoggingKeyword(TIL_KEYWORD_TRACE));
} }
// Method Description:
// - Request and return the window layout from the current TerminalPage
// Arguments:
// - <none>
// Return Value:
// - the window layout as a json string
hstring Peasant::GetWindowLayout()
{
auto args = winrt::make_self<implementation::GetWindowLayoutArgs>();
_GetWindowLayoutRequestedHandlers(nullptr, *args);
if (const auto op = args->WindowLayoutJsonAsync())
{
// This will fail if called on the UI thread, so the monarch should
// never set WindowLayoutJsonAsync.
auto str = op.get();
return str;
}
return args->WindowLayoutJson();
}
} }

View file

@ -36,6 +36,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs(); winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();
winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs();
winrt::hstring GetWindowLayout();
WINRT_PROPERTY(winrt::hstring, WindowName); WINRT_PROPERTY(winrt::hstring, WindowName);
WINRT_PROPERTY(winrt::hstring, ActiveTabTitle); WINRT_PROPERTY(winrt::hstring, ActiveTabTitle);
@ -49,6 +52,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs);
private: private:
Peasant(const uint64_t testPID); Peasant(const uint64_t testPID);

View file

@ -30,6 +30,11 @@ namespace Microsoft.Terminal.Remoting
Windows.Foundation.DateTime ActivatedTime { get; }; Windows.Foundation.DateTime ActivatedTime { get; };
}; };
[default_interface] runtimeclass GetWindowLayoutArgs {
GetWindowLayoutArgs();
String WindowLayoutJson;
Windows.Foundation.IAsyncOperation<String> WindowLayoutJsonAsync;
}
enum MonitorBehavior enum MonitorBehavior
{ {
@ -69,6 +74,7 @@ namespace Microsoft.Terminal.Remoting
void RequestHideNotificationIcon(); void RequestHideNotificationIcon();
void RequestQuitAll(); void RequestQuitAll();
void Quit(); void Quit();
String GetWindowLayout();
event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated; event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested; event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
@ -78,6 +84,7 @@ namespace Microsoft.Terminal.Remoting
event Windows.Foundation.TypedEventHandler<Object, SummonWindowBehavior> SummonRequested; event Windows.Foundation.TypedEventHandler<Object, SummonWindowBehavior> SummonRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested; event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested; event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, GetWindowLayoutArgs> GetWindowLayoutRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested; event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested; event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
}; };

View file

@ -0,0 +1,5 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "QuitAllRequestedArgs.h"
#include "QuitAllRequestedArgs.g.cpp"

View file

@ -0,0 +1,30 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Class Name:
- QuitAllRequestedArgs.h
Abstract:
- This is a helper class for allowing the monarch to run code before telling all
peasants to quit. This way the monarch can raise an event and get back a future
to wait for before continuing.
--*/
#pragma once
#include "QuitAllRequestedArgs.g.h"
#include "../cascadia/inc/cppwinrt_utils.h"
namespace winrt::Microsoft::Terminal::Remoting::implementation
{
struct QuitAllRequestedArgs : public QuitAllRequestedArgsT<QuitAllRequestedArgs>
{
WINRT_PROPERTY(winrt::Windows::Foundation::IAsyncAction, BeforeQuitAllAction, nullptr)
};
}
namespace winrt::Microsoft::Terminal::Remoting::factory_implementation
{
BASIC_FACTORY(QuitAllRequestedArgs);
}

View file

@ -271,7 +271,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
_monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested }); _monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested });
_monarch.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequestedHandlers(*this, nullptr); }); _monarch.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequestedHandlers(*this, nullptr); });
_monarch.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequestedHandlers(*this, nullptr); }); _monarch.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequestedHandlers(*this, nullptr); });
_monarch.QuitAllRequested([this](auto&&, auto&&) { _QuitAllRequestedHandlers(*this, nullptr); }); _monarch.QuitAllRequested({ get_weak(), &WindowManager::_QuitAllRequestedHandlers });
_BecameMonarchHandlers(*this, nullptr); _BecameMonarchHandlers(*this, nullptr);
} }
@ -318,6 +318,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
} }
} }
_peasant.GetWindowLayoutRequested({ get_weak(), &WindowManager::_GetWindowLayoutRequestedHandlers });
TraceLoggingWrite(g_hRemotingProvider, TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_CreateOurPeasant", "WindowManager_CreateOurPeasant",
TraceLoggingUInt64(_peasant.GetID(), "peasantID", "The ID of our new peasant"), TraceLoggingUInt64(_peasant.GetID(), "peasantID", "The ID of our new peasant"),
@ -610,4 +612,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
{ {
winrt::get_self<implementation::Peasant>(_peasant)->ActiveTabTitle(title); winrt::get_self<implementation::Peasant>(_peasant)->ActiveTabTitle(title);
} }
Windows::Foundation::Collections::IVector<winrt::hstring> WindowManager::GetAllWindowLayouts()
{
if (_monarch)
{
try
{
return _monarch.GetAllWindowLayouts();
}
CATCH_LOG()
}
return nullptr;
}
} }

View file

@ -50,6 +50,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
winrt::fire_and_forget RequestQuitAll(); winrt::fire_and_forget RequestQuitAll();
bool DoesQuakeWindowExist(); bool DoesQuakeWindowExist();
void UpdateActiveTabTitle(winrt::hstring title); void UpdateActiveTabTitle(winrt::hstring title);
Windows::Foundation::Collections::IVector<winrt::hstring> GetAllWindowLayouts();
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
@ -57,7 +58,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs);
TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs);
private: private:
bool _shouldCreateWindow{ false }; bool _shouldCreateWindow{ false };

View file

@ -16,6 +16,8 @@ namespace Microsoft.Terminal.Remoting
void SummonAllWindows(); void SummonAllWindows();
void RequestShowNotificationIcon(); void RequestShowNotificationIcon();
void RequestHideNotificationIcon(); void RequestHideNotificationIcon();
Windows.Foundation.Collections.IVector<String> GetAllWindowLayouts();
UInt64 GetNumberOfPeasants(); UInt64 GetNumberOfPeasants();
void RequestQuitAll(); void RequestQuitAll();
void UpdateActiveTabTitle(String title); void UpdateActiveTabTitle(String title);
@ -25,8 +27,9 @@ namespace Microsoft.Terminal.Remoting
event Windows.Foundation.TypedEventHandler<Object, Object> BecameMonarch; event Windows.Foundation.TypedEventHandler<Object, Object> BecameMonarch;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated; event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed; event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
event Windows.Foundation.TypedEventHandler<Object, QuitAllRequestedArgs> QuitAllRequested;
event Windows.Foundation.TypedEventHandler<Object, GetWindowLayoutArgs> GetWindowLayoutRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested; event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested; event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
}; };
} }

View file

@ -55,9 +55,7 @@ HRESULT OpenTerminalHere::Invoke(IShellItemArray* psiItemArray,
STARTUPINFOEX siEx{ 0 }; STARTUPINFOEX siEx{ 0 };
siEx.StartupInfo.cb = sizeof(STARTUPINFOEX); siEx.StartupInfo.cb = sizeof(STARTUPINFOEX);
// Append a "\." to the given path, so that this will work in "C:\" auto cmdline{ wil::str_printf<std::wstring>(LR"-("%s" -d %s)-", GetWtExePath().c_str(), QuoteAndEscapeCommandlineArg(pszName.get()).c_str()) };
auto path{ wil::str_printf<std::wstring>(LR"-(%s\.)-", pszName.get()) };
auto cmdline{ wil::str_printf<std::wstring>(LR"-("%s" -d "%s")-", GetWtExePath().c_str(), path.c_str()) };
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW( RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(
nullptr, // lpApplicationName nullptr, // lpApplicationName
cmdline.data(), cmdline.data(),
@ -66,7 +64,7 @@ HRESULT OpenTerminalHere::Invoke(IShellItemArray* psiItemArray,
false, // bInheritHandles false, // bInheritHandles
EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
nullptr, // lpEnvironment nullptr, // lpEnvironment
path.data(), pszName.get(),
&siEx.StartupInfo, // lpStartupInfo &siEx.StartupInfo, // lpStartupInfo
&_piClient // lpProcessInformation &_piClient // lpProcessInformation
)); ));

View file

@ -67,41 +67,17 @@ namespace winrt::TerminalApp::implementation
// - <none> // - <none>
void TerminalPage::_EndPreviewColorScheme() void TerminalPage::_EndPreviewColorScheme()
{ {
// Get the focused control for (const auto& f : _restorePreviewFuncs)
if (const auto& activeControl{ _GetActiveControl() })
{ {
// Get the runtime settings of the focused control f();
const auto& controlSettings{ activeControl.Settings().as<TerminalSettings>() };
// Get the control's root settings, the ones that we actually
// assigned to it.
auto parentSettings{ controlSettings.GetParent() };
while (parentSettings.GetParent() != nullptr)
{
parentSettings = parentSettings.GetParent();
}
// If the root settings are the same as the ones we stashed,
// then reset the parent of the runtime settings to the stashed
// settings. This condition might be false if the settings
// hot-reloaded while the palette was open. In that case, we
// don't want to reset the settings to what they were _before_
// the hot-reload.
if (_originalSettings == parentSettings)
{
// Set the original settings as the parent of the control's settings
activeControl.Settings().as<TerminalSettings>().SetParent(_originalSettings);
}
activeControl.UpdateSettings();
} }
_originalSettings = nullptr; _restorePreviewFuncs.clear();
} }
// Method Description: // Method Description:
// - Preview handler for the SetColorScheme action. // - Preview handler for the SetColorScheme action.
// - This method will stash the settings of the current control in // - This method will stash functions to reset the settings of the selected controls in
// _originalSettings. Then it will create a new TerminalSettings object // _restorePreviewFuncs. Then it will create a new TerminalSettings object
// with only the properties from the ColorScheme set. It'll _insert_ a // with only the properties from the ColorScheme set. It'll _insert_ a
// TerminalSettings between the control's root settings (built from // TerminalSettings between the control's root settings (built from
// CascadiaSettings) and the control's runtime settings. That'll cause the // CascadiaSettings) and the control's runtime settings. That'll cause the
@ -112,33 +88,63 @@ namespace winrt::TerminalApp::implementation
// - <none> // - <none>
void TerminalPage::_PreviewColorScheme(const Settings::Model::SetColorSchemeArgs& args) void TerminalPage::_PreviewColorScheme(const Settings::Model::SetColorSchemeArgs& args)
{ {
// Get the focused control if (const auto& scheme{ _settings.GlobalSettings().ColorSchemes().TryLookup(args.SchemeName()) })
if (const auto& activeControl{ _GetActiveControl() })
{ {
if (const auto& scheme{ _settings.GlobalSettings().ColorSchemes().TryLookup(args.SchemeName()) }) // Clear the saved preview funcs because we don't need to add a restore each time
{ // the preview color changes, we only need to be able to restore the last one.
_restorePreviewFuncs.clear();
_ApplyToActiveControls([&](const auto& control) {
// Get the settings of the focused control and stash them // Get the settings of the focused control and stash them
const auto& controlSettings = activeControl.Settings().as<TerminalSettings>(); const auto& controlSettings = control.Settings().as<TerminalSettings>();
// Make sure to recurse up to the root - if you're doing // Make sure to recurse up to the root - if you're doing
// this while you're currently previewing a SetColorScheme // this while you're currently previewing a SetColorScheme
// action, then the parent of the control's settings is _the // action, then the parent of the control's settings is _the
// last preview TerminalSettings we inserted! We don't want // last preview TerminalSettings we inserted! We don't want
// to save that one! // to save that one!
_originalSettings = controlSettings.GetParent(); auto originalSettings = controlSettings.GetParent();
while (_originalSettings.GetParent() != nullptr) while (originalSettings.GetParent() != nullptr)
{ {
_originalSettings = _originalSettings.GetParent(); originalSettings = originalSettings.GetParent();
} }
// Create a new child for those settings // Create a new child for those settings
TerminalSettingsCreateResult fake{ _originalSettings }; TerminalSettingsCreateResult fake{ originalSettings };
const auto& childStruct = TerminalSettings::CreateWithParent(fake); const auto& childStruct = TerminalSettings::CreateWithParent(fake);
// Modify the child to have the applied color scheme // Modify the child to have the applied color scheme
childStruct.DefaultSettings().ApplyColorScheme(scheme); childStruct.DefaultSettings().ApplyColorScheme(scheme);
// Insert that new child as the parent of the control's settings // Insert that new child as the parent of the control's settings
controlSettings.SetParent(childStruct.DefaultSettings()); controlSettings.SetParent(childStruct.DefaultSettings());
activeControl.UpdateSettings(); control.UpdateSettings();
}
// Take a copy of the inputs, since they are pointers anyways.
_restorePreviewFuncs.emplace_back([=]() {
// Get the runtime settings of the focused control
const auto& controlSettings{ control.Settings().as<TerminalSettings>() };
// Get the control's root settings, the ones that we actually
// assigned to it.
auto parentSettings{ controlSettings.GetParent() };
while (parentSettings.GetParent() != nullptr)
{
parentSettings = parentSettings.GetParent();
}
// If the root settings are the same as the ones we stashed,
// then reset the parent of the runtime settings to the stashed
// settings. This condition might be false if the settings
// hot-reloaded while the palette was open. In that case, we
// don't want to reset the settings to what they were _before_
// the hot-reload.
if (originalSettings == parentSettings)
{
// Set the original settings as the parent of the control's settings
control.Settings().as<TerminalSettings>().SetParent(originalSettings);
}
control.UpdateSettings();
});
});
} }
} }

View file

@ -78,7 +78,7 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_HandleCloseWindow(const IInspectable& /*sender*/, void TerminalPage::_HandleCloseWindow(const IInspectable& /*sender*/,
const ActionEventArgs& args) const ActionEventArgs& args)
{ {
CloseWindow(false); _CloseRequestedHandlers(nullptr, nullptr);
args.Handled(true); args.Handled(true);
} }
@ -377,11 +377,10 @@ namespace winrt::TerminalApp::implementation
{ {
if (const auto& realArgs = args.ActionArgs().try_as<AdjustFontSizeArgs>()) if (const auto& realArgs = args.ActionArgs().try_as<AdjustFontSizeArgs>())
{ {
if (const auto& termControl{ _GetActiveControl() }) const auto res = _ApplyToActiveControls([&](auto& control) {
{ control.AdjustFontSize(realArgs.Delta());
termControl.AdjustFontSize(realArgs.Delta()); });
args.Handled(true); args.Handled(res);
}
} }
} }
@ -395,21 +394,19 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_HandleResetFontSize(const IInspectable& /*sender*/, void TerminalPage::_HandleResetFontSize(const IInspectable& /*sender*/,
const ActionEventArgs& args) const ActionEventArgs& args)
{ {
if (const auto& termControl{ _GetActiveControl() }) const auto res = _ApplyToActiveControls([](auto& control) {
{ control.ResetFontSize();
termControl.ResetFontSize(); });
args.Handled(true); args.Handled(res);
}
} }
void TerminalPage::_HandleToggleShaderEffects(const IInspectable& /*sender*/, void TerminalPage::_HandleToggleShaderEffects(const IInspectable& /*sender*/,
const ActionEventArgs& args) const ActionEventArgs& args)
{ {
if (const auto& termControl{ _GetActiveControl() }) const auto res = _ApplyToActiveControls([](auto& control) {
{ control.ToggleShaderEffects();
termControl.ToggleShaderEffects(); });
args.Handled(true); args.Handled(res);
}
} }
void TerminalPage::_HandleToggleFocusMode(const IInspectable& /*sender*/, void TerminalPage::_HandleToggleFocusMode(const IInspectable& /*sender*/,
@ -452,37 +449,33 @@ namespace winrt::TerminalApp::implementation
args.Handled(false); args.Handled(false);
if (const auto& realArgs = args.ActionArgs().try_as<SetColorSchemeArgs>()) if (const auto& realArgs = args.ActionArgs().try_as<SetColorSchemeArgs>())
{ {
if (const auto activeTab{ _GetFocusedTabImpl() }) if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName()))
{ {
if (auto activeControl = activeTab->GetActiveTerminalControl()) const auto res = _ApplyToActiveControls([&](auto& control) {
{ // Start by getting the current settings of the control
if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName())) auto controlSettings = control.Settings().as<TerminalSettings>();
auto parentSettings = controlSettings;
// Those are the _runtime_ settings however. What we
// need to do is:
//
// 1. Blow away any colors set in the runtime settings.
// 2. Apply the color scheme to the parent settings.
//
// 1 is important to make sure that the effects of
// something like `colortool` are cleared when setting
// the scheme.
if (controlSettings.GetParent() != nullptr)
{ {
// Start by getting the current settings of the control parentSettings = controlSettings.GetParent();
auto controlSettings = activeControl.Settings().as<TerminalSettings>();
auto parentSettings = controlSettings;
// Those are the _runtime_ settings however. What we
// need to do is:
//
// 1. Blow away any colors set in the runtime settings.
// 2. Apply the color scheme to the parent settings.
//
// 1 is important to make sure that the effects of
// something like `colortool` are cleared when setting
// the scheme.
if (controlSettings.GetParent() != nullptr)
{
parentSettings = controlSettings.GetParent();
}
// ApplyColorScheme(nullptr) will clear the old color scheme.
controlSettings.ApplyColorScheme(nullptr);
parentSettings.ApplyColorScheme(scheme);
activeControl.UpdateSettings();
args.Handled(true);
} }
}
// ApplyColorScheme(nullptr) will clear the old color scheme.
controlSettings.ApplyColorScheme(nullptr);
parentSettings.ApplyColorScheme(scheme);
control.UpdateSettings();
});
args.Handled(res);
} }
} }
} }
@ -896,11 +889,10 @@ namespace winrt::TerminalApp::implementation
{ {
if (const auto& realArgs = args.ActionArgs().try_as<ClearBufferArgs>()) if (const auto& realArgs = args.ActionArgs().try_as<ClearBufferArgs>())
{ {
if (const auto termControl{ _GetActiveControl() }) const auto res = _ApplyToActiveControls([&](auto& control) {
{ control.ClearBuffer(realArgs.Clear());
termControl.ClearBuffer(realArgs.Clear()); });
args.Handled(true); args.Handled(res);
}
} }
} }
} }

View file

@ -187,6 +187,10 @@ void AppCommandlineArgs::_buildParser()
_windowTarget, _windowTarget,
RS_A(L"CmdWindowTargetArgDesc")); RS_A(L"CmdWindowTargetArgDesc"));
_app.add_option("-s,--saved",
_loadPersistedLayoutIdx,
RS_A(L"CmdSavedLayoutArgDesc"));
// Subcommands // Subcommands
_buildNewTabParser(); _buildNewTabParser();
_buildSplitPaneParser(); _buildSplitPaneParser();
@ -700,6 +704,7 @@ void AppCommandlineArgs::_resetStateToDefault()
_swapPaneDirection = FocusDirection::None; _swapPaneDirection = FocusDirection::None;
_focusPaneTarget = -1; _focusPaneTarget = -1;
_loadPersistedLayoutIdx = -1;
// DON'T clear _launchMode here! This will get called once for every // DON'T clear _launchMode here! This will get called once for every
// subcommand, so we don't want `wt -F new-tab ; split-pane` clearing out // subcommand, so we don't want `wt -F new-tab ; split-pane` clearing out
@ -915,6 +920,12 @@ void AppCommandlineArgs::ValidateStartupCommands()
} }
} }
} }
std::optional<uint32_t> AppCommandlineArgs::GetPersistedLayoutIdx() const noexcept
{
return _loadPersistedLayoutIdx >= 0 ?
std::optional{ static_cast<uint32_t>(_loadPersistedLayoutIdx) } :
std::nullopt;
}
std::optional<winrt::Microsoft::Terminal::Settings::Model::LaunchMode> AppCommandlineArgs::GetLaunchMode() const noexcept std::optional<winrt::Microsoft::Terminal::Settings::Model::LaunchMode> AppCommandlineArgs::GetLaunchMode() const noexcept
{ {

View file

@ -39,6 +39,7 @@ public:
const std::string& GetExitMessage(); const std::string& GetExitMessage();
bool ShouldExitEarly() const noexcept; bool ShouldExitEarly() const noexcept;
std::optional<uint32_t> GetPersistedLayoutIdx() const noexcept;
std::optional<winrt::Microsoft::Terminal::Settings::Model::LaunchMode> GetLaunchMode() const noexcept; std::optional<winrt::Microsoft::Terminal::Settings::Model::LaunchMode> GetLaunchMode() const noexcept;
int ParseArgs(const winrt::Microsoft::Terminal::Settings::Model::ExecuteCommandlineArgs& args); int ParseArgs(const winrt::Microsoft::Terminal::Settings::Model::ExecuteCommandlineArgs& args);
@ -123,6 +124,7 @@ private:
std::string _exitMessage; std::string _exitMessage;
bool _shouldExitEarly{ false }; bool _shouldExitEarly{ false };
int _loadPersistedLayoutIdx{};
std::string _windowTarget{}; std::string _windowTarget{};
// Are you adding more args or attributes here? If they are not reset in _resetStateToDefault, make sure to reset them in FullResetState // Are you adding more args or attributes here? If they are not reset in _resetStateToDefault, make sure to reset them in FullResetState

View file

@ -375,6 +375,8 @@ namespace winrt::TerminalApp::implementation
co_return ContentDialogResult::None; co_return ContentDialogResult::None;
} }
_dialog = dialog;
// IMPORTANT: This is necessary as documented in the ContentDialog MSDN docs. // IMPORTANT: This is necessary as documented in the ContentDialog MSDN docs.
// Since we're hosting the dialog in a Xaml island, we need to connect it to the // Since we're hosting the dialog in a Xaml island, we need to connect it to the
// xaml tree somehow. // xaml tree somehow.
@ -412,6 +414,16 @@ namespace winrt::TerminalApp::implementation
// be released so another can be shown // be released so another can be shown
} }
// Method Description:
// - Dismiss the (only) visible ContentDialog
void AppLogic::DismissDialog()
{
if (auto localDialog = std::exchange(_dialog, nullptr))
{
localDialog.Hide();
}
}
// Method Description: // Method Description:
// - Displays a dialog for errors found while loading or validating the // - Displays a dialog for errors found while loading or validating the
// settings. Uses the resources under the provided title and content keys // settings. Uses the resources under the provided title and content keys
@ -600,13 +612,11 @@ namespace winrt::TerminalApp::implementation
winrt::Windows::Foundation::Size proposedSize{}; winrt::Windows::Foundation::Size proposedSize{};
const float scale = static_cast<float>(dpi) / static_cast<float>(USER_DEFAULT_SCREEN_DPI); const float scale = static_cast<float>(dpi) / static_cast<float>(USER_DEFAULT_SCREEN_DPI);
if (_root->ShouldUsePersistedLayout(_settings)) if (const auto layout = _root->LoadPersistedLayout(_settings))
{ {
const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); if (layout.InitialSize())
if (layouts && layouts.Size() > 0 && layouts.GetAt(0).InitialSize())
{ {
proposedSize = layouts.GetAt(0).InitialSize().Value(); proposedSize = layout.InitialSize().Value();
// The size is saved as a non-scaled real pixel size, // The size is saved as a non-scaled real pixel size,
// so we need to scale it appropriately. // so we need to scale it appropriately.
proposedSize.Height = proposedSize.Height * scale; proposedSize.Height = proposedSize.Height * scale;
@ -704,13 +714,11 @@ namespace winrt::TerminalApp::implementation
auto initialPosition{ _settings.GlobalSettings().InitialPosition() }; auto initialPosition{ _settings.GlobalSettings().InitialPosition() };
if (_root->ShouldUsePersistedLayout(_settings)) if (const auto layout = _root->LoadPersistedLayout(_settings))
{ {
const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); if (layout.InitialPosition())
if (layouts && layouts.Size() > 0 && layouts.GetAt(0).InitialPosition())
{ {
initialPosition = layouts.GetAt(0).InitialPosition().Value(); initialPosition = layout.InitialPosition().Value();
} }
} }
@ -1151,10 +1159,22 @@ namespace winrt::TerminalApp::implementation
// - <none> // - <none>
// Return Value: // Return Value:
// - <none> // - <none>
void AppLogic::WindowCloseButtonClicked() void AppLogic::CloseWindow(LaunchPosition pos)
{ {
if (_root) if (_root)
{ {
// If persisted layout is enabled and we are the last window closing
// we should save our state.
if (_root->ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1)
{
if (const auto layout = _root->GetWindowLayout())
{
layout.InitialPosition(pos);
const auto state = ApplicationState::SharedInstance();
state.PersistedWindowLayouts(winrt::single_threaded_vector<WindowLayout>({ layout }));
}
}
_root->CloseWindow(false); _root->CloseWindow(false);
} }
} }
@ -1168,6 +1188,16 @@ namespace winrt::TerminalApp::implementation
return {}; return {};
} }
bool AppLogic::HasCommandlineArguments() const noexcept
{
return _hasCommandLineArguments;
}
bool AppLogic::HasSettingsStartupActions() const noexcept
{
return _hasSettingsStartupActions;
}
// Method Description: // Method Description:
// - Sets the initial commandline to process on startup, and attempts to // - Sets the initial commandline to process on startup, and attempts to
// parse it. Commands will be parsed into a list of ShortcutActions that // parse it. Commands will be parsed into a list of ShortcutActions that
@ -1191,6 +1221,10 @@ namespace winrt::TerminalApp::implementation
// then it contains only the executable name and no other arguments. // then it contains only the executable name and no other arguments.
_hasCommandLineArguments = args.size() > 1; _hasCommandLineArguments = args.size() > 1;
_appArgs.ValidateStartupCommands(); _appArgs.ValidateStartupCommands();
if (const auto idx = _appArgs.GetPersistedLayoutIdx())
{
_root->SetPersistedLayoutIdx(idx.value());
}
_root->SetStartupActions(_appArgs.GetStartupActions()); _root->SetStartupActions(_appArgs.GetStartupActions());
// Check if we were started as a COM server for inbound connections of console sessions // Check if we were started as a COM server for inbound connections of console sessions
@ -1428,6 +1462,40 @@ namespace winrt::TerminalApp::implementation
return _settings.GlobalSettings().ActionMap().GlobalHotkeys(); return _settings.GlobalSettings().ActionMap().GlobalHotkeys();
} }
bool AppLogic::ShouldUsePersistedLayout()
{
return _root != nullptr ? _root->ShouldUsePersistedLayout(_settings) : false;
}
void AppLogic::SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector<hstring>& layouts)
{
std::vector<WindowLayout> converted;
converted.reserve(layouts.Size());
for (const auto& json : layouts)
{
if (json != L"")
{
converted.emplace_back(WindowLayout::FromJson(json));
}
}
ApplicationState::SharedInstance().PersistedWindowLayouts(winrt::single_threaded_vector(std::move(converted)));
}
hstring AppLogic::GetWindowLayoutJson(LaunchPosition position)
{
if (_root != nullptr)
{
if (const auto layout = _root->GetWindowLayout())
{
layout.InitialPosition(position);
return WindowLayout::ToJson(layout);
}
}
return L"";
}
void AppLogic::IdentifyWindow() void AppLogic::IdentifyWindow()
{ {
if (_root) if (_root)
@ -1459,8 +1527,17 @@ namespace winrt::TerminalApp::implementation
} }
} }
void AppLogic::SetPersistedLayoutIdx(const uint32_t idx)
{
if (_root)
{
_root->SetPersistedLayoutIdx(idx);
}
}
void AppLogic::SetNumberOfOpenWindows(const uint64_t num) void AppLogic::SetNumberOfOpenWindows(const uint64_t num)
{ {
_numOpenWindows = num;
if (_root) if (_root)
{ {
_root->SetNumberOfOpenWindows(num); _root->SetNumberOfOpenWindows(num);

View file

@ -55,6 +55,8 @@ namespace winrt::TerminalApp::implementation
void Quit(); void Quit();
bool HasCommandlineArguments() const noexcept;
bool HasSettingsStartupActions() const noexcept;
int32_t SetStartupCommandline(array_view<const winrt::hstring> actions); int32_t SetStartupCommandline(array_view<const winrt::hstring> actions);
int32_t ExecuteCommandline(array_view<const winrt::hstring> actions, const winrt::hstring& cwd); int32_t ExecuteCommandline(array_view<const winrt::hstring> actions, const winrt::hstring& cwd);
TerminalApp::FindTargetWindowResult FindTargetWindow(array_view<const winrt::hstring> actions); TerminalApp::FindTargetWindowResult FindTargetWindow(array_view<const winrt::hstring> actions);
@ -65,12 +67,16 @@ namespace winrt::TerminalApp::implementation
bool Fullscreen() const; bool Fullscreen() const;
bool AlwaysOnTop() const; bool AlwaysOnTop() const;
bool ShouldUsePersistedLayout();
hstring GetWindowLayoutJson(Microsoft::Terminal::Settings::Model::LaunchPosition position);
void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector<hstring>& layouts);
void IdentifyWindow(); void IdentifyWindow();
void RenameFailed(); void RenameFailed();
winrt::hstring WindowName(); winrt::hstring WindowName();
void WindowName(const winrt::hstring& name); void WindowName(const winrt::hstring& name);
uint64_t WindowId(); uint64_t WindowId();
void WindowId(const uint64_t& id); void WindowId(const uint64_t& id);
void SetPersistedLayoutIdx(const uint32_t idx);
void SetNumberOfOpenWindows(const uint64_t num); void SetNumberOfOpenWindows(const uint64_t num);
bool IsQuakeWindow() const noexcept; bool IsQuakeWindow() const noexcept;
@ -91,7 +97,7 @@ namespace winrt::TerminalApp::implementation
void TitlebarClicked(); void TitlebarClicked();
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
void WindowCloseButtonClicked(); void CloseWindow(Microsoft::Terminal::Settings::Model::LaunchPosition position);
winrt::TerminalApp::TaskbarState TaskbarState(); winrt::TerminalApp::TaskbarState TaskbarState();
@ -100,6 +106,7 @@ namespace winrt::TerminalApp::implementation
bool GetShowTitleInTitlebar(); bool GetShowTitleInTitlebar();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog); winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);
void DismissDialog();
Windows::Foundation::Collections::IMapView<Microsoft::Terminal::Control::KeyChord, Microsoft::Terminal::Settings::Model::Command> GlobalHotkeys(); Windows::Foundation::Collections::IMapView<Microsoft::Terminal::Control::KeyChord, Microsoft::Terminal::Settings::Model::Command> GlobalHotkeys();
@ -123,7 +130,10 @@ namespace winrt::TerminalApp::implementation
HRESULT _settingsLoadedResult = S_OK; HRESULT _settingsLoadedResult = S_OK;
bool _loadedInitialSettings = false; bool _loadedInitialSettings = false;
uint64_t _numOpenWindows{ 0 };
std::shared_mutex _dialogLock; std::shared_mutex _dialogLock;
winrt::Windows::UI::Xaml::Controls::ContentDialog _dialog;
::TerminalApp::AppCommandlineArgs _appArgs; ::TerminalApp::AppCommandlineArgs _appArgs;
::TerminalApp::AppCommandlineArgs _settingsAppArgs; ::TerminalApp::AppCommandlineArgs _settingsAppArgs;
@ -175,6 +185,7 @@ namespace winrt::TerminalApp::implementation
FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested); FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested);
FORWARDED_TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IsQuakeWindowChanged); FORWARDED_TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IsQuakeWindowChanged);
FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested); FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested);
FORWARDED_TYPED_EVENT(CloseRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, CloseRequested);
FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu); FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu);
FORWARDED_TYPED_EVENT(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested); FORWARDED_TYPED_EVENT(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested);

View file

@ -34,6 +34,8 @@ namespace TerminalApp
void RunAsUwp(); void RunAsUwp();
Boolean IsElevated(); Boolean IsElevated();
Boolean HasCommandlineArguments();
Boolean HasSettingsStartupActions();
Int32 SetStartupCommandline(String[] commands); Int32 SetStartupCommandline(String[] commands);
Int32 ExecuteCommandline(String[] commands, String cwd); Int32 ExecuteCommandline(String[] commands, String cwd);
String ParseCommandlineMessage { get; }; String ParseCommandlineMessage { get; };
@ -55,6 +57,7 @@ namespace TerminalApp
void IdentifyWindow(); void IdentifyWindow();
String WindowName; String WindowName;
UInt64 WindowId; UInt64 WindowId;
void SetPersistedLayoutIdx(UInt32 idx);
void SetNumberOfOpenWindows(UInt64 num); void SetNumberOfOpenWindows(UInt64 num);
void RenameFailed(); void RenameFailed();
Boolean IsQuakeWindow(); Boolean IsQuakeWindow();
@ -69,10 +72,14 @@ namespace TerminalApp
Boolean GetInitialAlwaysOnTop(); Boolean GetInitialAlwaysOnTop();
Single CalcSnappedDimension(Boolean widthOrHeight, Single dimension); Single CalcSnappedDimension(Boolean widthOrHeight, Single dimension);
void TitlebarClicked(); void TitlebarClicked();
void WindowCloseButtonClicked(); void CloseWindow(Microsoft.Terminal.Settings.Model.LaunchPosition position);
TaskbarState TaskbarState{ get; }; TaskbarState TaskbarState{ get; };
Boolean ShouldUsePersistedLayout();
String GetWindowLayoutJson(Microsoft.Terminal.Settings.Model.LaunchPosition position);
void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector<String> layouts);
Boolean GetMinimizeToNotificationArea(); Boolean GetMinimizeToNotificationArea();
Boolean GetAlwaysShowNotificationIcon(); Boolean GetAlwaysShowNotificationIcon();
Boolean GetShowTitleInTitlebar(); Boolean GetShowTitleInTitlebar();
@ -84,6 +91,7 @@ namespace TerminalApp
// See IDialogPresenter and TerminalPage's DialogPresenter for more // See IDialogPresenter and TerminalPage's DialogPresenter for more
// information. // information.
Windows.Foundation.IAsyncOperation<Windows.UI.Xaml.Controls.ContentDialogResult> ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); Windows.Foundation.IAsyncOperation<Windows.UI.Xaml.Controls.ContentDialogResult> ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog);
void DismissDialog();
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.UIElement> SetTitleBarContent; event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.UIElement> SetTitleBarContent;
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged; event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
@ -99,6 +107,7 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, Object> SettingsChanged; event Windows.Foundation.TypedEventHandler<Object, Object> SettingsChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged; event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested; event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> CloseRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu; event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested; event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
} }

View file

@ -21,8 +21,22 @@ namespace winrt::Microsoft::TerminalApp::implementation
} }
void Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/) {} void Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/) {}
~DebugInputTapConnection() = default; ~DebugInputTapConnection() = default;
void Start() winrt::fire_and_forget Start()
{ {
// GH#11282: It's possible that we're about to be started, _before_
// our paired connection is started. Both will get Start()'ed when
// their owning TermControl is finally laid out. However, if we're
// started first, then we'll immediately start printing to the other
// control as well, which might not have initialized yet. If we do
// that, we'll explode.
//
// Instead, wait here until the other connection is started too,
// before actually starting the connection to the client app. This
// will ensure both controls are initialized before the client app
// is.
co_await winrt::resume_background();
_pairedTap->_start.wait();
_wrappedConnection.Start(); _wrappedConnection.Start();
} }
void WriteInput(hstring const& data) void WriteInput(hstring const& data)
@ -59,6 +73,9 @@ namespace winrt::Microsoft::TerminalApp::implementation
void DebugTapConnection::Start() void DebugTapConnection::Start()
{ {
// presume the wrapped connection is started. // presume the wrapped connection is started.
// This is explained in the comment for GH#11282 above.
_start.count_down();
} }
void DebugTapConnection::WriteInput(hstring const& data) void DebugTapConnection::WriteInput(hstring const& data)

View file

@ -5,6 +5,7 @@
#include <winrt/Microsoft.Terminal.TerminalConnection.h> #include <winrt/Microsoft.Terminal.TerminalConnection.h>
#include "../../inc/cppwinrt_utils.h" #include "../../inc/cppwinrt_utils.h"
#include <til/latch.h>
namespace winrt::Microsoft::TerminalApp::implementation namespace winrt::Microsoft::TerminalApp::implementation
{ {
@ -36,6 +37,8 @@ namespace winrt::Microsoft::TerminalApp::implementation
winrt::weak_ref<Microsoft::Terminal::TerminalConnection::ITerminalConnection> _wrappedConnection; winrt::weak_ref<Microsoft::Terminal::TerminalConnection::ITerminalConnection> _wrappedConnection;
winrt::weak_ref<Microsoft::Terminal::TerminalConnection::ITerminalConnection> _inputSide; winrt::weak_ref<Microsoft::Terminal::TerminalConnection::ITerminalConnection> _inputSide;
til::latch _start{ 1 };
friend class DebugInputTapConnection; friend class DebugInputTapConnection;
}; };
} }

File diff suppressed because it is too large Load diff

View file

@ -40,7 +40,8 @@ enum class Borders : int
Top = 0x1, Top = 0x1,
Bottom = 0x2, Bottom = 0x2,
Left = 0x4, Left = 0x4,
Right = 0x8 Right = 0x8,
All = 0xF
}; };
DEFINE_ENUM_FLAG_OPERATORS(Borders); DEFINE_ENUM_FLAG_OPERATORS(Borders);
@ -58,7 +59,14 @@ public:
const winrt::Microsoft::Terminal::Control::TermControl& control, const winrt::Microsoft::Terminal::Control::TermControl& control,
const bool lastFocused = false); const bool lastFocused = false);
Pane(std::shared_ptr<Pane> first,
std::shared_ptr<Pane> second,
const SplitState splitType,
const float splitPosition,
const bool lastFocused = false);
std::shared_ptr<Pane> GetActivePane(); std::shared_ptr<Pane> GetActivePane();
winrt::Microsoft::Terminal::Control::TermControl GetLastFocusedTerminalControl();
winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl(); winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl();
winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile(); winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile();
@ -142,25 +150,43 @@ public:
// - true if the predicate returned true on any pane. // - true if the predicate returned true on any pane.
template<typename F> template<typename F>
//requires std::predicate<F, std::shared_ptr<Pane>> //requires std::predicate<F, std::shared_ptr<Pane>>
bool WalkTree(F f) auto WalkTree(F f) -> decltype(f(shared_from_this()))
{ {
if (f(shared_from_this())) using R = std::invoke_result_t<F, std::shared_ptr<Pane>>;
{ static constexpr auto IsVoid = std::is_void_v<R>;
return true;
}
if (!_IsLeaf()) if constexpr (IsVoid)
{ {
return _firstChild->WalkTree(f) || _secondChild->WalkTree(f); f(shared_from_this());
if (!_IsLeaf())
{
_firstChild->WalkTree(f);
_secondChild->WalkTree(f);
}
} }
else
{
if (f(shared_from_this()))
{
return true;
}
return false; if (!_IsLeaf())
{
return _firstChild->WalkTree(f) || _secondChild->WalkTree(f);
}
return false;
}
} }
void CollectTaskbarStates(std::vector<winrt::TerminalApp::TaskbarState>& states); void CollectTaskbarStates(std::vector<winrt::TerminalApp::TaskbarState>& states);
WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>); WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);
DECLARE_EVENT(GotFocus, _GotFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>);
using gotFocusArgs = winrt::delegate<std::shared_ptr<Pane>, winrt::Windows::UI::Xaml::FocusState>;
DECLARE_EVENT(GotFocus, _GotFocusHandlers, gotFocusArgs);
DECLARE_EVENT(LostFocus, _LostFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>); DECLARE_EVENT(LostFocus, _LostFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>);
DECLARE_EVENT(PaneRaiseBell, _PaneRaiseBellHandlers, winrt::Windows::Foundation::EventHandler<bool>); DECLARE_EVENT(PaneRaiseBell, _PaneRaiseBellHandlers, winrt::Windows::Foundation::EventHandler<bool>);
DECLARE_EVENT(Detached, _PaneDetachedHandlers, winrt::delegate<std::shared_ptr<Pane>>); DECLARE_EVENT(Detached, _PaneDetachedHandlers, winrt::delegate<std::shared_ptr<Pane>>);
@ -173,7 +199,8 @@ private:
struct LayoutSizeNode; struct LayoutSizeNode;
winrt::Windows::UI::Xaml::Controls::Grid _root{}; winrt::Windows::UI::Xaml::Controls::Grid _root{};
winrt::Windows::UI::Xaml::Controls::Border _border{}; winrt::Windows::UI::Xaml::Controls::Border _borderFirst{};
winrt::Windows::UI::Xaml::Controls::Border _borderSecond{};
winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr }; winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr };
winrt::Microsoft::Terminal::TerminalConnection::ConnectionState _connectionState{ winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::NotConnected }; winrt::Microsoft::Terminal::TerminalConnection::ConnectionState _connectionState{ winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::NotConnected };
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_focusedBorderBrush; static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_focusedBorderBrush;
@ -185,6 +212,7 @@ private:
float _desiredSplitPosition; float _desiredSplitPosition;
std::optional<uint32_t> _id; std::optional<uint32_t> _id;
std::weak_ptr<Pane> _parentChildPath{};
bool _lastActive{ false }; bool _lastActive{ false };
winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr }; winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr };
@ -205,6 +233,7 @@ private:
bool _IsLeaf() const noexcept; bool _IsLeaf() const noexcept;
bool _HasFocusedChild() const noexcept; bool _HasFocusedChild() const noexcept;
void _SetupChildCloseHandlers(); void _SetupChildCloseHandlers();
bool _HasChild(const std::shared_ptr<Pane> child);
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> _Split(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType, std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> _Split(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize, const float splitSize,
@ -232,6 +261,7 @@ private:
void _CloseChild(const bool closeFirst, const bool isDetaching); void _CloseChild(const bool closeFirst, const bool isDetaching);
winrt::fire_and_forget _CloseChildRoutine(const bool closeFirst); winrt::fire_and_forget _CloseChildRoutine(const bool closeFirst);
void _Focus();
void _FocusFirstChild(); void _FocusFirstChild();
void _ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/); void _ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
void _ControlWarningBellHandler(winrt::Windows::Foundation::IInspectable const& sender, void _ControlWarningBellHandler(winrt::Windows::Foundation::IInspectable const& sender,

View file

@ -190,6 +190,9 @@
<data name="CloseWindowWarningTitle" xml:space="preserve"> <data name="CloseWindowWarningTitle" xml:space="preserve">
<value>Do you want to close all tabs?</value> <value>Do you want to close all tabs?</value>
</data> </data>
<data name="MultiplePanes" xml:space="preserve">
<value>Multiple panes</value>
</data>
<data name="TabCloseSubMenu" xml:space="preserve"> <data name="TabCloseSubMenu" xml:space="preserve">
<value>Close...</value> <value>Close...</value>
</data> </data>
@ -381,6 +384,9 @@
<data name="CmdFocusDesc" xml:space="preserve"> <data name="CmdFocusDesc" xml:space="preserve">
<value>Launch the window in focus mode</value> <value>Launch the window in focus mode</value>
</data> </data>
<data name="CmdSavedLayoutArgDesc" xml:space="preserve">
<value>This parameter is an internal implementation detail and should not be used.</value>
</data>
<data name="CmdWindowTargetArgDesc" xml:space="preserve"> <data name="CmdWindowTargetArgDesc" xml:space="preserve">
<value>Specify a terminal window to run the given commandline in. "0" always refers to the current window. </value> <value>Specify a terminal window to run the given commandline in. "0" always refers to the current window. </value>
</data> </data>
@ -712,10 +718,17 @@
<data name="CloseOnExitInfoBar.Message" xml:space="preserve"> <data name="CloseOnExitInfoBar.Message" xml:space="preserve">
<value>Termination behavior can be configured in advanced profile settings.</value> <value>Termination behavior can be configured in advanced profile settings.</value>
</data> </data>
<data name="SetAsDefaultInfoBar.Message" xml:space="preserve">
<value>Windows Terminal can be set as the default terminal application in your settings.</value>
</data>
<data name="InfoBarDismissButton.Content" xml:space="preserve"> <data name="InfoBarDismissButton.Content" xml:space="preserve">
<value>Don't show again</value> <value>Don't show again</value>
</data> </data>
<data name="ElevationShield.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve"> <data name="ElevationShield.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>This Terminal window is running as Admin</value> <value>This Terminal window is running as Admin</value>
</data> </data>
<data name="SetAsDefaultTip_OpenSettingsLink.Content" xml:space="preserve">
<value>Open Settings</value>
<comment>This is a call-to-action hyperlink; it will open the settings.</comment>
</data>
</root> </root>

View file

@ -19,6 +19,9 @@
#include "ColorHelper.h" #include "ColorHelper.h"
#include "DebugTapConnection.h" #include "DebugTapConnection.h"
#include "SettingsTab.h" #include "SettingsTab.h"
#include "..\TerminalSettingsModel\FileUtils.h"
#include <shlobj.h>
using namespace winrt; using namespace winrt;
using namespace winrt::Windows::Foundation::Collections; using namespace winrt::Windows::Foundation::Collections;
@ -410,33 +413,45 @@ namespace winrt::TerminalApp::implementation
// - tab: tab to export // - tab: tab to export
winrt::fire_and_forget TerminalPage::_ExportTab(const TerminalTab& tab) winrt::fire_and_forget TerminalPage::_ExportTab(const TerminalTab& tab)
{ {
// This will be used to set up the file picker "filter", to select .txt
// files by default.
static constexpr COMDLG_FILTERSPEC supportedFileTypes[] = {
{ L"Text Files (*.txt)", L"*.txt" },
{ L"All Files (*.*)", L"*.*" }
};
// An arbitrary GUID to associate with all instances of this
// dialog, so they all re-open in the same path as they were
// open before:
static constexpr winrt::guid clientGuidExportFile{ 0xF6AF20BB, 0x0800, 0x48E6, { 0xB0, 0x17, 0xA1, 0x4C, 0xD8, 0x73, 0xDD, 0x58 } };
try try
{ {
if (const auto control{ tab.GetActiveTerminalControl() }) if (const auto control{ tab.GetActiveTerminalControl() })
{ {
const FileSavePicker savePicker; // GH#11356 - we can't use the UWP apis for writing the file,
savePicker.as<IInitializeWithWindow>()->Initialize(*_hostingHwnd); // because they don't work elevated (shocker) So just use the
savePicker.SuggestedStartLocation(PickerLocationId::Downloads); // shell32 file picker manually.
const auto fileChoices = single_threaded_vector<hstring>({ L".txt" }); auto path = co_await SaveFilePicker(*_hostingHwnd, [control](auto&& dialog) {
savePicker.FileTypeChoices().Insert(RS_(L"PlainText"), fileChoices); THROW_IF_FAILED(dialog->SetClientGuid(clientGuidExportFile));
savePicker.SuggestedFileName(control.Title()); try
{
// Default to the Downloads folder
auto folderShellItem{ winrt::capture<IShellItem>(&SHGetKnownFolderItem, FOLDERID_Downloads, KF_FLAG_DEFAULT, nullptr) };
dialog->SetDefaultFolder(folderShellItem.get());
}
CATCH_LOG(); // non-fatal
THROW_IF_FAILED(dialog->SetFileTypes(ARRAYSIZE(supportedFileTypes), supportedFileTypes));
THROW_IF_FAILED(dialog->SetFileTypeIndex(1)); // the array is 1-indexed
THROW_IF_FAILED(dialog->SetDefaultExtension(L"txt"));
const StorageFile file = co_await savePicker.PickSaveFileAsync(); // Default to using the tab title as the file name
if (file != nullptr) THROW_IF_FAILED(dialog->SetFileName((control.Title() + L".txt").c_str()));
});
if (!path.empty())
{ {
const auto buffer = control.ReadEntireBuffer(); const auto buffer = control.ReadEntireBuffer();
CachedFileManager::DeferUpdates(file); CascadiaSettings::ExportFile(path, buffer);
co_await FileIO::WriteTextAsync(file, buffer);
const auto status = co_await CachedFileManager::CompleteUpdatesAsync(file);
switch (status)
{
case FileUpdateStatus::Complete:
case FileUpdateStatus::CompleteAndRenamed:
_ShowControlNoticeDialog(RS_(L"NoticeInfo"), RS_(L"ExportSuccess"));
break;
default:
_ShowControlNoticeDialog(RS_(L"NoticeError"), RS_(L"ExportFailure"));
}
} }
} }
} }
@ -501,7 +516,9 @@ namespace winrt::TerminalApp::implementation
{ {
// If we are supposed to save state, make sure we clear it out // If we are supposed to save state, make sure we clear it out
// if the user manually closed all tabs. // if the user manually closed all tabs.
if (!_maintainStateOnTabClose && ShouldUsePersistedLayout(_settings)) // Do this only if we are the last window; the monarch will notice
// we are missing and remove us that way otherwise.
if (!_maintainStateOnTabClose && ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1)
{ {
auto state = ApplicationState::SharedInstance(); auto state = ApplicationState::SharedInstance();
state.PersistedWindowLayouts(nullptr); state.PersistedWindowLayouts(nullptr);
@ -734,31 +751,32 @@ namespace winrt::TerminalApp::implementation
{ {
_UnZoomIfNeeded(); _UnZoomIfNeeded();
auto pane = terminalTab->GetActivePane();
if (const auto pane{ terminalTab->GetActivePane() }) if (const auto pane{ terminalTab->GetActivePane() })
{ {
if (const auto control{ pane->GetTerminalControl() }) if (pane->ContainsReadOnly())
{ {
if (control.ReadOnly()) ContentDialogResult warningResult = co_await _ShowCloseReadOnlyDialog();
// If the user didn't explicitly click on close tab - leave
if (warningResult != ContentDialogResult::Primary)
{ {
ContentDialogResult warningResult = co_await _ShowCloseReadOnlyDialog(); co_return;
// If the user didn't explicitly click on close tab - leave
if (warningResult != ContentDialogResult::Primary)
{
co_return;
}
// Clean read-only mode to prevent additional prompt if closing the pane triggers closing of a hosting tab
if (control.ReadOnly())
{
control.ToggleReadOnly();
}
} }
pane->Close(); // Clean read-only mode to prevent additional prompt if closing the pane triggers closing of a hosting tab
pane->WalkTree([](auto p) {
if (const auto control{ p->GetTerminalControl() })
{
if (control.ReadOnly())
{
control.ToggleReadOnly();
}
}
return false;
});
} }
pane->Close();
} }
} }
else if (auto index{ _GetFocusedTabIndex() }) else if (auto index{ _GetFocusedTabIndex() })

View file

@ -23,7 +23,7 @@
<mux:TabView.TabStripHeader> <mux:TabView.TabStripHeader>
<!-- EA18 is the "Shield" glyph --> <!-- EA18 is the "Shield" glyph -->
<FontIcon x:Uid="ElevationShield" <FontIcon x:Uid="ElevationShield"
Margin="9,4,0,0" Margin="9,4,0,4"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
FontSize="16" FontSize="16"
Foreground="{ThemeResource SystemControlForegroundBaseMediumBrush}" Foreground="{ThemeResource SystemControlForegroundBaseMediumBrush}"

View file

@ -218,7 +218,7 @@ namespace winrt::TerminalApp::implementation
_RegisterActionCallbacks(); _RegisterActionCallbacks();
// Hook up inbound connection event handler // Hook up inbound connection event handler
TerminalConnection::ConptyConnection::NewConnection({ this, &TerminalPage::_OnNewConnection }); ConptyConnection::NewConnection({ this, &TerminalPage::_OnNewConnection });
//Event Bindings (Early) //Event Bindings (Early)
_newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) { _newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) {
@ -287,6 +287,8 @@ namespace winrt::TerminalApp::implementation
_defaultPointerCursor = CoreWindow::GetForCurrentThread().PointerCursor(); _defaultPointerCursor = CoreWindow::GetForCurrentThread().PointerCursor();
} }
CATCH_LOG(); CATCH_LOG();
ShowSetAsDefaultInfoBar();
} }
// Method Description; // Method Description;
@ -298,10 +300,37 @@ namespace winrt::TerminalApp::implementation
// - true if the ApplicationState should be used. // - true if the ApplicationState should be used.
bool TerminalPage::ShouldUsePersistedLayout(CascadiaSettings& settings) const bool TerminalPage::ShouldUsePersistedLayout(CascadiaSettings& settings) const
{ {
// If the setting is enabled, and we are the only window. // GH#5000 Until there is a separate state file for elevated sessions we should just not
// save at all while in an elevated window.
return Feature_PersistedWindowLayout::IsEnabled() && return Feature_PersistedWindowLayout::IsEnabled() &&
settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout && !IsElevated() &&
_numOpenWindows == 1; settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout;
}
// Method Description;
// - Checks if the current window is configured to load a particular layout
// Arguments:
// - settings: The settings to use as this may be called before the page is
// fully initialized.
// Return Value:
// - non-null if there is a particular saved layout to use
std::optional<uint32_t> TerminalPage::LoadPersistedLayoutIdx(CascadiaSettings& settings) const
{
return ShouldUsePersistedLayout(settings) ? _loadFromPersistedLayoutIdx : std::nullopt;
}
WindowLayout TerminalPage::LoadPersistedLayout(CascadiaSettings& settings) const
{
if (const auto idx = LoadPersistedLayoutIdx(settings))
{
const auto i = idx.value();
const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts();
if (layouts && layouts.Size() > i)
{
return layouts.GetAt(i);
}
}
return nullptr;
} }
winrt::fire_and_forget TerminalPage::NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e) winrt::fire_and_forget TerminalPage::NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e)
@ -387,30 +416,13 @@ namespace winrt::TerminalApp::implementation
{ {
_startupState = StartupState::InStartup; _startupState = StartupState::InStartup;
// If the user selected to save their tab layout, we are the first // If we are provided with an index, the cases where we have
// window opened, and wt was not run with any other arguments, then // commandline args and startup actions are already handled.
// we should use the saved settings. if (const auto layout = LoadPersistedLayout(_settings))
auto firstActionIsDefault = [](ActionAndArgs action) {
if (action.Action() != ShortcutAction::NewTab)
{
return false;
}
// If no commands were given, we will have default args
if (const auto args = action.Args().try_as<NewTabArgs>())
{
NewTerminalArgs defaultArgs{};
return args.TerminalArgs() == nullptr || args.TerminalArgs().Equals(defaultArgs);
}
return false;
};
if (ShouldUsePersistedLayout(_settings) && _startupActions.Size() == 1 && firstActionIsDefault(_startupActions.GetAt(0)))
{ {
auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); if (layout.TabLayout().Size() > 0)
if (layouts && layouts.Size() > 0 && layouts.GetAt(0).TabLayout() && layouts.GetAt(0).TabLayout().Size() > 0)
{ {
_startupActions = layouts.GetAt(0).TabLayout(); _startupActions = layout.TabLayout();
} }
} }
@ -1289,12 +1301,19 @@ namespace winrt::TerminalApp::implementation
// Method Description: // Method Description:
// - Saves the window position and tab layout to the application state // - Saves the window position and tab layout to the application state
// - This does not create the InitialPosition field, that needs to be
// added externally.
// Arguments: // Arguments:
// - <none> // - <none>
// Return Value: // Return Value:
// - <none> // - the window layout
void TerminalPage::PersistWindowLayout() WindowLayout TerminalPage::GetWindowLayout()
{ {
if (_startupState != StartupState::Initialized)
{
return nullptr;
}
std::vector<ActionAndArgs> actions; std::vector<ActionAndArgs> actions;
for (auto tab : _tabs) for (auto tab : _tabs)
@ -1302,7 +1321,7 @@ namespace winrt::TerminalApp::implementation
if (auto terminalTab = _GetTerminalTabImpl(tab)) if (auto terminalTab = _GetTerminalTabImpl(tab))
{ {
auto tabActions = terminalTab->BuildStartupActions(); auto tabActions = terminalTab->BuildStartupActions();
actions.insert(actions.end(), tabActions.begin(), tabActions.end()); actions.insert(actions.end(), std::make_move_iterator(tabActions.begin()), std::make_move_iterator(tabActions.end()));
} }
else if (tab.try_as<SettingsTab>()) else if (tab.try_as<SettingsTab>())
{ {
@ -1311,7 +1330,7 @@ namespace winrt::TerminalApp::implementation
OpenSettingsArgs args{ SettingsTarget::SettingsUI }; OpenSettingsArgs args{ SettingsTarget::SettingsUI };
action.Args(args); action.Args(args);
actions.push_back(action); actions.emplace_back(std::move(action));
} }
} }
@ -1324,7 +1343,18 @@ namespace winrt::TerminalApp::implementation
SwitchToTabArgs switchToTabArgs{ idx.value() }; SwitchToTabArgs switchToTabArgs{ idx.value() };
action.Args(switchToTabArgs); action.Args(switchToTabArgs);
actions.push_back(action); actions.emplace_back(std::move(action));
}
// If the user set a custom name, save it
if (_WindowName != L"")
{
ActionAndArgs action;
action.Action(ShortcutAction::RenameWindow);
RenameWindowArgs args{ _WindowName };
action.Args(args);
actions.emplace_back(std::move(action));
} }
WindowLayout layout{}; WindowLayout layout{};
@ -1337,33 +1367,7 @@ namespace winrt::TerminalApp::implementation
layout.InitialSize(windowSize); layout.InitialSize(windowSize);
if (_hostingHwnd) return layout;
{
// Get the position of the current window. This includes the
// non-client already.
RECT window{};
GetWindowRect(_hostingHwnd.value(), &window);
// We want to remove the non-client area so calculate that.
// We don't have access to the (NonClient)IslandWindow directly so
// just replicate the logic.
const auto windowStyle = static_cast<DWORD>(GetWindowLong(_hostingHwnd.value(), GWL_STYLE));
auto dpi = GetDpiForWindow(_hostingHwnd.value());
RECT nonClientArea{};
LOG_IF_WIN32_BOOL_FALSE(AdjustWindowRectExForDpi(&nonClientArea, windowStyle, false, 0, dpi));
// The nonClientArea adjustment is negative, so subtract that out.
// This way we save the user-visible location of the terminal.
LaunchPosition pos{};
pos.X = window.left - nonClientArea.left;
pos.Y = window.top;
layout.InitialPosition(pos);
}
auto state = ApplicationState::SharedInstance();
state.PersistedWindowLayouts(winrt::single_threaded_vector<WindowLayout>({ layout }));
} }
// Method Description: // Method Description:
@ -1392,8 +1396,9 @@ namespace winrt::TerminalApp::implementation
if (ShouldUsePersistedLayout(_settings)) if (ShouldUsePersistedLayout(_settings))
{ {
PersistWindowLayout(); // Don't delete the ApplicationState when all of the tabs are removed.
// don't delete the ApplicationState when all of the tabs are removed. // If there is still a monarch living they will get the event that
// a window closed and trigger a new save without this window.
_maintainStateOnTabClose = true; _maintainStateOnTabClose = true;
} }
@ -2692,52 +2697,40 @@ namespace winrt::TerminalApp::implementation
return _isAlwaysOnTop; return _isAlwaysOnTop;
} }
HRESULT TerminalPage::_OnNewConnection(winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection connection) HRESULT TerminalPage::_OnNewConnection(const ConptyConnection& connection)
{ {
// We need to be on the UI thread in order for _OpenNewTab to run successfully. // We need to be on the UI thread in order for _OpenNewTab to run successfully.
// HasThreadAccess will return true if we're currently on a UI thread and false otherwise. // HasThreadAccess will return true if we're currently on a UI thread and false otherwise.
// When we're on a COM thread, we'll need to dispatch the calls to the UI thread // When we're on a COM thread, we'll need to dispatch the calls to the UI thread
// and wait on it hence the locking mechanism. // and wait on it hence the locking mechanism.
if (Dispatcher().HasThreadAccess()) if (!Dispatcher().HasThreadAccess())
{
try
{
NewTerminalArgs newTerminalArgs{};
// TODO GH#10952: When we pass the actual commandline (or originating application), the
// settings model can choose the right settings based on command matching, or synthesize
// a profile from the registry/link settings (TODO GH#9458).
// TODO GH#9458: Get and pass the LNK/EXE filenames.
// Passing in a commandline forces GetProfileForArgs to use Base Layer instead of Default Profile;
// in the future, it can make a better decision based on the value we pull out of the process handle.
// TODO GH#5047: When we hang on to the N.T.A., try not to spawn "default... .exe" :)
newTerminalArgs.Commandline(L"default-terminal-invocation-placeholder");
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
const auto settings{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) };
_CreateNewTabWithProfileAndSettings(profile, settings, connection);
// Request a summon of this window to the foreground
_SummonWindowRequestedHandlers(*this, nullptr);
}
CATCH_RETURN();
return S_OK;
}
else
{ {
til::latch latch{ 1 }; til::latch latch{ 1 };
HRESULT finalVal = S_OK; HRESULT finalVal = S_OK;
Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [&]() { Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [&]() {
// Re-running ourselves under the dispatcher will cause us to take the first branch above.
finalVal = _OnNewConnection(connection); finalVal = _OnNewConnection(connection);
latch.count_down(); latch.count_down();
}); });
latch.wait(); latch.wait();
return finalVal; return finalVal;
} }
try
{
NewTerminalArgs newTerminalArgs;
newTerminalArgs.Commandline(connection.Commandline());
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
const auto settings{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) };
_CreateNewTabWithProfileAndSettings(profile, settings, connection);
// Request a summon of this window to the foreground
_SummonWindowRequestedHandlers(*this, nullptr);
return S_OK;
}
CATCH_RETURN()
} }
// Method Description: // Method Description:
@ -2890,6 +2883,30 @@ namespace winrt::TerminalApp::implementation
} }
} }
// Method Description:
// - Displays a info popup guiding the user into setting their default terminal.
void TerminalPage::ShowSetAsDefaultInfoBar() const
{
if (!CascadiaSettings::IsDefaultTerminalAvailable() || _IsMessageDismissed(InfoBarMessage::SetAsDefault))
{
return;
}
// If the user has already configured any terminal for hand-off we
// shouldn't inform them again about the possibility to do so.
if (CascadiaSettings::IsDefaultTerminalSet())
{
_DismissMessage(InfoBarMessage::SetAsDefault);
return;
}
if (const auto infoBar = FindName(L"SetAsDefaultInfoBar").try_as<MUX::Controls::InfoBar>())
{
TraceLoggingWrite(g_hTerminalAppProvider, "SetAsDefaultTipPresented", TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
infoBar.IsOpen(true);
}
}
// Function Description: // Function Description:
// - Helper function to get the OS-localized name for the "Touch Keyboard // - Helper function to get the OS-localized name for the "Touch Keyboard
// and Handwriting Panel Service". If we can't open up the service for any // and Handwriting Panel Service". If we can't open up the service for any
@ -3106,6 +3123,11 @@ namespace winrt::TerminalApp::implementation
} }
} }
void TerminalPage::SetPersistedLayoutIdx(const uint32_t idx)
{
_loadFromPersistedLayoutIdx = idx;
}
void TerminalPage::SetNumberOfOpenWindows(const uint64_t num) void TerminalPage::SetNumberOfOpenWindows(const uint64_t num)
{ {
_numOpenWindows = num; _numOpenWindows = num;
@ -3305,6 +3327,40 @@ namespace winrt::TerminalApp::implementation
} }
} }
// Method Description:
// - Persists the user's choice not to show the information bar warning about "Windows Terminal can be set as your default terminal application"
// Then hides this information buffer.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TerminalPage::_SetAsDefaultDismissHandler(const IInspectable& /*sender*/, const IInspectable& /*args*/)
{
_DismissMessage(InfoBarMessage::SetAsDefault);
if (const auto infoBar = FindName(L"SetAsDefaultInfoBar").try_as<MUX::Controls::InfoBar>())
{
infoBar.IsOpen(false);
}
TraceLoggingWrite(g_hTerminalAppProvider, "SetAsDefaultTipDismissed", TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
_FocusCurrentTab(true);
}
// Method Description:
// - Dismisses the Default Terminal tip and opens the settings.
void TerminalPage::_SetAsDefaultOpenSettingsHandler(const IInspectable& /*sender*/, const IInspectable& /*args*/)
{
if (const auto infoBar = FindName(L"SetAsDefaultInfoBar").try_as<MUX::Controls::InfoBar>())
{
infoBar.IsOpen(false);
}
TraceLoggingWrite(g_hTerminalAppProvider, "SetAsDefaultTipInteracted", TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
_OpenSettingsUI();
}
// Method Description: // Method Description:
// - Checks whether information bar message was dismissed earlier (in the application state) // - Checks whether information bar message was dismissed earlier (in the application state)
// Arguments: // Arguments:
@ -3334,13 +3390,20 @@ namespace winrt::TerminalApp::implementation
// - <none> // - <none>
void TerminalPage::_DismissMessage(const InfoBarMessage& message) void TerminalPage::_DismissMessage(const InfoBarMessage& message)
{ {
auto dismissedMessages = ApplicationState::SharedInstance().DismissedMessages(); const auto applicationState = ApplicationState::SharedInstance();
if (!dismissedMessages) std::vector<InfoBarMessage> messages;
if (const auto values = applicationState.DismissedMessages())
{ {
dismissedMessages = winrt::single_threaded_vector<InfoBarMessage>(); messages.resize(values.Size());
values.GetMany(0, messages);
} }
dismissedMessages.Append(message); if (std::none_of(messages.begin(), messages.end(), [&](const auto& m) { return m == message; }))
ApplicationState::SharedInstance().DismissedMessages(dismissedMessages); {
messages.emplace_back(message);
}
applicationState.DismissedMessages(std::move(messages));
} }
} }

View file

@ -59,6 +59,9 @@ namespace winrt::TerminalApp::implementation
void Create(); void Create();
bool ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; bool ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const;
std::optional<uint32_t> LoadPersistedLayoutIdx(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const;
winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const;
Microsoft::Terminal::Settings::Model::WindowLayout GetWindowLayout();
winrt::fire_and_forget NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e); winrt::fire_and_forget NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e);
@ -82,7 +85,6 @@ namespace winrt::TerminalApp::implementation
bool AlwaysOnTop() const; bool AlwaysOnTop() const;
void SetStartupActions(std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>& actions); void SetStartupActions(std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>& actions);
void PersistWindowLayout();
void SetInboundListener(bool isEmbedding); void SetInboundListener(bool isEmbedding);
static std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> ConvertExecuteCommandlineToActions(const Microsoft::Terminal::Settings::Model::ExecuteCommandlineArgs& args); static std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> ConvertExecuteCommandlineToActions(const Microsoft::Terminal::Settings::Model::ExecuteCommandlineArgs& args);
@ -93,6 +95,7 @@ namespace winrt::TerminalApp::implementation
winrt::TerminalApp::TaskbarState TaskbarState() const; winrt::TerminalApp::TaskbarState TaskbarState() const;
void ShowKeyboardServiceWarning() const; void ShowKeyboardServiceWarning() const;
void ShowSetAsDefaultInfoBar() const;
winrt::hstring KeyboardServiceDisabledText(); winrt::hstring KeyboardServiceDisabledText();
winrt::fire_and_forget IdentifyWindow(); winrt::fire_and_forget IdentifyWindow();
@ -111,6 +114,7 @@ namespace winrt::TerminalApp::implementation
void WindowId(const uint64_t& value); void WindowId(const uint64_t& value);
void SetNumberOfOpenWindows(const uint64_t value); void SetNumberOfOpenWindows(const uint64_t value);
void SetPersistedLayoutIdx(const uint32_t value);
winrt::hstring WindowIdForDisplay() const noexcept; winrt::hstring WindowIdForDisplay() const noexcept;
winrt::hstring WindowNameForDisplay() const noexcept; winrt::hstring WindowNameForDisplay() const noexcept;
@ -133,6 +137,7 @@ namespace winrt::TerminalApp::implementation
TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs); TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs);
TYPED_EVENT(IsQuakeWindowChanged, IInspectable, IInspectable); TYPED_EVENT(IsQuakeWindowChanged, IInspectable, IInspectable);
TYPED_EVENT(SummonWindowRequested, IInspectable, IInspectable); TYPED_EVENT(SummonWindowRequested, IInspectable, IInspectable);
TYPED_EVENT(CloseRequested, IInspectable, IInspectable);
TYPED_EVENT(OpenSystemMenu, IInspectable, IInspectable); TYPED_EVENT(OpenSystemMenu, IInspectable, IInspectable);
TYPED_EVENT(QuitRequested, IInspectable, IInspectable); TYPED_EVENT(QuitRequested, IInspectable, IInspectable);
@ -166,6 +171,7 @@ namespace winrt::TerminalApp::implementation
bool _isAlwaysOnTop{ false }; bool _isAlwaysOnTop{ false };
winrt::hstring _WindowName{}; winrt::hstring _WindowName{};
uint64_t _WindowId{ 0 }; uint64_t _WindowId{ 0 };
std::optional<uint32_t> _loadFromPersistedLayoutIdx{};
uint64_t _numOpenWindows{ 0 }; uint64_t _numOpenWindows{ 0 };
bool _maintainStateOnTabClose{ false }; bool _maintainStateOnTabClose{ false };
@ -257,6 +263,26 @@ namespace winrt::TerminalApp::implementation
bool _SwapPane(const Microsoft::Terminal::Settings::Model::FocusDirection& direction); bool _SwapPane(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
bool _MovePane(const uint32_t tabIdx); bool _MovePane(const uint32_t tabIdx);
template<typename F>
bool _ApplyToActiveControls(F f)
{
if (const auto tab{ _GetFocusedTabImpl() })
{
if (const auto activePane = tab->GetActivePane())
{
activePane->WalkTree([&](auto p) {
if (const auto& control{ p->GetTerminalControl() })
{
f(control);
}
});
return true;
}
}
return false;
}
winrt::Microsoft::Terminal::Control::TermControl _GetActiveControl(); winrt::Microsoft::Terminal::Control::TermControl _GetActiveControl();
std::optional<uint32_t> _GetFocusedTabIndex() const noexcept; std::optional<uint32_t> _GetFocusedTabIndex() const noexcept;
TerminalApp::TabBase _GetFocusedTab() const noexcept; TerminalApp::TabBase _GetFocusedTab() const noexcept;
@ -360,9 +386,9 @@ namespace winrt::TerminalApp::implementation
void _EndPreviewColorScheme(); void _EndPreviewColorScheme();
void _PreviewColorScheme(const Microsoft::Terminal::Settings::Model::SetColorSchemeArgs& args); void _PreviewColorScheme(const Microsoft::Terminal::Settings::Model::SetColorSchemeArgs& args);
winrt::Microsoft::Terminal::Settings::Model::Command _lastPreviewedCommand{ nullptr }; winrt::Microsoft::Terminal::Settings::Model::Command _lastPreviewedCommand{ nullptr };
winrt::Microsoft::Terminal::Settings::Model::TerminalSettings _originalSettings{ nullptr }; std::vector<std::function<void()>> _restorePreviewFuncs{};
HRESULT _OnNewConnection(winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection connection); HRESULT _OnNewConnection(const winrt::Microsoft::Terminal::TerminalConnection::ConptyConnection& connection);
void _HandleToggleInboundPty(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); void _HandleToggleInboundPty(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _WindowRenamerActionClick(const IInspectable& sender, const IInspectable& eventArgs); void _WindowRenamerActionClick(const IInspectable& sender, const IInspectable& eventArgs);
@ -378,6 +404,8 @@ namespace winrt::TerminalApp::implementation
winrt::fire_and_forget _ConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const; winrt::fire_and_forget _ConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const;
void _CloseOnExitInfoDismissHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const; void _CloseOnExitInfoDismissHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const;
void _KeyboardServiceWarningInfoDismissHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const; void _KeyboardServiceWarningInfoDismissHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const;
void _SetAsDefaultDismissHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
void _SetAsDefaultOpenSettingsHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
static bool _IsMessageDismissed(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message); static bool _IsMessageDismissed(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);
static void _DismissMessage(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message); static void _DismissMessage(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);

View file

@ -33,7 +33,6 @@ namespace TerminalApp
UInt64 WindowId; UInt64 WindowId;
String WindowNameForDisplay { get; }; String WindowNameForDisplay { get; };
String WindowIdForDisplay { get; }; String WindowIdForDisplay { get; };
void SetNumberOfOpenWindows(UInt64 num);
void RenameFailed(); void RenameFailed();
Boolean IsQuakeWindow(); Boolean IsQuakeWindow();
@ -58,6 +57,7 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested; event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged; event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested; event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> CloseRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu; event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
} }
} }

View file

@ -141,6 +141,20 @@
Click="_CloseOnExitInfoDismissHandler" /> Click="_CloseOnExitInfoDismissHandler" />
</mux:InfoBar.ActionButton> </mux:InfoBar.ActionButton>
</mux:InfoBar> </mux:InfoBar>
<mux:InfoBar x:Name="SetAsDefaultInfoBar"
x:Uid="SetAsDefaultInfoBar"
x:Load="False"
CloseButtonClick="_SetAsDefaultDismissHandler"
IsClosable="True"
IsIconVisible="True"
IsOpen="False"
Severity="Informational">
<mux:InfoBar.ActionButton>
<HyperlinkButton x:Uid="SetAsDefaultTip_OpenSettingsLink"
Click="_SetAsDefaultOpenSettingsHandler" />
</mux:InfoBar.ActionButton>
</mux:InfoBar>
</StackPanel> </StackPanel>
<!-- <!--

View file

@ -67,8 +67,11 @@ namespace winrt::TerminalApp::implementation
_rootPane->FocusPane(firstId); _rootPane->FocusPane(firstId);
_activePane = _rootPane->GetActivePane(); _activePane = _rootPane->GetActivePane();
} }
// Set the active control // If the focused pane is a leaf, add it to the MRU panes
_mruPanes.insert(_mruPanes.begin(), _activePane->Id().value()); if (const auto id = _activePane->Id())
{
_mruPanes.insert(_mruPanes.begin(), id.value());
}
_Setup(); _Setup();
} }
@ -180,8 +183,8 @@ namespace winrt::TerminalApp::implementation
// Method Description: // Method Description:
// - Returns nullptr if no children of this tab were the last control to be // - Returns nullptr if no children of this tab were the last control to be
// focused, or the TermControl that _was_ the last control to be focused (if // focused, the active control of the current pane, or the last active child control
// there was one). // of the active pane if it is a parent.
// - This control might not currently be focused, if the tab itself is not // - This control might not currently be focused, if the tab itself is not
// currently focused. // currently focused.
// Arguments: // Arguments:
@ -193,7 +196,7 @@ namespace winrt::TerminalApp::implementation
{ {
if (_activePane) if (_activePane)
{ {
return _activePane->GetTerminalControl(); return _activePane->GetLastFocusedTerminalControl();
} }
return nullptr; return nullptr;
} }
@ -390,6 +393,10 @@ namespace winrt::TerminalApp::implementation
{ {
return _runtimeTabText; return _runtimeTabText;
} }
if (!_activePane->_IsLeaf())
{
return RS_(L"MultiplePanes");
}
const auto lastFocusedControl = GetActiveTerminalControl(); const auto lastFocusedControl = GetActiveTerminalControl();
return lastFocusedControl ? lastFocusedControl.Title() : L""; return lastFocusedControl ? lastFocusedControl.Title() : L"";
} }
@ -450,12 +457,25 @@ namespace winrt::TerminalApp::implementation
// 1 for the child after the first split. // 1 for the child after the first split.
auto state = _rootPane->BuildStartupActions(0, 1); auto state = _rootPane->BuildStartupActions(0, 1);
ActionAndArgs newTabAction{}; {
newTabAction.Action(ShortcutAction::NewTab); ActionAndArgs newTabAction{};
NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane() }; newTabAction.Action(ShortcutAction::NewTab);
newTabAction.Args(newTabArgs); NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane() };
newTabAction.Args(newTabArgs);
state.args.emplace(state.args.begin(), std::move(newTabAction)); state.args.emplace(state.args.begin(), std::move(newTabAction));
}
if (_runtimeTabColor)
{
ActionAndArgs setColorAction{};
setColorAction.Action(ShortcutAction::SetTabColor);
SetTabColorArgs setColorArgs{ _runtimeTabColor.value() };
setColorAction.Args(setColorArgs);
state.args.emplace_back(std::move(setColorAction));
}
// If we only have one arg, we only have 1 pane so we don't need any // If we only have one arg, we only have 1 pane so we don't need any
// special focus logic // special focus logic
@ -501,19 +521,14 @@ namespace winrt::TerminalApp::implementation
// either the first or second child, but this will always return the // either the first or second child, but this will always return the
// original pane first. // original pane first.
auto [original, newPane] = _activePane->Split(splitType, splitSize, profile, control); auto [original, newPane] = _activePane->Split(splitType, splitSize, profile, control);
// The active pane has an id if it is a leaf
if (activePaneId) if (activePaneId)
{ {
original->Id(activePaneId.value()); original->Id(activePaneId.value());
newPane->Id(_nextPaneId);
++_nextPaneId;
}
else
{
original->Id(_nextPaneId);
++_nextPaneId;
newPane->Id(_nextPaneId);
++_nextPaneId;
} }
newPane->Id(_nextPaneId);
++_nextPaneId;
_activePane = original; _activePane = original;
// Add a event handlers to the new panes' GotFocus event. When the pane // Add a event handlers to the new panes' GotFocus event. When the pane
@ -538,8 +553,8 @@ namespace winrt::TerminalApp::implementation
// - The removed pane, if the remove succeeded. // - The removed pane, if the remove succeeded.
std::shared_ptr<Pane> TerminalTab::DetachPane() std::shared_ptr<Pane> TerminalTab::DetachPane()
{ {
// if we only have one pane, remove it entirely // if we only have one pane, or the focused pane is the root, remove it
// and close this tab // entirely and close this tab
if (_rootPane == _activePane) if (_rootPane == _activePane)
{ {
return DetachRoot(); return DetachRoot();
@ -614,16 +629,12 @@ namespace winrt::TerminalApp::implementation
// Add the new pane as an automatic split on the active pane. // Add the new pane as an automatic split on the active pane.
auto first = _activePane->AttachPane(pane, SplitDirection::Automatic); auto first = _activePane->AttachPane(pane, SplitDirection::Automatic);
// under current assumptions this condition should always be true. // This will be true if the original _activePane is a leaf pane.
// If it is a parent pane then we don't want to set an ID on it.
if (previousId) if (previousId)
{ {
first->Id(previousId.value()); first->Id(previousId.value());
} }
else
{
first->Id(_nextPaneId);
++_nextPaneId;
}
// Update with event handlers on the new child. // Update with event handlers on the new child.
_activePane = first; _activePane = first;
@ -699,7 +710,10 @@ namespace winrt::TerminalApp::implementation
// throughout the entire tree. // throughout the entire tree.
if (const auto newFocus = _rootPane->NavigateDirection(_activePane, direction, _mruPanes)) if (const auto newFocus = _rootPane->NavigateDirection(_activePane, direction, _mruPanes))
{ {
// Mark that we want the active pane to changed
_changingActivePane = true;
const auto res = _rootPane->FocusPane(newFocus); const auto res = _rootPane->FocusPane(newFocus);
_changingActivePane = false;
if (_zoomedPane) if (_zoomedPane)
{ {
@ -722,11 +736,22 @@ namespace winrt::TerminalApp::implementation
// - true if two panes were swapped. // - true if two panes were swapped.
bool TerminalTab::SwapPane(const FocusDirection& direction) bool TerminalTab::SwapPane(const FocusDirection& direction)
{ {
// You cannot swap panes with the parent/child pane because of the
// circular reference.
if (direction == FocusDirection::Parent || direction == FocusDirection::Child)
{
return false;
}
// NOTE: This _must_ be called on the root pane, so that it can propagate // NOTE: This _must_ be called on the root pane, so that it can propagate
// throughout the entire tree. // throughout the entire tree.
if (auto neighbor = _rootPane->NavigateDirection(_activePane, direction, _mruPanes)) if (auto neighbor = _rootPane->NavigateDirection(_activePane, direction, _mruPanes))
{ {
return _rootPane->SwapPanes(_activePane, neighbor); // SwapPanes will refocus the terminal to make sure that it has focus
// even after moving.
_changingActivePane = true;
const auto res = _rootPane->SwapPanes(_activePane, neighbor);
_changingActivePane = false;
return res;
} }
return false; return false;
@ -734,7 +759,10 @@ namespace winrt::TerminalApp::implementation
bool TerminalTab::FocusPane(const uint32_t id) bool TerminalTab::FocusPane(const uint32_t id)
{ {
return _rootPane->FocusPane(id); _changingActivePane = true;
const auto res = _rootPane->FocusPane(id);
_changingActivePane = false;
return res;
} }
// Method Description: // Method Description:
@ -1027,7 +1055,7 @@ namespace winrt::TerminalApp::implementation
auto weakThis{ get_weak() }; auto weakThis{ get_weak() };
std::weak_ptr<Pane> weakPane{ pane }; std::weak_ptr<Pane> weakPane{ pane };
auto gotFocusToken = pane->GotFocus([weakThis](std::shared_ptr<Pane> sender) { auto gotFocusToken = pane->GotFocus([weakThis](std::shared_ptr<Pane> sender, WUX::FocusState focus) {
// Do nothing if the Tab's lifetime is expired or pane isn't new. // Do nothing if the Tab's lifetime is expired or pane isn't new.
auto tab{ weakThis.get() }; auto tab{ weakThis.get() };
@ -1035,8 +1063,20 @@ namespace winrt::TerminalApp::implementation
{ {
if (sender != tab->_activePane) if (sender != tab->_activePane)
{ {
tab->_UpdateActivePane(sender); auto senderIsChild = tab->_activePane->_HasChild(sender);
tab->_RecalculateAndApplyTabColor();
// Only move focus if we the program moved focus, or the
// user moved with their mouse. This is a problem because a
// pane isn't a control itself, and if we have the parent
// focused we are fine if the terminal control is focused,
// but we don't want to update the active pane.
if (!senderIsChild ||
(focus == WUX::FocusState::Programmatic && tab->_changingActivePane) ||
focus == WUX::FocusState::Pointer)
{
tab->_UpdateActivePane(sender);
tab->_RecalculateAndApplyTabColor();
}
} }
tab->_focusState = WUX::FocusState::Programmatic; tab->_focusState = WUX::FocusState::Programmatic;
// This tab has gained focus, remove the bell indicator if it is active // This tab has gained focus, remove the bell indicator if it is active
@ -1071,8 +1111,19 @@ namespace winrt::TerminalApp::implementation
tab->Content(tab->_rootPane->GetRootElement()); tab->Content(tab->_rootPane->GetRootElement());
tab->ExitZoom(); tab->ExitZoom();
} }
if (auto pane = weakPane.lock()) if (auto pane = weakPane.lock())
{ {
// When a parent pane is selected, but one of its children
// close out under it we still need to update title/focus information
// but the GotFocus handler will rightly see that the _activePane
// did not actually change. Triggering
if (pane != tab->_activePane && !tab->_activePane->_IsLeaf())
{
co_await winrt::resume_foreground(tab->Content().Dispatcher());
tab->_UpdateActivePane(tab->_activePane);
}
for (auto i = tab->_mruPanes.begin(); i != tab->_mruPanes.end(); ++i) for (auto i = tab->_mruPanes.begin(); i != tab->_mruPanes.end(); ++i)
{ {
if (*i == pane->Id()) if (*i == pane->Id())
@ -1290,11 +1341,13 @@ namespace winrt::TerminalApp::implementation
// - The tab's color, if any // - The tab's color, if any
std::optional<winrt::Windows::UI::Color> TerminalTab::GetTabColor() std::optional<winrt::Windows::UI::Color> TerminalTab::GetTabColor()
{ {
const auto currControlColor{ GetActiveTerminalControl().TabColor() };
std::optional<winrt::Windows::UI::Color> controlTabColor; std::optional<winrt::Windows::UI::Color> controlTabColor;
if (currControlColor != nullptr) if (const auto& control = GetActiveTerminalControl())
{ {
controlTabColor = currControlColor.Value(); if (const auto color = control.TabColor())
{
controlTabColor = color.Value();
}
} }
// A Tab's color will be the result of layering a variety of sources, // A Tab's color will be the result of layering a variety of sources,
@ -1407,6 +1460,10 @@ namespace winrt::TerminalApp::implementation
// TabViewItem().Background() only sets the color of the tab background // TabViewItem().Background() only sets the color of the tab background
// when the TabViewItem is unselected. So we still need to set the other // when the TabViewItem is unselected. So we still need to set the other
// properties ourselves. // properties ourselves.
//
// In GH#11294 we thought we'd still need to set
// TabViewItemHeaderBackground manually, but GH#11382 discovered that
// Background() was actually okay after all.
TabViewItem().Background(deselectedTabBrush); TabViewItem().Background(deselectedTabBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush); TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush); TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush);
@ -1453,6 +1510,7 @@ namespace winrt::TerminalApp::implementation
void TerminalTab::_ClearTabBackgroundColor() void TerminalTab::_ClearTabBackgroundColor()
{ {
winrt::hstring keys[] = { winrt::hstring keys[] = {
L"TabViewItemHeaderBackground",
L"TabViewItemHeaderBackgroundSelected", L"TabViewItemHeaderBackgroundSelected",
L"TabViewItemHeaderBackgroundPointerOver", L"TabViewItemHeaderBackgroundPointerOver",
L"TabViewItemHeaderForeground", L"TabViewItemHeaderForeground",
@ -1473,8 +1531,10 @@ namespace winrt::TerminalApp::implementation
} }
} }
// Clear out the Background. // GH#11382 DON'T set the background to null. If you do that, then the
TabViewItem().Background(nullptr); // tab won't be hit testable at all. Transparent, however, is a totally
// valid hit test target. That makes sense.
TabViewItem().Background(WUX::Media::SolidColorBrush{ Windows::UI::Colors::Transparent() });
_RefreshVisualState(); _RefreshVisualState();
_colorCleared(); _colorCleared();
@ -1585,6 +1645,11 @@ namespace winrt::TerminalApp::implementation
void TerminalTab::EnterZoom() void TerminalTab::EnterZoom()
{ {
// Clear the content first, because with parent focusing it is possible
// to zoom the root pane, but setting the content will not trigger the
// property changed event since it is the same and you would end up with
// an empty tab.
Content(nullptr);
_zoomedPane = _activePane; _zoomedPane = _activePane;
_rootPane->Maximize(_zoomedPane); _rootPane->Maximize(_zoomedPane);
// Update the tab header to show the magnifying glass // Update the tab header to show the magnifying glass
@ -1593,6 +1658,7 @@ namespace winrt::TerminalApp::implementation
} }
void TerminalTab::ExitZoom() void TerminalTab::ExitZoom()
{ {
Content(nullptr);
_rootPane->Restore(_zoomedPane); _rootPane->Restore(_zoomedPane);
_zoomedPane = nullptr; _zoomedPane = nullptr;
// Update the tab header to hide the magnifying glass // Update the tab header to hide the magnifying glass
@ -1607,13 +1673,34 @@ namespace winrt::TerminalApp::implementation
// Method Description: // Method Description:
// - Toggle read-only mode on the active pane // - Toggle read-only mode on the active pane
// - If a parent pane is selected, this will ensure that all children have
// the same read-only status.
void TerminalTab::TogglePaneReadOnly() void TerminalTab::TogglePaneReadOnly()
{ {
auto control = GetActiveTerminalControl(); auto hasReadOnly = false;
if (control) auto allReadOnly = true;
{ _activePane->WalkTree([&](auto p) {
control.ToggleReadOnly(); if (const auto& control{ p->GetTerminalControl() })
} {
hasReadOnly |= control.ReadOnly();
allReadOnly &= control.ReadOnly();
}
});
_activePane->WalkTree([&](auto p) {
if (const auto& control{ p->GetTerminalControl() })
{
// If all controls have the same read only state then just toggle
if (allReadOnly || !hasReadOnly)
{
control.ToggleReadOnly();
}
// otherwise set to all read only.
else if (!control.ReadOnly())
{
control.ToggleReadOnly();
}
}
});
} }
// Method Description: // Method Description:

View file

@ -139,6 +139,7 @@ namespace winrt::TerminalApp::implementation
bool _receivedKeyDown{ false }; bool _receivedKeyDown{ false };
bool _iconHidden{ false }; bool _iconHidden{ false };
bool _changingActivePane{ false };
winrt::hstring _runtimeTabText{}; winrt::hstring _runtimeTabText{};
bool _inRename{ false }; bool _inRename{ false };

View file

@ -99,29 +99,57 @@ static HRESULT _duplicateHandle(const HANDLE in, HANDLE& out) noexcept
// from the registered handler event function. // from the registered handler event function.
HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client) HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client)
{ {
// Stash a local copy of _pfnHandoff before we stop listening. try
auto localPfnHandoff = _pfnHandoff; {
// Stash a local copy of _pfnHandoff before we stop listening.
auto localPfnHandoff = _pfnHandoff;
// Because we are REGCLS_SINGLEUSE... we need to `CoRevokeClassObject` after we handle this ONE call. // Because we are REGCLS_SINGLEUSE... we need to `CoRevokeClassObject` after we handle this ONE call.
// COM does not automatically clean that up for us. We must do it. // COM does not automatically clean that up for us. We must do it.
s_StopListening(); s_StopListening();
std::unique_lock lock{ _mtx }; std::unique_lock lock{ _mtx };
// Report an error if no one registered a handoff function before calling this. // Report an error if no one registered a handoff function before calling this.
RETURN_HR_IF_NULL(E_NOT_VALID_STATE, localPfnHandoff); THROW_HR_IF_NULL(E_NOT_VALID_STATE, localPfnHandoff);
// Duplicate the handles from what we received. // Duplicate the handles from what we received.
// The contract with COM specifies that any HANDLEs we receive from the caller belong // The contract with COM specifies that any HANDLEs we receive from the caller belong
// to the caller and will be freed when we leave the scope of this method. // to the caller and will be freed when we leave the scope of this method.
// Making our own duplicate copy ensures they hang around in our lifetime. // Making our own duplicate copy ensures they hang around in our lifetime.
RETURN_IF_FAILED(_duplicateHandle(in, in)); THROW_IF_FAILED(_duplicateHandle(in, in));
RETURN_IF_FAILED(_duplicateHandle(out, out)); THROW_IF_FAILED(_duplicateHandle(out, out));
RETURN_IF_FAILED(_duplicateHandle(signal, signal)); THROW_IF_FAILED(_duplicateHandle(signal, signal));
RETURN_IF_FAILED(_duplicateHandle(ref, ref)); THROW_IF_FAILED(_duplicateHandle(ref, ref));
RETURN_IF_FAILED(_duplicateHandle(server, server)); THROW_IF_FAILED(_duplicateHandle(server, server));
RETURN_IF_FAILED(_duplicateHandle(client, client)); THROW_IF_FAILED(_duplicateHandle(client, client));
// Call registered handler from when we started listening. // Call registered handler from when we started listening.
return localPfnHandoff(in, out, signal, ref, server, client); THROW_IF_FAILED(localPfnHandoff(in, out, signal, ref, server, client));
#pragma warning(suppress : 26477)
TraceLoggingWrite(
g_hTerminalConnectionProvider,
"ReceiveTerminalHandoff_Success",
TraceLoggingDescription("successfully received a terminal handoff"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
return S_OK;
}
catch (...)
{
const auto hr = wil::ResultFromCaughtException();
#pragma warning(suppress : 26477)
TraceLoggingWrite(
g_hTerminalConnectionProvider,
"ReceiveTerminalHandoff_Failed",
TraceLoggingDescription("failed while receiving a terminal handoff"),
TraceLoggingHResult(hr),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
return hr;
}
} }

View file

@ -1,12 +1,12 @@
// Copyright (c) Microsoft Corporation. // Copyright (c) Microsoft Corporation.
// Licensed under the MIT license. // Licensed under the MIT license.
#include "pch.h" #include "pch.h"
#include "ConptyConnection.h" #include "ConptyConnection.h"
#include <windows.h> #include <UserEnv.h>
#include <userenv.h> #include <winternl.h>
#include "ConptyConnection.g.cpp" #include "ConptyConnection.g.cpp"
#include "CTerminalHandoff.h" #include "CTerminalHandoff.h"
@ -276,24 +276,18 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const HANDLE hClientProcess) : const HANDLE hClientProcess) :
_initialRows{ 25 }, _initialRows{ 25 },
_initialCols{ 80 }, _initialCols{ 80 },
_commandline{ L"" }, _guid{ Utils::CreateGuid() },
_startingDirectory{ L"" },
_startingTitle{ L"" },
_environment{ nullptr },
_guid{},
_u8State{},
_u16Str{},
_buffer{},
_inPipe{ hIn }, _inPipe{ hIn },
_outPipe{ hOut } _outPipe{ hOut }
{ {
THROW_IF_FAILED(ConptyPackPseudoConsole(hServerProcess, hRef, hSig, &_hPC)); THROW_IF_FAILED(ConptyPackPseudoConsole(hServerProcess, hRef, hSig, &_hPC));
if (_guid == guid{})
{
_guid = Utils::CreateGuid();
}
_piClient.hProcess = hClientProcess; _piClient.hProcess = hClientProcess;
try
{
_commandline = _commandlineFromProcess(hClientProcess);
}
CATCH_LOG()
} }
// Function Description: // Function Description:
@ -355,6 +349,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
return _guid; return _guid;
} }
winrt::hstring ConptyConnection::Commandline() const
{
return _commandline;
}
void ConptyConnection::Start() void ConptyConnection::Start()
try try
{ {
@ -560,6 +559,38 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
} }
CATCH_LOG() CATCH_LOG()
// Returns the command line of the given process.
// Requires PROCESS_BASIC_INFORMATION | PROCESS_VM_READ privileges.
winrt::hstring ConptyConnection::_commandlineFromProcess(HANDLE process)
{
struct PROCESS_BASIC_INFORMATION
{
NTSTATUS ExitStatus;
PPEB PebBaseAddress;
ULONG_PTR AffinityMask;
KPRIORITY BasePriority;
ULONG_PTR UniqueProcessId;
ULONG_PTR InheritedFromUniqueProcessId;
} info;
THROW_IF_NTSTATUS_FAILED(NtQueryInformationProcess(process, ProcessBasicInformation, &info, sizeof(info), nullptr));
// PEB: Process Environment Block
// This is a funny structure allocated by the kernel which contains all sorts of useful
// information, only a tiny fraction of which are documented publicly unfortunately.
// Fortunately however it contains a copy of the command line the process launched with.
PEB peb;
THROW_IF_WIN32_BOOL_FALSE(ReadProcessMemory(process, info.PebBaseAddress, &peb, sizeof(peb), nullptr));
RTL_USER_PROCESS_PARAMETERS params;
THROW_IF_WIN32_BOOL_FALSE(ReadProcessMemory(process, peb.ProcessParameters, &params, sizeof(params), nullptr));
// Yeah I know... Don't use "impl" stuff... But why do you make something _that_ useful private? :(
// The hstring_builder allows us to create a hstring without intermediate copies. Neat!
winrt::impl::hstring_builder commandline{ params.CommandLine.Length / 2u };
THROW_IF_WIN32_BOOL_FALSE(ReadProcessMemory(process, params.CommandLine.Buffer, commandline.data(), params.CommandLine.Length, nullptr));
return commandline.to_hstring();
}
DWORD ConptyConnection::_OutputThread() DWORD ConptyConnection::_OutputThread()
{ {
// Keep us alive until the output thread terminates; the destructor // Keep us alive until the output thread terminates; the destructor
@ -636,8 +667,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
HRESULT ConptyConnection::NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client) noexcept HRESULT ConptyConnection::NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client) noexcept
try try
{ {
auto conn = winrt::make<implementation::ConptyConnection>(signal, in, out, ref, server, client); _newConnectionHandlers(winrt::make<ConptyConnection>(signal, in, out, ref, server, client));
_newConnectionHandlers(conn);
return S_OK; return S_OK;
} }

View file

@ -38,6 +38,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
void ClearBuffer(); void ClearBuffer();
winrt::guid Guid() const noexcept; winrt::guid Guid() const noexcept;
winrt::hstring Commandline() const;
static void StartInboundListener(); static void StartInboundListener();
static void StopInboundListener(); static void StopInboundListener();
@ -56,12 +57,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
WINRT_CALLBACK(TerminalOutput, TerminalOutputHandler); WINRT_CALLBACK(TerminalOutput, TerminalOutputHandler);
private: private:
static HRESULT NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client) noexcept;
static winrt::hstring _commandlineFromProcess(HANDLE process);
HRESULT _LaunchAttachedClient() noexcept; HRESULT _LaunchAttachedClient() noexcept;
void _indicateExitWithStatus(unsigned int status) noexcept; void _indicateExitWithStatus(unsigned int status) noexcept;
void _ClientTerminated() noexcept; void _ClientTerminated() noexcept;
static HRESULT NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client) noexcept;
uint32_t _initialRows{}; uint32_t _initialRows{};
uint32_t _initialCols{}; uint32_t _initialCols{};
hstring _commandline{}; hstring _commandline{};

View file

@ -5,10 +5,13 @@ import "ITerminalConnection.idl";
namespace Microsoft.Terminal.TerminalConnection namespace Microsoft.Terminal.TerminalConnection
{ {
delegate void NewConnectionHandler(ConptyConnection connection);
[default_interface] runtimeclass ConptyConnection : ITerminalConnection [default_interface] runtimeclass ConptyConnection : ITerminalConnection
{ {
ConptyConnection(); ConptyConnection();
Guid Guid { get; }; Guid Guid { get; };
String Commandline { get; };
void ClearBuffer(); void ClearBuffer();
static event NewConnectionHandler NewConnection; static event NewConnectionHandler NewConnection;

View file

@ -29,6 +29,4 @@ namespace Microsoft.Terminal.TerminalConnection
event Windows.Foundation.TypedEventHandler<ITerminalConnection, Object> StateChanged; event Windows.Foundation.TypedEventHandler<ITerminalConnection, Object> StateChanged;
ConnectionState State { get; }; ConnectionState State { get; };
}; };
delegate void NewConnectionHandler(ITerminalConnection connection);
} }

View file

@ -274,10 +274,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_updateAntiAliasingMode(_renderEngine.get()); _updateAntiAliasingMode(_renderEngine.get());
// GH#5098: Inform the engine of the opacity of the default text background. // GH#5098: Inform the engine of the opacity of the default text background.
if (_settings.UseAcrylic()) // GH#11315: Always do this, even if they don't have acrylic on.
{ _renderEngine->SetDefaultTextBackgroundOpacity(::base::saturated_cast<float>(_settings.Opacity()));
_renderEngine->SetDefaultTextBackgroundOpacity(::base::saturated_cast<float>(_settings.Opacity()));
}
THROW_IF_FAILED(_renderEngine->Enable()); THROW_IF_FAILED(_renderEngine->Enable());
@ -444,6 +442,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Opacity(newOpacity); Opacity(newOpacity);
// GH#11285 - If the user is on Windows 10, and they changed the
// transparency of the control s.t. it should be partially opaque, then
// opt them in to acrylic. It's the only way to have transparency on
// Windows 10.
// We'll also turn the acrylic back off when they're fully opaque, which
// is what the Terminal did prior to 1.12.
if (!IsVintageOpacityAvailable())
{
_settings.UseAcrylic(newOpacity < 1.0);
}
auto eventArgs = winrt::make_self<TransparencyChangedEventArgs>(newOpacity); auto eventArgs = winrt::make_self<TransparencyChangedEventArgs>(newOpacity);
_TransparencyChangedHandlers(*this, *eventArgs); _TransparencyChangedHandlers(*this, *eventArgs);
} }
@ -469,6 +478,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{ {
_renderEngine->ToggleShaderEffects(); _renderEngine->ToggleShaderEffects();
} }
// Always redraw after toggling effects. This way even if the control
// does not have focus it will update immediately.
_renderer->TriggerRedrawAll();
} }
// Method Description: // Method Description:
@ -570,6 +582,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_settings = settings; _settings = settings;
_runtimeOpacity = std::nullopt; _runtimeOpacity = std::nullopt;
_runtimeUseAcrylic = std::nullopt;
// GH#11285 - If the user is on Windows 10, and they wanted opacity, but
// didn't explicitly request acrylic, then opt them in to acrylic.
// On Windows 11+, this isn't needed, because we can have vintage opacity.
if (!IsVintageOpacityAvailable() && _settings.Opacity() < 1.0 && !_settings.UseAcrylic())
{
_runtimeUseAcrylic = true;
}
// Initialize our font information. // Initialize our font information.
const auto fontFace = _settings.FontFace(); const auto fontFace = _settings.FontFace();
@ -1026,7 +1047,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TerminalConnection::ConnectionState ControlCore::ConnectionState() const TerminalConnection::ConnectionState ControlCore::ConnectionState() const
{ {
return _connection.State(); return _connection ? _connection.State() : TerminalConnection::ConnectionState::Closed;
} }
hstring ControlCore::Title() hstring ControlCore::Title()
@ -1546,4 +1567,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return hstring(ss.str()); return hstring(ss.str());
} }
// Helper to check if we're on Windows 11 or not. This is used to check if
// we need to use acrylic to achieve transparency, because vintage opacity
// doesn't work in islands on win10.
// Remove when we can remove the rest of GH#11285
bool ControlCore::IsVintageOpacityAvailable() noexcept
{
OSVERSIONINFOEXW osver{};
osver.dwOSVersionInfoSize = sizeof(osver);
osver.dwBuildNumber = 22000;
DWORDLONG dwlConditionMask = 0;
VER_SET_CONDITION(dwlConditionMask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
return VerifyVersionInfoW(&osver, VER_BUILDNUMBER, dwlConditionMask) != FALSE;
}
} }

View file

@ -158,7 +158,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
hstring ReadEntireBuffer() const; hstring ReadEntireBuffer() const;
static bool IsVintageOpacityAvailable() noexcept;
RUNTIME_SETTING(double, Opacity, _settings.Opacity()); RUNTIME_SETTING(double, Opacity, _settings.Opacity());
RUNTIME_SETTING(bool, UseAcrylic, _settings.UseAcrylic());
// -------------------------------- WinRT Events --------------------------------- // -------------------------------- WinRT Events ---------------------------------
// clang-format off // clang-format off

View file

@ -43,6 +43,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{ {
_controlPadding = padding; _controlPadding = padding;
} }
void InteractivityAutomationPeer::ParentProvider(AutomationPeer parentProvider)
{
_parentProvider = parentProvider;
}
// Method Description: // Method Description:
// - Signals the ui automation client that the terminal's selection has // - Signals the ui automation client that the terminal's selection has
@ -110,30 +114,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// ScreenInfoUiaProvider doesn't actually use parameter, so just pass in nullptr // ScreenInfoUiaProvider doesn't actually use parameter, so just pass in nullptr
THROW_IF_FAILED(_uiaProvider->RangeFromChild(/* IRawElementProviderSimple */ nullptr, THROW_IF_FAILED(_uiaProvider->RangeFromChild(/* IRawElementProviderSimple */ nullptr,
&returnVal)); &returnVal));
return _CreateXamlUiaTextRange(returnVal);
const auto parentProvider = this->ProviderFromPeer(*this);
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
return xutr.as<XamlAutomation::ITextRangeProvider>();
} }
XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::RangeFromPoint(Windows::Foundation::Point screenLocation) XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::RangeFromPoint(Windows::Foundation::Point screenLocation)
{ {
UIA::ITextRangeProvider* returnVal; UIA::ITextRangeProvider* returnVal;
THROW_IF_FAILED(_uiaProvider->RangeFromPoint({ screenLocation.X, screenLocation.Y }, &returnVal)); THROW_IF_FAILED(_uiaProvider->RangeFromPoint({ screenLocation.X, screenLocation.Y }, &returnVal));
return _CreateXamlUiaTextRange(returnVal);
const auto parentProvider = this->ProviderFromPeer(*this);
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
return xutr.as<XamlAutomation::ITextRangeProvider>();
} }
XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::DocumentRange() XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::DocumentRange()
{ {
UIA::ITextRangeProvider* returnVal; UIA::ITextRangeProvider* returnVal;
THROW_IF_FAILED(_uiaProvider->get_DocumentRange(&returnVal)); THROW_IF_FAILED(_uiaProvider->get_DocumentRange(&returnVal));
return _CreateXamlUiaTextRange(returnVal);
const auto parentProvider = this->ProviderFromPeer(*this);
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
return xutr.as<XamlAutomation::ITextRangeProvider>();
} }
XamlAutomation::SupportedTextSelection InteractivityAutomationPeer::SupportedTextSelection() XamlAutomation::SupportedTextSelection InteractivityAutomationPeer::SupportedTextSelection()
@ -180,6 +175,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
#pragma endregion #pragma endregion
XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::_CreateXamlUiaTextRange(UIA::ITextRangeProvider* returnVal) const
{
// 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, parent.ProviderFromPeer(parent));
return xutr.as<XamlAutomation::ITextRangeProvider>();
};
// Method Description: // Method Description:
// - extracts the UiaTextRanges from the SAFEARRAY and converts them to Xaml ITextRangeProviders // - extracts the UiaTextRanges from the SAFEARRAY and converts them to Xaml ITextRangeProviders
// Arguments: // Arguments:
@ -194,11 +203,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
std::vector<XamlAutomation::ITextRangeProvider> vec; std::vector<XamlAutomation::ITextRangeProvider> vec;
vec.reserve(count); vec.reserve(count);
auto parentProvider = this->ProviderFromPeer(*this);
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
auto xutr = make_self<XamlUiaTextRange>(providers[i].detach(), parentProvider); if (auto xutr = _CreateXamlUiaTextRange(providers[i].detach()))
vec.emplace_back(xutr.as<XamlAutomation::ITextRangeProvider>()); {
vec.emplace_back(std::move(xutr));
}
} }
com_array<XamlAutomation::ITextRangeProvider> result{ vec }; com_array<XamlAutomation::ITextRangeProvider> result{ vec };

View file

@ -43,6 +43,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void SetControlBounds(const Windows::Foundation::Rect bounds); void SetControlBounds(const Windows::Foundation::Rect bounds);
void SetControlPadding(const Core::Padding padding); void SetControlPadding(const Core::Padding padding);
void ParentProvider(Windows::UI::Xaml::Automation::Peers::AutomationPeer parentProvider);
#pragma region IUiaEventDispatcher #pragma region IUiaEventDispatcher
void SignalSelectionChanged() override; void SignalSelectionChanged() override;
@ -74,8 +75,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TYPED_EVENT(CursorChanged, IInspectable, IInspectable); TYPED_EVENT(CursorChanged, IInspectable, IInspectable);
private: private:
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider _CreateXamlUiaTextRange(::ITextRangeProvider* returnVal) const;
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider; ::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
winrt::Microsoft::Terminal::Control::implementation::ControlInteractivity* _interactivity; winrt::Microsoft::Terminal::Control::implementation::ControlInteractivity* _interactivity;
weak_ref<Windows::UI::Xaml::Automation::Peers::AutomationPeer> _parentProvider;
til::rectangle _controlBounds{}; til::rectangle _controlBounds{};
til::rectangle _controlPadding{}; til::rectangle _controlPadding{};

View file

@ -1,6 +1,5 @@
// Copyright (c) Microsoft Corporation. // Copyright (c) Microsoft Corporation.
// Licensed under the MIT license. // Licensed under the MIT license.
namespace Microsoft.Terminal.Control namespace Microsoft.Terminal.Control
{ {
[default_interface] runtimeclass InteractivityAutomationPeer : [default_interface] runtimeclass InteractivityAutomationPeer :
@ -10,6 +9,7 @@ namespace Microsoft.Terminal.Control
void SetControlBounds(Windows.Foundation.Rect bounds); void SetControlBounds(Windows.Foundation.Rect bounds);
void SetControlPadding(Microsoft.Terminal.Core.Padding padding); void SetControlPadding(Microsoft.Terminal.Core.Padding padding);
void ParentProvider(Windows.UI.Xaml.Automation.Peers.AutomationPeer parentProvider);
event Windows.Foundation.TypedEventHandler<Object, Object> SelectionChanged; event Windows.Foundation.TypedEventHandler<Object, Object> SelectionChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> TextChanged; event Windows.Foundation.TypedEventHandler<Object, Object> TextChanged;

View file

@ -454,7 +454,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
// GH#5098: Inform the engine of the new opacity of the default text background. // GH#5098: Inform the engine of the new opacity of the default text background.
_core.SetBackgroundOpacity(::base::saturated_cast<float>(_core.Opacity())); //
// TODO! This sure seems contrived, shouldn't the core... already
// know this?
_core.SetBackgroundOpacity(_core.Opacity());
} }
else else
{ {

View file

@ -45,6 +45,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_contentAutomationPeer.SelectionChanged([this](auto&&, auto&&) { SignalSelectionChanged(); }); _contentAutomationPeer.SelectionChanged([this](auto&&, auto&&) { SignalSelectionChanged(); });
_contentAutomationPeer.TextChanged([this](auto&&, auto&&) { SignalTextChanged(); }); _contentAutomationPeer.TextChanged([this](auto&&, auto&&) { SignalTextChanged(); });
_contentAutomationPeer.CursorChanged([this](auto&&, auto&&) { SignalCursorChanged(); }); _contentAutomationPeer.CursorChanged([this](auto&&, auto&&) { SignalCursorChanged(); });
_contentAutomationPeer.ParentProvider(*this);
}; };
// Method Description: // Method Description:
@ -226,30 +227,4 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
#pragma endregion #pragma endregion
// Method Description:
// - extracts the UiaTextRanges from the SAFEARRAY and converts them to Xaml ITextRangeProviders
// Arguments:
// - SAFEARRAY of UIA::UiaTextRange (ITextRangeProviders)
// Return Value:
// - com_array of Xaml Wrapped UiaTextRange (ITextRangeProviders)
com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges)
{
// transfer ownership of UiaTextRanges to this new vector
auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::TermControlUiaTextRange>(textRanges);
int count = gsl::narrow<int>(providers.size());
std::vector<XamlAutomation::ITextRangeProvider> vec;
vec.reserve(count);
auto parentProvider = this->ProviderFromPeer(*this);
for (int i = 0; i < count; i++)
{
auto xutr = make_self<XamlUiaTextRange>(providers[i].detach(), parentProvider);
vec.emplace_back(xutr.as<XamlAutomation::ITextRangeProvider>());
}
com_array<XamlAutomation::ITextRangeProvider> result{ vec };
return result;
}
} }

View file

@ -78,7 +78,5 @@ namespace winrt::Microsoft::Terminal::Control::implementation
private: private:
winrt::Microsoft::Terminal::Control::implementation::TermControl* _termControl; winrt::Microsoft::Terminal::Control::implementation::TermControl* _termControl;
Control::InteractivityAutomationPeer _contentAutomationPeer; Control::InteractivityAutomationPeer _contentAutomationPeer;
winrt::com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges);
}; };
} }

View file

@ -6,6 +6,7 @@
#include "../types/TermControlUiaTextRange.hpp" #include "../types/TermControlUiaTextRange.hpp"
#include <UIAutomationClient.h> #include <UIAutomationClient.h>
#include <UIAutomationCoreApi.h> #include <UIAutomationCoreApi.h>
#include "../types/UiaTracing.h"
// the same as COR_E_NOTSUPPORTED // the same as COR_E_NOTSUPPORTED
// we don't want to import the CLR headers to get it // we don't want to import the CLR headers to get it
@ -182,6 +183,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
XamlAutomation::IRawElementProviderSimple XamlUiaTextRange::GetEnclosingElement() XamlAutomation::IRawElementProviderSimple XamlUiaTextRange::GetEnclosingElement()
{ {
::Microsoft::Console::Types::UiaTracing::TextRange::GetEnclosingElement(*static_cast<::Microsoft::Console::Types::UiaTextRangeBase*>(_uiaProvider.get()));
return _parentProvider; return _parentProvider;
} }

View file

@ -0,0 +1,228 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// A lot of code was taken from
// https://github.com/Maximus5/ConEmu/blob/master/src/ConEmu/ColorFix.cpp
// and then adjusted to fit our style guidelines
#include "pch.h"
#include <Windows.h>
#include "ColorFix.hpp"
static constexpr double gMinThreshold = 12.0;
static constexpr double gExpThreshold = 20.0;
static constexpr double gLStep = 5.0;
static constexpr double rad006 = 0.104719755119659774;
static constexpr double rad025 = 0.436332312998582394;
static constexpr double rad030 = 0.523598775598298873;
static constexpr double rad060 = 1.047197551196597746;
static constexpr double rad063 = 1.099557428756427633;
static constexpr double rad180 = 3.141592653589793238;
static constexpr double rad275 = 4.799655442984406336;
static constexpr double rad360 = 6.283185307179586476;
ColorFix::ColorFix(COLORREF color)
{
rgb = color;
_ToLab();
}
// Method Description:
// - Helper function to calculate HPrime
double ColorFix::_GetHPrimeFn(double x, double y)
{
if (x == 0 && y == 0)
{
return 0;
}
const auto hueAngle = atan2(x, y);
return hueAngle >= 0 ? hueAngle : hueAngle + rad360;
}
// Method Description:
// - Given 2 colors, computes the DeltaE value between them
// Arguments:
// - x1: the first color
// - x2: the second color
// Return Value:
// - The DeltaE value between x1 and x2
double ColorFix::_GetDeltaE(ColorFix x1, ColorFix x2)
{
constexpr double kSubL = 1;
constexpr double kSubC = 1;
constexpr double kSubH = 1;
// Delta L Prime
const double deltaLPrime = x2.L - x1.L;
// L Bar
const double lBar = (x1.L + x2.L) / 2;
// C1 & C2
const double c1 = sqrt(pow(x1.A, 2) + pow(x1.B, 2));
const double c2 = sqrt(pow(x2.A, 2) + pow(x2.B, 2));
// C Bar
const double cBar = (c1 + c2) / 2;
// A Prime 1
const double aPrime1 = x1.A + (x1.A / 2) * (1 - sqrt(pow(cBar, 7) / (pow(cBar, 7) + pow(25.0, 7))));
// A Prime 2
const double aPrime2 = x2.A + (x2.A / 2) * (1 - sqrt(pow(cBar, 7) / (pow(cBar, 7) + pow(25.0, 7))));
// C Prime 1
const double cPrime1 = sqrt(pow(aPrime1, 2) + pow(x1.B, 2));
// C Prime 2
const double cPrime2 = sqrt(pow(aPrime2, 2) + pow(x2.B, 2));
// C Bar Prime
const double cBarPrime = (cPrime1 + cPrime2) / 2;
// Delta C Prime
const double deltaCPrime = cPrime2 - cPrime1;
// S sub L
const double sSubL = 1 + ((0.015 * pow(lBar - 50, 2)) / sqrt(20 + pow(lBar - 50, 2)));
// S sub C
const double sSubC = 1 + 0.045 * cBarPrime;
// h Prime 1
const double hPrime1 = _GetHPrimeFn(x1.B, aPrime1);
// h Prime 2
const double hPrime2 = _GetHPrimeFn(x2.B, aPrime2);
// Delta H Prime
const double deltaHPrime = 0 == c1 || 0 == c2 ? 0 : 2 * sqrt(cPrime1 * cPrime2) * sin(abs(hPrime1 - hPrime2) <= rad180 ? hPrime2 - hPrime1 : (hPrime2 <= hPrime1 ? hPrime2 - hPrime1 + rad360 : hPrime2 - hPrime1 - rad360) / 2);
// H Bar Prime
const double hBarPrime = (abs(hPrime1 - hPrime2) > rad180) ? (hPrime1 + hPrime2 + rad360) / 2 : (hPrime1 + hPrime2) / 2;
// T
const double t = 1 - 0.17 * cos(hBarPrime - rad030) + 0.24 * cos(2 * hBarPrime) + 0.32 * cos(3 * hBarPrime + rad006) - 0.20 * cos(4 * hBarPrime - rad063);
// S sub H
const double sSubH = 1 + 0.015 * cBarPrime * t;
// R sub T
const double rSubT = -2 * sqrt(pow(cBarPrime, 7) / (pow(cBarPrime, 7) + pow(25.0, 7))) * sin(rad060 * exp(-pow((hBarPrime - rad275) / rad025, 2)));
// Put it all together!
const double lightness = deltaLPrime / (kSubL * sSubL);
const double chroma = deltaCPrime / (kSubC * sSubC);
const double hue = deltaHPrime / (kSubH * sSubH);
return sqrt(pow(lightness, 2) + pow(chroma, 2) + pow(hue, 2) + rSubT * chroma * hue);
}
// Method Description:
// - Populates our L, A, B values, based on our r, g, b values
// - Converts a color in rgb format to a color in lab format
// - Reference: http://www.easyrgb.com/index.php?X=MATH&H=01#text1
void ColorFix::_ToLab()
{
double var_R = r / 255.0;
double var_G = g / 255.0;
double var_B = b / 255.0;
var_R = var_R > 0.04045 ? pow(((var_R + 0.055) / 1.055), 2.4) : var_R / 12.92;
var_G = var_G > 0.04045 ? pow(((var_G + 0.055) / 1.055), 2.4) : var_G / 12.92;
var_B = var_B > 0.04045 ? pow(((var_B + 0.055) / 1.055), 2.4) : var_B / 12.92;
var_R = var_R * 100.;
var_G = var_G * 100.;
var_B = var_B * 100.;
//Observer. = 2 degrees, Illuminant = D65
const double X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805;
const double Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722;
const double Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505;
double var_X = X / 95.047; //ref_X = 95.047 (Observer= 2 degrees, Illuminant= D65)
double var_Y = Y / 100.000; //ref_Y = 100.000
double var_Z = Z / 108.883; //ref_Z = 108.883
var_X = var_X > 0.008856 ? pow(var_X, (1. / 3.)) : (7.787 * var_X) + (16. / 116.);
var_Y = var_Y > 0.008856 ? pow(var_Y, (1. / 3.)) : (7.787 * var_Y) + (16. / 116.);
var_Z = var_Z > 0.008856 ? pow(var_Z, (1. / 3.)) : (7.787 * var_Z) + (16. / 116.);
L = (116. * var_Y) - 16.;
A = 500. * (var_X - var_Y);
B = 200. * (var_Y - var_Z);
}
// Method Description:
// - Populates our r, g, b values, based on our L, A, B values
// - Converts a color in lab format to a color in rgb format
// - Reference: http://www.easyrgb.com/index.php?X=MATH&H=01#text1
void ColorFix::_ToRGB()
{
double var_Y = (L + 16.) / 116.;
double var_X = A / 500. + var_Y;
double var_Z = var_Y - B / 200.;
var_Y = (pow(var_Y, 3) > 0.008856) ? pow(var_Y, 3) : (var_Y - 16. / 116.) / 7.787;
var_X = (pow(var_X, 3) > 0.008856) ? pow(var_X, 3) : (var_X - 16. / 116.) / 7.787;
var_Z = (pow(var_Z, 3) > 0.008856) ? pow(var_Z, 3) : (var_Z - 16. / 116.) / 7.787;
double X = 95.047 * var_X; //ref_X = 95.047 (Observer= 2 degrees, Illuminant= D65)
double Y = 100.000 * var_Y; //ref_Y = 100.000
double Z = 108.883 * var_Z; //ref_Z = 108.883
var_X = X / 100.; //X from 0 to 95.047 (Observer = 2 degrees, Illuminant = D65)
var_Y = Y / 100.; //Y from 0 to 100.000
var_Z = Z / 100.; //Z from 0 to 108.883
double var_R = var_X * 3.2406 + var_Y * -1.5372 + var_Z * -0.4986;
double var_G = var_X * -0.9689 + var_Y * 1.8758 + var_Z * 0.0415;
double var_B = var_X * 0.0557 + var_Y * -0.2040 + var_Z * 1.0570;
var_R = var_R > 0.0031308 ? 1.055 * pow(var_R, (1 / 2.4)) - 0.055 : var_R = 12.92 * var_R;
var_G = var_G > 0.0031308 ? 1.055 * pow(var_G, (1 / 2.4)) - 0.055 : var_G = 12.92 * var_G;
var_B = var_B > 0.0031308 ? 1.055 * pow(var_B, (1 / 2.4)) - 0.055 : var_B = 12.92 * var_B;
r = (BYTE)std::clamp(var_R * 255., 0., 255.);
g = (BYTE)std::clamp(var_G * 255., 0., 255.);
b = (BYTE)std::clamp(var_B * 255., 0., 255.);
}
// Method Description:
// - Given foreground and background colors, change the foreground color to
// make it more perceivable if necessary
// - Arguments:
// - fg: the foreground color
// - bg: the background color
// - Return Value:
// - The foreground color after performing any necessary changes to make it more perceivable
COLORREF ColorFix::GetPerceivableColor(COLORREF fg, COLORREF bg)
{
ColorFix backLab(bg);
ColorFix frontLab(fg);
const double de1 = _GetDeltaE(frontLab, backLab);
if (de1 < gMinThreshold)
{
for (int i = 0; i <= 1; i++)
{
const double step = (i == 0) ? gLStep : -gLStep;
frontLab.L += step;
while (((i == 0) && (frontLab.L <= 100)) || ((i == 1) && (frontLab.L >= 0)))
{
const double de2 = _GetDeltaE(frontLab, backLab);
if (de2 >= gExpThreshold)
{
frontLab._ToRGB();
return frontLab.rgb;
}
frontLab.L += step;
}
}
}
return frontLab.rgb;
}

View file

@ -0,0 +1,49 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- ColorFix
Abstract:
- Implementation of perceptual color nudging, which allows the Terminal
to slightly shift the foreground color to make it more perceivable on
the current background (for cases where the foreground is very close
to being imperceivable on the background).
Author(s):
- Pankaj Bhojwani - Sep 2021
--*/
#pragma once
struct ColorFix
{
public:
ColorFix(COLORREF color);
static COLORREF GetPerceivableColor(COLORREF fg, COLORREF bg);
// RGB
union
{
struct
{
BYTE r, g, b, dummy;
};
COLORREF rgb;
};
// Lab
struct
{
double L, A, B;
};
private:
static double _GetHPrimeFn(double x, double y);
static double _GetDeltaE(ColorFix x1, ColorFix x2);
void _ToLab();
void _ToRGB();
};

View file

@ -67,5 +67,6 @@ namespace Microsoft.Terminal.Core
CursorStyle CursorShape; CursorStyle CursorShape;
UInt32 CursorHeight; UInt32 CursorHeight;
Boolean IntenseIsBright; Boolean IntenseIsBright;
Boolean AdjustIndistinguishableColors;
}; };
} }

View file

@ -53,7 +53,8 @@ Terminal::Terminal() :
_taskbarState{ 0 }, _taskbarState{ 0 },
_taskbarProgress{ 0 }, _taskbarProgress{ 0 },
_trimBlockSelection{ false }, _trimBlockSelection{ false },
_intenseIsBright{ true } _intenseIsBright{ true },
_adjustIndistinguishableColors{ true }
{ {
auto dispatch = std::make_unique<TerminalDispatch>(*this); auto dispatch = std::make_unique<TerminalDispatch>(*this);
auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch)); auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
@ -175,11 +176,16 @@ void Terminal::UpdateAppearance(const ICoreAppearance& appearance)
_defaultBg = newBackgroundColor.with_alpha(0); _defaultBg = newBackgroundColor.with_alpha(0);
_defaultFg = appearance.DefaultForeground(); _defaultFg = appearance.DefaultForeground();
_intenseIsBright = appearance.IntenseIsBright(); _intenseIsBright = appearance.IntenseIsBright();
_adjustIndistinguishableColors = appearance.AdjustIndistinguishableColors();
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
{ {
_colorTable.at(i) = til::color{ appearance.GetColorTableEntry(i) }; _colorTable.at(i) = til::color{ appearance.GetColorTableEntry(i) };
} }
if (_adjustIndistinguishableColors)
{
_MakeAdjustedColorArray();
}
CursorType cursorShape = CursorType::VerticalBar; CursorType cursorShape = CursorType::VerticalBar;
switch (appearance.CursorShape()) switch (appearance.CursorShape())
@ -849,7 +855,13 @@ WORD Terminal::_TakeVirtualKeyFromLastKeyEvent(const WORD scanCode) noexcept
// will release this lock when it's destructed. // will release this lock when it's destructed.
[[nodiscard]] std::unique_lock<til::ticket_lock> Terminal::LockForReading() [[nodiscard]] std::unique_lock<til::ticket_lock> Terminal::LockForReading()
{ {
#ifdef NDEBUG
return std::unique_lock{ _readWriteLock }; return std::unique_lock{ _readWriteLock };
#else
auto lock = std::unique_lock{ _readWriteLock };
_lastLocker = GetCurrentThreadId();
return lock;
#endif
} }
// Method Description: // Method Description:
@ -859,7 +871,13 @@ WORD Terminal::_TakeVirtualKeyFromLastKeyEvent(const WORD scanCode) noexcept
// will release this lock when it's destructed. // will release this lock when it's destructed.
[[nodiscard]] std::unique_lock<til::ticket_lock> Terminal::LockForWriting() [[nodiscard]] std::unique_lock<til::ticket_lock> Terminal::LockForWriting()
{ {
#ifdef NDEBUG
return std::unique_lock{ _readWriteLock }; return std::unique_lock{ _readWriteLock };
#else
auto lock = std::unique_lock{ _readWriteLock };
_lastLocker = GetCurrentThreadId();
return lock;
#endif
} }
Viewport Terminal::_GetMutableViewport() const noexcept Viewport Terminal::_GetMutableViewport() const noexcept

View file

@ -199,6 +199,7 @@ public:
const COORD GetSelectionEnd() const noexcept override; const COORD GetSelectionEnd() const noexcept override;
const std::wstring_view GetConsoleTitle() const noexcept override; const std::wstring_view GetConsoleTitle() const noexcept override;
void ColorSelection(const COORD coordSelectionStart, const COORD coordSelectionEnd, const TextAttribute) override; void ColorSelection(const COORD coordSelectionStart, const COORD coordSelectionEnd, const TextAttribute) override;
const bool IsUiaDataInitialized() const noexcept override;
#pragma endregion #pragma endregion
void SetWriteInputCallback(std::function<void(std::wstring&)> pfn) noexcept; void SetWriteInputCallback(std::function<void(std::wstring&)> pfn) noexcept;
@ -268,6 +269,9 @@ private:
// But we can abuse the fact that the surrounding members rarely change and are huge // But we can abuse the fact that the surrounding members rarely change and are huge
// (std::function is like 64 bytes) to create some natural padding without wasting space. // (std::function is like 64 bytes) to create some natural padding without wasting space.
til::ticket_lock _readWriteLock; til::ticket_lock _readWriteLock;
#ifndef NDEBUG
DWORD _lastLocker;
#endif
std::function<void(const int, const int, const int)> _pfnScrollPositionChanged; std::function<void(const int, const int, const int)> _pfnScrollPositionChanged;
std::function<void(const til::color)> _pfnBackgroundColorChanged; std::function<void(const til::color)> _pfnBackgroundColorChanged;
@ -297,6 +301,7 @@ private:
bool _bracketedPasteMode; bool _bracketedPasteMode;
bool _trimBlockSelection; bool _trimBlockSelection;
bool _intenseIsBright; bool _intenseIsBright;
bool _adjustIndistinguishableColors;
size_t _taskbarState; size_t _taskbarState;
size_t _taskbarProgress; size_t _taskbarProgress;
@ -397,6 +402,9 @@ private:
Microsoft::Console::VirtualTerminal::SgrStack _sgrStack; Microsoft::Console::VirtualTerminal::SgrStack _sgrStack;
void _MakeAdjustedColorArray();
std::array<std::array<COLORREF, 18>, 18> _adjustedForegroundColors;
#ifdef UNIT_TESTING #ifdef UNIT_TESTING
friend class TerminalCoreUnitTests::TerminalBufferTests; friend class TerminalCoreUnitTests::TerminalBufferTests;
friend class TerminalCoreUnitTests::TerminalApiTest; friend class TerminalCoreUnitTests::TerminalApiTest;

View file

@ -8,6 +8,7 @@
<ClCompile Include="..\TerminalSelection.cpp" /> <ClCompile Include="..\TerminalSelection.cpp" />
<ClCompile Include="..\TerminalApi.cpp" /> <ClCompile Include="..\TerminalApi.cpp" />
<ClCompile Include="..\Terminal.cpp" /> <ClCompile Include="..\Terminal.cpp" />
<ClCompile Include="..\ColorFix.cpp" />
<ClCompile Include="..\pch.cpp"> <ClCompile Include="..\pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader> <PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile> </ClCompile>
@ -19,6 +20,7 @@
<ClInclude Include="..\ITerminalApi.hpp" /> <ClInclude Include="..\ITerminalApi.hpp" />
<ClInclude Include="..\pch.h" /> <ClInclude Include="..\pch.h" />
<ClInclude Include="..\Terminal.hpp" /> <ClInclude Include="..\Terminal.hpp" />
<ClInclude Include="..\ColorFix.hpp" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -3,11 +3,16 @@
#include "pch.h" #include "pch.h"
#include "Terminal.hpp" #include "Terminal.hpp"
#include "ColorFix.hpp"
#include <DefaultSettings.h> #include <DefaultSettings.h>
using namespace Microsoft::Terminal::Core; using namespace Microsoft::Terminal::Core;
using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Types;
using namespace Microsoft::Console::Render; using namespace Microsoft::Console::Render;
static constexpr size_t DefaultBgIndex{ 16 };
static constexpr size_t DefaultFgIndex{ 17 };
Viewport Terminal::GetViewport() noexcept Viewport Terminal::GetViewport() noexcept
{ {
return _GetVisibleViewport(); return _GetVisibleViewport();
@ -44,14 +49,47 @@ const TextAttribute Terminal::GetDefaultBrushColors() noexcept
std::pair<COLORREF, COLORREF> Terminal::GetAttributeColors(const TextAttribute& attr) const noexcept std::pair<COLORREF, COLORREF> Terminal::GetAttributeColors(const TextAttribute& attr) const noexcept
{ {
std::pair<COLORREF, COLORREF> colors;
_blinkingState.RecordBlinkingUsage(attr); _blinkingState.RecordBlinkingUsage(attr);
auto colors = attr.CalculateRgbColors( const auto fgTextColor = attr.GetForeground();
_colorTable, const auto bgTextColor = attr.GetBackground();
_defaultFg,
_defaultBg, // We want to nudge the foreground color to make it more perceivable only for the
_screenReversed, // default color pairs within the color table
_blinkingState.IsBlinkingFaint(), if (_adjustIndistinguishableColors &&
_intenseIsBright); !(attr.IsFaint() || (attr.IsBlinking() && _blinkingState.IsBlinkingFaint())) &&
(fgTextColor.IsDefault() || fgTextColor.IsLegacy()) &&
(bgTextColor.IsDefault() || bgTextColor.IsLegacy()))
{
const auto bgIndex = bgTextColor.IsDefault() ? DefaultBgIndex : bgTextColor.GetIndex();
auto fgIndex = fgTextColor.IsDefault() ? DefaultFgIndex : fgTextColor.GetIndex();
if (fgTextColor.IsIndex16() && (fgIndex < 8) && attr.IsBold() && _intenseIsBright)
{
// There is a special case for bold here - we need to get the bright version of the foreground color
fgIndex += 8;
}
if (attr.IsReverseVideo() ^ _screenReversed)
{
colors.first = _adjustedForegroundColors[fgIndex][bgIndex];
colors.second = fgTextColor.GetColor(_colorTable, _defaultFg);
}
else
{
colors.first = _adjustedForegroundColors[bgIndex][fgIndex];
colors.second = bgTextColor.GetColor(_colorTable, _defaultBg);
}
}
else
{
colors = attr.CalculateRgbColors(_colorTable,
_defaultFg,
_defaultBg,
_screenReversed,
_blinkingState.IsBlinkingFaint(),
_intenseIsBright);
}
colors.first |= 0xff000000; colors.first |= 0xff000000;
// We only care about alpha for the default BG (which enables acrylic) // We only care about alpha for the default BG (which enables acrylic)
// If the bg isn't the default bg color, or reverse video is enabled, make it fully opaque. // If the bg isn't the default bg color, or reverse video is enabled, make it fully opaque.
@ -233,6 +271,9 @@ catch (...)
void Terminal::LockConsole() noexcept void Terminal::LockConsole() noexcept
{ {
_readWriteLock.lock(); _readWriteLock.lock();
#ifndef NDEBUG
_lastLocker = GetCurrentThreadId();
#endif
} }
// Method Description: // Method Description:
@ -250,3 +291,43 @@ bool Terminal::IsScreenReversed() const noexcept
{ {
return _screenReversed; return _screenReversed;
} }
const bool Terminal::IsUiaDataInitialized() const noexcept
{
// GH#11135: Windows Terminal needs to create and return an automation peer
// when a screen reader requests it. However, the terminal might not be fully
// initialized yet. So we use this to check if any crucial components of
// UiaData are not yet initialized.
return !!_buffer;
}
// Method Description:
// - Creates the adjusted color array, which contains the possible foreground colors,
// adjusted for perceivability
// - The adjusted color array is 2-d, and effectively maps a background and foreground
// color pair to the adjusted foreground for that color pair
void Terminal::_MakeAdjustedColorArray()
{
// The color table has 16 colors, but the adjusted color table needs to be 18
// to include the default background and default foreground colors
std::array<COLORREF, 18> colorTableWithDefaults;
std::copy_n(std::begin(_colorTable), 16, std::begin(colorTableWithDefaults));
colorTableWithDefaults[DefaultBgIndex] = _defaultBg;
colorTableWithDefaults[DefaultFgIndex] = _defaultFg;
for (auto fgIndex = 0; fgIndex < 18; ++fgIndex)
{
const auto fg = til::at(colorTableWithDefaults, fgIndex);
for (auto bgIndex = 0; bgIndex < 18; ++bgIndex)
{
if (fgIndex == bgIndex)
{
_adjustedForegroundColors[bgIndex][fgIndex] = fg;
}
else
{
const auto bg = til::at(colorTableWithDefaults, bgIndex);
_adjustedForegroundColors[bgIndex][fgIndex] = ColorFix::GetPerceivableColor(fg, bg);
}
}
}
}

View file

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. // Copyright (c) Microsoft Corporation.
// Licensed under the MIT license. // Licensed under the MIT license.
#include "pch.h" #include "pch.h"
@ -369,7 +369,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{ {
const auto kbdVM{ get_self<KeyBindingViewModel>(_KeyBindingList.GetAt(i)) }; const auto kbdVM{ get_self<KeyBindingViewModel>(_KeyBindingList.GetAt(i)) };
const auto& otherKeys{ kbdVM->CurrentKeys() }; const auto& otherKeys{ kbdVM->CurrentKeys() };
if (keys.Modifiers() == otherKeys.Modifiers() && keys.Vkey() == otherKeys.Vkey()) if (otherKeys && keys.Modifiers() == otherKeys.Modifiers() && keys.Vkey() == otherKeys.Vkey())
{ {
return i; return i;
} }

View file

@ -40,6 +40,7 @@
<StackPanel Margin="{StaticResource StandardControlMargin}"> <StackPanel Margin="{StaticResource StandardControlMargin}">
<local:SettingContainer x:Uid="AddProfile_Duplicate"> <local:SettingContainer x:Uid="AddProfile_Duplicate">
<muxc:RadioButtons x:Name="Profiles" <muxc:RadioButtons x:Name="Profiles"
AutomationProperties.AccessibilityView="Content"
ItemsSource="{x:Bind State.Settings.AllProfiles, Mode=OneWay}"> ItemsSource="{x:Bind State.Settings.AllProfiles, Mode=OneWay}">
<muxc:RadioButtons.ItemTemplate> <muxc:RadioButtons.ItemTemplate>
<DataTemplate x:DataType="model:Profile"> <DataTemplate x:DataType="model:Profile">

View file

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. // Copyright (c) Microsoft Corporation.
// Licensed under the MIT license. // Licensed under the MIT license.
#include "pch.h" #include "pch.h"
@ -7,6 +7,7 @@
#include "EnumEntry.h" #include "EnumEntry.h"
#include <LibraryResources.h> #include <LibraryResources.h>
#include "..\WinRTUtils\inc\Utils.h"
using namespace winrt::Windows::UI::Text; using namespace winrt::Windows::UI::Text;
using namespace winrt::Windows::UI::Xaml; using namespace winrt::Windows::UI::Xaml;

View file

@ -1,4 +1,4 @@
/*++ /*++
Copyright (c) Microsoft Corporation Copyright (c) Microsoft Corporation
Licensed under the MIT license. Licensed under the MIT license.
@ -93,6 +93,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
OBSERVABLE_PROJECTED_SETTING(_appearance, BackgroundImageStretchMode); OBSERVABLE_PROJECTED_SETTING(_appearance, BackgroundImageStretchMode);
OBSERVABLE_PROJECTED_SETTING(_appearance, BackgroundImageAlignment); OBSERVABLE_PROJECTED_SETTING(_appearance, BackgroundImageAlignment);
OBSERVABLE_PROJECTED_SETTING(_appearance, IntenseTextStyle); OBSERVABLE_PROJECTED_SETTING(_appearance, IntenseTextStyle);
OBSERVABLE_PROJECTED_SETTING(_appearance, AdjustIndistinguishableColors);
private: private:
Model::AppearanceConfig _appearance; Model::AppearanceConfig _appearance;

View file

@ -46,6 +46,7 @@ namespace Microsoft.Terminal.Settings.Editor
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.UI.Xaml.Media.Stretch, BackgroundImageStretchMode); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.UI.Xaml.Media.Stretch, BackgroundImageStretchMode);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Microsoft.Terminal.Settings.Model.ConvergedAlignment, BackgroundImageAlignment); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Microsoft.Terminal.Settings.Model.ConvergedAlignment, BackgroundImageAlignment);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Microsoft.Terminal.Settings.Model.IntenseStyle, IntenseTextStyle); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Microsoft.Terminal.Settings.Model.IntenseStyle, IntenseTextStyle);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Boolean, AdjustIndistinguishableColors);
} }
[default_interface] runtimeclass Appearances : Windows.UI.Xaml.Controls.UserControl, Windows.UI.Xaml.Data.INotifyPropertyChanged [default_interface] runtimeclass Appearances : Windows.UI.Xaml.Controls.UserControl, Windows.UI.Xaml.Data.INotifyPropertyChanged

View file

@ -72,13 +72,15 @@
two font lists causes a crash within the ComboBox code. two font lists causes a crash within the ComboBox code.
As a workaround, introduce two ComboBox controls and only display one at a time. As a workaround, introduce two ComboBox controls and only display one at a time.
--> -->
<ComboBox ItemTemplate="{StaticResource FontFaceComboBoxItemTemplate}" <ComboBox x:Uid="Profile_FontFaceBox"
ItemTemplate="{StaticResource FontFaceComboBoxItemTemplate}"
ItemsSource="{x:Bind SourceProfile.MonospaceFontList, Mode=OneWay}" ItemsSource="{x:Bind SourceProfile.MonospaceFontList, Mode=OneWay}"
SelectedItem="{x:Bind CurrentFontFace, Mode=OneWay}" SelectedItem="{x:Bind CurrentFontFace, Mode=OneWay}"
SelectionChanged="FontFace_SelectionChanged" SelectionChanged="FontFace_SelectionChanged"
Style="{StaticResource ComboBoxSettingStyle}" Style="{StaticResource ComboBoxSettingStyle}"
Visibility="{x:Bind local:Converters.InvertedBooleanToVisibility(ShowAllFonts), Mode=OneWay}" /> Visibility="{x:Bind local:Converters.InvertedBooleanToVisibility(ShowAllFonts), Mode=OneWay}" />
<ComboBox ItemTemplate="{StaticResource FontFaceComboBoxItemTemplate}" <ComboBox x:Uid="Profile_FontFaceBox"
ItemTemplate="{StaticResource FontFaceComboBoxItemTemplate}"
ItemsSource="{x:Bind SourceProfile.CompleteFontList, Mode=OneWay}" ItemsSource="{x:Bind SourceProfile.CompleteFontList, Mode=OneWay}"
SelectedItem="{x:Bind CurrentFontFace, Mode=OneWay}" SelectedItem="{x:Bind CurrentFontFace, Mode=OneWay}"
SelectionChanged="FontFace_SelectionChanged" SelectionChanged="FontFace_SelectionChanged"
@ -97,7 +99,8 @@
HasSettingValue="{x:Bind Appearance.HasFontSize, Mode=OneWay}" HasSettingValue="{x:Bind Appearance.HasFontSize, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.FontSizeOverrideSource, Mode=OneWay}" SettingOverrideSource="{x:Bind Appearance.FontSizeOverrideSource, Mode=OneWay}"
Visibility="{x:Bind Appearance.IsDefault, Mode=OneWay}"> Visibility="{x:Bind Appearance.IsDefault, Mode=OneWay}">
<muxc:NumberBox AcceptsExpression="False" <muxc:NumberBox x:Uid="Profile_FontSizeBox"
AcceptsExpression="False"
LargeChange="10" LargeChange="10"
Maximum="128" Maximum="128"
Minimum="1" Minimum="1"
@ -150,6 +153,14 @@
SettingOverrideSource="{x:Bind Appearance.RetroTerminalEffectOverrideSource, Mode=OneWay}"> SettingOverrideSource="{x:Bind Appearance.RetroTerminalEffectOverrideSource, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind Appearance.RetroTerminalEffect, Mode=TwoWay}" /> <ToggleSwitch IsOn="{x:Bind Appearance.RetroTerminalEffect, Mode=TwoWay}" />
</local:SettingContainer> </local:SettingContainer>
<!-- Adjust Indistinguishable Colors -->
<local:SettingContainer x:Uid="Profile_AdjustIndistinguishableColors"
ClearSettingValue="{x:Bind Appearance.ClearAdjustIndistinguishableColors}"
HasSettingValue="{x:Bind Appearance.HasAdjustIndistinguishableColors, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.AdjustIndistinguishableColorsOverrideSource, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind Appearance.AdjustIndistinguishableColors, Mode=TwoWay}" />
</local:SettingContainer>
</StackPanel> </StackPanel>
<!-- Grouping: Cursor --> <!-- Grouping: Cursor -->
@ -163,7 +174,8 @@
ClearSettingValue="{x:Bind Appearance.ClearCursorShape}" ClearSettingValue="{x:Bind Appearance.ClearCursorShape}"
HasSettingValue="{x:Bind Appearance.HasCursorShape, Mode=OneWay}" HasSettingValue="{x:Bind Appearance.HasCursorShape, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.CursorShapeOverrideSource, Mode=OneWay}"> SettingOverrideSource="{x:Bind Appearance.CursorShapeOverrideSource, Mode=OneWay}">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}" <muxc:RadioButtons AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
ItemsSource="{x:Bind CursorShapeList, Mode=OneWay}" ItemsSource="{x:Bind CursorShapeList, Mode=OneWay}"
SelectedItem="{x:Bind CurrentCursorShape, Mode=TwoWay}" /> SelectedItem="{x:Bind CurrentCursorShape, Mode=TwoWay}" />
</local:SettingContainer> </local:SettingContainer>
@ -226,7 +238,8 @@
HasSettingValue="{x:Bind Appearance.HasBackgroundImageStretchMode, Mode=OneWay}" HasSettingValue="{x:Bind Appearance.HasBackgroundImageStretchMode, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.BackgroundImageStretchModeOverrideSource, Mode=OneWay}" SettingOverrideSource="{x:Bind Appearance.BackgroundImageStretchModeOverrideSource, Mode=OneWay}"
Visibility="{x:Bind Appearance.BackgroundImageSettingsVisible, Mode=OneWay}"> Visibility="{x:Bind Appearance.BackgroundImageSettingsVisible, Mode=OneWay}">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}" <muxc:RadioButtons AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
ItemsSource="{x:Bind BackgroundImageStretchModeList, Mode=OneWay}" ItemsSource="{x:Bind BackgroundImageStretchModeList, Mode=OneWay}"
SelectedItem="{x:Bind CurrentBackgroundImageStretchMode, Mode=TwoWay}" /> SelectedItem="{x:Bind CurrentBackgroundImageStretchMode, Mode=TwoWay}" />
</local:SettingContainer> </local:SettingContainer>
@ -441,7 +454,8 @@
ClearSettingValue="{x:Bind Appearance.ClearIntenseTextStyle}" ClearSettingValue="{x:Bind Appearance.ClearIntenseTextStyle}"
HasSettingValue="{x:Bind Appearance.HasIntenseTextStyle, Mode=OneWay}" HasSettingValue="{x:Bind Appearance.HasIntenseTextStyle, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.IntenseTextStyleOverrideSource, Mode=OneWay}"> SettingOverrideSource="{x:Bind Appearance.IntenseTextStyleOverrideSource, Mode=OneWay}">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}" <muxc:RadioButtons AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
ItemsSource="{x:Bind IntenseTextStyleList, Mode=OneWay}" ItemsSource="{x:Bind IntenseTextStyleList, Mode=OneWay}"
SelectedItem="{x:Bind CurrentIntenseTextStyle, Mode=TwoWay}" /> SelectedItem="{x:Bind CurrentIntenseTextStyle, Mode=TwoWay}" />
</local:SettingContainer> </local:SettingContainer>

View file

@ -42,7 +42,8 @@
<!-- Theme --> <!-- Theme -->
<local:SettingContainer x:Uid="Globals_Theme"> <local:SettingContainer x:Uid="Globals_Theme">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}" <muxc:RadioButtons AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
ItemsSource="{x:Bind ThemeList, Mode=OneWay}" ItemsSource="{x:Bind ThemeList, Mode=OneWay}"
SelectedItem="{x:Bind CurrentTheme, Mode=TwoWay}" /> SelectedItem="{x:Bind CurrentTheme, Mode=TwoWay}" />
</local:SettingContainer> </local:SettingContainer>
@ -74,7 +75,8 @@
<!-- Tab Width Mode --> <!-- Tab Width Mode -->
<local:SettingContainer x:Uid="Globals_TabWidthMode"> <local:SettingContainer x:Uid="Globals_TabWidthMode">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}" <muxc:RadioButtons AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
ItemsSource="{x:Bind TabWidthModeList, Mode=OneWay}" ItemsSource="{x:Bind TabWidthModeList, Mode=OneWay}"
SelectedItem="{x:Bind CurrentTabWidthMode, Mode=TwoWay}" /> SelectedItem="{x:Bind CurrentTabWidthMode, Mode=TwoWay}" />
</local:SettingContainer> </local:SettingContainer>

View file

@ -34,7 +34,8 @@
<!-- Copy Format --> <!-- Copy Format -->
<local:SettingContainer x:Uid="Globals_CopyFormat"> <local:SettingContainer x:Uid="Globals_CopyFormat">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}" <muxc:RadioButtons AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
ItemsSource="{x:Bind CopyFormatList, Mode=OneWay}" ItemsSource="{x:Bind CopyFormatList, Mode=OneWay}"
SelectedItem="{x:Bind CurrentCopyFormat, Mode=TwoWay}" /> SelectedItem="{x:Bind CurrentCopyFormat, Mode=TwoWay}" />
</local:SettingContainer> </local:SettingContainer>
@ -58,7 +59,8 @@
<!-- Tab Switcher Mode --> <!-- Tab Switcher Mode -->
<local:SettingContainer x:Uid="Globals_TabSwitcherMode"> <local:SettingContainer x:Uid="Globals_TabSwitcherMode">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}" <muxc:RadioButtons AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
ItemsSource="{x:Bind TabSwitcherModeList}" ItemsSource="{x:Bind TabSwitcherModeList}"
SelectedItem="{x:Bind CurrentTabSwitcherMode, Mode=TwoWay}" /> SelectedItem="{x:Bind CurrentTabSwitcherMode, Mode=TwoWay}" />
</local:SettingContainer> </local:SettingContainer>

View file

@ -136,22 +136,26 @@
<!-- First Window Behavior --> <!-- First Window Behavior -->
<local:SettingContainer x:Uid="Globals_FirstWindowPreference" <local:SettingContainer x:Uid="Globals_FirstWindowPreference"
Visibility="{x:Bind ShowFirstWindowPreference}"> Visibility="{x:Bind ShowFirstWindowPreference}">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}" <muxc:RadioButtons AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
ItemsSource="{x:Bind FirstWindowPreferenceList}" ItemsSource="{x:Bind FirstWindowPreferenceList}"
SelectedItem="{x:Bind CurrentFirstWindowPreference, Mode=TwoWay}" /> SelectedItem="{x:Bind CurrentFirstWindowPreference, Mode=TwoWay}" />
</local:SettingContainer> </local:SettingContainer>
<!-- Launch Mode --> <!-- Launch Mode -->
<local:SettingContainer x:Uid="Globals_LaunchMode"> <local:SettingContainer x:Name="Globals_LaunchMode"
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}" x:Uid="Globals_LaunchMode">
<muxc:RadioButtons AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
ItemsSource="{x:Bind LaunchModeList}" ItemsSource="{x:Bind LaunchModeList}"
SelectedItem="{x:Bind CurrentLaunchMode, Mode=TwoWay}" /> SelectedItem="{x:Bind CurrentLaunchMode, Mode=TwoWay}" />
</local:SettingContainer> </local:SettingContainer>
<!-- Launch Mode --> <!-- Launch Mode -->
<local:SettingContainer x:Uid="Globals_WindowingBehavior"> <local:SettingContainer x:Uid="Globals_WindowingBehavior">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}" <muxc:RadioButtons AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
ItemsSource="{x:Bind WindowingBehaviorList}" ItemsSource="{x:Bind WindowingBehaviorList}"
SelectedItem="{x:Bind CurrentWindowingBehavior, Mode=TwoWay}" /> SelectedItem="{x:Bind CurrentWindowingBehavior, Mode=TwoWay}" />
</local:SettingContainer> </local:SettingContainer>
@ -166,12 +170,14 @@
<!-- Columns --> <!-- Columns -->
<local:SettingContainer x:Uid="Globals_InitialCols" <local:SettingContainer x:Uid="Globals_InitialCols"
Margin="0"> Margin="0">
<muxc:NumberBox Style="{StaticResource LaunchSizeNumberBoxStyle}" <muxc:NumberBox x:Uid="Globals_InitialColsBox"
Style="{StaticResource LaunchSizeNumberBoxStyle}"
Value="{x:Bind State.Settings.GlobalSettings.InitialCols, Mode=TwoWay}" /> Value="{x:Bind State.Settings.GlobalSettings.InitialCols, Mode=TwoWay}" />
</local:SettingContainer> </local:SettingContainer>
<!-- Rows --> <!-- Rows -->
<local:SettingContainer x:Uid="Globals_InitialRows"> <local:SettingContainer x:Uid="Globals_InitialRows">
<muxc:NumberBox Style="{StaticResource LaunchSizeNumberBoxStyle}" <muxc:NumberBox x:Uid="Globals_InitialRowsBox"
Style="{StaticResource LaunchSizeNumberBoxStyle}"
Value="{x:Bind State.Settings.GlobalSettings.InitialRows, Mode=TwoWay}" /> Value="{x:Bind State.Settings.GlobalSettings.InitialRows, Mode=TwoWay}" />
</local:SettingContainer> </local:SettingContainer>
</StackPanel> </StackPanel>

View file

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. // Copyright (c) Microsoft Corporation.
// Licensed under the MIT license. // Licensed under the MIT license.
#include "pch.h" #include "pch.h"
@ -8,6 +8,7 @@
#include "EnumEntry.h" #include "EnumEntry.h"
#include <LibraryResources.h> #include <LibraryResources.h>
#include "..\WinRTUtils\inc\Utils.h"
using namespace winrt::Windows::UI::Text; using namespace winrt::Windows::UI::Text;
using namespace winrt::Windows::UI::Xaml; using namespace winrt::Windows::UI::Xaml;
@ -46,6 +47,22 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// NOTE: this is similar to what is done with BackgroundImagePath above // NOTE: this is similar to what is done with BackgroundImagePath above
_NotifyChanges(L"UseParentProcessDirectory", L"UseCustomStartingDirectory"); _NotifyChanges(L"UseParentProcessDirectory", L"UseCustomStartingDirectory");
} }
else if (viewModelProperty == L"UseAcrylic")
{
// GH#11372: If we're on Windows 10, and someone turns off
// acrylic, we're going to disable opacity for them. Opacity
// doesn't work without acrylic on Windows 10.
//
// BODGY: CascadiaSettings's function IsDefaultTerminalAvailable
// is basically a "are we on Windows 11" check, because defterm
// only works on Win11. So we'll use that.
//
// Remove when we can remove the rest of GH#11285
if (!UseAcrylic() && !CascadiaSettings::IsDefaultTerminalAvailable())
{
Opacity(1.0);
}
}
}); });
// Do the same for the starting directory // Do the same for the starting directory

View file

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. // Copyright (c) Microsoft Corporation.
// Licensed under the MIT license. // Licensed under the MIT license.
#pragma once #pragma once
@ -22,7 +22,21 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void SetAcrylicOpacityPercentageValue(double value) void SetAcrylicOpacityPercentageValue(double value)
{ {
_profile.DefaultAppearance().Opacity(winrt::Microsoft::Terminal::Settings::Editor::Converters::PercentageValueToPercentage(value)); Opacity(winrt::Microsoft::Terminal::Settings::Editor::Converters::PercentageValueToPercentage(value));
// GH#11372: If we're on Windows 10, and someone wants opacity, then
// we'll turn acrylic on for them. Opacity doesn't work without
// acrylic on Windows 10.
//
// BODGY: CascadiaSettings's function IsDefaultTerminalAvailable
// is basically a "are we on Windows 11" check, because defterm
// only works on Win11. So we'll use that.
//
// Remove when we can remove the rest of GH#11285
if (value < 100.0 && winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings::IsDefaultTerminalAvailable())
{
UseAcrylic(true);
}
}; };
void SetPadding(double value) void SetPadding(double value)

View file

@ -81,7 +81,8 @@
SettingOverrideSource="{x:Bind State.Profile.CommandlineOverrideSource, Mode=OneWay}" SettingOverrideSource="{x:Bind State.Profile.CommandlineOverrideSource, Mode=OneWay}"
Visibility="{x:Bind local:Converters.InvertedBooleanToVisibility(State.Profile.IsBaseLayer), Mode=OneWay}"> Visibility="{x:Bind local:Converters.InvertedBooleanToVisibility(State.Profile.IsBaseLayer), Mode=OneWay}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBox IsSpellCheckEnabled="False" <TextBox x:Uid="Profile_CommandlineBox"
IsSpellCheckEnabled="False"
Style="{StaticResource TextBoxSettingStyle}" Style="{StaticResource TextBoxSettingStyle}"
Text="{x:Bind State.Profile.Commandline, Mode=TwoWay}" /> Text="{x:Bind State.Profile.Commandline, Mode=TwoWay}" />
<Button x:Uid="Profile_CommandlineBrowse" <Button x:Uid="Profile_CommandlineBrowse"
@ -99,7 +100,8 @@
SettingOverrideSource="{x:Bind State.Profile.StartingDirectoryOverrideSource, Mode=OneWay}"> SettingOverrideSource="{x:Bind State.Profile.StartingDirectoryOverrideSource, Mode=OneWay}">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBox IsEnabled="{x:Bind State.Profile.UseCustomStartingDirectory, Mode=OneWay}" <TextBox x:Uid="Profile_StartingDirectoryBox"
IsEnabled="{x:Bind State.Profile.UseCustomStartingDirectory, Mode=OneWay}"
IsSpellCheckEnabled="False" IsSpellCheckEnabled="False"
Style="{StaticResource TextBoxSettingStyle}" Style="{StaticResource TextBoxSettingStyle}"
Text="{x:Bind State.Profile.StartingDirectory, Mode=TwoWay}" /> Text="{x:Bind State.Profile.StartingDirectory, Mode=TwoWay}" />
@ -121,7 +123,8 @@
HasSettingValue="{x:Bind State.Profile.HasIcon, Mode=OneWay}" HasSettingValue="{x:Bind State.Profile.HasIcon, Mode=OneWay}"
SettingOverrideSource="{x:Bind State.Profile.IconOverrideSource, Mode=OneWay}"> SettingOverrideSource="{x:Bind State.Profile.IconOverrideSource, Mode=OneWay}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBox FontFamily="Segoe UI, Segoe MDL2 Assets" <TextBox x:Uid="Profile_IconBox"
FontFamily="Segoe UI, Segoe MDL2 Assets"
IsSpellCheckEnabled="False" IsSpellCheckEnabled="False"
Style="{StaticResource TextBoxSettingStyle}" Style="{StaticResource TextBoxSettingStyle}"
Text="{x:Bind State.Profile.Icon, Mode=TwoWay}" /> Text="{x:Bind State.Profile.Icon, Mode=TwoWay}" />
@ -246,6 +249,7 @@
<!-- Opacity --> <!-- Opacity -->
<local:SettingContainer x:Name="OpacityContainer" <local:SettingContainer x:Name="OpacityContainer"
x:Uid="Profile_Opacity" x:Uid="Profile_Opacity"
Margin="0"
ClearSettingValue="{x:Bind State.Profile.ClearOpacity}" ClearSettingValue="{x:Bind State.Profile.ClearOpacity}"
HasSettingValue="{x:Bind State.Profile.HasOpacity, Mode=OneWay}" HasSettingValue="{x:Bind State.Profile.HasOpacity, Mode=OneWay}"
SettingOverrideSource="{x:Bind State.Profile.OpacityOverrideSource, Mode=OneWay}"> SettingOverrideSource="{x:Bind State.Profile.OpacityOverrideSource, Mode=OneWay}">
@ -307,7 +311,8 @@
ClearSettingValue="{x:Bind State.Profile.ClearScrollState}" ClearSettingValue="{x:Bind State.Profile.ClearScrollState}"
HasSettingValue="{x:Bind State.Profile.HasScrollState, Mode=OneWay}" HasSettingValue="{x:Bind State.Profile.HasScrollState, Mode=OneWay}"
SettingOverrideSource="{x:Bind State.Profile.ScrollStateOverrideSource, Mode=OneWay}"> SettingOverrideSource="{x:Bind State.Profile.ScrollStateOverrideSource, Mode=OneWay}">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}" <muxc:RadioButtons AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
ItemsSource="{x:Bind ScrollStateList, Mode=OneWay}" ItemsSource="{x:Bind ScrollStateList, Mode=OneWay}"
SelectedItem="{x:Bind CurrentScrollState, Mode=TwoWay}" /> SelectedItem="{x:Bind CurrentScrollState, Mode=TwoWay}" />
</local:SettingContainer> </local:SettingContainer>
@ -412,7 +417,8 @@
ClearSettingValue="{x:Bind State.Profile.ClearAntialiasingMode}" ClearSettingValue="{x:Bind State.Profile.ClearAntialiasingMode}"
HasSettingValue="{x:Bind State.Profile.HasAntialiasingMode, Mode=OneWay}" HasSettingValue="{x:Bind State.Profile.HasAntialiasingMode, Mode=OneWay}"
SettingOverrideSource="{x:Bind State.Profile.AntialiasingModeOverrideSource, Mode=OneWay}"> SettingOverrideSource="{x:Bind State.Profile.AntialiasingModeOverrideSource, Mode=OneWay}">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}" <muxc:RadioButtons AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
ItemsSource="{x:Bind AntiAliasingModeList, Mode=OneWay}" ItemsSource="{x:Bind AntiAliasingModeList, Mode=OneWay}"
SelectedItem="{x:Bind CurrentAntiAliasingMode, Mode=TwoWay}" /> SelectedItem="{x:Bind CurrentAntiAliasingMode, Mode=TwoWay}" />
</local:SettingContainer> </local:SettingContainer>
@ -438,7 +444,8 @@
ClearSettingValue="{x:Bind State.Profile.ClearHistorySize}" ClearSettingValue="{x:Bind State.Profile.ClearHistorySize}"
HasSettingValue="{x:Bind State.Profile.HasHistorySize, Mode=OneWay}" HasSettingValue="{x:Bind State.Profile.HasHistorySize, Mode=OneWay}"
SettingOverrideSource="{x:Bind State.Profile.HistorySizeOverrideSource, Mode=OneWay}"> SettingOverrideSource="{x:Bind State.Profile.HistorySizeOverrideSource, Mode=OneWay}">
<muxc:NumberBox LargeChange="100" <muxc:NumberBox x:Uid="Profile_HistorySizeBox"
LargeChange="100"
Minimum="0" Minimum="0"
SmallChange="10" SmallChange="10"
Style="{StaticResource NumberBoxSettingStyle}" Style="{StaticResource NumberBoxSettingStyle}"
@ -450,7 +457,8 @@
ClearSettingValue="{x:Bind State.Profile.ClearCloseOnExit}" ClearSettingValue="{x:Bind State.Profile.ClearCloseOnExit}"
HasSettingValue="{x:Bind State.Profile.HasCloseOnExit, Mode=OneWay}" HasSettingValue="{x:Bind State.Profile.HasCloseOnExit, Mode=OneWay}"
SettingOverrideSource="{x:Bind State.Profile.CloseOnExitOverrideSource, Mode=OneWay}"> SettingOverrideSource="{x:Bind State.Profile.CloseOnExitOverrideSource, Mode=OneWay}">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}" <muxc:RadioButtons AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
ItemsSource="{x:Bind CloseOnExitModeList, Mode=OneWay}" ItemsSource="{x:Bind CloseOnExitModeList, Mode=OneWay}"
SelectedItem="{x:Bind CurrentCloseOnExitMode, Mode=TwoWay}" /> SelectedItem="{x:Bind CurrentCloseOnExitMode, Mode=TwoWay}" />
</local:SettingContainer> </local:SettingContainer>

View file

@ -291,6 +291,14 @@
<value>The number of rows displayed in the window upon first load. Measured in characters.</value> <value>The number of rows displayed in the window upon first load. Measured in characters.</value>
<comment>A description for what the "rows" setting does. Presented near "Globals_InitialRows.Header".</comment> <comment>A description for what the "rows" setting does. Presented near "Globals_InitialRows.Header".</comment>
</data> </data>
<data name="Globals_InitialColsBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Initial Columns</value>
<comment>Name for a control to choose the number of columns in the terminal's text grid.</comment>
</data>
<data name="Globals_InitialRowsBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Initial Rows</value>
<comment>Name for a control to choose the number of rows in the terminal's text grid.</comment>
</data>
<data name="Globals_FirstWindowPreference.Header" xml:space="preserve"> <data name="Globals_FirstWindowPreference.Header" xml:space="preserve">
<value>When Terminal starts</value> <value>When Terminal starts</value>
<comment>Header for a control to select how the terminal should load its first window.</comment> <comment>Header for a control to select how the terminal should load its first window.</comment>
@ -304,8 +312,8 @@
<comment>An option to choose from for the "First window preference" setting. Open the default profile.</comment> <comment>An option to choose from for the "First window preference" setting. Open the default profile.</comment>
</data> </data>
<data name="Globals_FirstWindowPreferencePersistedWindowLayout.Content" xml:space="preserve"> <data name="Globals_FirstWindowPreferencePersistedWindowLayout.Content" xml:space="preserve">
<value>Open tabs from a previous session</value> <value>Open windows from a previous session</value>
<comment>An option to choose from for the "First window preference" setting. Reopen the layout from the last session.</comment> <comment>An option to choose from for the "First window preference" setting. Reopen the layouts from the last session.</comment>
</data> </data>
<data name="Globals_LaunchMode.Header" xml:space="preserve"> <data name="Globals_LaunchMode.Header" xml:space="preserve">
<value>Launch mode</value> <value>Launch mode</value>
@ -496,7 +504,7 @@
<comment>A description for what the "acrylic opacity" setting does. Presented near "Profile_AcrylicOpacity.Header".</comment> <comment>A description for what the "acrylic opacity" setting does. Presented near "Profile_AcrylicOpacity.Header".</comment>
</data> </data>
<data name="Profile_Opacity.Header" xml:space="preserve"> <data name="Profile_Opacity.Header" xml:space="preserve">
<value>Background Opacity</value> <value>Background opacity</value>
<comment>Header for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque.</comment> <comment>Header for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque.</comment>
</data> </data>
<data name="Profile_Opacity.HelpText" xml:space="preserve"> <data name="Profile_Opacity.HelpText" xml:space="preserve">
@ -655,6 +663,10 @@
<value>Command line</value> <value>Command line</value>
<comment>Header for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched.</comment> <comment>Header for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched.</comment>
</data> </data>
<data name="Profile_CommandlineBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Command line</value>
<comment>Name for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched.</comment>
</data>
<data name="Profile_Commandline.HelpText" xml:space="preserve"> <data name="Profile_Commandline.HelpText" xml:space="preserve">
<value>Executable used in the profile.</value> <value>Executable used in the profile.</value>
<comment>A description for what the "command line" setting does. Presented near "Profile_Commandline".</comment> <comment>A description for what the "command line" setting does. Presented near "Profile_Commandline".</comment>
@ -703,6 +715,10 @@
<value>Font face</value> <value>Font face</value>
<comment>Header for a control to select the font for text in the app.</comment> <comment>Header for a control to select the font for text in the app.</comment>
</data> </data>
<data name="Profile_FontFaceBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Font face</value>
<comment>Name for a control to select the font for text in the app.</comment>
</data>
<data name="Profile_FontFace.HelpText" xml:space="preserve"> <data name="Profile_FontFace.HelpText" xml:space="preserve">
<value>Name of the font face used in the profile.</value> <value>Name of the font face used in the profile.</value>
<comment>A description for what the "font face" setting does. Presented near "Profile_FontFace".</comment> <comment>A description for what the "font face" setting does. Presented near "Profile_FontFace".</comment>
@ -711,6 +727,10 @@
<value>Font size</value> <value>Font size</value>
<comment>Header for a control to determine the size of the text in the app.</comment> <comment>Header for a control to determine the size of the text in the app.</comment>
</data> </data>
<data name="Profile_FontSizeBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Font size</value>
<comment>Name for a control to determine the size of the text in the app.</comment>
</data>
<data name="Profile_FontSize.HelpText" xml:space="preserve"> <data name="Profile_FontSize.HelpText" xml:space="preserve">
<value>Size of the font in points.</value> <value>Size of the font in points.</value>
<comment>A description for what the "font size" setting does. Presented near "Profile_FontSize".</comment> <comment>A description for what the "font size" setting does. Presented near "Profile_FontSize".</comment>
@ -739,6 +759,10 @@
<value>History size</value> <value>History size</value>
<comment>Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session.</comment> <comment>Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session.</comment>
</data> </data>
<data name="Profile_HistorySizeBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>History size</value>
<comment>Name for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session.</comment>
</data>
<data name="Profile_HistorySize.HelpText" xml:space="preserve"> <data name="Profile_HistorySize.HelpText" xml:space="preserve">
<value>The number of lines above the ones displayed in the window you can scroll back to.</value> <value>The number of lines above the ones displayed in the window you can scroll back to.</value>
<comment>A description for what the "history size" setting does. Presented near "Profile_HistorySize".</comment> <comment>A description for what the "history size" setting does. Presented near "Profile_HistorySize".</comment>
@ -747,6 +771,10 @@
<value>Icon</value> <value>Icon</value>
<comment>Header for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one.</comment> <comment>Header for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one.</comment>
</data> </data>
<data name="Profile_IconBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Icon</value>
<comment>Name for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one.</comment>
</data>
<data name="Profile_Icon.HelpText" xml:space="preserve"> <data name="Profile_Icon.HelpText" xml:space="preserve">
<value>Emoji or image file location of the icon used in the profile.</value> <value>Emoji or image file location of the icon used in the profile.</value>
<comment>A description for what the "icon" setting does. Presented near "Profile_Icon".</comment> <comment>A description for what the "icon" setting does. Presented near "Profile_Icon".</comment>
@ -771,6 +799,14 @@
<value>When enabled, enables retro terminal effects such as glowing text and scan lines.</value> <value>When enabled, enables retro terminal effects such as glowing text and scan lines.</value>
<comment>A description for what the "retro terminal effects" setting does. Presented near "Profile_RetroTerminalEffect".</comment> <comment>A description for what the "retro terminal effects" setting does. Presented near "Profile_RetroTerminalEffect".</comment>
</data> </data>
<data name="Profile_AdjustIndistinguishableColors.Header" xml:space="preserve">
<value>Automatically adjust lightness of indistinguishable text</value>
<comment>Header for a control to toggle if we should adjust the foreground color's lightness to make it more visible when necessary, based on the background color.</comment>
</data>
<data name="Profile_AdjustIndistinguishableColors.HelpText" xml:space="preserve">
<value>When enabled, enables automatic adjustment of indistinguishable colors, which will, only when necessary, adjust the foreground color's lightness to make it more visible (based on the background color).</value>
<comment>A description for what the "adjust indistinguishable colors" setting does. Presented near "Profile_AdjustIndistinguishableColors".</comment>
</data>
<data name="Profile_ScrollbarVisibility.Header" xml:space="preserve"> <data name="Profile_ScrollbarVisibility.Header" xml:space="preserve">
<value>Scrollbar visibility</value> <value>Scrollbar visibility</value>
<comment>Header for a control to select the visibility of the scrollbar in a session.</comment> <comment>Header for a control to select the visibility of the scrollbar in a session.</comment>
@ -791,6 +827,10 @@
<value>Starting directory</value> <value>Starting directory</value>
<comment>Header for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths.</comment> <comment>Header for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths.</comment>
</data> </data>
<data name="Profile_StartingDirectoryBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Starting directory</value>
<comment>Name for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths.</comment>
</data>
<data name="Profile_StartingDirectory.HelpText" xml:space="preserve"> <data name="Profile_StartingDirectory.HelpText" xml:space="preserve">
<value>The directory the profile starts in when it is loaded.</value> <value>The directory the profile starts in when it is loaded.</value>
<comment>A description for what the "starting directory" setting does. Presented near "Profile_StartingDirectory".</comment> <comment>A description for what the "starting directory" setting does. Presented near "Profile_StartingDirectory".</comment>
@ -1227,7 +1267,7 @@
<comment>Header for a control to how text is formatted</comment> <comment>Header for a control to how text is formatted</comment>
</data> </data>
<data name="Appearance_IntenseTextStyle.Header" xml:space="preserve"> <data name="Appearance_IntenseTextStyle.Header" xml:space="preserve">
<value>Intense Text Style</value> <value>Intense text style</value>
<comment>Header for a control to select how "intense" text is formatted (bold, bright, both or none)</comment> <comment>Header for a control to select how "intense" text is formatted (bold, bright, both or none)</comment>
</data> </data>
<data name="Appearance_IntenseTextStyleNone.Content" xml:space="preserve"> <data name="Appearance_IntenseTextStyleNone.Content" xml:space="preserve">

View file

@ -15,30 +15,6 @@ using namespace winrt::Windows::UI::Xaml;
UTILS_DEFINE_LIBRARY_RESOURCE_SCOPE(L"Microsoft.Terminal.Settings.Editor/Resources"); UTILS_DEFINE_LIBRARY_RESOURCE_SCOPE(L"Microsoft.Terminal.Settings.Editor/Resources");
// Function Description:
// - Helper that opens a file picker pre-seeded with image file types.
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenImagePicker(HWND parentHwnd)
{
static constexpr COMDLG_FILTERSPEC supportedImageFileTypes[] = {
{ L"All Supported Bitmap Types (*.jpg, *.jpeg, *.png, *.bmp, *.gif, *.tiff, *.ico)", L"*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.tiff;*.ico" },
{ L"All Files (*.*)", L"*.*" }
};
static constexpr winrt::guid clientGuidImagePicker{ 0x55675F54, 0x74A1, 0x4552, { 0xA3, 0x9D, 0x94, 0xAE, 0x85, 0xD8, 0xF2, 0x7A } };
return OpenFilePicker(parentHwnd, [](auto&& dialog) {
THROW_IF_FAILED(dialog->SetClientGuid(clientGuidImagePicker));
try
{
auto pictureFolderShellItem{ winrt::capture<IShellItem>(&SHGetKnownFolderItem, FOLDERID_PicturesLibrary, KF_FLAG_DEFAULT, nullptr) };
dialog->SetDefaultFolder(pictureFolderShellItem.get());
}
CATCH_LOG(); // non-fatal
THROW_IF_FAILED(dialog->SetFileTypes(ARRAYSIZE(supportedImageFileTypes), supportedImageFileTypes));
THROW_IF_FAILED(dialog->SetFileTypeIndex(1)); // the array is 1-indexed
THROW_IF_FAILED(dialog->SetDefaultExtension(L"jpg;jpeg;png;bmp;gif;tiff;ico"));
});
}
namespace winrt::Microsoft::Terminal::Settings namespace winrt::Microsoft::Terminal::Settings
{ {
hstring GetSelectedItemTag(winrt::Windows::Foundation::IInspectable const& comboBoxAsInspectable) hstring GetSelectedItemTag(winrt::Windows::Foundation::IInspectable const& comboBoxAsInspectable)

View file

@ -97,42 +97,6 @@ public: \
private: \ private: \
static winrt::Windows::UI::Xaml::DependencyProperty _##name##Property; static winrt::Windows::UI::Xaml::DependencyProperty _##name##Property;
// Function Description:
// - This function presents a File Open "common dialog" and returns its selected file asynchronously.
// Parameters:
// - customize: A lambda that receives an IFileDialog* to customize.
// Return value:
// (async) path to the selected item.
template<typename TLambda>
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenFilePicker(HWND parentHwnd, TLambda&& customize)
{
auto fileDialog{ winrt::create_instance<IFileDialog>(CLSID_FileOpenDialog) };
DWORD flags{};
THROW_IF_FAILED(fileDialog->GetOptions(&flags));
THROW_IF_FAILED(fileDialog->SetOptions(flags | FOS_FORCEFILESYSTEM | FOS_NOCHANGEDIR | FOS_DONTADDTORECENT)); // filesystem objects only; no recent places
customize(fileDialog.get());
auto hr{ fileDialog->Show(parentHwnd) };
if (!SUCCEEDED(hr))
{
if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))
{
co_return winrt::hstring{};
}
THROW_HR(hr);
}
winrt::com_ptr<IShellItem> result;
THROW_IF_FAILED(fileDialog->GetResult(result.put()));
wil::unique_cotaskmem_string filePath;
THROW_IF_FAILED(result->GetDisplayName(SIGDN_FILESYSPATH, &filePath));
co_return winrt::hstring{ filePath.get() };
}
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenImagePicker(HWND parentHwnd);
namespace winrt::Microsoft::Terminal::Settings namespace winrt::Microsoft::Terminal::Settings
{ {
winrt::hstring GetSelectedItemTag(winrt::Windows::Foundation::IInspectable const& comboBoxAsInspectable); winrt::hstring GetSelectedItemTag(winrt::Windows::Foundation::IInspectable const& comboBoxAsInspectable);

View file

@ -38,6 +38,7 @@
#include "MultipleActionsArgs.g.cpp" #include "MultipleActionsArgs.g.cpp"
#include <LibraryResources.h> #include <LibraryResources.h>
#include <WtExeUtils.h>
using namespace winrt::Microsoft::Terminal::Control; using namespace winrt::Microsoft::Terminal::Control;
@ -121,15 +122,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
if (!StartingDirectory().empty()) if (!StartingDirectory().empty())
{ {
// If the directory ends in a '\', we need to add another one on so that the enclosing quote added ss << fmt::format(L"--startingDirectory {} ", QuoteAndEscapeCommandlineArg(StartingDirectory()));
// afterwards isn't escaped
const auto trailingBackslashEscape = StartingDirectory().back() == L'\\' ? L"\\" : L"";
ss << fmt::format(L"--startingDirectory \"{}{}\" ", StartingDirectory(), trailingBackslashEscape);
} }
if (!TabTitle().empty()) if (!TabTitle().empty())
{ {
ss << fmt::format(L"--title \"{}\" ", TabTitle()); ss << fmt::format(L"--title {} ", QuoteAndEscapeCommandlineArg(TabTitle()));
} }
if (TabColor()) if (TabColor())
@ -152,7 +150,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
if (!ColorScheme().empty()) if (!ColorScheme().empty())
{ {
ss << fmt::format(L"--colorScheme \"{}\" ", ColorScheme()); ss << fmt::format(L"--colorScheme {} ", QuoteAndEscapeCommandlineArg(ColorScheme()));
} }
if (!Commandline().empty()) if (!Commandline().empty())
@ -300,6 +298,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return RS_(L"MoveFocusPreviousInOrder"); return RS_(L"MoveFocusPreviousInOrder");
case FocusDirection::First: case FocusDirection::First:
return RS_(L"MoveFocusFirstPane"); return RS_(L"MoveFocusFirstPane");
case FocusDirection::Parent:
return RS_(L"MoveFocusParentPane");
case FocusDirection::Child:
return RS_(L"MoveFocusChildPane");
} }
return winrt::hstring{ return winrt::hstring{

View file

@ -864,6 +864,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
struct SetTabColorArgs : public SetTabColorArgsT<SetTabColorArgs> struct SetTabColorArgs : public SetTabColorArgsT<SetTabColorArgs>
{ {
SetTabColorArgs() = default; SetTabColorArgs() = default;
SetTabColorArgs(Windows::UI::Color tabColor) :
_TabColor{ tabColor } {}
ACTION_ARG(Windows::Foundation::IReference<Windows::UI::Color>, TabColor, nullptr); ACTION_ARG(Windows::Foundation::IReference<Windows::UI::Color>, TabColor, nullptr);
static constexpr std::string_view ColorKey{ "color" }; static constexpr std::string_view ColorKey{ "color" };
@ -1582,6 +1584,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
struct RenameWindowArgs : public RenameWindowArgsT<RenameWindowArgs> struct RenameWindowArgs : public RenameWindowArgsT<RenameWindowArgs>
{ {
RenameWindowArgs() = default; RenameWindowArgs() = default;
RenameWindowArgs(winrt::hstring name) :
_Name{ name } {};
ACTION_ARG(winrt::hstring, Name); ACTION_ARG(winrt::hstring, Name);
static constexpr std::string_view NameKey{ "name" }; static constexpr std::string_view NameKey{ "name" };
@ -1679,7 +1683,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
JsonUtils::SetValueForKey(json, NameKey, args->_Name); JsonUtils::SetValueForKey(json, NameKey, args->_Name);
JsonUtils::SetValueForKey(json, DesktopKey, args->_Desktop); JsonUtils::SetValueForKey(json, DesktopKey, args->_Desktop);
JsonUtils::SetValueForKey(json, MonitorKey, args->_Monitor); JsonUtils::SetValueForKey(json, MonitorKey, args->_Monitor);
JsonUtils::GetValueForKey(json, DropdownDurationKey, args->_DropdownDuration); JsonUtils::SetValueForKey(json, DropdownDurationKey, args->_DropdownDuration);
JsonUtils::SetValueForKey(json, ToggleVisibilityKey, args->_ToggleVisibility); JsonUtils::SetValueForKey(json, ToggleVisibilityKey, args->_ToggleVisibility);
return json; return json;
} }
@ -1869,9 +1873,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
BASIC_FACTORY(NewTabArgs); BASIC_FACTORY(NewTabArgs);
BASIC_FACTORY(MoveFocusArgs); BASIC_FACTORY(MoveFocusArgs);
BASIC_FACTORY(MovePaneArgs); BASIC_FACTORY(MovePaneArgs);
BASIC_FACTORY(SetTabColorArgs);
BASIC_FACTORY(SwapPaneArgs); BASIC_FACTORY(SwapPaneArgs);
BASIC_FACTORY(SplitPaneArgs); BASIC_FACTORY(SplitPaneArgs);
BASIC_FACTORY(SetColorSchemeArgs); BASIC_FACTORY(SetColorSchemeArgs);
BASIC_FACTORY(RenameWindowArgs);
BASIC_FACTORY(ExecuteCommandlineArgs); BASIC_FACTORY(ExecuteCommandlineArgs);
BASIC_FACTORY(CloseOtherTabsArgs); BASIC_FACTORY(CloseOtherTabsArgs);
BASIC_FACTORY(CloseTabsAfterArgs); BASIC_FACTORY(CloseTabsAfterArgs);

View file

@ -38,7 +38,9 @@ namespace Microsoft.Terminal.Settings.Model
Previous, Previous,
PreviousInOrder, PreviousInOrder,
NextInOrder, NextInOrder,
First First,
Parent,
Child
}; };
enum SplitDirection enum SplitDirection
@ -213,6 +215,7 @@ namespace Microsoft.Terminal.Settings.Model
[default_interface] runtimeclass SetTabColorArgs : IActionArgs [default_interface] runtimeclass SetTabColorArgs : IActionArgs
{ {
SetTabColorArgs(Windows.UI.Color tabColor);
Windows.Foundation.IReference<Windows.UI.Color> TabColor { get; }; Windows.Foundation.IReference<Windows.UI.Color> TabColor { get; };
}; };
@ -294,6 +297,7 @@ namespace Microsoft.Terminal.Settings.Model
[default_interface] runtimeclass RenameWindowArgs : IActionArgs [default_interface] runtimeclass RenameWindowArgs : IActionArgs
{ {
RenameWindowArgs(String name);
String Name { get; }; String Name { get; };
}; };

View file

@ -94,15 +94,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static void RegisterShortcutAction(ShortcutAction shortcutAction, std::unordered_map<hstring, Model::ActionAndArgs>& list, std::unordered_set<InternalActionID>& visited) static void RegisterShortcutAction(ShortcutAction shortcutAction, std::unordered_map<hstring, Model::ActionAndArgs>& list, std::unordered_set<InternalActionID>& visited)
{ {
const auto actionAndArgs{ make_self<ActionAndArgs>(shortcutAction) }; const auto actionAndArgs{ make_self<ActionAndArgs>(shortcutAction) };
if (actionAndArgs->Action() != ShortcutAction::Invalid) /*We have a valid action.*/
/*Check if the action was already added.*/
if (visited.find(Hash(*actionAndArgs)) == visited.end())
{ {
/*We have a valid action.*/ /*This is an action that wasn't added!*/
/*Check if the action was already added.*/ /*Let's add it if it has a name.*/
if (visited.find(Hash(*actionAndArgs)) == visited.end()) if (const auto name{ actionAndArgs->GenerateName() }; !name.empty())
{ {
/*This is an action that wasn't added!*/
/*Let's add it.*/
const auto name{ actionAndArgs->GenerateName() };
list.insert({ name, *actionAndArgs }); list.insert({ name, *actionAndArgs });
} }
} }

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